mercurial/revlogutils/debug.py
changeset 49660 bd3b6f363fb9
parent 49659 7c0a383849a8
child 49661 511106bcb16c
equal deleted inserted replaced
49659:7c0a383849a8 49660:bd3b6f363fb9
     4 # Copyright 2022 Octobus <contact@octobus.net>
     4 # Copyright 2022 Octobus <contact@octobus.net>
     5 #
     5 #
     6 # This software may be used and distributed according to the terms of the
     6 # This software may be used and distributed according to the terms of the
     7 # GNU General Public License version 2 or any later version.
     7 # GNU General Public License version 2 or any later version.
     8 
     8 
       
     9 import collections
       
    10 import string
       
    11 
     9 from .. import (
    12 from .. import (
    10     node as nodemod,
    13     node as nodemod,
       
    14     util,
    11 )
    15 )
    12 
    16 
    13 from . import (
    17 from . import (
    14     constants,
    18     constants,
    15 )
    19 )
   265                 compression,
   269                 compression,
   266                 len(heads),
   270                 len(heads),
   267                 clen,
   271                 clen,
   268             )
   272             )
   269         )
   273         )
       
   274 
       
   275 
       
   276 def debug_revlog(ui, revlog):
       
   277     """code for `hg debugrevlog`"""
       
   278     r = revlog
       
   279     format = r._format_version
       
   280     v = r._format_flags
       
   281     flags = []
       
   282     gdelta = False
       
   283     if v & constants.FLAG_INLINE_DATA:
       
   284         flags.append(b'inline')
       
   285     if v & constants.FLAG_GENERALDELTA:
       
   286         gdelta = True
       
   287         flags.append(b'generaldelta')
       
   288     if not flags:
       
   289         flags = [b'(none)']
       
   290 
       
   291     ### tracks merge vs single parent
       
   292     nummerges = 0
       
   293 
       
   294     ### tracks ways the "delta" are build
       
   295     # nodelta
       
   296     numempty = 0
       
   297     numemptytext = 0
       
   298     numemptydelta = 0
       
   299     # full file content
       
   300     numfull = 0
       
   301     # intermediate snapshot against a prior snapshot
       
   302     numsemi = 0
       
   303     # snapshot count per depth
       
   304     numsnapdepth = collections.defaultdict(lambda: 0)
       
   305     # delta against previous revision
       
   306     numprev = 0
       
   307     # delta against first or second parent (not prev)
       
   308     nump1 = 0
       
   309     nump2 = 0
       
   310     # delta against neither prev nor parents
       
   311     numother = 0
       
   312     # delta against prev that are also first or second parent
       
   313     # (details of `numprev`)
       
   314     nump1prev = 0
       
   315     nump2prev = 0
       
   316 
       
   317     # data about delta chain of each revs
       
   318     chainlengths = []
       
   319     chainbases = []
       
   320     chainspans = []
       
   321 
       
   322     # data about each revision
       
   323     datasize = [None, 0, 0]
       
   324     fullsize = [None, 0, 0]
       
   325     semisize = [None, 0, 0]
       
   326     # snapshot count per depth
       
   327     snapsizedepth = collections.defaultdict(lambda: [None, 0, 0])
       
   328     deltasize = [None, 0, 0]
       
   329     chunktypecounts = {}
       
   330     chunktypesizes = {}
       
   331 
       
   332     def addsize(size, l):
       
   333         if l[0] is None or size < l[0]:
       
   334             l[0] = size
       
   335         if size > l[1]:
       
   336             l[1] = size
       
   337         l[2] += size
       
   338 
       
   339     numrevs = len(r)
       
   340     for rev in range(numrevs):
       
   341         p1, p2 = r.parentrevs(rev)
       
   342         delta = r.deltaparent(rev)
       
   343         if format > 0:
       
   344             addsize(r.rawsize(rev), datasize)
       
   345         if p2 != nodemod.nullrev:
       
   346             nummerges += 1
       
   347         size = r.length(rev)
       
   348         if delta == nodemod.nullrev:
       
   349             chainlengths.append(0)
       
   350             chainbases.append(r.start(rev))
       
   351             chainspans.append(size)
       
   352             if size == 0:
       
   353                 numempty += 1
       
   354                 numemptytext += 1
       
   355             else:
       
   356                 numfull += 1
       
   357                 numsnapdepth[0] += 1
       
   358                 addsize(size, fullsize)
       
   359                 addsize(size, snapsizedepth[0])
       
   360         else:
       
   361             chainlengths.append(chainlengths[delta] + 1)
       
   362             baseaddr = chainbases[delta]
       
   363             revaddr = r.start(rev)
       
   364             chainbases.append(baseaddr)
       
   365             chainspans.append((revaddr - baseaddr) + size)
       
   366             if size == 0:
       
   367                 numempty += 1
       
   368                 numemptydelta += 1
       
   369             elif r.issnapshot(rev):
       
   370                 addsize(size, semisize)
       
   371                 numsemi += 1
       
   372                 depth = r.snapshotdepth(rev)
       
   373                 numsnapdepth[depth] += 1
       
   374                 addsize(size, snapsizedepth[depth])
       
   375             else:
       
   376                 addsize(size, deltasize)
       
   377                 if delta == rev - 1:
       
   378                     numprev += 1
       
   379                     if delta == p1:
       
   380                         nump1prev += 1
       
   381                     elif delta == p2:
       
   382                         nump2prev += 1
       
   383                 elif delta == p1:
       
   384                     nump1 += 1
       
   385                 elif delta == p2:
       
   386                     nump2 += 1
       
   387                 elif delta != nodemod.nullrev:
       
   388                     numother += 1
       
   389 
       
   390         # Obtain data on the raw chunks in the revlog.
       
   391         if util.safehasattr(r, '_getsegmentforrevs'):
       
   392             segment = r._getsegmentforrevs(rev, rev)[1]
       
   393         else:
       
   394             segment = r._revlog._getsegmentforrevs(rev, rev)[1]
       
   395         if segment:
       
   396             chunktype = bytes(segment[0:1])
       
   397         else:
       
   398             chunktype = b'empty'
       
   399 
       
   400         if chunktype not in chunktypecounts:
       
   401             chunktypecounts[chunktype] = 0
       
   402             chunktypesizes[chunktype] = 0
       
   403 
       
   404         chunktypecounts[chunktype] += 1
       
   405         chunktypesizes[chunktype] += size
       
   406 
       
   407     # Adjust size min value for empty cases
       
   408     for size in (datasize, fullsize, semisize, deltasize):
       
   409         if size[0] is None:
       
   410             size[0] = 0
       
   411 
       
   412     numdeltas = numrevs - numfull - numempty - numsemi
       
   413     numoprev = numprev - nump1prev - nump2prev
       
   414     totalrawsize = datasize[2]
       
   415     datasize[2] /= numrevs
       
   416     fulltotal = fullsize[2]
       
   417     if numfull == 0:
       
   418         fullsize[2] = 0
       
   419     else:
       
   420         fullsize[2] /= numfull
       
   421     semitotal = semisize[2]
       
   422     snaptotal = {}
       
   423     if numsemi > 0:
       
   424         semisize[2] /= numsemi
       
   425     for depth in snapsizedepth:
       
   426         snaptotal[depth] = snapsizedepth[depth][2]
       
   427         snapsizedepth[depth][2] /= numsnapdepth[depth]
       
   428 
       
   429     deltatotal = deltasize[2]
       
   430     if numdeltas > 0:
       
   431         deltasize[2] /= numdeltas
       
   432     totalsize = fulltotal + semitotal + deltatotal
       
   433     avgchainlen = sum(chainlengths) / numrevs
       
   434     maxchainlen = max(chainlengths)
       
   435     maxchainspan = max(chainspans)
       
   436     compratio = 1
       
   437     if totalsize:
       
   438         compratio = totalrawsize / totalsize
       
   439 
       
   440     basedfmtstr = b'%%%dd\n'
       
   441     basepcfmtstr = b'%%%dd %s(%%5.2f%%%%)\n'
       
   442 
       
   443     def dfmtstr(max):
       
   444         return basedfmtstr % len(str(max))
       
   445 
       
   446     def pcfmtstr(max, padding=0):
       
   447         return basepcfmtstr % (len(str(max)), b' ' * padding)
       
   448 
       
   449     def pcfmt(value, total):
       
   450         if total:
       
   451             return (value, 100 * float(value) / total)
       
   452         else:
       
   453             return value, 100.0
       
   454 
       
   455     ui.writenoi18n(b'format : %d\n' % format)
       
   456     ui.writenoi18n(b'flags  : %s\n' % b', '.join(flags))
       
   457 
       
   458     ui.write(b'\n')
       
   459     fmt = pcfmtstr(totalsize)
       
   460     fmt2 = dfmtstr(totalsize)
       
   461     ui.writenoi18n(b'revisions     : ' + fmt2 % numrevs)
       
   462     ui.writenoi18n(b'    merges    : ' + fmt % pcfmt(nummerges, numrevs))
       
   463     ui.writenoi18n(
       
   464         b'    normal    : ' + fmt % pcfmt(numrevs - nummerges, numrevs)
       
   465     )
       
   466     ui.writenoi18n(b'revisions     : ' + fmt2 % numrevs)
       
   467     ui.writenoi18n(b'    empty     : ' + fmt % pcfmt(numempty, numrevs))
       
   468     ui.writenoi18n(
       
   469         b'                   text  : '
       
   470         + fmt % pcfmt(numemptytext, numemptytext + numemptydelta)
       
   471     )
       
   472     ui.writenoi18n(
       
   473         b'                   delta : '
       
   474         + fmt % pcfmt(numemptydelta, numemptytext + numemptydelta)
       
   475     )
       
   476     ui.writenoi18n(
       
   477         b'    snapshot  : ' + fmt % pcfmt(numfull + numsemi, numrevs)
       
   478     )
       
   479     for depth in sorted(numsnapdepth):
       
   480         ui.write(
       
   481             (b'      lvl-%-3d :       ' % depth)
       
   482             + fmt % pcfmt(numsnapdepth[depth], numrevs)
       
   483         )
       
   484     ui.writenoi18n(b'    deltas    : ' + fmt % pcfmt(numdeltas, numrevs))
       
   485     ui.writenoi18n(b'revision size : ' + fmt2 % totalsize)
       
   486     ui.writenoi18n(
       
   487         b'    snapshot  : ' + fmt % pcfmt(fulltotal + semitotal, totalsize)
       
   488     )
       
   489     for depth in sorted(numsnapdepth):
       
   490         ui.write(
       
   491             (b'      lvl-%-3d :       ' % depth)
       
   492             + fmt % pcfmt(snaptotal[depth], totalsize)
       
   493         )
       
   494     ui.writenoi18n(b'    deltas    : ' + fmt % pcfmt(deltatotal, totalsize))
       
   495 
       
   496     letters = string.ascii_letters.encode('ascii')
       
   497 
       
   498     def fmtchunktype(chunktype):
       
   499         if chunktype == b'empty':
       
   500             return b'    %s     : ' % chunktype
       
   501         elif chunktype in letters:
       
   502             return b'    0x%s (%s)  : ' % (nodemod.hex(chunktype), chunktype)
       
   503         else:
       
   504             return b'    0x%s      : ' % nodemod.hex(chunktype)
       
   505 
       
   506     ui.write(b'\n')
       
   507     ui.writenoi18n(b'chunks        : ' + fmt2 % numrevs)
       
   508     for chunktype in sorted(chunktypecounts):
       
   509         ui.write(fmtchunktype(chunktype))
       
   510         ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
       
   511     ui.writenoi18n(b'chunks size   : ' + fmt2 % totalsize)
       
   512     for chunktype in sorted(chunktypecounts):
       
   513         ui.write(fmtchunktype(chunktype))
       
   514         ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
       
   515 
       
   516     ui.write(b'\n')
       
   517     fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
       
   518     ui.writenoi18n(b'avg chain length  : ' + fmt % avgchainlen)
       
   519     ui.writenoi18n(b'max chain length  : ' + fmt % maxchainlen)
       
   520     ui.writenoi18n(b'max chain reach   : ' + fmt % maxchainspan)
       
   521     ui.writenoi18n(b'compression ratio : ' + fmt % compratio)
       
   522 
       
   523     if format > 0:
       
   524         ui.write(b'\n')
       
   525         ui.writenoi18n(
       
   526             b'uncompressed data size (min/max/avg) : %d / %d / %d\n'
       
   527             % tuple(datasize)
       
   528         )
       
   529     ui.writenoi18n(
       
   530         b'full revision size (min/max/avg)     : %d / %d / %d\n'
       
   531         % tuple(fullsize)
       
   532     )
       
   533     ui.writenoi18n(
       
   534         b'inter-snapshot size (min/max/avg)    : %d / %d / %d\n'
       
   535         % tuple(semisize)
       
   536     )
       
   537     for depth in sorted(snapsizedepth):
       
   538         if depth == 0:
       
   539             continue
       
   540         ui.writenoi18n(
       
   541             b'    level-%-3d (min/max/avg)          : %d / %d / %d\n'
       
   542             % ((depth,) + tuple(snapsizedepth[depth]))
       
   543         )
       
   544     ui.writenoi18n(
       
   545         b'delta size (min/max/avg)             : %d / %d / %d\n'
       
   546         % tuple(deltasize)
       
   547     )
       
   548 
       
   549     if numdeltas > 0:
       
   550         ui.write(b'\n')
       
   551         fmt = pcfmtstr(numdeltas)
       
   552         fmt2 = pcfmtstr(numdeltas, 4)
       
   553         ui.writenoi18n(
       
   554             b'deltas against prev  : ' + fmt % pcfmt(numprev, numdeltas)
       
   555         )
       
   556         if numprev > 0:
       
   557             ui.writenoi18n(
       
   558                 b'    where prev = p1  : ' + fmt2 % pcfmt(nump1prev, numprev)
       
   559             )
       
   560             ui.writenoi18n(
       
   561                 b'    where prev = p2  : ' + fmt2 % pcfmt(nump2prev, numprev)
       
   562             )
       
   563             ui.writenoi18n(
       
   564                 b'    other            : ' + fmt2 % pcfmt(numoprev, numprev)
       
   565             )
       
   566         if gdelta:
       
   567             ui.writenoi18n(
       
   568                 b'deltas against p1    : ' + fmt % pcfmt(nump1, numdeltas)
       
   569             )
       
   570             ui.writenoi18n(
       
   571                 b'deltas against p2    : ' + fmt % pcfmt(nump2, numdeltas)
       
   572             )
       
   573             ui.writenoi18n(
       
   574                 b'deltas against other : ' + fmt % pcfmt(numother, numdeltas)
       
   575             )