Skip to content

Commit a85f22c

Browse files
github-actions[bot]P403n1x87tylfin
authored
fix: iteration block hook injection [backport 2.21] (#13801)
Backport d7b62d5 from #13772 to 2.21. We fix an issue with the instrumentation of the beginning of iteration blocks (e.g. for loops) whereby instrumenting the end might cause unexpected behaviour when the modified function is invoked. This is due to the bytecode VM not liking any bytecode interposed before the instruction that ends the iteration block (e.g. `END_FOR`). This also solves the problem of double-calls of the instrumented hook when hitting the beginning of such blocks. ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) --------- Co-authored-by: Gabriele N. Tornetta <[email protected]> Co-authored-by: Tyler Finethy <[email protected]>
1 parent 8ecaa16 commit a85f22c

File tree

3 files changed

+28
-0
lines changed

3 files changed

+28
-0
lines changed

ddtrace/internal/injection.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing import Tuple # noqa:F401
88

99
from bytecode import Bytecode
10+
from bytecode import Instr
1011

1112
from ddtrace.internal.assembly import Assembly
1213

@@ -109,6 +110,12 @@ def _inject_hook(code: Bytecode, hook: HookType, lineno: int, arg: Any) -> None:
109110
raise InvalidLine("Line %d does not exist or is either blank or a comment" % lineno)
110111

111112
for i in locs:
113+
instr = code[i]
114+
if isinstance(instr, Instr) and instr.name.startswith("END_"):
115+
# This is the end of a block, e.g. a for loop. We have already
116+
# instrumented the block on entry, so we skip instrumenting the
117+
# end as well.
118+
continue
112119
code[i:i] = INJECTION_ASSEMBLY.bind(dict(hook=hook, arg=arg), lineno=lineno)
113120

114121

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
fixes:
3+
- |
4+
dynamic instrumentation: fixed an issue with the instrumentation of the
5+
first line of an iteration block (e.g. for loops) that could have caused
6+
undefined behavior.

tests/internal/test_injection.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,18 @@ def test_finally():
253253
f()
254254

255255
hook.assert_called_once_with(arg)
256+
257+
258+
def test_for_block():
259+
def for_loop():
260+
a = []
261+
for i in range(10):
262+
a.append(i)
263+
return a
264+
265+
hook, arg = mock.Mock(), mock.Mock()
266+
267+
with injected_hook(for_loop, hook, arg, line=for_loop.__code__.co_firstlineno + 2):
268+
for_loop()
269+
270+
hook.assert_called_once_with(arg)

0 commit comments

Comments
 (0)