Add code to walk the list of thread states #13
Comments
Related: it would be great to have a way to generate one flamegraph svg file per active thread in a multithreaded python program. |
I gave a few more details about this in #41 . This is probably the next major feature I'll add to Pyflame (after fixing #34, which has a PR by me out). However, I'm going to spend most of November traveling. If someone wants to surprise me with a PR to fix this in the interim that would be great, otherwise I'll probably get some time in December. |
Did some initial work on this at batterseapower@36ba9e1 Right now it doesn't seem to be able to find interp_head, which is necessary if we want to do anything interesting (PyThreadState_Current is NULL if the GIL is not held). This should be fixable because e.g. nm -a knows about interp_head. |
Fixed that, but PyThreadState.frame seems to always be null in the cases I looked at. |
@batterseapower That makes sense. |
I've got some further work on jamespic@db3feaa. @batterseapower I think you were actually on the right lines. What you were doing should have worked. I thought I was getting null frames, until I realised that I was running against a debug build (since normal builds don't expose Meanwhile, I also managed to get it profiling non-GIL frames on non-debug builds (builds without My apologies for the scrappiness of the code - it's literally the first thing I've ever written in C++. |
I have a version that copies the code into a new page: 2ac20de . This still crashes the Python process, but it seems like it might crash less frequently? This version also leaks a page per pyflame invocation (it's just a POC). Hopefully I can take a look again tomorrow, if not you can try this approach if you like. |
Leaking a page isn't too terrible if we only do it once per invocation - and I don't think there's anywhere we can safely munmap it from.
Looking at your code, I think it should be thread safe (apologies for the earlier comment rambling about SIGSTOP - I hadn't grokked your code yet, and wasn't aware you could safely change `rip` in ptraced code, so thought we'd still have to inject a `JMP` somewhere). I'll try and find some time to investigate later today.
|
I've managed to reproduce locally. It's still not thread safe. The initial memory overwrite for I'll look at stopping threads whilst code is in an unsafe state. |
There are a couple of answers here on how to pause all threads: http://stackoverflow.com/questions/18577956/how-to-use-ptrace-to-get-a-consistent-view-of-multiple-threads Not leaking a page is pretty easy -- you just need to use the |
I added thread pausing in jamespic@e038c99, which seems to have solved the crashing issue for me. |
This is looking good to me, and seems stable on my system. You might want to rung If you submit another PR I'll do another look over the whole diff. Before merging:
|
Walk threads and handle non-GIL code - Implements #13
Thanks @jamespic for fixing this! |
This is how you do it:
tstate_head
which is the head of a linked list of thread statesnext
which is a pointer to the next thread statenext
Likewise:
interp
interp_head
In the single-threaded case there is one interpreter and one thread state.
In the generic case the way you enumerate the thread states is:
_PyThreadState_Current
interp
up to the interpretertstate_head
to the first thread state (which will always be the same as_PyThreadState_Current
for a single-threaded program, but could be different for a multi-threaded program)next
field untilNULL
is encounteredIt should be fine to ignore the multiple interpreter thing, no one really uses that feature nowadays.
The text was updated successfully, but these errors were encountered: