Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 53d4d57

Browse filesBrowse files
committed
Add macOS support
1 parent be520c8 commit 53d4d57
Copy full SHA for 53d4d57

File tree

Expand file treeCollapse file tree

2 files changed

+61
-55
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+61
-55
lines changed

‎Lib/test/test_external_inspection.py

Copy file name to clipboardExpand all lines: Lib/test/test_external_inspection.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def _make_test_script(script_dir, script_basename, source):
2323

2424
class TestGetStackTrace(unittest.TestCase):
2525

26+
@unittest.skipIf(sys.platform != "darwin" and sys.platform != "linux", "Test only runs on Linux and MacOS")
2627
@unittest.skipIf(sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, "Test only runs on Linux with process_vm_readv support")
2728
def test_stack_trace(self):
2829
# Spawn a process with some realistic Python code

‎Modules/_testexternalinspection.c

Copy file name to clipboardExpand all lines: Modules/_testexternalinspection.c
+60-55Lines changed: 60 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
#include <mach/mach_vm.h>
2121
#include <mach/machine.h>
2222
#include <libproc.h>
23-
#endif
23+
#include <sys/proc.h>
24+
#include <sys/sysctl.h>
25+
#endif
2426

2527
#include <errno.h>
2628
#include <fcntl.h>
@@ -31,9 +33,7 @@
3133
#include <string.h>
3234
#include <sys/mman.h>
3335
#include <sys/param.h>
34-
#include <sys/proc.h>
3536
#include <sys/stat.h>
36-
#include <sys/sysctl.h>
3737
#include <sys/types.h>
3838
#include <unistd.h>
3939

@@ -166,6 +166,7 @@ static mach_port_t pid_to_task(pid_t pid) {
166166
result = task_for_pid(mach_task_self(), pid, &task);
167167
if (result != KERN_SUCCESS) {
168168
printf("Call to task_for_pid failed on PID %d: %s", pid, mach_error_string(result));
169+
PyErr_SetString(PyExc_PermissionError, "Cannot get task for PID");
169170
return 0;
170171
}
171172
return task;
@@ -189,7 +190,7 @@ static void* get_py_runtime(pid_t pid) {
189190
void* result_address = NULL;
190191
while (mach_vm_region(
191192
proc_ref, &address, &size, VM_REGION_BASIC_INFO_64,
192-
(vm_region_info_t)&region_info, // cppcheck-suppress [uninitvar]
193+
(vm_region_info_t)&region_info,
193194
&count, &object_name) == KERN_SUCCESS) {
194195

195196
int path_len = proc_regionfilename(pid, address, map_filename, MAXPATHLEN);
@@ -224,6 +225,55 @@ static void* get_py_runtime(pid_t pid) {
224225

225226

226227
#ifdef __linux__
228+
unsigned long
229+
find_python_map_start_address(pid_t pid, char* result_filename) {
230+
char maps_file_path[64];
231+
sprintf(maps_file_path, "/proc/%d/maps", pid);
232+
233+
FILE* maps_file = fopen(maps_file_path, "r");
234+
if (maps_file == NULL) {
235+
PyErr_SetFromErrno(PyExc_OSError);
236+
return 0;
237+
}
238+
239+
int match_found = 0;
240+
241+
char line[256];
242+
char map_filename[256];
243+
unsigned long result_address = 0;
244+
while (fgets(line, sizeof(line), maps_file) != NULL) {
245+
unsigned long start_address = 0;
246+
sscanf(line, "%lx-%*x %*s %*s %*s %*s %s", &start_address, map_filename);
247+
char* filename = strrchr(map_filename, '/');
248+
if (filename != NULL) {
249+
filename++; // Move past the '/'
250+
} else {
251+
filename = map_filename; // No path, use the whole string
252+
}
253+
254+
// Check if the filename starts with "python" or "libpython"
255+
if (!match_found && strncmp(filename, "python", 6) == 0) {
256+
match_found = 1;
257+
result_address = start_address;
258+
strcpy(result_filename, map_filename);
259+
}
260+
if (strncmp(filename, "libpython", 9) == 0) {
261+
match_found = 1;
262+
result_address = start_address;
263+
strcpy(result_filename, map_filename);
264+
break;
265+
}
266+
}
267+
268+
fclose(maps_file);
269+
270+
if (!match_found) {
271+
map_filename[0] = '\0';
272+
}
273+
274+
return result_address;
275+
}
276+
227277
void*
228278
get_py_runtime(pid_t pid) {
229279

@@ -279,54 +329,7 @@ get_py_runtime(pid_t pid) {
279329
return result;
280330
}
281331

282-
unsigned long
283-
find_python_map_start_address(pid_t pid, char* result_filename) {
284-
char maps_file_path[64];
285-
sprintf(maps_file_path, "/proc/%d/maps", pid);
286-
287-
FILE* maps_file = fopen(maps_file_path, "r");
288-
if (maps_file == NULL) {
289-
PyErr_SetFromErrno(PyExc_OSError);
290-
return 0;
291-
}
292-
293-
int match_found = 0;
294-
295-
char line[256];
296-
char map_filename[256];
297-
unsigned long result_address = 0;
298-
while (fgets(line, sizeof(line), maps_file) != NULL) {
299-
unsigned long start_address = 0;
300-
sscanf(line, "%lx-%*x %*s %*s %*s %*s %s", &start_address, map_filename);
301-
char* filename = strrchr(map_filename, '/');
302-
if (filename != NULL) {
303-
filename++; // Move past the '/'
304-
} else {
305-
filename = map_filename; // No path, use the whole string
306-
}
307332

308-
// Check if the filename starts with "python" or "libpython"
309-
if (!match_found && strncmp(filename, "python", 6) == 0) {
310-
match_found = 1;
311-
result_address = start_address;
312-
strcpy(result_filename, map_filename);
313-
}
314-
if (strncmp(filename, "libpython", 9) == 0) {
315-
match_found = 1;
316-
result_address = start_address;
317-
strcpy(result_filename, map_filename);
318-
break;
319-
}
320-
}
321-
322-
fclose(maps_file);
323-
324-
if (!match_found) {
325-
map_filename[0] = '\0';
326-
}
327-
328-
return result_address;
329-
}
330333
#endif
331334

332335
ssize_t
@@ -363,7 +366,7 @@ read_memory(pid_t pid, void* remote_address, ssize_t size, void* local_address)
363366
return -1;
364367
}
365368
total_bytes_read = size;
366-
#else
369+
#else
367370
return -1;
368371
#endif
369372
return total_bytes_read;
@@ -391,8 +394,8 @@ read_string(pid_t pid, _Py_DebugOffsets *debug_offsets, void* address, char* buf
391394

392395
static PyObject*
393396
get_stack_trace(PyObject* self, PyObject* args) {
394-
#ifndef HAVE_PROCESS_VM_READV
395-
PyErr_SetString(PyExc_RuntimeError, "process_vm_readv not available on this platform");
397+
#if (!defined(__linux__) && !defined(__APPLE__)) || (defined(__linux__) && !HAVE_PROCESS_VM_READV)
398+
PyErr_SetString(PyExc_RuntimeError, "get_strac_trace is not supported on this platform");
396399
return NULL;
397400
#endif
398401
int pid;
@@ -403,7 +406,9 @@ get_stack_trace(PyObject* self, PyObject* args) {
403406

404407
void* runtime_start_address = get_py_runtime(pid);
405408
if (runtime_start_address == NULL) {
406-
PyErr_SetString(PyExc_RuntimeError, "Failed to get .PyRuntime address");
409+
if(!PyErr_Occurred()) {
410+
PyErr_SetString(PyExc_RuntimeError, "Failed to get .PyRuntime address");
411+
}
407412
return NULL;
408413
}
409414

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.