Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32.4k
Description
Bug report
Bug description:
A bunch of the instrumentation state is per-code object, such as the active montiors. The modifications also typically happen lazily when a code object is executed after instrumentation is enabled/disabled.
cpython/Python/instrumentation.c
Lines 1812 to 1814 in0240ef4
if (monitors_are_empty(new_events)&&monitors_are_empty(removed_events)) { | |
gotodone; | |
} |
However, if you create a new thread, then it will be initialized without the instrumented bytecodes. Here's an example that breaks:
- Enable instrumentation and call some function. This will replace things like
CALL
withINSTRUMENTED_CALL
. - Disable instrumentation. Note that this doesn't immediately change
INSTRUMENTED_CALL
back toCALL
! - Start a new thread, enable instrumentation, and call that same function - uh oh!
In (3), the new thread gets a clean copy of the bytecode without instrumentation:
Lines 3333 to 3341 in0240ef4
staticvoid | |
copy_code(_Py_CODEUNIT*dst,PyCodeObject*co) | |
{ | |
intcode_len= (int)Py_SIZE(co); | |
for (inti=0;i<code_len;i+=_PyInstruction_GetLength(co,i)) { | |
dst[i]=_Py_GetBaseCodeUnit(co,i); | |
} | |
_PyCode_Quicken(dst,code_len,1); | |
} |
However, the code object still has instrumentation enabled, so themonitors_are_empty
check above returns with instrumenting the bytecode. Missing events!
Adapted from@pablogsal's repro:
importsysimportthreadingimportdisdeflooooooser(x):print("I am a looooooser")defLOSER():looooooser(42)TRACES= []deftracing_function(frame,event,arg):function_name=frame.f_code.co_nameTRACES.append((function_name,event,arg))deffunc1():sys.setprofile(tracing_function)LOSER()sys.setprofile(None)TRACES.clear()deffunc2():defthread_body():sys.setprofile(tracing_function)LOSER()sys.setprofile(None)dis.dis(looooooser,adaptive=True)# WHENbg_thread=threading.Thread(target=thread_body)bg_thread.start()bg_thread.join()fortraceinTRACES:print(trace)assert ('looooooser','call',None)inTRACESfunc1()func2()
cc@mpage
CPython versions tested on:
CPython main branch, 3.14, 3.15
Operating systems tested on:
No response