|
21 | 21 | #ifdef HAVE_UNISTD_H |
22 | 22 | # include <unistd.h> // lseek() |
23 | 23 | #endif |
| 24 | +#if defined(HAVE_EXECINFO_H) && defined(HAVE_DLFCN_H) && defined(HAVE_LINK_H) |
| 25 | +# include <execinfo.h> // backtrace(), backtrace_symbols() |
| 26 | +# include <dlfcn.h> // dladdr1() |
| 27 | +# include <link.h> // struct DL_info |
| 28 | +# if defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS) && defined(HAVE_DLADDR1) |
| 29 | +# define CAN_C_BACKTRACE |
| 30 | +# endif |
| 31 | +#endif |
24 | 32 |
|
| 33 | +#if defined(__STDC_NO_VLA__) && (__STDC_NO_VLA__ == 1) |
| 34 | +/* Use alloca() for VLAs. */ |
| 35 | +# define VLA(type, name, size) type *name = alloca(size) |
| 36 | +#elif !defined(__STDC_NO_VLA__) || (__STDC_NO_VLA__ == 0) |
| 37 | +/* Use actual C VLAs.*/ |
| 38 | +# define VLA(type, name, size) type name[size] |
| 39 | +#elif defined(CAN_C_BACKTRACE) |
| 40 | +/* VLAs are not possible. Disable C stack trace functions. */ |
| 41 | +# undef CAN_C_BACKTRACE |
| 42 | +#endif |
25 | 43 |
|
26 | 44 | #define OFF(x) offsetof(PyTracebackObject, x) |
27 | 45 | #define PUTS(fd, str) (void)_Py_write_noraise(fd, str, (int)strlen(str)) |
@@ -1110,3 +1128,94 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, |
1110 | 1128 | return NULL; |
1111 | 1129 | } |
1112 | 1130 |
|
| 1131 | +#ifdef CAN_C_BACKTRACE |
| 1132 | +/* Based on glibc's implementation of backtrace_symbols(), but only uses stack memory. */ |
| 1133 | +void |
| 1134 | +_Py_backtrace_symbols_fd(int fd, void *const *array, Py_ssize_t size) |
| 1135 | +{ |
| 1136 | + VLA(Dl_info, info, size); |
| 1137 | + VLA(int, status, size); |
| 1138 | + /* Fill in the information we can get from dladdr() */ |
| 1139 | + for (Py_ssize_t i = 0; i < size; ++i) { |
| 1140 | + struct link_map *map; |
| 1141 | + status[i] = dladdr1(array[i], &info[i], (void **)&map, RTLD_DL_LINKMAP); |
| 1142 | + if (status[i] != 0 |
| 1143 | + && info[i].dli_fname != NULL |
| 1144 | + && info[i].dli_fname[0] != '\0') { |
| 1145 | + /* The load bias is more useful to the user than the load |
| 1146 | + address. The use of these addresses is to calculate an |
| 1147 | + address in the ELF file, so its prelinked bias is not |
| 1148 | + something we want to subtract out */ |
| 1149 | + info[i].dli_fbase = (void *) map->l_addr; |
| 1150 | + } |
| 1151 | + } |
| 1152 | + for (Py_ssize_t i = 0; i < size; ++i) { |
| 1153 | + if (status[i] == 0 |
| 1154 | + || info[i].dli_fname == NULL |
| 1155 | + || info[i].dli_fname[0] == '\0' |
| 1156 | + ) { |
| 1157 | + dprintf(fd, " Binary file '<unknown>' [%p]\n", array[i]); |
| 1158 | + continue; |
| 1159 | + } |
| 1160 | + |
| 1161 | + if (info[i].dli_sname == NULL) { |
| 1162 | + /* We found no symbol name to use, so describe it as |
| 1163 | + relative to the file. */ |
| 1164 | + info[i].dli_saddr = info[i].dli_fbase; |
| 1165 | + } |
| 1166 | + |
| 1167 | + if (info[i].dli_sname == NULL |
| 1168 | + && info[i].dli_saddr == 0) { |
| 1169 | + dprintf(fd, " Binary file \"%s\" [%p]\n", |
| 1170 | + info[i].dli_fname, |
| 1171 | + array[i]); |
| 1172 | + } |
| 1173 | + else { |
| 1174 | + char sign; |
| 1175 | + ptrdiff_t offset; |
| 1176 | + if (array[i] >= (void *) info[i].dli_saddr) { |
| 1177 | + sign = '+'; |
| 1178 | + offset = array[i] - info[i].dli_saddr; |
| 1179 | + } |
| 1180 | + else { |
| 1181 | + sign = '-'; |
| 1182 | + offset = info[i].dli_saddr - array[i]; |
| 1183 | + } |
| 1184 | + const char *symbol_name = info[i].dli_sname != NULL ? info[i].dli_sname : ""; |
| 1185 | + dprintf(fd, " Binary file \"%s\", at %s%c%#tx [%p]\n", |
| 1186 | + info[i].dli_fname, |
| 1187 | + symbol_name, |
| 1188 | + sign, offset, array[i]); |
| 1189 | + } |
| 1190 | + } |
| 1191 | +} |
| 1192 | + |
| 1193 | +void |
| 1194 | +_Py_DumpStack(int fd) |
| 1195 | +{ |
| 1196 | +#define BACKTRACE_SIZE 32 |
| 1197 | + PUTS(fd, "Current thread's C stack trace (most recent call first):\n"); |
| 1198 | + VLA(void *, callstack, BACKTRACE_SIZE); |
| 1199 | + int frames = backtrace(callstack, BACKTRACE_SIZE); |
| 1200 | + if (frames == 0) { |
| 1201 | + // Some systems won't return anything for the stack trace |
| 1202 | + PUTS(fd, " <system returned no stack trace>\n"); |
| 1203 | + return; |
| 1204 | + } |
| 1205 | + |
| 1206 | + _Py_backtrace_symbols_fd(fd, callstack, frames); |
| 1207 | + if (frames == BACKTRACE_SIZE) { |
| 1208 | + PUTS(fd, " <truncated rest of calls>\n"); |
| 1209 | + } |
| 1210 | + |
| 1211 | +#undef BACKTRACE_SIZE |
| 1212 | +} |
| 1213 | +#else |
| 1214 | +void |
| 1215 | +_Py_DumpStack(int fd) |
| 1216 | +{ |
| 1217 | + PUTS(fd, "Current thread's C stack trace (most recent call first):\n"); |
| 1218 | + PUTS(fd, " <cannot get C stack on this system>\n"); |
| 1219 | +} |
| 1220 | +#endif |
| 1221 | + |
0 commit comments