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.
Files changed (86) hide show
  1. {pystack-1.2.0 → pystack-1.4.0}/.bumpversion.cfg +1 -1
  2. {pystack-1.2.0 → pystack-1.4.0}/MANIFEST.in +1 -0
  3. {pystack-1.2.0 → pystack-1.4.0}/Makefile +5 -0
  4. pystack-1.4.0/NEWS.rst +75 -0
  5. {pystack-1.2.0 → pystack-1.4.0}/PKG-INFO +5 -2
  6. {pystack-1.2.0 → pystack-1.4.0}/README.md +3 -1
  7. pystack-1.4.0/build_scripts/elfutils_contiguous_segments.diff +66 -0
  8. {pystack-1.2.0 → pystack-1.4.0}/pyproject.toml +10 -1
  9. {pystack-1.2.0 → pystack-1.4.0}/setup.py +14 -3
  10. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/__main__.py +29 -6
  11. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/corefile.cpp +1 -0
  12. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/code.h +35 -7
  13. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/dict.h +19 -1
  14. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/frame.h +2 -10
  15. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/gc.h +23 -4
  16. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/interpreter.h +90 -8
  17. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/object.h +0 -6
  18. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/runtime.h +179 -2
  19. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/string.h +17 -24
  20. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/thread.h +60 -11
  21. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/logging.cpp +1 -1
  22. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/mem.cpp +2 -49
  23. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/mem.h +0 -20
  24. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/mem.pxd +0 -5
  25. pystack-1.4.0/src/pystack/_pystack/process.cpp +1379 -0
  26. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/process.h +44 -16
  27. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/process.pxd +7 -2
  28. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pycode.cpp +19 -10
  29. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pycode.h +1 -2
  30. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pyframe.cpp +46 -39
  31. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pyframe.h +10 -9
  32. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pyframe.pxd +1 -0
  33. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pythread.cpp +55 -59
  34. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pythread.h +6 -5
  35. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pytypes.cpp +97 -71
  36. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pytypes.h +6 -4
  37. pystack-1.4.0/src/pystack/_pystack/structure.h +91 -0
  38. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/unwinder.cpp +1 -0
  39. pystack-1.4.0/src/pystack/_pystack/version.cpp +761 -0
  40. pystack-1.4.0/src/pystack/_pystack/version.h +323 -0
  41. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack.pyi +3 -1
  42. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack.pyx +30 -11
  43. pystack-1.4.0/src/pystack/_version.py +1 -0
  44. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/process.py +47 -1
  45. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/traceback_formatter.py +4 -4
  46. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/types.py +9 -0
  47. {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/PKG-INFO +5 -2
  48. {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/SOURCES.txt +2 -0
  49. pystack-1.2.0/NEWS.rst +0 -43
  50. pystack-1.2.0/src/pystack/_pystack/process.cpp +0 -585
  51. pystack-1.2.0/src/pystack/_pystack/version.cpp +0 -401
  52. pystack-1.2.0/src/pystack/_pystack/version.h +0 -141
  53. pystack-1.2.0/src/pystack/_version.py +0 -1
  54. {pystack-1.2.0 → pystack-1.4.0}/.pre-commit-config.yaml +0 -0
  55. {pystack-1.2.0 → pystack-1.4.0}/LICENSE +0 -0
  56. {pystack-1.2.0 → pystack-1.4.0}/setup.cfg +0 -0
  57. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/__init__.py +0 -0
  58. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/CMakeLists.txt +0 -0
  59. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/__init__.pxd +0 -0
  60. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/compat.h +0 -0
  61. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/corefile.h +0 -0
  62. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/corefile.pxd +0 -0
  63. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/float.h +0 -0
  64. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/int.h +0 -0
  65. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/list.h +0 -0
  66. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/pthread.h +0 -0
  67. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/cpython/tuple.h +0 -0
  68. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/elf_common.cpp +0 -0
  69. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/elf_common.h +0 -0
  70. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/elf_common.pxd +0 -0
  71. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/logging.h +0 -0
  72. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/logging.pxd +0 -0
  73. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/native_frame.h +0 -0
  74. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/native_frame.pxd +0 -0
  75. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pycode.pxd +0 -0
  76. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pycompat.h +0 -0
  77. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/pythread.pxd +0 -0
  78. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/_pystack/unwinder.h +0 -0
  79. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/colors.py +0 -0
  80. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/engine.py +0 -0
  81. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/errors.py +0 -0
  82. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/maps.py +0 -0
  83. {pystack-1.2.0 → pystack-1.4.0}/src/pystack/py.typed +0 -0
  84. {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/dependency_links.txt +0 -0
  85. {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/entry_points.txt +0 -0
  86. {pystack-1.2.0 → pystack-1.4.0}/src/pystack.egg-info/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 1.2.0
2
+ current_version = 1.4.0
3
3
  commit = True
4
4
  message =
5
5
  Prepare for {new_version} release
@@ -14,6 +14,7 @@ include README.md
14
14
  include src/pystack/py.typed
15
15
  include .bumpversion.cfg
16
16
  include .pre-commit-config.yaml
17
+ include build_scripts/elfutils_contiguous_segments.diff
17
18
 
18
19
  recursive-include src/pystack/_pystack *
19
20
  recursive-include src/pystack *.py
@@ -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.2.0
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] [--self] pid
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] [--self] pid
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"memray does not support this platform ({platform})")
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}"
@@ -1,3 +1,4 @@
1
+ #include <algorithm>
1
2
  #include <cassert>
2
3
  #include <cstring>
3
4
  #include <fstream>
@@ -160,13 +160,41 @@ typedef struct
160
160
  } PyCodeObject;
161
161
  } // namespace Python3_12
162
162
 
163
- typedef union {
164
- Python2::PyCodeObject v2;
165
- Python3_3::PyCodeObject v3_3;
166
- Python3_6::PyCodeObject v3_6;
167
- Python3_8::PyCodeObject v3_8;
168
- Python3_11::PyCodeObject v3_11;
169
- Python3_12::PyCodeObject v3_12;
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
- PyCodeObject* f_code;
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
- PyCodeObject* f_code;
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
- typedef union {
87
- struct Python3_7::_gc_runtime_state v3_7;
88
- struct Python3_8::_gc_runtime_state v3_8;
89
- } GCRuntimeState;
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
- typedef union {
251
- Python2::PyInterpreterState v2;
252
- Python3_5::PyInterpreterState v3_5;
253
- Python3_7::PyInterpreterState v3_7;
254
- Python3_8::PyInterpreterState v3_8;
255
- Python3_9::PyInterpreterState v3_9;
256
- Python3_11::PyInterpreterState v3_11;
257
- Python3_12::PyInterpreterState v3_12;
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;