-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sys.monitoring: local events still sent after free_tool_id #111963
Comments
Agreed, events should be unset and callbacks should be unregistered when |
For now I don't think whether the tool_id is used is checked at all in the instrumentation process. It was only checked on higher level functions like It's NOT against the documentation, but I also think it might be better to free all the hooks once the tool_id is freed. Actually, I'd propose another API However, I believe the reason this is the way it is now, is because instrumentation is lazy-loaded. We don't track all the instrumented code objects and de-instrument them immediately once an event is unset. Consider the following scenario: def f():
pass
sys.monitoring.use_tool_id(1, "tool1")
sys.set_local_events(1, f.__code__, LINE)
sys.monitoring.free_tool_id(1, "tool1")
sys.monitoring.use_tool_id(1, "tool2")
# What would happen here? Does the event fire? As the instrumentation won't even trigger before In order to achieve that, we may need to put a version number on all events, and on tool_id. That solution might not need a potentially growing piece of memory if the user just toggles tool_id for fun. Just saying - it's not a trivial issue which we can resolve by adding an if statement somewhere. |
Interesting. So it seems fairly straightforward to unregister callbacks, and clear global events, but not to clear local events. In your example, though, I'd definitely expect neither tool to see the line events on |
Actually, global events are lazily instrumented as well. So no that won't work either. Unregister callbacks are easy but that would be even worse if the events are not disabled. |
For the version, it would be one version per event (local + global) per code object, plus a version for the tool id. Basically we need to version everything. |
Hm, okay. This is definitely harder than it seems. |
Yeah I guess that's why Mark did not do it to begin with. None of the instrumentation commands take effects immediately so the instrumentation need to somehow depend on the current state only. We can disable the events if the tool_id is not currently used, that's easy, but that just hides the dirt under the carpet. Or we could say that it's the user's responsibility to clean everything up before freeing the id - it's a low level API. If we do want a clean mechanism, some level of restructure might be needed unless I'm missing something obvious. |
I've adapted by holding a WeakSet of the code objects I've set events on, and using it to clear the events when I'm shutting down. This seems to work. At least the documentation could be clearer on this. |
But further experimentation drew me into the question of equality of code objects. It looks like the code_richcompare function looks at the bytecodes themselves. Doesn't the specializing interpreter change the bytecodes? Apart from the WeakSet of code objects, I'm using a dict with code object keys, and seeing some mysterious behavior where code objects are added and then can't be found. Could this be a problem? |
Code object hashes shouldn’t change. Last I checked, when comparing the bytecode for hashing and equality purposes, we deoptimize each instruction so that the hashing and equality look the same as for a “fresh” code object. Ditto for instrumented instructions. It’s a bit finicky though, so I wouldn’t be totally surprised if there’s a case where we compute the wrong result in the presence of instrumentation or specialization. Do you have a reproducer? |
The code object hash/equality problem is its own issue: #111984 |
There's another problem with keeping a set of code objects: two different code objects can hash and compare equal, so only one of them will be kept. That would mean there would be code objects that didn't get unregistered. What's the right way to do this? |
Never mind, I'm using the id of the code objects as the keys. Two different code objects being equal means code objects as keys won't work for me. |
…ythonGH-112291) (cherry picked from commit 46500c4) Co-authored-by: Tian Gao <gaogaotiantian@hotmail.com>
Docs have been updated to clarify current behavior. Should I leave this open to continue exploring changes to that behavior? |
I think this issue can be closed. |
Hi @nedbat. We added a I'll close this issue now because it's really fixed. |
Bug report
Bug description:
I use set_local_events with a tool id. Later I free the tool id, but then still get those events. Shouldn't freeing the tool id stop all events?
When I run this, I see:
Shouldn't freeing the tool id stop sysmon_line from being called again?
The same behavior happens with 3.13.0a1.
cc @markshannon
CPython versions tested on:
3.12, 3.13
Operating systems tested on:
macOS
Linked PRs
sys.monitoring.free_tool_id
docs for clarification #112291The text was updated successfully, but these errors were encountered: