pystack 1.2.0__tar.gz → 1.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pystack-1.2.0 → pystack-1.4.0}/.bumpversion.cfg +1 -1
- {pystack-1.2.0 → pystack-1.4.0}/MANIFEST.in +1 -0
- {pystack-1.2.0 → pystack-1.4.0}/Makefile +5 -0
- pystack-1.4.0/NEWS.rst +75 -0
- {pystack-1.2.0 → pystack-1.4.0}/PKG-INFO +5 -2
- {pystack-1.2.0 → pystack-1.4.0}/README.md +3 -1
- pystack-1.4.0/build_scripts/elfutils_contiguous_segments.diff +66 -0
- {pystack-1.2.0 → pystack-1.4.0}/pyproject.toml +10 -1
- {pystack-1.2.0 → pystack-1.4.0}/setup.py +14 -3
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/__main__.py +29 -6
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/corefile.cpp +1 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/code.h +35 -7
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/dict.h +19 -1
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/frame.h +2 -10
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/gc.h +23 -4
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/interpreter.h +90 -8
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/object.h +0 -6
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/runtime.h +179 -2
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/string.h +17 -24
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/thread.h +60 -11
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/logging.cpp +1 -1
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/mem.cpp +2 -49
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/mem.h +0 -20
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/mem.pxd +0 -5
- pystack-1.4.0/src/pystack/_pystack/process.cpp +1379 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/process.h +44 -16
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/process.pxd +7 -2
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pycode.cpp +19 -10
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pycode.h +1 -2
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pyframe.cpp +46 -39
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pyframe.h +10 -9
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pyframe.pxd +1 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pythread.cpp +55 -59
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pythread.h +6 -5
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pytypes.cpp +97 -71
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pytypes.h +6 -4
- pystack-1.4.0/src/pystack/_pystack/structure.h +91 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/unwinder.cpp +1 -0
- pystack-1.4.0/src/pystack/_pystack/version.cpp +761 -0
- pystack-1.4.0/src/pystack/_pystack/version.h +323 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack.pyi +3 -1
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack.pyx +30 -11
- pystack-1.4.0/src/pystack/_version.py +1 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/process.py +47 -1
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/traceback_formatter.py +4 -4
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/types.py +9 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/PKG-INFO +5 -2
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/SOURCES.txt +2 -0
- pystack-1.2.0/NEWS.rst +0 -43
- pystack-1.2.0/src/pystack/_pystack/process.cpp +0 -585
- pystack-1.2.0/src/pystack/_pystack/version.cpp +0 -401
- pystack-1.2.0/src/pystack/_pystack/version.h +0 -141
- pystack-1.2.0/src/pystack/_version.py +0 -1
- {pystack-1.2.0 → pystack-1.4.0}/.pre-commit-config.yaml +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/LICENSE +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/setup.cfg +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/__init__.py +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/CMakeLists.txt +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/__init__.pxd +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/compat.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/corefile.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/corefile.pxd +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/float.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/int.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/list.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/pthread.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/tuple.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/elf_common.cpp +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/elf_common.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/elf_common.pxd +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/logging.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/logging.pxd +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/native_frame.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/native_frame.pxd +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pycode.pxd +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pycompat.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pythread.pxd +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/unwinder.h +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/colors.py +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/engine.py +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/errors.py +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/maps.py +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack/py.typed +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/dependency_links.txt +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/entry_points.txt +0 -0
- {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/top_level.txt +0 -0
|
@@ -93,6 +93,11 @@ docs: ## Generate documentation
|
|
|
93
93
|
$(MAKE) -C docs clean
|
|
94
94
|
$(MAKE) -C docs html
|
|
95
95
|
|
|
96
|
+
.PHONY: docs-live
|
|
97
|
+
docs-live: ## Serve documentation on localhost:8000, with live-reload
|
|
98
|
+
$(MAKE) -C docs clean
|
|
99
|
+
$(MAKE) -C docs livehtml
|
|
100
|
+
|
|
96
101
|
.PHONY: gh-pages
|
|
97
102
|
gh-pages: ## Publish documentation on GitHub Pages
|
|
98
103
|
$(eval GIT_REMOTE := $(shell git remote get-url $(UPSTREAM_GIT_REMOTE)))
|
pystack-1.4.0/NEWS.rst
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
.. note
|
|
2
|
+
You should *NOT* add new change log entries to this file, this
|
|
3
|
+
file is managed by towncrier. You *may* edit previous change logs to
|
|
4
|
+
fix problems like typo corrections or such.
|
|
5
|
+
|
|
6
|
+
Changelog
|
|
7
|
+
=========
|
|
8
|
+
|
|
9
|
+
.. towncrier release notes start
|
|
10
|
+
|
|
11
|
+
pystack 1.4.0 (2024-09-11)
|
|
12
|
+
--------------------------
|
|
13
|
+
|
|
14
|
+
Features
|
|
15
|
+
~~~~~~~~
|
|
16
|
+
|
|
17
|
+
- Add support for Python 3.13 (#186)
|
|
18
|
+
- Add a new ``--version`` command line flag to show the version of pystack (#203)
|
|
19
|
+
- Support debugging free-threading (a.k.a. "nogil") Python 3.13 builds. Note that PyStack can't itself be run with ``python3.13t``, it can only attach to a ``python3.13t`` process or core file from another interpreter. (#206)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Bug Fixes
|
|
23
|
+
~~~~~~~~~
|
|
24
|
+
|
|
25
|
+
- Fix a bug that was causing Python scripts executed directly via shebang to report the shell script as the executable. (#184)
|
|
26
|
+
- Heap corruption could cause PyStack to fail to generate a stack when ``--locals`` mode was used. This has been fixed by falling back to a reasonable default when attempting to format the repr of a local variable causes a dereference of an invalid pointer. (#194)
|
|
27
|
+
- Fix a crash when analysing processes where the eval loop has a shim frame at the bottom of the stack (#204)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
pystack 1.3.0 (2023-11-28)
|
|
31
|
+
--------------------------
|
|
32
|
+
|
|
33
|
+
Bug Fixes
|
|
34
|
+
~~~~~~~~~
|
|
35
|
+
|
|
36
|
+
- Add a patch to the bundled elfutils used to create wheels to account for a bug when analysing cores with interleaved segments (#153)
|
|
37
|
+
- Removed the unused ``--self`` flag. (#141)
|
|
38
|
+
- Fix some instances when identifying the pthread id was failing in systems without GLIBC (#152)
|
|
39
|
+
- Fix several some race conditions when stopping threads in multithreaded programs (#155)
|
|
40
|
+
- Ensure log messages that contain non-UTF-8 data are not lost (#155)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
pystack 1.2.0 (2023-07-31)
|
|
44
|
+
--------------------------
|
|
45
|
+
|
|
46
|
+
Features
|
|
47
|
+
~~~~~~~~
|
|
48
|
+
|
|
49
|
+
- Add support for Python 3.12 (#108)
|
|
50
|
+
- Improve the performance of reading memory from running processes (#124)
|
|
51
|
+
- Improve the performance of reading memory from core files (#126)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
pystack 1.1.0 (2023-07-10)
|
|
55
|
+
--------------------------
|
|
56
|
+
|
|
57
|
+
Bug Fixes
|
|
58
|
+
~~~~~~~~~
|
|
59
|
+
|
|
60
|
+
- Allow building with older elfutils than 0.188 when building with glibc (for musl libc we still need newer versions). (#40)
|
|
61
|
+
- Improve error reporting when attaching to a process is forbidden. (#98)
|
|
62
|
+
- Fix a crash that could occur under some unusual conditions if elfutils could not unwind the stack. (#101)
|
|
63
|
+
- Drop a use of the f-string ``=`` specifier, which wasn't introduced until Python 3.8 (running PyStack with Python 3.7 was failing because of this). (#92)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
pystack 1.0.1 (2023-04-11)
|
|
67
|
+
--------------------------
|
|
68
|
+
|
|
69
|
+
No significant changes.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
pystack 1.0.0 (2023-04-06)
|
|
73
|
+
--------------------------
|
|
74
|
+
|
|
75
|
+
- Initial release.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pystack
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Analysis of the stack of remote python processes
|
|
5
5
|
Home-page: https://github.com/bloomberg/pystack
|
|
6
6
|
Author: Pablo Galindo Salgado
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
16
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
16
17
|
Classifier: Topic :: Software Development :: Debuggers
|
|
17
18
|
Requires-Python: >=3.7.0
|
|
@@ -88,6 +89,8 @@ package.
|
|
|
88
89
|
Check your package manager on how to install these dependencies (e.g.,
|
|
89
90
|
`apt-get install libdw-dev libelf-dev` in Debian-based systems). Note that you may need to tell the
|
|
90
91
|
compiler where to find the header and library files of the dependencies for the build to succeed.
|
|
92
|
+
If `pkg-config` is available (e.g. `apt-get install pkg-config` on Debian-based systems), it will
|
|
93
|
+
automatically be used to locate the libraries and configure the correct build flags.
|
|
91
94
|
Check your distribution's documentation to determine the location of the header and library files
|
|
92
95
|
or for more detailed information. When building on Alpine Linux (or any other distribution that
|
|
93
96
|
doesn't use glibc) you'll need elfutils 0.188 or newer. You may need to build this from source if
|
|
@@ -152,7 +155,7 @@ where the running process must not be impacted in any way (other than being temp
|
|
|
152
155
|
though `--no-block` can avoid even that). There are several options available:
|
|
153
156
|
|
|
154
157
|
```shell
|
|
155
|
-
usage: pystack remote [-h] [-v] [--no-color] [--no-block] [--native] [--native-all] [--locals] [--exhaustive]
|
|
158
|
+
usage: pystack remote [-h] [-v] [--no-color] [--no-block] [--native] [--native-all] [--locals] [--exhaustive] pid
|
|
156
159
|
|
|
157
160
|
positional arguments:
|
|
158
161
|
pid The PID of the remote process
|
|
@@ -68,6 +68,8 @@ package.
|
|
|
68
68
|
Check your package manager on how to install these dependencies (e.g.,
|
|
69
69
|
`apt-get install libdw-dev libelf-dev` in Debian-based systems). Note that you may need to tell the
|
|
70
70
|
compiler where to find the header and library files of the dependencies for the build to succeed.
|
|
71
|
+
If `pkg-config` is available (e.g. `apt-get install pkg-config` on Debian-based systems), it will
|
|
72
|
+
automatically be used to locate the libraries and configure the correct build flags.
|
|
71
73
|
Check your distribution's documentation to determine the location of the header and library files
|
|
72
74
|
or for more detailed information. When building on Alpine Linux (or any other distribution that
|
|
73
75
|
doesn't use glibc) you'll need elfutils 0.188 or newer. You may need to build this from source if
|
|
@@ -132,7 +134,7 @@ where the running process must not be impacted in any way (other than being temp
|
|
|
132
134
|
though `--no-block` can avoid even that). There are several options available:
|
|
133
135
|
|
|
134
136
|
```shell
|
|
135
|
-
usage: pystack remote [-h] [-v] [--no-color] [--no-block] [--native] [--native-all] [--locals] [--exhaustive]
|
|
137
|
+
usage: pystack remote [-h] [-v] [--no-color] [--no-block] [--native] [--native-all] [--locals] [--exhaustive] pid
|
|
136
138
|
|
|
137
139
|
positional arguments:
|
|
138
140
|
pid The PID of the remote process
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c
|
|
2
|
+
index 3ef62a7d..09ee37b3 100644
|
|
3
|
+
--- a/libdwfl/dwfl_segment_report_module.c
|
|
4
|
+
+++ b/libdwfl/dwfl_segment_report_module.c
|
|
5
|
+
@@ -737,17 +737,34 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
|
|
6
|
+
&& invalid_elf (module->elf, module->disk_file_has_build_id,
|
|
7
|
+
&build_id))
|
|
8
|
+
{
|
|
9
|
+
- elf_end (module->elf);
|
|
10
|
+
- close (module->fd);
|
|
11
|
+
- module->elf = NULL;
|
|
12
|
+
- module->fd = -1;
|
|
13
|
+
+ /* If MODULE's build-id doesn't match the disk file's
|
|
14
|
+
+ build-id, close ELF only if MODULE and ELF refer to
|
|
15
|
+
+ different builds of files with the same name. This
|
|
16
|
+
+ prevents premature closure of the correct ELF in cases
|
|
17
|
+
+ where segments of a module are non-contiguous in memory. */
|
|
18
|
+
+ if (name != NULL && module->name[0] != '\0'
|
|
19
|
+
+ && strcmp (basename (module->name), basename (name)) == 0)
|
|
20
|
+
+ {
|
|
21
|
+
+ elf_end (module->elf);
|
|
22
|
+
+ close (module->fd);
|
|
23
|
+
+ module->elf = NULL;
|
|
24
|
+
+ module->fd = -1;
|
|
25
|
+
+ }
|
|
26
|
+
}
|
|
27
|
+
- if (module->elf != NULL)
|
|
28
|
+
+ else if (module->elf != NULL)
|
|
29
|
+
{
|
|
30
|
+
- /* Ignore this found module if it would conflict in address
|
|
31
|
+
- space with any already existing module of DWFL. */
|
|
32
|
+
+ /* This module has already been reported. */
|
|
33
|
+
skip_this_module = true;
|
|
34
|
+
}
|
|
35
|
+
+ else
|
|
36
|
+
+ {
|
|
37
|
+
+ /* Only report this module if we haven't already done so. */
|
|
38
|
+
+ for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL;
|
|
39
|
+
+ mod = mod->next)
|
|
40
|
+
+ if (mod->low_addr == module_start
|
|
41
|
+
+ && mod->high_addr == module_end)
|
|
42
|
+
+ skip_this_module = true;
|
|
43
|
+
+ }
|
|
44
|
+
}
|
|
45
|
+
if (skip_this_module)
|
|
46
|
+
goto out;
|
|
47
|
+
@@ -781,10 +798,6 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- /* Our return value now says to skip the segments contained
|
|
52
|
+
- within the module. */
|
|
53
|
+
- ndx = addr_segndx (dwfl, segment, module_end, true);
|
|
54
|
+
-
|
|
55
|
+
/* Examine its .dynamic section to get more interesting details.
|
|
56
|
+
If it has DT_SONAME, we'll use that as the module name.
|
|
57
|
+
If it has a DT_DEBUG, then it's actually a PIE rather than a DSO.
|
|
58
|
+
@@ -929,6 +942,8 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
|
|
59
|
+
ndx = -1;
|
|
60
|
+
goto out;
|
|
61
|
+
}
|
|
62
|
+
+ else
|
|
63
|
+
+ ndx++;
|
|
64
|
+
|
|
65
|
+
/* We have reported the module. Now let the caller decide whether we
|
|
66
|
+
should read the whole thing in right now. */
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
requires = [
|
|
4
4
|
"setuptools",
|
|
5
5
|
"wheel",
|
|
6
|
-
"Cython"
|
|
6
|
+
"Cython",
|
|
7
|
+
"pkgconfig"
|
|
7
8
|
]
|
|
8
9
|
|
|
9
10
|
build-backend = 'setuptools.build_meta'
|
|
@@ -45,6 +46,7 @@ underlines = "-~"
|
|
|
45
46
|
build = ["cp38-*", "cp39-*", "cp310-*", "cp311-*"]
|
|
46
47
|
manylinux-x86_64-image = "manylinux2014"
|
|
47
48
|
manylinux-i686-image = "manylinux2014"
|
|
49
|
+
musllinux-x86_64-image = "musllinux_1_1"
|
|
48
50
|
skip = "*-musllinux_aarch64"
|
|
49
51
|
|
|
50
52
|
[tool.cibuildwheel.linux]
|
|
@@ -55,6 +57,7 @@ before-all = [
|
|
|
55
57
|
"curl https://sourceware.org/elfutils/ftp/$VERS/elfutils-$VERS.tar.bz2 > ./elfutils.tar.bz2",
|
|
56
58
|
"tar -xf elfutils.tar.bz2",
|
|
57
59
|
"cd elfutils-$VERS",
|
|
60
|
+
"patch libdwfl/dwfl_segment_report_module.c < {package}/build_scripts/elfutils_contiguous_segments.diff",
|
|
58
61
|
"CFLAGS='-w' ./configure --enable-libdebuginfod=dummy --disable-debuginfod --prefix=/usr --libdir=/usr/lib64",
|
|
59
62
|
"make install"
|
|
60
63
|
]
|
|
@@ -92,6 +95,7 @@ before-all = [
|
|
|
92
95
|
"curl https://sourceware.org/elfutils/ftp/$VERS/elfutils-$VERS.tar.bz2 > ./elfutils.tar.bz2",
|
|
93
96
|
"tar -xf elfutils.tar.bz2",
|
|
94
97
|
"cd elfutils-$VERS",
|
|
98
|
+
"patch libdwfl/dwfl_segment_report_module.c < {package}/build_scripts/elfutils_contiguous_segments.diff",
|
|
95
99
|
"CFLAGS='-w -DFNM_EXTMATCH=0' ./configure --prefix=/usr --disable-nls --disable-libdebuginfod --disable-debuginfod --with-zstd",
|
|
96
100
|
"make install"
|
|
97
101
|
]
|
|
@@ -112,3 +116,8 @@ omit = [
|
|
|
112
116
|
|
|
113
117
|
[tool.coverage.report]
|
|
114
118
|
show_missing = true
|
|
119
|
+
|
|
120
|
+
[tool.pytest.ini_options]
|
|
121
|
+
# pytest retains all temp files from the last 3 test suite runs by default.
|
|
122
|
+
# Keep only ones for failed tests to avoid filling up a disk.
|
|
123
|
+
tmp_path_retention_policy = "failed"
|
|
@@ -3,13 +3,14 @@ import pathlib
|
|
|
3
3
|
import sys
|
|
4
4
|
from sys import platform
|
|
5
5
|
|
|
6
|
+
import pkgconfig
|
|
6
7
|
import setuptools
|
|
7
8
|
from Cython.Build import cythonize
|
|
8
9
|
|
|
9
10
|
IS_LINUX = "linux" in platform
|
|
10
11
|
|
|
11
12
|
if not IS_LINUX:
|
|
12
|
-
raise RuntimeError(f"
|
|
13
|
+
raise RuntimeError(f"pystack does not support this platform ({platform})")
|
|
13
14
|
|
|
14
15
|
install_requires = []
|
|
15
16
|
|
|
@@ -52,6 +53,16 @@ if TEST_BUILD:
|
|
|
52
53
|
}
|
|
53
54
|
DEFINE_MACROS.extend([("CYTHON_TRACE", "1"), ("CYTHON_TRACE_NOGIL", "1")])
|
|
54
55
|
|
|
56
|
+
library_flags = {"libraries": ["elf", "dw"]}
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
library_flags = pkgconfig.parse("libelf libdw")
|
|
60
|
+
except EnvironmentError as e:
|
|
61
|
+
print("pkg-config not found.", e)
|
|
62
|
+
print("Falling back to static flags.")
|
|
63
|
+
except pkgconfig.PackageNotFoundError as e:
|
|
64
|
+
print("Package Not Found", e)
|
|
65
|
+
print("Falling back to static flags.")
|
|
55
66
|
|
|
56
67
|
PYSTACK_EXTENSION = setuptools.Extension(
|
|
57
68
|
name="pystack._pystack",
|
|
@@ -69,12 +80,11 @@ PYSTACK_EXTENSION = setuptools.Extension(
|
|
|
69
80
|
"src/pystack/_pystack/unwinder.cpp",
|
|
70
81
|
"src/pystack/_pystack/version.cpp",
|
|
71
82
|
],
|
|
72
|
-
libraries=["elf", "dw"],
|
|
73
|
-
include_dirs=["src"],
|
|
74
83
|
language="c++",
|
|
75
84
|
extra_compile_args=["-std=c++17"],
|
|
76
85
|
extra_link_args=["-std=c++17"],
|
|
77
86
|
define_macros=DEFINE_MACROS,
|
|
87
|
+
**library_flags,
|
|
78
88
|
)
|
|
79
89
|
|
|
80
90
|
PYSTACK_EXTENSION.libraries.extend(["dl", "stdc++fs"])
|
|
@@ -105,6 +115,7 @@ setuptools.setup(
|
|
|
105
115
|
"Programming Language :: Python :: 3.10",
|
|
106
116
|
"Programming Language :: Python :: 3.11",
|
|
107
117
|
"Programming Language :: Python :: 3.12",
|
|
118
|
+
"Programming Language :: Python :: 3.13",
|
|
108
119
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
109
120
|
"Topic :: Software Development :: Debuggers",
|
|
110
121
|
],
|
|
@@ -12,8 +12,11 @@ from typing import NoReturn
|
|
|
12
12
|
from typing import Optional
|
|
13
13
|
from typing import Set
|
|
14
14
|
|
|
15
|
+
from pystack import __version__
|
|
15
16
|
from pystack.errors import InvalidPythonProcess
|
|
17
|
+
from pystack.process import decompress_gzip
|
|
16
18
|
from pystack.process import is_elf
|
|
19
|
+
from pystack.process import is_gzip
|
|
17
20
|
|
|
18
21
|
from . import errors
|
|
19
22
|
from . import print_thread
|
|
@@ -116,6 +119,9 @@ def generate_cli_parser() -> argparse.ArgumentParser:
|
|
|
116
119
|
parser.add_argument(
|
|
117
120
|
"-v", "--verbose", action="count", default=0, dest="global_verbose"
|
|
118
121
|
)
|
|
122
|
+
parser.add_argument(
|
|
123
|
+
"--version", action="version", version=__version__, help="Show version"
|
|
124
|
+
)
|
|
119
125
|
parser.add_argument(
|
|
120
126
|
"--no-color",
|
|
121
127
|
action="store_true",
|
|
@@ -172,12 +178,6 @@ def generate_cli_parser() -> argparse.ArgumentParser:
|
|
|
172
178
|
default=False,
|
|
173
179
|
help="Use all possible methods to obtain the Python stack info (may be slow)",
|
|
174
180
|
)
|
|
175
|
-
remote_parser.add_argument(
|
|
176
|
-
"--self",
|
|
177
|
-
action="store_true",
|
|
178
|
-
default=False,
|
|
179
|
-
help="Introspect the same process that invoke this program",
|
|
180
|
-
)
|
|
181
181
|
core_parser = subparsers.add_parser(
|
|
182
182
|
"core",
|
|
183
183
|
help="Analyze a core dump file given its location and the executable",
|
|
@@ -325,9 +325,32 @@ def process_core(parser: argparse.ArgumentParser, args: argparse.Namespace) -> N
|
|
|
325
325
|
if not corefile.exists():
|
|
326
326
|
parser.error(f"Core {corefile} does not exist")
|
|
327
327
|
|
|
328
|
+
if is_gzip(corefile):
|
|
329
|
+
corefile = decompress_gzip(corefile)
|
|
330
|
+
|
|
328
331
|
if args.executable is None:
|
|
329
332
|
corefile_analyzer = CoreFileAnalyzer(corefile)
|
|
330
333
|
executable = pathlib.Path(corefile_analyzer.extract_executable())
|
|
334
|
+
if not executable.exists() or not is_elf(executable):
|
|
335
|
+
first_map = next(
|
|
336
|
+
(
|
|
337
|
+
map
|
|
338
|
+
for map in corefile_analyzer.extract_maps()
|
|
339
|
+
if map.path is not None
|
|
340
|
+
),
|
|
341
|
+
None,
|
|
342
|
+
)
|
|
343
|
+
if (
|
|
344
|
+
first_map is not None
|
|
345
|
+
and first_map.path is not None
|
|
346
|
+
and first_map.path.exists()
|
|
347
|
+
and is_elf(first_map.path)
|
|
348
|
+
):
|
|
349
|
+
executable = first_map.path
|
|
350
|
+
LOGGER.info(
|
|
351
|
+
"Setting executable automatically to the first map in the core: %s",
|
|
352
|
+
executable,
|
|
353
|
+
)
|
|
331
354
|
if not executable.exists():
|
|
332
355
|
raise errors.DetectedExecutableNotFound(
|
|
333
356
|
f"Detected executable doesn't exist: {executable}"
|
|
@@ -160,13 +160,41 @@ typedef struct
|
|
|
160
160
|
} PyCodeObject;
|
|
161
161
|
} // namespace Python3_12
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
163
|
+
namespace Python3_13 {
|
|
164
|
+
typedef uint16_t _Py_CODEUNIT;
|
|
165
|
+
typedef struct
|
|
166
|
+
{
|
|
167
|
+
PyObject_VAR_HEAD;
|
|
168
|
+
PyObject* co_consts;
|
|
169
|
+
PyObject* co_names;
|
|
170
|
+
PyObject* co_exceptiontable;
|
|
171
|
+
int co_flags;
|
|
172
|
+
int co_argcount;
|
|
173
|
+
int co_posonlyargcount;
|
|
174
|
+
int co_kwonlyargcount;
|
|
175
|
+
int co_stacksize;
|
|
176
|
+
int co_firstlineno;
|
|
177
|
+
int co_nlocalsplus;
|
|
178
|
+
int co_framesize;
|
|
179
|
+
int co_nlocals;
|
|
180
|
+
int co_ncellvars;
|
|
181
|
+
int co_nfreevars;
|
|
182
|
+
uint32_t co_version;
|
|
183
|
+
PyObject* co_localsplusnames;
|
|
184
|
+
PyObject* co_localspluskinds;
|
|
185
|
+
PyObject* co_filename;
|
|
186
|
+
PyObject* co_name;
|
|
187
|
+
PyObject* co_qualname;
|
|
188
|
+
PyObject* co_linetable;
|
|
189
|
+
PyObject* co_weakreflist;
|
|
190
|
+
void* co_executors;
|
|
191
|
+
void* _co_cached;
|
|
192
|
+
uintptr_t _co_instrumentation_version;
|
|
193
|
+
void* _co_monitoring;
|
|
194
|
+
int _co_firsttraceable;
|
|
195
|
+
void* co_extra;
|
|
196
|
+
char co_code_adaptive[1];
|
|
170
197
|
} PyCodeObject;
|
|
198
|
+
} // namespace Python3_13
|
|
171
199
|
|
|
172
200
|
} // namespace pystack
|
|
@@ -47,6 +47,11 @@ typedef struct
|
|
|
47
47
|
PyObject** ma_values;
|
|
48
48
|
} PyDictObject;
|
|
49
49
|
|
|
50
|
+
typedef struct _dictvalues
|
|
51
|
+
{
|
|
52
|
+
PyObject* values[1];
|
|
53
|
+
} PyDictValuesObject;
|
|
54
|
+
|
|
50
55
|
} // namespace Python3
|
|
51
56
|
namespace Python3_3 {
|
|
52
57
|
typedef struct _dictkeysobject
|
|
@@ -77,8 +82,21 @@ typedef struct _dictkeysobject
|
|
|
77
82
|
uint32_t dk_version;
|
|
78
83
|
Py_ssize_t dk_usable;
|
|
79
84
|
Py_ssize_t dk_nentries;
|
|
80
|
-
char dk_indices[]; /* char is required to avoid strict aliasing. */
|
|
85
|
+
char dk_indices[1]; /* char is required to avoid strict aliasing. */
|
|
81
86
|
} PyDictKeysObject;
|
|
82
87
|
} // namespace Python3_11
|
|
83
88
|
|
|
89
|
+
namespace Python3_13 {
|
|
90
|
+
|
|
91
|
+
typedef struct _dictvalues
|
|
92
|
+
{
|
|
93
|
+
uint8_t capacity;
|
|
94
|
+
uint8_t size;
|
|
95
|
+
uint8_t embedded;
|
|
96
|
+
uint8_t valid;
|
|
97
|
+
PyObject* values[1];
|
|
98
|
+
} PyDictValuesObject;
|
|
99
|
+
|
|
100
|
+
} // namespace Python3_13
|
|
101
|
+
|
|
84
102
|
} // namespace pystack
|
|
@@ -39,7 +39,7 @@ namespace Python3_7 {
|
|
|
39
39
|
typedef struct _pyframeobject
|
|
40
40
|
{
|
|
41
41
|
PyObject_VAR_HEAD struct _pyframeobject* f_back;
|
|
42
|
-
|
|
42
|
+
PyObject* f_code;
|
|
43
43
|
PyObject* f_builtins;
|
|
44
44
|
PyObject* f_globals;
|
|
45
45
|
PyObject* f_locals;
|
|
@@ -64,7 +64,7 @@ typedef signed char PyFrameState;
|
|
|
64
64
|
typedef struct _pyframeobject
|
|
65
65
|
{
|
|
66
66
|
PyObject_VAR_HEAD struct _pyframeobject* f_back;
|
|
67
|
-
|
|
67
|
+
PyObject* f_code;
|
|
68
68
|
PyObject* f_builtins;
|
|
69
69
|
PyObject* f_globals;
|
|
70
70
|
PyObject* f_locals;
|
|
@@ -126,12 +126,4 @@ typedef struct _interpreter_frame
|
|
|
126
126
|
|
|
127
127
|
} // namespace Python3_12
|
|
128
128
|
|
|
129
|
-
typedef union {
|
|
130
|
-
Python2::PyFrameObject v2;
|
|
131
|
-
Python3_7::PyFrameObject v3_7;
|
|
132
|
-
Python3_10::PyFrameObject v3_10;
|
|
133
|
-
Python3_11::PyFrameObject v3_11;
|
|
134
|
-
Python3_12::PyFrameObject v3_12;
|
|
135
|
-
} PyFrameObject;
|
|
136
|
-
|
|
137
129
|
} // namespace pystack
|
|
@@ -83,8 +83,27 @@ struct _gc_runtime_state
|
|
|
83
83
|
|
|
84
84
|
} // namespace Python3_8
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
namespace Python3_13 {
|
|
87
|
+
|
|
88
|
+
using Python3_8::gc_generation;
|
|
89
|
+
|
|
90
|
+
struct _gc_runtime_state
|
|
91
|
+
{
|
|
92
|
+
PyObject* trash_delete_later;
|
|
93
|
+
int trash_delete_nesting;
|
|
94
|
+
int enabled;
|
|
95
|
+
int debug;
|
|
96
|
+
struct gc_generation young;
|
|
97
|
+
struct gc_generation old[2];
|
|
98
|
+
struct gc_generation permanent_generation;
|
|
99
|
+
struct gc_generation_stats generation_stats[NUM_GENERATIONS];
|
|
100
|
+
int collecting;
|
|
101
|
+
PyObject* garbage;
|
|
102
|
+
PyObject* callbacks;
|
|
103
|
+
Py_ssize_t heap_size;
|
|
104
|
+
Py_ssize_t work_to_do;
|
|
105
|
+
int visited_space;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
} // namespace Python3_13
|
|
90
109
|
} // namespace pystack
|
|
@@ -247,13 +247,95 @@ typedef struct _is
|
|
|
247
247
|
} PyInterpreterState;
|
|
248
248
|
} // namespace Python3_12
|
|
249
249
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
250
|
+
namespace Python3_13 {
|
|
251
|
+
|
|
252
|
+
struct _pythreadstate; /* Forward */
|
|
253
|
+
struct PyMutex
|
|
254
|
+
{
|
|
255
|
+
uint8_t v;
|
|
256
|
+
};
|
|
257
|
+
typedef int (*_Py_pending_call_func)(void*);
|
|
258
|
+
struct _pending_call
|
|
259
|
+
{
|
|
260
|
+
_Py_pending_call_func func;
|
|
261
|
+
void* arg;
|
|
262
|
+
int flags;
|
|
263
|
+
};
|
|
264
|
+
struct _pending_calls
|
|
265
|
+
{
|
|
266
|
+
void* handling_thread;
|
|
267
|
+
PyMutex mutex;
|
|
268
|
+
int32_t npending;
|
|
269
|
+
int32_t max;
|
|
270
|
+
int32_t maxloop;
|
|
271
|
+
struct _pending_call calls[300];
|
|
272
|
+
int first;
|
|
273
|
+
int next;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
struct _ceval_state
|
|
277
|
+
{
|
|
278
|
+
uintptr_t instrumentation_version;
|
|
279
|
+
int recursion_limit;
|
|
280
|
+
struct _gil_runtime_state* gil;
|
|
281
|
+
int own_gil;
|
|
282
|
+
struct _pending_calls pending;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
struct _import_state
|
|
286
|
+
{
|
|
287
|
+
PyObject* modules;
|
|
288
|
+
PyObject* modules_by_index;
|
|
289
|
+
PyObject* importlib;
|
|
290
|
+
int override_frozen_modules;
|
|
291
|
+
int override_multi_interp_extensions_check;
|
|
292
|
+
int dlopenflags;
|
|
293
|
+
PyObject* import_func;
|
|
294
|
+
struct
|
|
295
|
+
{
|
|
296
|
+
PyThread_type_lock mutex;
|
|
297
|
+
unsigned long thread;
|
|
298
|
+
int level;
|
|
299
|
+
} lock;
|
|
300
|
+
struct
|
|
301
|
+
{
|
|
302
|
+
int import_level;
|
|
303
|
+
int64_t accumulated;
|
|
304
|
+
int header;
|
|
305
|
+
} find_and_load;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
typedef struct _is
|
|
309
|
+
{
|
|
310
|
+
struct _ceval_state ceval;
|
|
311
|
+
struct _is* next;
|
|
312
|
+
|
|
313
|
+
int64_t id;
|
|
314
|
+
int64_t id_refcount;
|
|
315
|
+
int requires_idref;
|
|
316
|
+
PyThread_type_lock id_mutex;
|
|
317
|
+
long _whence;
|
|
318
|
+
int _initialized;
|
|
319
|
+
int _ready;
|
|
320
|
+
int finalizing;
|
|
321
|
+
uintptr_t last_restart_version;
|
|
322
|
+
struct pythreads
|
|
323
|
+
{
|
|
324
|
+
uint64_t next_unique_id;
|
|
325
|
+
_pythreadstate* head;
|
|
326
|
+
_pythreadstate* main;
|
|
327
|
+
Py_ssize_t count;
|
|
328
|
+
size_t stacksize;
|
|
329
|
+
} threads;
|
|
330
|
+
struct pyruntimestate* runtime;
|
|
331
|
+
void* _finalizing;
|
|
332
|
+
unsigned long _finalizing_id;
|
|
333
|
+
struct Python3_8::_gc_runtime_state gc;
|
|
334
|
+
// Dictionary of the sys module
|
|
335
|
+
PyObject* sysdict;
|
|
336
|
+
// Dictionary of the builtins module
|
|
337
|
+
PyObject* builtins;
|
|
338
|
+
struct _import_state imports;
|
|
258
339
|
} PyInterpreterState;
|
|
340
|
+
} // namespace Python3_13
|
|
259
341
|
} // namespace pystack
|
|
@@ -205,12 +205,6 @@ typedef struct _typeobject
|
|
|
205
205
|
} PyTypeObject;
|
|
206
206
|
} // namespace Python3_8
|
|
207
207
|
|
|
208
|
-
typedef union {
|
|
209
|
-
Python2::PyTypeObject v2;
|
|
210
|
-
Python3_3::PyTypeObject v3_3;
|
|
211
|
-
Python3_8::PyTypeObject v3_8;
|
|
212
|
-
} PyTypeObject;
|
|
213
|
-
|
|
214
208
|
/* These flags are used to determine if a type is a subclass. */
|
|
215
209
|
constexpr long Pystack_TPFLAGS_INT_SUBCLASS = 1ul << 23u;
|
|
216
210
|
constexpr long Pystack_TPFLAGS_LONG_SUBCLASS = 1ul << 24u;
|