diff options
Diffstat (limited to 'mysys/my_addr_resolve.c')
-rw-r--r-- | mysys/my_addr_resolve.c | 165 |
1 files changed, 92 insertions, 73 deletions
diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c index 83716bf11e1..10f8552f226 100644 --- a/mysys/my_addr_resolve.c +++ b/mysys/my_addr_resolve.c @@ -34,13 +34,6 @@ static const char *strip_path(const char *s) return prev; } -#if defined(HAVE_LINK_H) && defined(HAVE_DLOPEN) -#include <link.h> -static ptrdiff_t offset= 0; -#else -#define offset 0 -#endif - /* The following is very much single-threaded code and it's only supposed to be used on shutdown or for a crash report @@ -145,10 +138,52 @@ err: #include <m_string.h> #include <ctype.h> +#include <sys/wait.h> static int in[2], out[2]; -static int initialized= 0; +static pid_t pid; +static char addr2line_binary[1024]; static char output[1024]; + +int start_addr2line_fork(const char *binary_path) +{ + + if (pid > 0) + { + /* Don't leak FDs */ + close(in[1]); + close(out[0]); + /* Don't create zombie processes. */ + waitpid(pid, NULL, 0); + } + + if (pipe(in) < 0) + return 1; + if (pipe(out) < 0) + return 1; + + pid = fork(); + if (pid == -1) + return 1; + + if (!pid) /* child */ + { + dup2(in[0], 0); + dup2(out[1], 1); + close(in[0]); + close(in[1]); + close(out[0]); + close(out[1]); + execlp("addr2line", "addr2line", "-C", "-f", "-e", binary_path, NULL); + exit(1); + } + + close(in[0]); + close(out[1]); + + return 0; +} + int my_addr_resolve(void *ptr, my_addr_loc *loc) { char input[32]; @@ -156,69 +191,88 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) ssize_t total_bytes_read = 0; ssize_t extra_bytes_read = 0; + ssize_t parsed = 0; fd_set set; struct timeval timeout; int filename_start = -1; int line_number_start = -1; - ssize_t i; - FD_ZERO(&set); - FD_SET(out[0], &set); + Dl_info info; + void *offset; + if (!dladdr(ptr, &info)) + return 1; + + if (strcmp(addr2line_binary, info.dli_fname)) + { + /* We use dli_fname in case the path is longer than the length of our static + string. We don't want to allocate anything dynamicaly here as we are in + a "crashed" state. */ + if (start_addr2line_fork(info.dli_fname)) + { + addr2line_binary[0] = '\0'; + return 2; + } + /* Save result for future comparisons. */ + strnmov(addr2line_binary, info.dli_fname, sizeof(addr2line_binary)); + } + offset = info.dli_fbase; len= my_snprintf(input, sizeof(input), "%p\n", ptr - offset); if (write(in[1], input, len) <= 0) - return 1; + return 3; + + FD_ZERO(&set); + FD_SET(out[0], &set); - /* 10 ms should be plenty of time for addr2line to issue a response. */ + /* 100 ms should be plenty of time for addr2line to issue a response. */ timeout.tv_sec = 0; - timeout.tv_usec = 10000; + timeout.tv_usec = 100000; /* Read in a loop till all the output from addr2line is complete. */ - while (select(out[0] + 1, &set, NULL, NULL, &timeout) > 0) + while (parsed == total_bytes_read && + select(out[0] + 1, &set, NULL, NULL, &timeout) > 0) { extra_bytes_read= read(out[0], output + total_bytes_read, sizeof(output) - total_bytes_read); if (extra_bytes_read < 0) - return 2; + return 4; /* Timeout or max bytes read. */ if (extra_bytes_read == 0) break; total_bytes_read += extra_bytes_read; - } - - /* Failed starting addr2line. */ - if (total_bytes_read == 0) - return 3; - /* Go through the addr2line response and get the required data. - The response is structured in 2 lines. The first line contains the function - name, while the second one contains <filename>:<line number> */ - for (i = 0; i < total_bytes_read; i++) { - if (output[i] == '\n') { - filename_start = i + 1; - output[i] = '\0'; - } - if (filename_start != -1 && output[i] == ':') { - line_number_start = i + 1; - output[i] = '\0'; - } - if (line_number_start != -1) { - loc->line= atoi(output + line_number_start); - break; + /* Go through the addr2line response and get the required data. + The response is structured in 2 lines. The first line contains the function + name, while the second one contains <filename>:<line number> */ + for (; parsed < total_bytes_read; parsed++) + { + if (output[parsed] == '\n') + { + filename_start = parsed + 1; + output[parsed] = '\0'; + } + if (filename_start != -1 && output[parsed] == ':') + { + line_number_start = parsed + 1; + output[parsed] = '\0'; + break; + } } } + /* Response is malformed. */ if (filename_start == -1 || line_number_start == -1) - return 4; + return 5; loc->func= output; loc->file= output + filename_start; + loc->line= atoi(output + line_number_start); /* Addr2line was unable to extract any meaningful information. */ if (strcmp(loc->file, "??") == 0) - return 5; + return 6; loc->file= strip_path(loc->file); @@ -227,41 +281,6 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) const char *my_addr_resolve_init() { - if (!initialized) - { - pid_t pid; - -#if defined(HAVE_LINK_H) && defined(HAVE_DLOPEN) - struct link_map *lm = (struct link_map*) dlopen(0, RTLD_NOW); - if (lm) - offset= lm->l_addr; -#endif - - if (pipe(in) < 0) - return "pipe(in)"; - if (pipe(out) < 0) - return "pipe(out)"; - - pid = fork(); - if (pid == -1) - return "fork"; - - if (!pid) /* child */ - { - dup2(in[0], 0); - dup2(out[1], 1); - close(in[0]); - close(in[1]); - close(out[0]); - close(out[1]); - execlp("addr2line", "addr2line", "-C", "-f", "-e", my_progname, NULL); - exit(1); - } - - close(in[0]); - close(out[1]); - initialized= 1; - } return 0; } #endif |