2 * Example program for unwinding core dumps.
7 * -lunwind -lunwind-x86 -lunwind-coredump \
8 * example-core-unwind.c \
10 * -oexample-core-unwind
13 * eu-unstrip -n --core COREDUMP
14 * figure out which virtual addresses in COREDUMP correspond to which mapped executable files
15 * (binary and libraries), then supply them like this:
16 * ./example-core-unwind COREDUMP 0x400000:/bin/crashed_program 0x3458600000:/lib/libc.so.6 [...]
18 * Note: Program eu-unstrip is part of elfutils, virtual addresses of shared
19 * libraries can be determined by ldd (at least on linux).
45 #include <sys/socket.h>
48 #include <sys/types.h>
50 #include <sys/param.h>
59 /* For SIGSEGV handler code */
61 #include <sys/ucontext.h>
63 #include <libunwind-coredump.h>
66 /* Utility logging functions */
70 LOGMODE_STDIO = (1 << 0),
71 LOGMODE_SYSLOG = (1 << 1),
72 LOGMODE_BOTH = LOGMODE_SYSLOG + LOGMODE_STDIO,
74 const char *msg_prefix = "";
75 const char *msg_eol = "\n";
76 int logmode = LOGMODE_STDIO;
77 int xfunc_error_retval = EXIT_FAILURE;
81 exit(xfunc_error_retval);
84 static void verror_msg_helper(const char *s,
90 int prefix_len, strerr_len, msgeol_len, used;
95 used = vasprintf(&msg, s, p);
99 /* This is ugly and costs +60 bytes compared to multiple
100 * fprintf's, but is guaranteed to do a single write.
101 * This is needed for e.g. when multiple children
102 * can produce log messages simultaneously. */
104 prefix_len = msg_prefix[0] ? strlen(msg_prefix) + 2 : 0;
105 strerr_len = strerr ? strlen(strerr) : 0;
106 msgeol_len = strlen(msg_eol);
107 /* +3 is for ": " before strerr and for terminating NUL */
108 char *msg1 = (char*) realloc(msg, prefix_len + used + strerr_len + msgeol_len + 3);
115 /* TODO: maybe use writev instead of memmoving? Need full_writev? */
119 memmove(msg + prefix_len, msg, used);
121 p = stpcpy(msg, msg_prefix);
132 strcpy(&msg[used], strerr);
135 strcpy(&msg[used], msg_eol);
137 if (flags & LOGMODE_STDIO)
140 write(STDERR_FILENO, msg, used + msgeol_len);
142 msg[used] = '\0'; /* remove msg_eol (usually "\n") */
143 if (flags & LOGMODE_SYSLOG)
145 syslog(LOG_ERR, "%s", msg + prefix_len);
150 void log_msg(const char *s, ...)
154 verror_msg_helper(s, p, NULL, logmode);
157 /* It's a macro, not function, since it collides with log() from math.h */
159 #define log(...) log_msg(__VA_ARGS__)
161 void error_msg(const char *s, ...)
165 verror_msg_helper(s, p, NULL, logmode);
169 void error_msg_and_die(const char *s, ...)
173 verror_msg_helper(s, p, NULL, logmode);
178 void perror_msg(const char *s, ...)
182 /* Guard against "<error message>: Success" */
183 verror_msg_helper(s, p, errno ? strerror(errno) : NULL, logmode);
187 void perror_msg_and_die(const char *s, ...)
191 /* Guard against "<error message>: Success" */
192 verror_msg_helper(s, p, errno ? strerror(errno) : NULL, logmode);
197 void die_out_of_memory(void)
199 error_msg_and_die("Out of memory, exiting");
202 /* End of utility logging functions */
207 void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
210 ucontext_t *uc UNUSED;
213 #if defined(__linux__)
214 #ifdef UNW_TARGET_X86
215 ip = uc->uc_mcontext.gregs[REG_EIP];
216 #elif defined(UNW_TARGET_X86_64)
217 ip = uc->uc_mcontext.gregs[REG_RIP];
218 #elif defined(UNW_TARGET_ARM)
219 ip = uc->uc_mcontext.arm_pc;
221 #elif defined(__FreeBSD__)
223 ip = uc->uc_mcontext.mc_eip;
224 #elif defined(__amd64__)
225 ip = uc->uc_mcontext.mc_rip;
232 dprintf(2, "signal:%d address:0x%lx ip:0x%lx\n",
234 /* this is void*, but using %p would print "(null)"
235 * even for ptrs which are not exactly 0, but, say, 0x123:
241 /* glibc extension */
244 size = backtrace(array, 50);
246 backtrace_symbols_fd(array, size, 2);
253 static void install_sigsegv_handler(void)
256 memset(&sa, 0, sizeof(sa));
257 sa.sa_sigaction = handle_sigsegv;
258 sa.sa_flags = SA_SIGINFO;
259 sigaction(SIGSEGV, &sa, NULL);
260 sigaction(SIGILL, &sa, NULL);
261 sigaction(SIGFPE, &sa, NULL);
262 sigaction(SIGBUS, &sa, NULL);
266 main(int argc UNUSED, char **argv)
273 #define TEST_FRAMES 4
274 #define TEST_NAME_LEN 32
277 long test_start_ips[TEST_FRAMES];
278 char test_names[TEST_FRAMES][TEST_NAME_LEN];
280 install_sigsegv_handler();
282 const char *progname = strrchr(argv[0], '/');
289 error_msg_and_die("Usage: %s COREDUMP [VADDR:BINARY_FILE]...", progname);
291 msg_prefix = progname;
293 as = unw_create_addr_space(&_UCD_accessors, 0);
295 error_msg_and_die("unw_create_addr_space() failed");
297 ui = _UCD_create(argv[1]);
299 error_msg_and_die("_UCD_create('%s') failed", argv[1]);
300 ret = unw_init_remote(&c, as, ui);
302 error_msg_and_die("unw_init_remote() failed: ret=%d\n", ret);
306 /* Enable checks for the crasher test program? */
307 if (*argv && !strcmp(*argv, "-testcase"))
310 logmode = LOGMODE_NONE;
317 unsigned long vaddr = strtoul(*argv, &colon, 16);
319 error_msg_and_die("Bad format: '%s'", *argv);
320 if (_UCD_add_backing_file_at_vaddr(ui, vaddr, colon + 1) < 0)
321 error_msg_and_die("Can't add backing file '%s'", colon + 1);
328 ret = unw_get_reg(&c, UNW_REG_IP, &ip);
330 error_msg_and_die("unw_get_reg(UNW_REG_IP) failed: ret=%d\n", ret);
333 ret = unw_get_proc_info(&c, &pi);
335 error_msg_and_die("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret);
338 printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx\n",
340 (long) pi.start_ip, (long) pi.end_ip,
341 (long) pi.handler, (long) pi.lsda);
343 if (testcase && test_cur < TEST_FRAMES)
347 test_start_ips[test_cur] = (long) pi.start_ip;
348 if (unw_get_proc_name(&c, test_names[test_cur], sizeof(test_names[0]), &off) != 0)
350 test_names[test_cur][0] = '\0';
357 log("step done:%d", ret);
359 error_msg_and_die("FAILURE: unw_step() returned %d", ret);
363 log("stepping ended");
365 /* Check that the second and third frames are equal, but distinct of the
369 || test_start_ips[1] != test_start_ips[2]
370 || test_start_ips[0] == test_start_ips[1]
371 || test_start_ips[2] == test_start_ips[3]
375 fprintf(stderr, "FAILURE: start IPs incorrect\n");
380 ( strcmp(test_names[0], "a")
381 || strcmp(test_names[1], "b")
382 || strcmp(test_names[2], "b")
383 || strcmp(test_names[3], "main")
387 fprintf(stderr, "FAILURE: procedure names are missing/incorrect\n");
392 unw_destroy_addr_space(as);