Skip to content

Commit d5884f4

Browse files
authored
Merge pull request #48202 from makortel/edmStorageTrace_map
Extend edmStorageTrace.py to print read offset ranges and map those to the TFile contents
2 parents f173c07 + be41ec0 commit d5884f4

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

Utilities/StorageFactory/scripts/edmStorageTrace.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,66 @@ def summary(logEntries):
294294
for h, q in quantities:
295295
print_summary(h, q)
296296

297+
####################
298+
# Read ranges
299+
####################
300+
def printReadRanges(logEntries):
301+
for entry in logEntries:
302+
if isinstance(entry, Entries.Read):
303+
print(f"# id {entry.id}")
304+
print(f"{entry.offset} {entry.offset+entry.requested}")
305+
elif isinstance(entry, Entries.Readv):
306+
print(f"# id {entry.id}")
307+
for element in entry.elements:
308+
print(f"{element.offset} {element.offset+element.requested}")
309+
310+
####################
311+
# Map read ranges
312+
####################
313+
MapChunk = namedtuple("MapChunk", ("begin", "end", "type", "content"))
314+
def searchMapChunk(chunks, offset, size):
315+
if len(chunks) == 0:
316+
return []
317+
318+
offset_end = offset+size
319+
import bisect
320+
i = bisect.bisect_left(chunks, MapChunk(offset, 0, "", ""))
321+
ret = []
322+
if i > 0 and offset < chunks[i-1].end:
323+
ret.append(i-1)
324+
while i < len(chunks) and chunks[i].begin < offset_end:
325+
ret.append(i)
326+
i += 1
327+
328+
return ret
329+
330+
def printMapReadRanges(logEntries, mapFileName):
331+
chunks = []
332+
with open(mapFileName) as f:
333+
import re
334+
# Extract the offset (At:...), size (N=...), type, and content
335+
# (with name and possibly title) of an element in
336+
# TFile::Map("extended") printout
337+
line_re = re.compile("At:(?P<offset>\d+)\s*N=(?P<size>\d+)\s*(?P<type>\w+).*(?P<content>name:.*$)")
338+
for line in f:
339+
m = line_re.search(line)
340+
if m:
341+
offset = int(m.group("offset"))
342+
chunks.append(MapChunk(offset, offset+int(m.group("size")), m.group("type"), m.group("content")))
343+
344+
for entry in logEntries:
345+
if isinstance(entry, Entries.Read):
346+
print(f"# id {entry.id}")
347+
for i in searchMapChunk(chunks, entry.offset, entry.requested):
348+
ch = chunks[i]
349+
print(f"{ch.begin} {ch.end} {ch.type} {ch.content}")
350+
elif isinstance(entry, Entries.Readv):
351+
print(f"# id {entry.id}")
352+
for element in entry.elements:
353+
for i in searchMapChunk(chunks, element.offset, element.requested):
354+
ch = chunks[i]
355+
print(f"{ch.begin} {ch.end} {ch.type} {ch.content}")
356+
297357
####################
298358
# Main function
299359
####################
@@ -307,6 +367,10 @@ def main(logEntries, args):
307367
if args.readOverlaps:
308368
analyzeReadOverlaps(logEntries, args)
309369
print()
370+
if args.readRanges:
371+
printReadRanges(logEntries)
372+
if args.mapReadRanges:
373+
printMapReadRanges(logEntries, args.mapReadRanges)
310374
pass
311375

312376
####################
@@ -427,6 +491,30 @@ def test_processReadOverlaps(self):
427491
# Value 2 here is debatable
428492
self.assertEqual(result.overlap_count, 2)
429493

494+
def test_searchMapChunk(self):
495+
self.assertEqual(searchMapChunk([], 0, 10), [])
496+
497+
chunks = []
498+
for i in range(0, 100):
499+
chunks.append(MapChunk(i*100, i*100+50, "", i))
500+
self.assertEqual(searchMapChunk(chunks, 0, 10), [0])
501+
self.assertEqual(searchMapChunk(chunks, 0, 50), [0])
502+
self.assertEqual(searchMapChunk(chunks, 0, 100), [0])
503+
self.assertEqual(searchMapChunk(chunks, 10, 50), [0])
504+
self.assertEqual(searchMapChunk(chunks, 10, 90), [0])
505+
self.assertEqual(searchMapChunk(chunks, 9900, 50), [99])
506+
self.assertEqual(searchMapChunk(chunks, 9900, 100), [99])
507+
self.assertEqual(searchMapChunk(chunks, 9900, 100), [99])
508+
509+
self.assertEqual(searchMapChunk(chunks, -10, 5), [])
510+
self.assertEqual(searchMapChunk(chunks, 50, 40), [])
511+
self.assertEqual(searchMapChunk(chunks, 50, 50), [])
512+
self.assertEqual(searchMapChunk(chunks, 9950, 10), [])
513+
514+
self.assertEqual(searchMapChunk(chunks, 0, 200), [0, 1])
515+
self.assertEqual(searchMapChunk(chunks, 49, 101-49), [0, 1])
516+
self.assertEqual(searchMapChunk(chunks, 149, 301-149), [1, 2, 3])
517+
430518
def test():
431519
import sys
432520
unittest.main(argv=sys.argv[:1])
@@ -453,12 +541,16 @@ def printHelp():
453541
parser.add_argument("--summary", action="store_true", help="Print high-level summary of storage operations")
454542
parser.add_argument("--readOrder", action="store_true", help="Analyze ordering of reads")
455543
parser.add_argument("--readOverlaps", action="store_true", help="Analyze overlaps of reads")
544+
parser.add_argument("--readRanges", action="store_true", help="Print offset ranges of each read element")
545+
parser.add_argument("--mapReadRanges", type=str, default=None, help="Like --readRanges, but uses the output of TFile::Map() to map the file regions to TFile content. The argument should be a file containing the output of 'edmFileUtil --map'.")
456546
parser.add_argument("--test", action="store_true", help="Run internal tests")
457547

458548
args = parser.parse_args()
459549
if args.test:
460550
test()
461551
else:
552+
if args.readRanges and args.mapReadRanges:
553+
parser.error("Only one of --readRanges and --mapReadRanges can be given")
462554
if args.filename is None:
463555
parser.error("filename argument is missing")
464556
with open(args.filename) as f:

0 commit comments

Comments
 (0)