Skip to content

Commit

Permalink
Implement injection index finder for python 3.10
Browse files Browse the repository at this point in the history
  • Loading branch information
labbati committed Dec 14, 2024
1 parent b0cf0ca commit 964701a
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 16 deletions.
2 changes: 0 additions & 2 deletions ddtrace/internal/bytecode_injection/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,6 @@ def _generate_adjusted_location_data_3_10(
"""
See format here: https://github.com/python/cpython/blob/3.10/Objects/lnotab_notes.txt
"""
print("Offsets map:", offsets_map)
print("Extended arg offsets:", extended_arg_offsets)
old_data = code.co_linetable
old_data_size = len(old_data)
new_data = bytearray()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,53 @@ def injection_lines_cb(_: InjectionContext):


def _find_bytecode_indexes_3_10(code: CodeType) -> t.List[int]:
return []
DUP_TOP = dis.opmap["DUP_TOP"]
JUMP_IF_NOT_EXC_MATCH = dis.opmap["JUMP_IF_NOT_EXC_MATCH"]
POP_TOP = dis.opmap["POP_TOP"]
SETUP_FINALLY = dis.opmap["SETUP_FINALLY"]

injection_indexes = set()

lines_offsets = [o for o, _ in dis.findlinestarts(code)]

def inject_conditionally(offset: int):
if offset in lines_offsets:
injection_indexes.add(offset)

def first_offset_not_matching(start: int, *opcodes: int):
while code.co_code[start] in opcodes:
start += 2
return start

potential_marks = set()

co_code = code.co_code
waiting_for_except = False
for idx in range(0, len(code.co_code), 2):
current_opcode = co_code[idx]
current_arg = co_code[idx + 1]
if current_opcode == JUMP_IF_NOT_EXC_MATCH:
target = current_arg << 1
potential_marks.add(target)
continue

if idx in potential_marks:
if current_opcode == DUP_TOP:
waiting_for_except = True
elif current_opcode == POP_TOP:
inject_conditionally(first_offset_not_matching(idx, POP_TOP))
continue

if current_opcode == SETUP_FINALLY:
if waiting_for_except:
waiting_for_except = False
inject_conditionally(idx + 2)
else:
target = idx + (current_arg << 1) + 2
potential_marks.add(target)
continue

return sorted(list(injection_indexes))


def _find_bytecode_indexes_3_11(code: CodeType) -> t.List[int]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,48 +43,50 @@ def func():

@skipif_bytecode_injection_not_supported
def test_generic_finally():
value = 0
value = ''

def callback(*args):
nonlocal value
value |= CALLBACK
_, e, _ = sys.exc_info()
value += str(e)

def func():
nonlocal value
try:
value |= TRY
raise ValueError('value error')
value += '<try>'
raise ValueError('<error>')
except:
value |= EXCEPT_VALUEERROR
value += '<except>'
finally:
value |= FINALLY
value += '<finally>'

_inject_handled_exception_reporting(func, callback)
func()

assert value == TRY + CALLBACK + EXCEPT_VALUEERROR + FINALLY
assert value == '<try><error><except><finally>'


@skipif_bytecode_injection_not_supported
def test_matched_except():
value = 0
value = ''

def callback(*args):
nonlocal value
value |= CALLBACK
_, e, _ = sys.exc_info()
value += str(e)

def func():
nonlocal value
try:
value |= TRY
raise ValueError('value error')
value += '<try>'
raise ValueError('<error>')
except ValueError as _:
value |= EXCEPT_VALUEERROR
value += '<except>'

_inject_handled_exception_reporting(func, callback)
func()

assert value == TRY + CALLBACK + EXCEPT_VALUEERROR
assert value == '<try><error><except>'


@skipif_bytecode_injection_not_supported
Expand Down

0 comments on commit 964701a

Please sign in to comment.