Skip to content

Commit 2ebbe1b

Browse files
committed
fix(linux): improved container support
We make use of the /proc/root of the procfs to improve the chances that we can infer the required information, such as the location of the runtime structure and the interpreter version from binaries pointed at by the actual process root. This is relevant for processes running inside containers, since their FS root does not coincide with the host root.
1 parent 5303860 commit 2ebbe1b

File tree

4 files changed

+45
-24
lines changed

4 files changed

+45
-24
lines changed

ChangeLog

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
2024-xx-xx v3.6.1
22

3+
Improve support for Python processes running in containers.
4+
35
Bugfix: fixed a bug with the MOJO binary format that caused the line end
46
position to wrongly be set to a non-zero value for CPython < 3.11, where line
57
end information is not actually available.

src/linux/common.h

+23
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
#include <pthread.h>
2727
#include <stdint.h>
2828
#include <stdio.h>
29+
#include <string.h>
2930
#include <sys/ptrace.h>
3031

3132
#include "../error.h"
33+
#include "../hints.h"
3234
#include "../stats.h"
3335

3436

@@ -109,3 +111,24 @@ _procfs(pid_t pid, char * file) {
109111

110112
return fp;
111113
}
114+
115+
116+
// ----------------------------------------------------------------------------
117+
static inline char *
118+
proc_root(pid_t pid, char * file) {
119+
if (file[0] != '/') {
120+
log_e("File path is not absolute");
121+
return NULL;
122+
}
123+
124+
char * proc_root = calloc(1, strlen(file) + 24);
125+
if (!isvalid(proc_root))
126+
return NULL;
127+
128+
if (sprintf(proc_root, "/proc/%d/root%s", pid, file) < 0) {
129+
free(proc_root);
130+
return NULL;
131+
}
132+
133+
return proc_root;
134+
}

src/linux/py_proc.h

+10-10
Original file line numberDiff line numberDiff line change
@@ -455,16 +455,16 @@ _py_proc__parse_maps_file(py_proc_t * self) {
455455
// The first memory map of the executable
456456
if (!isvalid(pd->maps[MAP_BIN].path) && strcmp(pd->exe_path, pathname) == 0) {
457457
map = &(pd->maps[MAP_BIN]);
458-
map->path = strndup(pathname, strlen(pathname));
458+
map->path = proc_root(self->pid, pathname);
459459
if (!isvalid(map->path)) {
460-
log_ie("Cannot duplicate path name");
460+
log_e("Cannot get proc root path for %s", pathname);
461461
set_error(EPROC);
462462
FAIL;
463463
}
464-
map->file_size = _file_size(pathname);
464+
map->file_size = _file_size(map->path);
465465
map->base = (void *) lower;
466466
map->size = upper - lower;
467-
map->has_symbols = success(_py_proc__analyze_elf(self, pathname, (void *) lower));
467+
map->has_symbols = success(_py_proc__analyze_elf(self, map->path, (void *) lower));
468468
if (map->has_symbols) {
469469
map->bss_base = self->map.bss.base;
470470
map->bss_size = self->map.bss.size;
@@ -479,13 +479,13 @@ _py_proc__parse_maps_file(py_proc_t * self) {
479479
int has_symbols = success(_py_proc__analyze_elf(self, pathname, (void *) lower));
480480
if (has_symbols) {
481481
map = &(pd->maps[MAP_LIBSYM]);
482-
map->path = strndup(pathname, strlen(pathname));
482+
map->path = proc_root(self->pid, pathname);
483483
if (!isvalid(map->path)) {
484-
log_ie("Cannot duplicate path name");
484+
log_e("Cannot get proc root path for %s", pathname);
485485
set_error(EPROC);
486486
FAIL;
487487
}
488-
map->file_size = _file_size(pathname);
488+
map->file_size = _file_size(map->path);
489489
map->base = (void *) lower;
490490
map->size = upper - lower;
491491
map->has_symbols = TRUE;
@@ -503,13 +503,13 @@ _py_proc__parse_maps_file(py_proc_t * self) {
503503
unsigned int v;
504504
if (sscanf(needle, "libpython%u.%u", &v, &v) == 2) {
505505
map = &(pd->maps[MAP_LIBNEEDLE]);
506-
map->path = needle_path = strndup(pathname, strlen(pathname));
506+
map->path = needle_path = proc_root(self->pid, pathname);
507507
if (!isvalid(map->path)) {
508-
log_ie("Cannot duplicate path name");
508+
log_e("Cannot get proc root path for %s", pathname);
509509
set_error(EPROC);
510510
FAIL;
511511
}
512-
map->file_size = _file_size(pathname);
512+
map->file_size = _file_size(map->path);
513513
map->base = (void *) lower;
514514
map->size = upper - lower;
515515
map->has_symbols = FALSE;

src/py_proc.c

+10-14
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,14 @@ _get_version_from_executable(char * binary, int * major, int * minor, int * patc
110110
#endif
111111

112112
fp = _popen(cmd, "r");
113-
if (!isvalid(fp)) {
114-
set_error(EPROC);
113+
if (!isvalid(fp))
115114
FAIL;
116-
}
117115

118116
while (fgets(version, sizeof(version) - 1, fp) != NULL) {
119117
if (sscanf(version, "Python %d.%d.%d", major, minor, patch) == 3)
120118
SUCCESS;
121119
}
122120

123-
set_error(EPROC);
124121
FAIL;
125122
} /* _get_version_from_executable */
126123

@@ -129,37 +126,34 @@ _get_version_from_filename(char * filename, const char * needle, int * major, in
129126
#if defined PL_LINUX /* LINUX */
130127
char * base = filename;
131128
char * end = base + strlen(base);
129+
size_t needle_len = strlen(needle);
132130

133131
while (base < end) {
134132
base = strstr(base, needle);
135133
if (!isvalid(base)) {
136134
break;
137135
}
138-
if (sscanf(base + strlen(needle), "%u.%u", major, minor) == 2) {
136+
base += needle_len;
137+
if (sscanf(base, "%u.%u", major, minor) == 2) {
139138
SUCCESS;
140139
}
141140
}
142141

143142
#elif defined PL_WIN /* WIN */
144143
// Assume the library path is of the form *.python3[0-9]+[.]dll
145144
int n = strlen(filename);
146-
if (n < 10) {
147-
set_error(EPROC);
145+
if (n < 10)
148146
FAIL;
149-
}
150147

151148
char * p = filename + n - 1;
152149
while (*(p--) != 'n' && p > filename);
153150
p++;
154151
*major = *(p++) - '0';
155-
if (*major != 3) {
156-
set_error(EPROC);
152+
if (*major != 3)
157153
FAIL;
158-
}
159154

160-
if (sscanf(p,"%d.dll", minor) == 1) {
155+
if (sscanf(p,"%d.dll", minor) == 1)
161156
SUCCESS;
162-
}
163157

164158
#elif defined PL_MACOS /* MAC */
165159
char * ver_needle = strstr(filename, "3.");
@@ -169,7 +163,6 @@ _get_version_from_filename(char * filename, const char * needle, int * major, in
169163

170164
#endif
171165

172-
set_error(EPROC);
173166
FAIL;
174167
} /* _get_version_from_filename */
175168

@@ -648,6 +641,9 @@ _py_proc__run(py_proc_t * self) {
648641
TIMER_STOP;
649642
}
650643

644+
if (is_fatal(austin_errno))
645+
FAIL;
646+
651647
TIMER_END
652648

653649
log_d("_py_proc__init timer loop terminated");

0 commit comments

Comments
 (0)