Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"src/pystack/_pystack.pyx",
"src/pystack/_pystack/corefile.cpp",
"src/pystack/_pystack/elf_common.cpp",
"src/pystack/_pystack/interpreter.cpp",
"src/pystack/_pystack/logging.cpp",
"src/pystack/_pystack/mem.cpp",
"src/pystack/_pystack/process.cpp",
Expand Down
2 changes: 0 additions & 2 deletions src/pystack/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from ._version import __version__
from .traceback_formatter import print_thread

__all__ = [
"__version__",
"print_thread",
]
10 changes: 7 additions & 3 deletions src/pystack/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
from pystack.process import is_gzip

from . import errors
from . import print_thread
from .colors import colored
from .engine import CoreFileAnalyzer
from .engine import NativeReportingMode
from .engine import StackMethod
from .engine import get_process_threads
from .engine import get_process_threads_for_core
from .traceback_formatter import TracebackPrinter

PERMISSION_ERROR_MSG = "Operation not permitted"
NO_SUCH_PROCESS_ERROR_MSG = "No such process"
Expand Down Expand Up @@ -285,14 +285,17 @@ def process_remote(parser: argparse.ArgumentParser, args: argparse.Namespace) ->
if not args.block and args.native_mode != NativeReportingMode.OFF:
parser.error("Native traces are only available in blocking mode")

printer = TracebackPrinter(
native_mode=args.native_mode, include_subinterpreters=True
)
for thread in get_process_threads(
args.pid,
stop_process=args.block,
native_mode=args.native_mode,
locals=args.locals,
method=StackMethod.ALL if args.exhaustive else StackMethod.AUTO,
):
print_thread(thread, args.native_mode)
printer.print_thread(thread)


def format_psinfo_information(psinfo: Dict[str, Any]) -> str:
Expand Down Expand Up @@ -414,6 +417,7 @@ def process_core(parser: argparse.ArgumentParser, args: argparse.Namespace) -> N
elf_id if elf_id else "<MISSING>",
)

printer = TracebackPrinter(args.native_mode, include_subinterpreters=True)
for thread in get_process_threads_for_core(
corefile,
executable,
Expand All @@ -422,7 +426,7 @@ def process_core(parser: argparse.ArgumentParser, args: argparse.Namespace) -> N
locals=args.locals,
method=StackMethod.ALL if args.exhaustive else StackMethod.AUTO,
):
print_thread(thread, args.native_mode)
printer.print_thread(thread)


if __name__ == "__main__": # pragma: no cover
Expand Down
18 changes: 14 additions & 4 deletions src/pystack/_pystack.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ from _pystack.elf_common cimport CoreFileAnalyzer as NativeCoreFileAnalyzer
from _pystack.elf_common cimport ProcessAnalyzer as NativeProcessAnalyzer
from _pystack.elf_common cimport SectionInfo
from _pystack.elf_common cimport getSectionInfo
from _pystack.interpreter cimport InterpreterUtils
from _pystack.logging cimport initializePythonLoggerInterface
from _pystack.mem cimport AbstractRemoteMemoryManager
from _pystack.mem cimport MemoryMapInformation as CppMemoryMapInformation
Expand Down Expand Up @@ -462,6 +463,7 @@ cdef object _construct_threads_from_interpreter_state(
bint add_native_traces,
bint resolve_locals,
):
interpreter_id = InterpreterUtils.getInterpreterId(manager, head)
LOGGER.info("Fetching Python threads")
threads = []

Expand All @@ -486,6 +488,7 @@ cdef object _construct_threads_from_interpreter_state(
current_thread.isGilHolder(),
current_thread.isGCCollecting(),
python_version,
interpreter_id,
name=get_thread_name(pid, current_thread.Tid()),
)
)
Expand Down Expand Up @@ -622,7 +625,7 @@ def _get_process_threads(
)

all_tids = list(manager.get().Tids())
if head:
while head:
add_native_traces = native_mode != NativeReportingMode.OFF
for thread in _construct_threads_from_interpreter_state(
manager,
Expand All @@ -635,6 +638,7 @@ def _get_process_threads(
if thread.tid in all_tids:
all_tids.remove(thread.tid)
yield thread
head = InterpreterUtils.getNextInterpreter(manager, head)

if native_mode == NativeReportingMode.ALL:
yield from _construct_os_threads(manager, pid, all_tids)
Expand Down Expand Up @@ -769,14 +773,20 @@ def _get_process_threads_for_core(

all_tids = list(manager.get().Tids())

if head:
native = native_mode in {NativeReportingMode.PYTHON, NativeReportingMode.ALL}
while head:
add_native_traces = native_mode != NativeReportingMode.OFF
for thread in _construct_threads_from_interpreter_state(
manager, head, pymanager.pid, pymanager.python_version, native, locals
manager,
head,
pymanager.pid,
pymanager.python_version,
add_native_traces,
locals,
):
if thread.tid in all_tids:
all_tids.remove(thread.tid)
yield thread
head = InterpreterUtils.getNextInterpreter(manager, head)

if native_mode == NativeReportingMode.ALL:
yield from _construct_os_threads(manager, pymanager.pid, all_tids)
3 changes: 2 additions & 1 deletion src/pystack/_pystack/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_library(_pystack STATIC
pythread.cpp
version.cpp
elf_common.cpp
pytypes.cpp)
pytypes.cpp
interpreter.cpp)
set_property(TARGET _pystack PROPERTY POSITION_INDEPENDENT_CODE ON)
include_directories("." "cpython" ${PYTHON_INCLUDE_DIRS})
4 changes: 2 additions & 2 deletions src/pystack/_pystack/cpython/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,10 @@ struct _gil_runtime_state
int locked;
unsigned long switch_number;
pthread_cond_t cond;
pthread_cond_t mutex;
pthread_mutex_t mutex;
#ifdef FORCE_SWITCHING
pthread_cond_t switch_cond;
pthread_cond_t switch_mutex;
pthread_mutex_t switch_mutex;
#endif
};

Expand Down
36 changes: 36 additions & 0 deletions src/pystack/_pystack/interpreter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <memory>

#include "interpreter.h"
#include "logging.h"
#include "process.h"
#include "structure.h"
#include "version.h"

namespace pystack {

remote_addr_t
InterpreterUtils::getNextInterpreter(
const std::shared_ptr<const AbstractProcessManager>& manager,
remote_addr_t interpreter_addr)
{
Structure<py_is_v> is(manager, interpreter_addr);
return is.getField(&py_is_v::o_next);
}

int64_t
InterpreterUtils::getInterpreterId(
const std::shared_ptr<const AbstractProcessManager>& manager,
remote_addr_t interpreter_addr)
{
if (!manager->versionIsAtLeast(3, 7)) {
// No support for subinterpreters so the only interpreter is ID 0.
return 0;
}

Structure<py_is_v> is(manager, interpreter_addr);
int64_t id_value = is.getField(&py_is_v::o_id);

return id_value;
}

} // namespace pystack
24 changes: 24 additions & 0 deletions src/pystack/_pystack/interpreter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <cstdint>
#include <memory>

#include "mem.h"
#include "process.h"

namespace pystack {

class InterpreterUtils
{
public:
// Static Methods
static remote_addr_t getNextInterpreter(
const std::shared_ptr<const AbstractProcessManager>& manager,
remote_addr_t interpreter_addr);

static int64_t getInterpreterId(
const std::shared_ptr<const AbstractProcessManager>& manager,
remote_addr_t interpreter_addr);
};

} // namespace pystack
13 changes: 13 additions & 0 deletions src/pystack/_pystack/interpreter.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from _pystack.mem cimport remote_addr_t
from _pystack.process cimport AbstractProcessManager
from libc.stdint cimport int64_t
from libcpp.memory cimport shared_ptr


cdef extern from "interpreter.h" namespace "pystack":
cdef cppclass InterpreterUtils:
@staticmethod
remote_addr_t getNextInterpreter(shared_ptr[AbstractProcessManager] manager, remote_addr_t interpreter_addr) except +

@staticmethod
int64_t getInterpreterId(shared_ptr[AbstractProcessManager] manager, remote_addr_t interpreter_addr) except +
1 change: 1 addition & 0 deletions src/pystack/_pystack/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@ AbstractProcessManager::copyDebugOffsets(Structure<py_runtime_v>& py_runtime, py
set_offset(py_is.o_sysdict, &py_runtime_v::o_dbg_off_interpreter_state_sysdict);
set_offset(py_is.o_builtins, &py_runtime_v::o_dbg_off_interpreter_state_builtins);
set_offset(py_is.o_gil_runtime_state, &py_runtime_v::o_dbg_off_interpreter_state_ceval_gil);
set_offset(py_is.o_id, &py_runtime_v::o_dbg_off_interpreter_state_id);

set_size(py_thread, &py_runtime_v::o_dbg_off_thread_state_struct_size);
set_offset(py_thread.o_prev, &py_runtime_v::o_dbg_off_thread_state_prev);
Expand Down
Loading
Loading