Skip to content
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

Consider a "brief" native mode only showing C frames below the last Python frame #142

Open
1 task done
godlygeek opened this issue Aug 23, 2023 · 3 comments
Open
1 task done
Assignees
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@godlygeek
Copy link
Contributor

godlygeek commented Aug 23, 2023

Is there an existing proposal for this?

  • I have searched the existing proposals

Is your feature request related to a problem?

We default to showing pure Python stacks, both for performance and for legibility to people who aren't systems programmers, but often these aren't very useful for figuring out exactly why a program had a hang or crash, since it's not always clear what might be happening inside of the current Python call. The solution to this is running with --native or --native-all mode, but these modes are both very verbose.

Describe the solution you'd like

@alicederyn has pitched an idea for me: it might be possible to eliminate some of the noise and performance implications of --native mode while still providing enough information to puzzle out deadlocks or blocking happening in the C code called from the user's Python code if have a mode that only shows the C frames after the last Python frame. That is, instead of our pure-Python stacks:

Traceback for thread 112 [] (most recent call last):
    (Python) File "/test.py", line 17, in <module>
        first_func()
    (Python) File "/test.py", line 6, in first_func
        second_func()
    (Python) File "/test.py", line 10, in second_func
        third_func()
    (Python) File "/test.py", line 14, in third_func
        time.sleep(1000)

And instead of the hybrid stacks that we show with --native:

Traceback for thread 112 [] (most recent call last):
    (C) File "???", line 0, in _start ()
    (C) File "???", line 0, in __libc_start_main ()
    (C) File "Modules/main.c", line 743, in Py_BytesMain (/usr/lib/libpython3.8.so.1.0)
    (C) File "Modules/main.c", line 689, in Py_RunMain (/usr/lib/libpython3.8.so.1.0)
    (C) File "Modules/main.c", line 610, in pymain_run_python (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "Modules/main.c", line 385, in pymain_run_file (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "Python/pythonrun.c", line 472, in PyRun_SimpleFileExFlags (/usr/lib/libpython3.8.so.1.0)
    (C) File "Python/pythonrun.c", line 439, in pyrun_simple_file (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "Python/pythonrun.c", line 1085, in pyrun_file (/usr/lib/libpython3.8.so.1.0)
    (C) File "Python/pythonrun.c", line 1188, in run_mod (/usr/lib/libpython3.8.so.1.0)
    (C) File "Python/pythonrun.c", line 1166, in run_eval_code_obj (/usr/lib/libpython3.8.so.1.0)
    (Python) File "/test.py", line 17, in <module>
        first_func()
    (C) File "Python/ceval.c", line 4963, in call_function (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "Objects/call.c", line 284, in function_code_fastcall (inlined) (/usr/lib/libpython3.8.so.1.0)
    (Python) File "/test.py", line 6, in first_func
        second_func()
    (C) File "Python/ceval.c", line 4963, in call_function (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "Objects/call.c", line 284, in function_code_fastcall (inlined) (/usr/lib/libpython3.8.so.1.0)
    (Python) File "/test.py", line 10, in second_func
        third_func()
    (C) File "Python/ceval.c", line 4963, in call_function (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "Objects/call.c", line 284, in function_code_fastcall (inlined) (/usr/lib/libpython3.8.so.1.0)
    (Python) File "/test.py", line 14, in third_func
        time.sleep(1000)
    (C) File "Python/ceval.c", line 4963, in call_function (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "Modules/timemodule.c", line 338, in time_sleep (/usr/lib/libpython3.8.so.1.0)
    (C) File "Modules/timemodule.c", line 1866, in pysleep (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "???", line 0, in __select ()

We'd show a stack that switches to showing C frames automatically after the last Python frame:

Traceback for thread 112 [] (most recent call last):
    (Python) File "/test.py", line 17, in <module>
        first_func()
    (Python) File "/test.py", line 6, in first_func
        second_func()
    (Python) File "/test.py", line 10, in second_func
        third_func()
    (Python) File "/test.py", line 14, in third_func
        time.sleep(1000)
    (C) File "Python/ceval.c", line 4963, in call_function (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "Modules/timemodule.c", line 338, in time_sleep (/usr/lib/libpython3.8.so.1.0)
    (C) File "Modules/timemodule.c", line 1866, in pysleep (inlined) (/usr/lib/libpython3.8.so.1.0)
    (C) File "???", line 0, in __select ()

This could be faster than running with --native, as we should be able to avoid unwinding and symbolizing the entire call stack and instead stop when we reach the first _PyEval_EvalFrameDefault call (though currently we do all the unwinding before any of the symbolizing, and that would need to change if we wanted to break out of unwinding early).

@pablogsal feels this would be a confusing default, but is willing to consider it as a --native=brief or --native-all=brief flag.

Alternatives you considered

No response

@godlygeek godlygeek added the enhancement New feature or request label Aug 23, 2023
@godlygeek godlygeek changed the title Consider showing C frames below the last Python frame by default Consider a "brief" native mode only showing C frames below the last Python Aug 23, 2023
@godlygeek godlygeek changed the title Consider a "brief" native mode only showing C frames below the last Python Consider a "brief" native mode only showing C frames below the last Python frame Aug 23, 2023
@alicederyn
Copy link
Contributor

alicederyn commented Aug 24, 2023

This may also be of interest: https://www.benfrederickson.com/profiling-native-python-extensions-with-py-spy/

Apologies if you've already seen this and implemented it or decided it's not a good fit 😅

@pablogsal
Copy link
Member

pablogsal commented Aug 24, 2023

This may also be of interest: benfrederickson.com/profiling-native-python-extensions-with-py-spy

Apologies if you've already seen this and implemented it or decided it's not a good fit 😅

Thanks for the link! I was already aware of this, and we used to do something similar in the first versions of pystack. On the other hand this information is a bit outdated (handling the native pthread id) because I changed this in CPython so we store the pthread id in the thread object so there is no need for dealing with registers (which is also very brittle). We have also a better way for older versions that rely on glibc internals but is actually much more resilient than that approach.

Also, in any case, all of that is already in pystack, what we are discussing in this issue if I understand correctly is just modifying the reporter under some option to just show the native stack under the Python stack.

@sarahmonod sarahmonod added the good first issue Good for newcomers label May 20, 2024
@Kretzer6019
Copy link

I would like to work in this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

5 participants