##// END OF EJS Templates
debug-revlog-stats: make it use the new store entry API...
marmoute -
r51574:47b44d80 default
parent child Browse files
Show More
@@ -1,721 +1,710 b''
1 1 # revlogutils/debug.py - utility used for revlog debuging
2 2 #
3 3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 4 # Copyright 2022 Octobus <contact@octobus.net>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 import collections
10 10 import string
11 11
12 12 from .. import (
13 13 mdiff,
14 14 node as nodemod,
15 15 revlogutils,
16 16 util,
17 17 )
18 18
19 19 from . import (
20 20 constants,
21 21 deltas as deltautil,
22 22 )
23 23
24 24 INDEX_ENTRY_DEBUG_COLUMN = []
25 25
26 26 NODE_SIZE = object()
27 27
28 28
29 29 class _column_base:
30 30 """constains the definition of a revlog column
31 31
32 32 name: the column header,
33 33 value_func: the function called to get a value,
34 34 size: the width of the column,
35 35 verbose_only: only include the column in verbose mode.
36 36 """
37 37
38 38 def __init__(self, name, value_func, size=None, verbose=False):
39 39 self.name = name
40 40 self.value_func = value_func
41 41 if size is not NODE_SIZE:
42 42 if size is None:
43 43 size = 8 # arbitrary default
44 44 size = max(len(name), size)
45 45 self._size = size
46 46 self.verbose_only = verbose
47 47
48 48 def get_size(self, node_size):
49 49 if self._size is NODE_SIZE:
50 50 return node_size
51 51 else:
52 52 return self._size
53 53
54 54
55 55 def debug_column(name, size=None, verbose=False):
56 56 """decorated function is registered as a column
57 57
58 58 name: the name of the column,
59 59 size: the expected size of the column.
60 60 """
61 61
62 62 def register(func):
63 63 entry = _column_base(
64 64 name=name,
65 65 value_func=func,
66 66 size=size,
67 67 verbose=verbose,
68 68 )
69 69 INDEX_ENTRY_DEBUG_COLUMN.append(entry)
70 70 return entry
71 71
72 72 return register
73 73
74 74
75 75 @debug_column(b"rev", size=6)
76 76 def _rev(index, rev, entry, hexfn):
77 77 return b"%d" % rev
78 78
79 79
80 80 @debug_column(b"rank", size=6, verbose=True)
81 81 def rank(index, rev, entry, hexfn):
82 82 return b"%d" % entry[constants.ENTRY_RANK]
83 83
84 84
85 85 @debug_column(b"linkrev", size=6)
86 86 def _linkrev(index, rev, entry, hexfn):
87 87 return b"%d" % entry[constants.ENTRY_LINK_REV]
88 88
89 89
90 90 @debug_column(b"nodeid", size=NODE_SIZE)
91 91 def _nodeid(index, rev, entry, hexfn):
92 92 return hexfn(entry[constants.ENTRY_NODE_ID])
93 93
94 94
95 95 @debug_column(b"p1-rev", size=6, verbose=True)
96 96 def _p1_rev(index, rev, entry, hexfn):
97 97 return b"%d" % entry[constants.ENTRY_PARENT_1]
98 98
99 99
100 100 @debug_column(b"p1-nodeid", size=NODE_SIZE)
101 101 def _p1_node(index, rev, entry, hexfn):
102 102 parent = entry[constants.ENTRY_PARENT_1]
103 103 p_entry = index[parent]
104 104 return hexfn(p_entry[constants.ENTRY_NODE_ID])
105 105
106 106
107 107 @debug_column(b"p2-rev", size=6, verbose=True)
108 108 def _p2_rev(index, rev, entry, hexfn):
109 109 return b"%d" % entry[constants.ENTRY_PARENT_2]
110 110
111 111
112 112 @debug_column(b"p2-nodeid", size=NODE_SIZE)
113 113 def _p2_node(index, rev, entry, hexfn):
114 114 parent = entry[constants.ENTRY_PARENT_2]
115 115 p_entry = index[parent]
116 116 return hexfn(p_entry[constants.ENTRY_NODE_ID])
117 117
118 118
119 119 @debug_column(b"full-size", size=20, verbose=True)
120 120 def full_size(index, rev, entry, hexfn):
121 121 return b"%d" % entry[constants.ENTRY_DATA_UNCOMPRESSED_LENGTH]
122 122
123 123
124 124 @debug_column(b"delta-base", size=6, verbose=True)
125 125 def delta_base(index, rev, entry, hexfn):
126 126 return b"%d" % entry[constants.ENTRY_DELTA_BASE]
127 127
128 128
129 129 @debug_column(b"flags", size=2, verbose=True)
130 130 def flags(index, rev, entry, hexfn):
131 131 field = entry[constants.ENTRY_DATA_OFFSET]
132 132 field &= 0xFFFF
133 133 return b"%d" % field
134 134
135 135
136 136 @debug_column(b"comp-mode", size=4, verbose=True)
137 137 def compression_mode(index, rev, entry, hexfn):
138 138 return b"%d" % entry[constants.ENTRY_DATA_COMPRESSION_MODE]
139 139
140 140
141 141 @debug_column(b"data-offset", size=20, verbose=True)
142 142 def data_offset(index, rev, entry, hexfn):
143 143 field = entry[constants.ENTRY_DATA_OFFSET]
144 144 field >>= 16
145 145 return b"%d" % field
146 146
147 147
148 148 @debug_column(b"chunk-size", size=10, verbose=True)
149 149 def data_chunk_size(index, rev, entry, hexfn):
150 150 return b"%d" % entry[constants.ENTRY_DATA_COMPRESSED_LENGTH]
151 151
152 152
153 153 @debug_column(b"sd-comp-mode", size=7, verbose=True)
154 154 def sidedata_compression_mode(index, rev, entry, hexfn):
155 155 compression = entry[constants.ENTRY_SIDEDATA_COMPRESSION_MODE]
156 156 if compression == constants.COMP_MODE_PLAIN:
157 157 return b"plain"
158 158 elif compression == constants.COMP_MODE_DEFAULT:
159 159 return b"default"
160 160 elif compression == constants.COMP_MODE_INLINE:
161 161 return b"inline"
162 162 else:
163 163 return b"%d" % compression
164 164
165 165
166 166 @debug_column(b"sidedata-offset", size=20, verbose=True)
167 167 def sidedata_offset(index, rev, entry, hexfn):
168 168 return b"%d" % entry[constants.ENTRY_SIDEDATA_OFFSET]
169 169
170 170
171 171 @debug_column(b"sd-chunk-size", size=10, verbose=True)
172 172 def sidedata_chunk_size(index, rev, entry, hexfn):
173 173 return b"%d" % entry[constants.ENTRY_SIDEDATA_COMPRESSED_LENGTH]
174 174
175 175
176 176 def debug_index(
177 177 ui,
178 178 repo,
179 179 formatter,
180 180 revlog,
181 181 full_node,
182 182 ):
183 183 """display index data for a revlog"""
184 184 if full_node:
185 185 hexfn = nodemod.hex
186 186 else:
187 187 hexfn = nodemod.short
188 188
189 189 idlen = 12
190 190 for i in revlog:
191 191 idlen = len(hexfn(revlog.node(i)))
192 192 break
193 193
194 194 fm = formatter
195 195
196 196 header_pieces = []
197 197 for column in INDEX_ENTRY_DEBUG_COLUMN:
198 198 if column.verbose_only and not ui.verbose:
199 199 continue
200 200 size = column.get_size(idlen)
201 201 name = column.name
202 202 header_pieces.append(name.rjust(size))
203 203
204 204 fm.plain(b' '.join(header_pieces) + b'\n')
205 205
206 206 index = revlog.index
207 207
208 208 for rev in revlog:
209 209 fm.startitem()
210 210 entry = index[rev]
211 211 first = True
212 212 for column in INDEX_ENTRY_DEBUG_COLUMN:
213 213 if column.verbose_only and not ui.verbose:
214 214 continue
215 215 if not first:
216 216 fm.plain(b' ')
217 217 first = False
218 218
219 219 size = column.get_size(idlen)
220 220 value = column.value_func(index, rev, entry, hexfn)
221 221 display = b"%%%ds" % size
222 222 fm.write(column.name, display, value)
223 223 fm.plain(b'\n')
224 224
225 225 fm.end()
226 226
227 227
228 228 def dump(ui, revlog):
229 229 """perform the work for `hg debugrevlog --dump"""
230 230 # XXX seems redundant with debug index ?
231 231 r = revlog
232 232 numrevs = len(r)
233 233 ui.write(
234 234 (
235 235 b"# rev p1rev p2rev start end deltastart base p1 p2"
236 236 b" rawsize totalsize compression heads chainlen\n"
237 237 )
238 238 )
239 239 ts = 0
240 240 heads = set()
241 241
242 242 for rev in range(numrevs):
243 243 dbase = r.deltaparent(rev)
244 244 if dbase == -1:
245 245 dbase = rev
246 246 cbase = r.chainbase(rev)
247 247 clen = r.chainlen(rev)
248 248 p1, p2 = r.parentrevs(rev)
249 249 rs = r.rawsize(rev)
250 250 ts = ts + rs
251 251 heads -= set(r.parentrevs(rev))
252 252 heads.add(rev)
253 253 try:
254 254 compression = ts / r.end(rev)
255 255 except ZeroDivisionError:
256 256 compression = 0
257 257 ui.write(
258 258 b"%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
259 259 b"%11d %5d %8d\n"
260 260 % (
261 261 rev,
262 262 p1,
263 263 p2,
264 264 r.start(rev),
265 265 r.end(rev),
266 266 r.start(dbase),
267 267 r.start(cbase),
268 268 r.start(p1),
269 269 r.start(p2),
270 270 rs,
271 271 ts,
272 272 compression,
273 273 len(heads),
274 274 clen,
275 275 )
276 276 )
277 277
278 278
279 279 def debug_revlog(ui, revlog):
280 280 """code for `hg debugrevlog`"""
281 281 r = revlog
282 282 format = r._format_version
283 283 v = r._format_flags
284 284 flags = []
285 285 gdelta = False
286 286 if v & constants.FLAG_INLINE_DATA:
287 287 flags.append(b'inline')
288 288 if v & constants.FLAG_GENERALDELTA:
289 289 gdelta = True
290 290 flags.append(b'generaldelta')
291 291 if not flags:
292 292 flags = [b'(none)']
293 293
294 294 ### the total size of stored content if incompressed.
295 295 full_text_total_size = 0
296 296 ### tracks merge vs single parent
297 297 nummerges = 0
298 298
299 299 ### tracks ways the "delta" are build
300 300 # nodelta
301 301 numempty = 0
302 302 numemptytext = 0
303 303 numemptydelta = 0
304 304 # full file content
305 305 numfull = 0
306 306 # intermediate snapshot against a prior snapshot
307 307 numsemi = 0
308 308 # snapshot count per depth
309 309 numsnapdepth = collections.defaultdict(lambda: 0)
310 310 # number of snapshots with a non-ancestor delta
311 311 numsnapdepth_nad = collections.defaultdict(lambda: 0)
312 312 # delta against previous revision
313 313 numprev = 0
314 314 # delta against prev, where prev is a non-ancestor
315 315 numprev_nad = 0
316 316 # delta against first or second parent (not prev)
317 317 nump1 = 0
318 318 nump2 = 0
319 319 # delta against neither prev nor parents
320 320 numother = 0
321 321 # delta against other that is a non-ancestor
322 322 numother_nad = 0
323 323 # delta against prev that are also first or second parent
324 324 # (details of `numprev`)
325 325 nump1prev = 0
326 326 nump2prev = 0
327 327
328 328 # data about delta chain of each revs
329 329 chainlengths = []
330 330 chainbases = []
331 331 chainspans = []
332 332
333 333 # data about each revision
334 334 datasize = [None, 0, 0]
335 335 fullsize = [None, 0, 0]
336 336 semisize = [None, 0, 0]
337 337 # snapshot count per depth
338 338 snapsizedepth = collections.defaultdict(lambda: [None, 0, 0])
339 339 deltasize = [None, 0, 0]
340 340 chunktypecounts = {}
341 341 chunktypesizes = {}
342 342
343 343 def addsize(size, l):
344 344 if l[0] is None or size < l[0]:
345 345 l[0] = size
346 346 if size > l[1]:
347 347 l[1] = size
348 348 l[2] += size
349 349
350 350 numrevs = len(r)
351 351 for rev in range(numrevs):
352 352 p1, p2 = r.parentrevs(rev)
353 353 delta = r.deltaparent(rev)
354 354 if format > 0:
355 355 s = r.rawsize(rev)
356 356 full_text_total_size += s
357 357 addsize(s, datasize)
358 358 if p2 != nodemod.nullrev:
359 359 nummerges += 1
360 360 size = r.length(rev)
361 361 if delta == nodemod.nullrev:
362 362 chainlengths.append(0)
363 363 chainbases.append(r.start(rev))
364 364 chainspans.append(size)
365 365 if size == 0:
366 366 numempty += 1
367 367 numemptytext += 1
368 368 else:
369 369 numfull += 1
370 370 numsnapdepth[0] += 1
371 371 addsize(size, fullsize)
372 372 addsize(size, snapsizedepth[0])
373 373 else:
374 374 nad = (
375 375 delta != p1 and delta != p2 and not r.isancestorrev(delta, rev)
376 376 )
377 377 chainlengths.append(chainlengths[delta] + 1)
378 378 baseaddr = chainbases[delta]
379 379 revaddr = r.start(rev)
380 380 chainbases.append(baseaddr)
381 381 chainspans.append((revaddr - baseaddr) + size)
382 382 if size == 0:
383 383 numempty += 1
384 384 numemptydelta += 1
385 385 elif r.issnapshot(rev):
386 386 addsize(size, semisize)
387 387 numsemi += 1
388 388 depth = r.snapshotdepth(rev)
389 389 numsnapdepth[depth] += 1
390 390 if nad:
391 391 numsnapdepth_nad[depth] += 1
392 392 addsize(size, snapsizedepth[depth])
393 393 else:
394 394 addsize(size, deltasize)
395 395 if delta == rev - 1:
396 396 numprev += 1
397 397 if delta == p1:
398 398 nump1prev += 1
399 399 elif delta == p2:
400 400 nump2prev += 1
401 401 elif nad:
402 402 numprev_nad += 1
403 403 elif delta == p1:
404 404 nump1 += 1
405 405 elif delta == p2:
406 406 nump2 += 1
407 407 elif delta != nodemod.nullrev:
408 408 numother += 1
409 409 numother_nad += 1
410 410
411 411 # Obtain data on the raw chunks in the revlog.
412 412 if util.safehasattr(r, '_getsegmentforrevs'):
413 413 segment = r._getsegmentforrevs(rev, rev)[1]
414 414 else:
415 415 segment = r._revlog._getsegmentforrevs(rev, rev)[1]
416 416 if segment:
417 417 chunktype = bytes(segment[0:1])
418 418 else:
419 419 chunktype = b'empty'
420 420
421 421 if chunktype not in chunktypecounts:
422 422 chunktypecounts[chunktype] = 0
423 423 chunktypesizes[chunktype] = 0
424 424
425 425 chunktypecounts[chunktype] += 1
426 426 chunktypesizes[chunktype] += size
427 427
428 428 # Adjust size min value for empty cases
429 429 for size in (datasize, fullsize, semisize, deltasize):
430 430 if size[0] is None:
431 431 size[0] = 0
432 432
433 433 numdeltas = numrevs - numfull - numempty - numsemi
434 434 numoprev = numprev - nump1prev - nump2prev - numprev_nad
435 435 num_other_ancestors = numother - numother_nad
436 436 totalrawsize = datasize[2]
437 437 datasize[2] /= numrevs
438 438 fulltotal = fullsize[2]
439 439 if numfull == 0:
440 440 fullsize[2] = 0
441 441 else:
442 442 fullsize[2] /= numfull
443 443 semitotal = semisize[2]
444 444 snaptotal = {}
445 445 if numsemi > 0:
446 446 semisize[2] /= numsemi
447 447 for depth in snapsizedepth:
448 448 snaptotal[depth] = snapsizedepth[depth][2]
449 449 snapsizedepth[depth][2] /= numsnapdepth[depth]
450 450
451 451 deltatotal = deltasize[2]
452 452 if numdeltas > 0:
453 453 deltasize[2] /= numdeltas
454 454 totalsize = fulltotal + semitotal + deltatotal
455 455 avgchainlen = sum(chainlengths) / numrevs
456 456 maxchainlen = max(chainlengths)
457 457 maxchainspan = max(chainspans)
458 458 compratio = 1
459 459 if totalsize:
460 460 compratio = totalrawsize / totalsize
461 461
462 462 basedfmtstr = b'%%%dd\n'
463 463 basepcfmtstr = b'%%%dd %s(%%5.2f%%%%)\n'
464 464
465 465 def dfmtstr(max):
466 466 return basedfmtstr % len(str(max))
467 467
468 468 def pcfmtstr(max, padding=0):
469 469 return basepcfmtstr % (len(str(max)), b' ' * padding)
470 470
471 471 def pcfmt(value, total):
472 472 if total:
473 473 return (value, 100 * float(value) / total)
474 474 else:
475 475 return value, 100.0
476 476
477 477 ui.writenoi18n(b'format : %d\n' % format)
478 478 ui.writenoi18n(b'flags : %s\n' % b', '.join(flags))
479 479
480 480 ui.write(b'\n')
481 481 fmt = pcfmtstr(totalsize)
482 482 fmt2 = dfmtstr(totalsize)
483 483 ui.writenoi18n(b'revisions : ' + fmt2 % numrevs)
484 484 ui.writenoi18n(b' merges : ' + fmt % pcfmt(nummerges, numrevs))
485 485 ui.writenoi18n(
486 486 b' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs)
487 487 )
488 488 ui.writenoi18n(b'revisions : ' + fmt2 % numrevs)
489 489 ui.writenoi18n(b' empty : ' + fmt % pcfmt(numempty, numrevs))
490 490 ui.writenoi18n(
491 491 b' text : '
492 492 + fmt % pcfmt(numemptytext, numemptytext + numemptydelta)
493 493 )
494 494 ui.writenoi18n(
495 495 b' delta : '
496 496 + fmt % pcfmt(numemptydelta, numemptytext + numemptydelta)
497 497 )
498 498 ui.writenoi18n(
499 499 b' snapshot : ' + fmt % pcfmt(numfull + numsemi, numrevs)
500 500 )
501 501 for depth in sorted(numsnapdepth):
502 502 base = b' lvl-%-3d : ' % depth
503 503 count = fmt % pcfmt(numsnapdepth[depth], numrevs)
504 504 pieces = [base, count]
505 505 if numsnapdepth_nad[depth]:
506 506 pieces[-1] = count = count[:-1] # drop the final '\n'
507 507 more = b' non-ancestor-bases: '
508 508 anc_count = fmt
509 509 anc_count %= pcfmt(numsnapdepth_nad[depth], numsnapdepth[depth])
510 510 pieces.append(more)
511 511 pieces.append(anc_count)
512 512 ui.write(b''.join(pieces))
513 513 ui.writenoi18n(b' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
514 514 ui.writenoi18n(b'revision size : ' + fmt2 % totalsize)
515 515 ui.writenoi18n(
516 516 b' snapshot : ' + fmt % pcfmt(fulltotal + semitotal, totalsize)
517 517 )
518 518 for depth in sorted(numsnapdepth):
519 519 ui.write(
520 520 (b' lvl-%-3d : ' % depth)
521 521 + fmt % pcfmt(snaptotal[depth], totalsize)
522 522 )
523 523 ui.writenoi18n(b' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
524 524
525 525 letters = string.ascii_letters.encode('ascii')
526 526
527 527 def fmtchunktype(chunktype):
528 528 if chunktype == b'empty':
529 529 return b' %s : ' % chunktype
530 530 elif chunktype in letters:
531 531 return b' 0x%s (%s) : ' % (nodemod.hex(chunktype), chunktype)
532 532 else:
533 533 return b' 0x%s : ' % nodemod.hex(chunktype)
534 534
535 535 ui.write(b'\n')
536 536 ui.writenoi18n(b'chunks : ' + fmt2 % numrevs)
537 537 for chunktype in sorted(chunktypecounts):
538 538 ui.write(fmtchunktype(chunktype))
539 539 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
540 540 ui.writenoi18n(b'chunks size : ' + fmt2 % totalsize)
541 541 for chunktype in sorted(chunktypecounts):
542 542 ui.write(fmtchunktype(chunktype))
543 543 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
544 544
545 545 ui.write(b'\n')
546 546 b_total = b"%d" % full_text_total_size
547 547 p_total = []
548 548 while len(b_total) > 3:
549 549 p_total.append(b_total[-3:])
550 550 b_total = b_total[:-3]
551 551 p_total.append(b_total)
552 552 p_total.reverse()
553 553 b_total = b' '.join(p_total)
554 554
555 555 ui.write(b'\n')
556 556 ui.writenoi18n(b'total-stored-content: %s bytes\n' % b_total)
557 557 ui.write(b'\n')
558 558 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
559 559 ui.writenoi18n(b'avg chain length : ' + fmt % avgchainlen)
560 560 ui.writenoi18n(b'max chain length : ' + fmt % maxchainlen)
561 561 ui.writenoi18n(b'max chain reach : ' + fmt % maxchainspan)
562 562 ui.writenoi18n(b'compression ratio : ' + fmt % compratio)
563 563
564 564 if format > 0:
565 565 ui.write(b'\n')
566 566 ui.writenoi18n(
567 567 b'uncompressed data size (min/max/avg) : %d / %d / %d\n'
568 568 % tuple(datasize)
569 569 )
570 570 ui.writenoi18n(
571 571 b'full revision size (min/max/avg) : %d / %d / %d\n'
572 572 % tuple(fullsize)
573 573 )
574 574 ui.writenoi18n(
575 575 b'inter-snapshot size (min/max/avg) : %d / %d / %d\n'
576 576 % tuple(semisize)
577 577 )
578 578 for depth in sorted(snapsizedepth):
579 579 if depth == 0:
580 580 continue
581 581 ui.writenoi18n(
582 582 b' level-%-3d (min/max/avg) : %d / %d / %d\n'
583 583 % ((depth,) + tuple(snapsizedepth[depth]))
584 584 )
585 585 ui.writenoi18n(
586 586 b'delta size (min/max/avg) : %d / %d / %d\n'
587 587 % tuple(deltasize)
588 588 )
589 589
590 590 if numdeltas > 0:
591 591 ui.write(b'\n')
592 592 fmt = pcfmtstr(numdeltas)
593 593 fmt2 = pcfmtstr(numdeltas, 4)
594 594 ui.writenoi18n(
595 595 b'deltas against prev : ' + fmt % pcfmt(numprev, numdeltas)
596 596 )
597 597 if numprev > 0:
598 598 ui.writenoi18n(
599 599 b' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev)
600 600 )
601 601 ui.writenoi18n(
602 602 b' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev)
603 603 )
604 604 ui.writenoi18n(
605 605 b' other-ancestor : ' + fmt2 % pcfmt(numoprev, numprev)
606 606 )
607 607 ui.writenoi18n(
608 608 b' unrelated : ' + fmt2 % pcfmt(numoprev, numprev)
609 609 )
610 610 if gdelta:
611 611 ui.writenoi18n(
612 612 b'deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas)
613 613 )
614 614 ui.writenoi18n(
615 615 b'deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas)
616 616 )
617 617 ui.writenoi18n(
618 618 b'deltas against ancs : '
619 619 + fmt % pcfmt(num_other_ancestors, numdeltas)
620 620 )
621 621 ui.writenoi18n(
622 622 b'deltas against other : '
623 623 + fmt % pcfmt(numother_nad, numdeltas)
624 624 )
625 625
626 626
627 627 def debug_delta_find(ui, revlog, rev, base_rev=nodemod.nullrev):
628 628 """display the search process for a delta"""
629 629 deltacomputer = deltautil.deltacomputer(
630 630 revlog,
631 631 write_debug=ui.write,
632 632 debug_search=not ui.quiet,
633 633 )
634 634
635 635 node = revlog.node(rev)
636 636 p1r, p2r = revlog.parentrevs(rev)
637 637 p1 = revlog.node(p1r)
638 638 p2 = revlog.node(p2r)
639 639 full_text = revlog.revision(rev)
640 640 btext = [full_text]
641 641 textlen = len(btext[0])
642 642 cachedelta = None
643 643 flags = revlog.flags(rev)
644 644
645 645 if base_rev != nodemod.nullrev:
646 646 base_text = revlog.revision(base_rev)
647 647 delta = mdiff.textdiff(base_text, full_text)
648 648
649 649 cachedelta = (base_rev, delta, constants.DELTA_BASE_REUSE_TRY)
650 650 btext = [None]
651 651
652 652 revinfo = revlogutils.revisioninfo(
653 653 node,
654 654 p1,
655 655 p2,
656 656 btext,
657 657 textlen,
658 658 cachedelta,
659 659 flags,
660 660 )
661 661
662 662 fh = revlog._datafp()
663 663 deltacomputer.finddeltainfo(revinfo, fh, target_rev=rev)
664 664
665 665
666 def _get_revlogs(repo, changelog: bool, manifest: bool, filelogs: bool):
667 """yield revlogs from this repository"""
668 if changelog:
669 yield repo.changelog
670
671 if manifest:
672 # XXX: Handle tree manifest
673 root_mf = repo.manifestlog.getstorage(b'')
674 assert not root_mf._treeondisk
675 yield root_mf._revlog
676
677 if filelogs:
678 files = set()
679 for rev in repo:
680 ctx = repo[rev]
681 files |= set(ctx.files())
682
683 for f in sorted(files):
684 yield repo.file(f)._revlog
685
686
687 666 def debug_revlog_stats(
688 667 repo, fm, changelog: bool, manifest: bool, filelogs: bool
689 668 ):
690 669 """Format revlog statistics for debugging purposes
691 670
692 671 fm: the output formatter.
693 672 """
694 673 fm.plain(b'rev-count data-size inl type target \n')
695 674
696 for rlog in _get_revlogs(repo, changelog, manifest, filelogs):
675 revlog_entries = [e for e in repo.store.walk() if e.is_revlog]
676 revlog_entries.sort(key=lambda e: (e.revlog_type, e.target_id))
677
678 for entry in revlog_entries:
679 if not changelog and entry.is_changelog:
680 continue
681 elif not manifest and entry.is_manifestlog:
682 continue
683 elif not filelogs and entry.is_filelog:
684 continue
685 rlog = entry.get_revlog_instance(repo).get_revlog()
697 686 fm.startitem()
698 687 nb_rev = len(rlog)
699 688 inline = rlog._inline
700 689 data_size = rlog._get_data_offset(nb_rev - 1)
701 690
702 691 target = rlog.target
703 692 revlog_type = b'unknown'
704 693 revlog_target = b''
705 694 if target[0] == constants.KIND_CHANGELOG:
706 695 revlog_type = b'changelog'
707 696 elif target[0] == constants.KIND_MANIFESTLOG:
708 697 revlog_type = b'manifest'
709 698 revlog_target = target[1]
710 699 elif target[0] == constants.KIND_FILELOG:
711 700 revlog_type = b'file'
712 701 revlog_target = target[1]
713 702
714 703 fm.write(b'revlog.rev-count', b'%9d', nb_rev)
715 704 fm.write(b'revlog.data-size', b'%12d', data_size)
716 705
717 706 fm.write(b'revlog.inline', b' %-3s', b'yes' if inline else b'no')
718 707 fm.write(b'revlog.type', b' %-9s', revlog_type)
719 708 fm.write(b'revlog.target', b' %s', revlog_target)
720 709
721 710 fm.plain(b'\n')
@@ -1,77 +1,75 b''
1 1 Force revlog max inline value to be smaller than default
2 2
3 3 $ mkdir $TESTTMP/ext
4 4 $ cat << EOF > $TESTTMP/ext/small_inline.py
5 5 > from mercurial import revlog
6 6 > revlog._maxinline = 8
7 7 > EOF
8 8
9 9 $ cat << EOF >> $HGRCPATH
10 10 > [extensions]
11 11 > small_inline=$TESTTMP/ext/small_inline.py
12 12 > EOF
13 13
14 14 $ hg init repo
15 15 $ cd repo
16 16
17 17 Try on an empty repository
18 18
19 19 $ hg debug-revlog-stats
20 20 rev-count data-size inl type target
21 0 0 yes changelog
22 0 0 yes manifest
23 21
24 22 $ mkdir folder
25 23 $ touch a b folder/c folder/d
26 24 $ hg commit -Aqm 0
27 25 $ echo "text" > a
28 26 $ hg rm b
29 27 $ echo "longer string" > folder/d
30 28 $ hg commit -Aqm 1
31 29
32 30 Differences in data size observed with pure is due to different compression
33 31 algorithms
34 32
35 33 $ hg debug-revlog-stats
36 34 rev-count data-size inl type target
37 35 2 138 no changelog (no-pure !)
38 36 2 137 no changelog (pure !)
39 37 2 177 no manifest (no-pure !)
40 38 2 168 no manifest (pure !)
41 39 2 6 yes file a
42 40 1 0 yes file b
43 41 1 0 yes file folder/c
44 42 2 15 no file folder/d
45 43
46 44 Test 'changelog' command argument
47 45
48 46 $ hg debug-revlog-stats -c
49 47 rev-count data-size inl type target
50 48 2 138 no changelog (no-pure !)
51 49 2 137 no changelog (pure !)
52 50
53 51 Test 'manifest' command argument
54 52
55 53 $ hg debug-revlog-stats -m
56 54 rev-count data-size inl type target
57 55 2 177 no manifest (no-pure !)
58 56 2 168 no manifest (pure !)
59 57
60 58 Test 'file' command argument
61 59
62 60 $ hg debug-revlog-stats -f
63 61 rev-count data-size inl type target
64 62 2 6 yes file a
65 63 1 0 yes file b
66 64 1 0 yes file folder/c
67 65 2 15 no file folder/d
68 66
69 67 Test multiple command arguments
70 68
71 69 $ hg debug-revlog-stats -cm
72 70 rev-count data-size inl type target
73 71 2 138 no changelog (no-pure !)
74 72 2 137 no changelog (pure !)
75 73 2 177 no manifest (no-pure !)
76 74 2 168 no manifest (pure !)
77 75
General Comments 0
You need to be logged in to leave comments. Login now