Skip to content

Commit 1e99d28

Browse files
committed
perf(sysmon): improve speed
Don't compute trails until we need them. No logging. Tweak the environment variables.
1 parent c9908d7 commit 1e99d28

File tree

1 file changed

+46
-39
lines changed

1 file changed

+46
-39
lines changed

coverage/sysmon.py

+46-39
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,11 @@
4545

4646
# pylint: disable=unused-argument
4747

48-
# $set_env.py: COVERAGE_LOG_SYSMON - Log sys.monitoring activity
49-
LOG = bool(int(os.getenv("COVERAGE_LOG_SYSMON", 0)))
48+
# $set_env.py: COVERAGE_SYSMON_LOG - Log sys.monitoring activity
49+
LOG = bool(int(os.getenv("COVERAGE_SYSMON_LOG", 0)))
50+
51+
# $set_env.py: COVERAGE_SYSMON_STATS - Collect sys.monitoring stats
52+
COLLECT_STATS = bool(int(os.getenv("COVERAGE_SYSMON_STATS", 0)))
5053

5154
# This module will be imported in all versions of Python, but only used in 3.12+
5255
# It will be type-checked for 3.12, but not for earlier versions.
@@ -236,17 +239,17 @@ def populate_branch_trails(code: CodeType, code_info: CodeInfo) -> None:
236239
arc from the original instruction's line to the new source line.
237240
238241
"""
239-
log(f"populate_branch_trails: {code}")
242+
# log(f"populate_branch_trails: {code}")
240243
iwalker = InstructionWalker(code)
241244
for inst in iwalker.walk(follow_jumps=False):
242-
log(f"considering {inst=}")
245+
# log(f"considering {inst=}")
243246
if not inst.jump_target:
244247
# We only care about instructions with jump targets.
245-
log("no jump_target")
248+
# log("no jump_target")
246249
continue
247250
if inst.opcode in ALWAYS_JUMPS:
248251
# We don't care about unconditional jumps.
249-
log("always jumps")
252+
# log("always jumps")
250253
continue
251254

252255
from_line = inst.line_number
@@ -265,23 +268,23 @@ def walk_one_branch(
265268
to_line = inst2.line_number
266269
break
267270
elif inst2.jump_target and (inst2.opcode not in ALWAYS_JUMPS):
268-
log(
269-
f"stop: {inst2.jump_target=}, "
270-
+ f"{inst2.opcode=} ({dis.opname[inst2.opcode]}), "
271-
+ f"{ALWAYS_JUMPS=}"
272-
)
271+
# log(
272+
# f"stop: {inst2.jump_target=}, "
273+
# + f"{inst2.opcode=} ({dis.opname[inst2.opcode]}), "
274+
# + f"{ALWAYS_JUMPS=}"
275+
# )
273276
break
274277
elif inst2.opcode in RETURNS:
275278
to_line = -code.co_firstlineno
276279
break
277280
if to_line is not None:
278-
log(
279-
f"possible branch from @{start_at}: "
280-
+ f"{inst_offsets}, {(from_line, to_line)} {code}"
281-
)
281+
# log(
282+
# f"possible branch from @{start_at}: "
283+
# + f"{inst_offsets}, {(from_line, to_line)} {code}"
284+
# )
282285
return inst_offsets, (from_line, to_line)
283286
else:
284-
log(f"no possible branch from @{start_at}: {inst_offsets}")
287+
# log(f"no possible branch from @{start_at}: {inst_offsets}")
285288
return [], None
286289

287290
# Calculate two trails: one from the next instruction, and one from the
@@ -302,6 +305,7 @@ def walk_one_branch(
302305
code_info.branch_trails[offset] = []
303306
code_info.branch_trails[offset].append(trail)
304307

308+
305309
@dataclass
306310
class CodeInfo:
307311
"""The information we want about each code object."""
@@ -363,9 +367,11 @@ def __init__(self, tool_id: int) -> None:
363367
self.sysmon_on = False
364368
self.lock = threading.Lock()
365369

366-
self.stats = {
367-
"starts": 0,
368-
}
370+
self.stats: dict[str, int] | None = None
371+
if COLLECT_STATS:
372+
self.stats = {
373+
"starts": 0,
374+
}
369375

370376
self.stopped = False
371377
self._activity = False
@@ -431,7 +437,7 @@ def reset_activity(self) -> None:
431437

432438
def get_stats(self) -> dict[str, int] | None:
433439
"""Return a dictionary of statistics, or None."""
434-
return None
440+
return self.stats
435441

436442
@panopticon("code", "@")
437443
def sysmon_py_start( # pylint: disable=useless-return
@@ -440,7 +446,8 @@ def sysmon_py_start( # pylint: disable=useless-return
440446
"""Handle sys.monitoring.events.PY_START events."""
441447
# Entering a new frame. Decide if we should trace in this file.
442448
self._activity = True
443-
self.stats["starts"] += 1
449+
if self.stats is not None:
450+
self.stats["starts"] += 1
444451

445452
code_info = self.code_infos.get(id(code))
446453
tracing_code: bool | None = None
@@ -483,9 +490,6 @@ def sysmon_py_start( # pylint: disable=useless-return
483490
branch_trails={},
484491
)
485492
self.code_infos[id(code)] = code_info
486-
if self.trace_arcs:
487-
populate_branch_trails(code, code_info)
488-
log(f"branch_trails for {code}:\n {code_info.branch_trails}")
489493
self.code_objects.append(code)
490494

491495
if tracing_code:
@@ -519,7 +523,7 @@ def sysmon_py_return( # pylint: disable=useless-return
519523
if last_line is not None:
520524
arc = (last_line, -code.co_firstlineno)
521525
cast(set[TArc], code_info.file_data).add(arc)
522-
log(f"adding {arc=}")
526+
# log(f"adding {arc=}")
523527
return None
524528

525529
@panopticon("code", "line")
@@ -528,7 +532,7 @@ def sysmon_line_lines(self, code: CodeType, line_number: TLineNo) -> MonitorRetu
528532
code_info = self.code_infos.get(id(code))
529533
if code_info is not None and code_info.file_data is not None:
530534
cast(set[TLineNo], code_info.file_data).add(line_number)
531-
log(f"adding {line_number=}")
535+
# log(f"adding {line_number=}")
532536
return DISABLE
533537

534538
@panopticon("code", "line")
@@ -538,7 +542,7 @@ def sysmon_line_arcs(self, code: CodeType, line_number: TLineNo) -> MonitorRetur
538542
if code_info.file_data is not None:
539543
arc = (line_number, line_number)
540544
cast(set[TArc], code_info.file_data).add(arc)
541-
log(f"adding {arc=}")
545+
# log(f"adding {arc=}")
542546
return DISABLE
543547

544548
@panopticon("code", "@", "@")
@@ -547,28 +551,31 @@ def sysmon_branch_either(
547551
) -> MonitorReturn:
548552
"""Handle BRANCH_RIGHT and BRANCH_LEFT events."""
549553
code_info = self.code_infos[id(code)]
550-
added_arc = False
551554
if code_info.file_data is not None:
555+
if not code_info.branch_trails:
556+
populate_branch_trails(code, code_info)
557+
# log(f"branch_trails for {code}:\n {code_info.branch_trails}")
558+
added_arc = False
552559
dest_info = code_info.branch_trails.get(instruction_offset)
553-
log(f"{dest_info = }")
560+
# log(f"{dest_info = }")
554561
if dest_info is not None:
555562
for offsets, arc in dest_info:
556563
if arc is None:
557564
continue
558565
if destination_offset in offsets:
559566
cast(set[TArc], code_info.file_data).add(arc)
560-
log(f"adding {arc=}")
567+
# log(f"adding {arc=}")
561568
added_arc = True
562569
break
563570

564-
if not added_arc:
565-
# This could be an exception jumping from line to line.
566-
assert code_info.byte_to_line is not None
567-
l1 = code_info.byte_to_line[instruction_offset]
568-
l2 = code_info.byte_to_line[destination_offset]
569-
if l1 != l2:
570-
arc = (l1, l2)
571-
cast(set[TArc], code_info.file_data).add(arc)
572-
log(f"adding unforeseen {arc=}")
571+
if not added_arc:
572+
# This could be an exception jumping from line to line.
573+
assert code_info.byte_to_line is not None
574+
l1 = code_info.byte_to_line[instruction_offset]
575+
l2 = code_info.byte_to_line[destination_offset]
576+
if l1 != l2:
577+
arc = (l1, l2)
578+
cast(set[TArc], code_info.file_data).add(arc)
579+
# log(f"adding unforeseen {arc=}")
573580

574581
return DISABLE

0 commit comments

Comments
 (0)