1 | /* |
2 | Copyright (c) 2001, 2011, Oracle and/or its affiliates |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
16 | |
17 | #include <my_global.h> |
18 | #include <my_stacktrace.h> |
19 | |
20 | #ifndef __WIN__ |
21 | #include <signal.h> |
22 | #include <my_pthread.h> |
23 | #include <m_string.h> |
24 | #ifdef HAVE_STACKTRACE |
25 | #include <unistd.h> |
26 | #include <strings.h> |
27 | |
28 | #ifdef __linux__ |
29 | #include <ctype.h> /* isprint */ |
30 | #include <sys/syscall.h> /* SYS_gettid */ |
31 | #endif |
32 | |
33 | #if HAVE_EXECINFO_H |
34 | #include <execinfo.h> |
35 | #endif |
36 | |
37 | #define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end) |
38 | |
39 | static char *heap_start; |
40 | |
41 | #if(defined HAVE_BSS_START) && !(defined __linux__) |
42 | extern char *__bss_start; |
43 | #endif |
44 | |
45 | void my_init_stacktrace() |
46 | { |
47 | #if(defined HAVE_BSS_START) && !(defined __linux__) |
48 | heap_start = (char*) &__bss_start; |
49 | #endif |
50 | } |
51 | |
52 | #ifdef __linux__ |
53 | |
54 | static void print_buffer(char *buffer, size_t count) |
55 | { |
56 | const char s[]= " " ; |
57 | for (; count && *buffer; --count) |
58 | { |
59 | my_write_stderr(isprint(*buffer) ? buffer : s, 1); |
60 | ++buffer; |
61 | } |
62 | } |
63 | |
64 | /** |
65 | Access the pages of this process through /proc/self/task/<tid>/mem |
66 | in order to safely print the contents of a memory address range. |
67 | |
68 | @param addr The address at the start of the memory region. |
69 | @param max_len The length of the memory region. |
70 | |
71 | @return Zero on success. |
72 | */ |
73 | static int safe_print_str(const char *addr, size_t max_len) |
74 | { |
75 | int fd; |
76 | pid_t tid; |
77 | off_t offset; |
78 | ssize_t nbytes= 0; |
79 | size_t total, count; |
80 | char buf[256]; |
81 | |
82 | tid= (pid_t) syscall(SYS_gettid); |
83 | |
84 | sprintf(buf, "/proc/self/task/%d/mem" , tid); |
85 | |
86 | if ((fd= open(buf, O_RDONLY)) < 0) |
87 | return -1; |
88 | |
89 | /* Ensure that off_t can hold a pointer. */ |
90 | compile_time_assert(sizeof(off_t) >= sizeof(intptr)); |
91 | |
92 | total= max_len; |
93 | offset= (intptr) addr; |
94 | |
95 | /* Read up to the maximum number of bytes. */ |
96 | while (total) |
97 | { |
98 | count= MY_MIN(sizeof(buf), total); |
99 | |
100 | if ((nbytes= pread(fd, buf, count, offset)) < 0) |
101 | { |
102 | /* Just in case... */ |
103 | if (errno == EINTR) |
104 | continue; |
105 | else |
106 | break; |
107 | } |
108 | |
109 | /* Advance offset into memory. */ |
110 | total-= nbytes; |
111 | offset+= nbytes; |
112 | addr+= nbytes; |
113 | |
114 | /* Output the printable characters. */ |
115 | print_buffer(buf, nbytes); |
116 | |
117 | /* Break if less than requested... */ |
118 | if ((count - nbytes)) |
119 | break; |
120 | } |
121 | |
122 | if (nbytes == -1) |
123 | my_safe_printf_stderr("Can't read from address %p" , addr); |
124 | |
125 | close(fd); |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | #endif |
131 | |
132 | /* |
133 | Attempt to print a char * pointer as a string. |
134 | |
135 | SYNOPSIS |
136 | Prints either until the end of string ('\0'), or max_len characters have |
137 | been printed. |
138 | |
139 | RETURN VALUE |
140 | 0 Pointer was within the heap address space. |
141 | The string was printed fully, or until the end of the heap address space. |
142 | 1 Pointer is outside the heap address space. Printed as invalid. |
143 | |
144 | NOTE |
145 | On some systems, we can have valid pointers outside the heap address space. |
146 | This is through the use of mmap inside malloc calls. When this function |
147 | returns 1, it does not mean 100% that the pointer is corrupted. |
148 | */ |
149 | |
150 | int my_safe_print_str(const char* val, size_t max_len) |
151 | { |
152 | char *heap_end; |
153 | |
154 | #ifdef __linux__ |
155 | // Try and make use of /proc filesystem to safely print memory contents. |
156 | if (!safe_print_str(val, max_len)) |
157 | return 0; |
158 | #endif |
159 | |
160 | heap_end= (char*) sbrk(0); |
161 | |
162 | if (!PTR_SANE(val)) |
163 | { |
164 | my_safe_printf_stderr("%s" , "is an invalid pointer" ); |
165 | return 1; |
166 | } |
167 | |
168 | for (; max_len && PTR_SANE(val) && *val; --max_len) |
169 | my_write_stderr((val++), 1); |
170 | my_safe_printf_stderr("%s" , "\n" ); |
171 | |
172 | return 0; |
173 | } |
174 | |
175 | #if defined(HAVE_PRINTSTACK) |
176 | |
177 | /* Use Solaris' symbolic stack trace routine. */ |
178 | #include <ucontext.h> |
179 | |
180 | void my_print_stacktrace(uchar* stack_bottom __attribute__((unused)), |
181 | ulong thread_stack __attribute__((unused)), |
182 | my_bool silent) |
183 | { |
184 | if (printstack(fileno(stderr)) == -1) |
185 | my_safe_printf_stderr("%s" , |
186 | "Error when traversing the stack, stack appears corrupt.\n" ); |
187 | else if (!silent) |
188 | my_safe_printf_stderr("%s" , |
189 | "Please read " |
190 | "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n" |
191 | "and follow instructions on how to resolve the stack trace.\n" |
192 | "Resolved stack trace is much more helpful in diagnosing the\n" |
193 | "problem, so please do resolve it\n" ); |
194 | } |
195 | |
196 | #elif HAVE_BACKTRACE && (HAVE_BACKTRACE_SYMBOLS || HAVE_BACKTRACE_SYMBOLS_FD) |
197 | |
198 | #if BACKTRACE_DEMANGLE |
199 | |
200 | char __attribute__ ((weak)) * |
201 | my_demangle(const char *mangled_name __attribute__((unused)), |
202 | int *status __attribute__((unused))) |
203 | { |
204 | return NULL; |
205 | } |
206 | |
207 | static void my_demangle_symbols(char **addrs, int n) |
208 | { |
209 | int status, i; |
210 | char *begin, *end, *demangled; |
211 | |
212 | for (i= 0; i < n; i++) |
213 | { |
214 | demangled= NULL; |
215 | begin= strchr(addrs[i], '('); |
216 | end= begin ? strchr(begin, '+') : NULL; |
217 | |
218 | if (begin && end) |
219 | { |
220 | *begin++= *end++= '\0'; |
221 | demangled= my_demangle(begin, &status); |
222 | if (!demangled || status) |
223 | { |
224 | demangled= NULL; |
225 | begin[-1]= '('; |
226 | end[-1]= '+'; |
227 | } |
228 | } |
229 | |
230 | if (demangled) |
231 | my_safe_printf_stderr("%s(%s+%s\n" , addrs[i], demangled, end); |
232 | else |
233 | my_safe_printf_stderr("%s\n" , addrs[i]); |
234 | } |
235 | } |
236 | |
237 | #endif /* BACKTRACE_DEMANGLE */ |
238 | |
239 | #if HAVE_MY_ADDR_RESOLVE |
240 | static int print_with_addr_resolve(void **addrs, int n) |
241 | { |
242 | int i; |
243 | const char *err; |
244 | |
245 | if ((err= my_addr_resolve_init())) |
246 | { |
247 | my_safe_printf_stderr("(my_addr_resolve failure: %s)\n" , err); |
248 | return 0; |
249 | } |
250 | |
251 | for (i= 0; i < n; i++) |
252 | { |
253 | my_addr_loc loc; |
254 | if (my_addr_resolve(addrs[i], &loc)) |
255 | backtrace_symbols_fd(addrs+i, 1, fileno(stderr)); |
256 | else |
257 | my_safe_printf_stderr("%s:%u(%s)[%p]\n" , |
258 | loc.file, loc.line, loc.func, addrs[i]); |
259 | } |
260 | return 1; |
261 | } |
262 | #endif |
263 | |
264 | void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack, |
265 | my_bool silent __attribute__((unused))) |
266 | { |
267 | void *addrs[128]; |
268 | char **strings __attribute__((unused)) = NULL; |
269 | int n = backtrace(addrs, array_elements(addrs)); |
270 | my_safe_printf_stderr("stack_bottom = %p thread_stack 0x%lx\n" , |
271 | stack_bottom, thread_stack); |
272 | #if HAVE_MY_ADDR_RESOLVE |
273 | if (print_with_addr_resolve(addrs, n)) |
274 | return; |
275 | #endif |
276 | #if BACKTRACE_DEMANGLE |
277 | if ((strings= backtrace_symbols(addrs, n))) |
278 | { |
279 | my_demangle_symbols(strings, n); |
280 | free(strings); |
281 | return; |
282 | } |
283 | #endif |
284 | #if HAVE_BACKTRACE_SYMBOLS_FD |
285 | backtrace_symbols_fd(addrs, n, fileno(stderr)); |
286 | #endif |
287 | } |
288 | |
289 | #elif defined(TARGET_OS_LINUX) |
290 | |
291 | #ifdef __i386__ |
292 | #define SIGRETURN_FRAME_OFFSET 17 |
293 | #endif |
294 | |
295 | #ifdef __x86_64__ |
296 | #define SIGRETURN_FRAME_OFFSET 23 |
297 | #endif |
298 | |
299 | #if defined(__alpha__) && defined(__GNUC__) |
300 | /* |
301 | The only way to backtrace without a symbol table on alpha |
302 | is to find stq fp,N(sp), and the first byte |
303 | of the instruction opcode will give us the value of N. From this |
304 | we can find where the old value of fp is stored |
305 | */ |
306 | |
307 | #define MAX_INSTR_IN_FUNC 10000 |
308 | |
309 | inline uchar** find_prev_fp(uint32* pc, uchar** fp) |
310 | { |
311 | int i; |
312 | for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc) |
313 | { |
314 | uchar* p = (uchar*)pc; |
315 | if (p[2] == 222 && p[3] == 35) |
316 | { |
317 | return (uchar**)((uchar*)fp - *(short int*)p); |
318 | } |
319 | } |
320 | return 0; |
321 | } |
322 | |
323 | inline uint32* find_prev_pc(uint32* pc, uchar** fp) |
324 | { |
325 | int i; |
326 | for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc) |
327 | { |
328 | char* p = (char*)pc; |
329 | if (p[1] == 0 && p[2] == 94 && p[3] == -73) |
330 | { |
331 | uint32* prev_pc = (uint32*)*((fp+p[0]/sizeof(fp))); |
332 | return prev_pc; |
333 | } |
334 | } |
335 | return 0; |
336 | } |
337 | #endif /* defined(__alpha__) && defined(__GNUC__) */ |
338 | |
339 | void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack, |
340 | my_bool silent) |
341 | { |
342 | uchar** UNINIT_VAR(fp); |
343 | uint frame_count = 0, sigreturn_frame_count; |
344 | #if defined(__alpha__) && defined(__GNUC__) |
345 | uint32* pc; |
346 | #endif |
347 | |
348 | |
349 | #ifdef __i386__ |
350 | __asm __volatile__ ("movl %%ebp,%0" |
351 | :"=r" (fp) |
352 | :"r" (fp)); |
353 | #endif |
354 | #ifdef __x86_64__ |
355 | __asm __volatile__ ("movq %%rbp,%0" |
356 | :"=r" (fp) |
357 | :"r" (fp)); |
358 | #endif |
359 | #if defined(__alpha__) && defined(__GNUC__) |
360 | __asm __volatile__ ("mov $30,%0" |
361 | :"=r" (fp) |
362 | :"r" (fp)); |
363 | #endif |
364 | if (!fp) |
365 | { |
366 | my_safe_printf_stderr("%s" , |
367 | "frame pointer is NULL, did you compile with\n" |
368 | "-fomit-frame-pointer? Aborting backtrace!\n" ); |
369 | return; |
370 | } |
371 | |
372 | if (!stack_bottom || (uchar*) stack_bottom > (uchar*) &fp) |
373 | { |
374 | ulong tmp= MY_MIN(0x10000,thread_stack); |
375 | /* Assume that the stack starts at the previous even 65K */ |
376 | stack_bottom= (uchar*) (((ulong) &fp + tmp) & ~(ulong) 0xFFFF); |
377 | my_safe_printf_stderr("Cannot determine thread, fp=%p, " |
378 | "backtrace may not be correct.\n" , fp); |
379 | } |
380 | if (fp > (uchar**) stack_bottom || |
381 | fp < (uchar**) stack_bottom - thread_stack) |
382 | { |
383 | my_safe_printf_stderr("Bogus stack limit or frame pointer, " |
384 | "fp=%p, stack_bottom=%p, thread_stack=%ld, " |
385 | "aborting backtrace.\n" , |
386 | fp, stack_bottom, thread_stack); |
387 | return; |
388 | } |
389 | |
390 | my_safe_printf_stderr("%s" , |
391 | "Stack range sanity check OK, backtrace follows:\n" ); |
392 | #if defined(__alpha__) && defined(__GNUC__) |
393 | my_safe_printf_stderr("%s" , |
394 | "Warning: Alpha stacks are difficult -" |
395 | "will be taking some wild guesses, stack trace may be incorrect or " |
396 | "terminate abruptly\n" ); |
397 | |
398 | /* On Alpha, we need to get pc */ |
399 | __asm __volatile__ ("bsr %0, do_next; do_next: " |
400 | :"=r" (pc) |
401 | :"r" (pc)); |
402 | #endif /* __alpha__ */ |
403 | |
404 | /* We are 1 frame above signal frame with NPTL and 2 frames above with LT */ |
405 | sigreturn_frame_count = thd_lib_detected == THD_LIB_LT ? 2 : 1; |
406 | |
407 | while (fp < (uchar**) stack_bottom) |
408 | { |
409 | #if defined(__i386__) || defined(__x86_64__) |
410 | uchar** new_fp = (uchar**)*fp; |
411 | my_safe_printf_stderr("%p\n" , |
412 | frame_count == sigreturn_frame_count ? |
413 | *(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1)); |
414 | #endif /* defined(__386__) || defined(__x86_64__) */ |
415 | |
416 | #if defined(__alpha__) && defined(__GNUC__) |
417 | uchar** new_fp = find_prev_fp(pc, fp); |
418 | if (frame_count == sigreturn_frame_count - 1) |
419 | { |
420 | new_fp += 90; |
421 | } |
422 | |
423 | if (fp && pc) |
424 | { |
425 | pc = find_prev_pc(pc, fp); |
426 | if (pc) |
427 | my_safe_printf_stderr("%p\n" , pc); |
428 | else |
429 | { |
430 | my_safe_printf_stderr("%s" , |
431 | "Not smart enough to deal with the rest of this stack\n" ); |
432 | goto end; |
433 | } |
434 | } |
435 | else |
436 | { |
437 | my_safe_printf_stderr("%s" , |
438 | "Not smart enough to deal with the rest of this stack\n" ); |
439 | goto end; |
440 | } |
441 | #endif /* defined(__alpha__) && defined(__GNUC__) */ |
442 | if (new_fp <= fp ) |
443 | { |
444 | my_safe_printf_stderr("New value of fp=%p failed sanity check, " |
445 | "terminating stack trace!\n" , new_fp); |
446 | goto end; |
447 | } |
448 | fp = new_fp; |
449 | ++frame_count; |
450 | } |
451 | my_safe_printf_stderr("%s" , |
452 | "Stack trace seems successful - bottom reached\n" ); |
453 | |
454 | end: |
455 | if (!silent) |
456 | my_safe_printf_stderr("%s" , |
457 | "Please read " |
458 | "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n" |
459 | "and follow instructions on how to resolve the stack trace.\n" |
460 | "Resolved stack trace is much more helpful in diagnosing the\n" |
461 | "problem, so please do resolve it\n" ); |
462 | } |
463 | #endif /* TARGET_OS_LINUX */ |
464 | #endif /* HAVE_STACKTRACE */ |
465 | |
466 | /* Produce a core for the thread */ |
467 | void my_write_core(int sig) |
468 | { |
469 | #ifdef HAVE_gcov |
470 | extern void __gcov_flush(void); |
471 | #endif |
472 | signal(sig, SIG_DFL); |
473 | #ifdef HAVE_gcov |
474 | /* |
475 | For GCOV build, crashing will prevent the writing of code coverage |
476 | information from this process, causing gcov output to be incomplete. |
477 | So we force the writing of coverage information here before terminating. |
478 | */ |
479 | __gcov_flush(); |
480 | #endif |
481 | pthread_kill(pthread_self(), sig); |
482 | #if defined(P_MYID) && !defined(SCO) |
483 | /* On Solaris, the above kill is not enough */ |
484 | sigsend(P_PID,P_MYID,sig); |
485 | #endif |
486 | } |
487 | |
488 | #else /* __WIN__*/ |
489 | |
490 | #ifdef _MSC_VER |
491 | /* Silence warning in OS header dbghelp.h */ |
492 | #pragma warning(push) |
493 | #pragma warning(disable : 4091) |
494 | #endif |
495 | |
496 | #include <dbghelp.h> |
497 | |
498 | #ifdef _MSC_VER |
499 | #pragma warning(pop) |
500 | #endif |
501 | |
502 | #include <tlhelp32.h> |
503 | #include <my_sys.h> |
504 | #if _MSC_VER |
505 | #pragma comment(lib, "dbghelp") |
506 | #endif |
507 | |
508 | static EXCEPTION_POINTERS *exception_ptrs; |
509 | |
510 | #define MODULE64_SIZE_WINXP 576 |
511 | #define STACKWALK_MAX_FRAMES 64 |
512 | |
513 | void my_init_stacktrace() |
514 | { |
515 | } |
516 | |
517 | |
518 | void my_set_exception_pointers(EXCEPTION_POINTERS *ep) |
519 | { |
520 | exception_ptrs = ep; |
521 | } |
522 | |
523 | /* |
524 | Appends directory to symbol path. |
525 | */ |
526 | static void add_to_symbol_path(char *path, size_t path_buffer_size, |
527 | char *dir, size_t dir_buffer_size) |
528 | { |
529 | strcat_s(dir, dir_buffer_size, ";" ); |
530 | if (!strstr(path, dir)) |
531 | { |
532 | strcat_s(path, path_buffer_size, dir); |
533 | } |
534 | } |
535 | |
536 | /* |
537 | Get symbol path - semicolon-separated list of directories to search |
538 | for debug symbols. We expect PDB in the same directory as |
539 | corresponding exe or dll, so the path is build from directories of |
540 | the loaded modules. If environment variable _NT_SYMBOL_PATH is set, |
541 | it's value appended to the symbol search path |
542 | */ |
543 | static void get_symbol_path(char *path, size_t size) |
544 | { |
545 | HANDLE hSnap; |
546 | char *envvar; |
547 | char *p; |
548 | #ifndef DBUG_OFF |
549 | static char pdb_debug_dir[MAX_PATH + 7]; |
550 | #endif |
551 | |
552 | path[0]= '\0'; |
553 | |
554 | #ifndef DBUG_OFF |
555 | /* |
556 | Add "debug" subdirectory of the application directory, sometimes PDB will |
557 | placed here by installation. |
558 | */ |
559 | GetModuleFileName(NULL, pdb_debug_dir, MAX_PATH); |
560 | p= strrchr(pdb_debug_dir, '\\'); |
561 | if(p) |
562 | { |
563 | *p= 0; |
564 | strcat_s(pdb_debug_dir, sizeof(pdb_debug_dir), "\\debug;" ); |
565 | add_to_symbol_path(path, size, pdb_debug_dir, sizeof(pdb_debug_dir)); |
566 | } |
567 | #endif |
568 | |
569 | /* |
570 | Enumerate all modules, and add their directories to the path. |
571 | Avoid duplicate entries. |
572 | */ |
573 | hSnap= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); |
574 | if (hSnap != INVALID_HANDLE_VALUE) |
575 | { |
576 | BOOL ret; |
577 | MODULEENTRY32 mod; |
578 | mod.dwSize= sizeof(MODULEENTRY32); |
579 | for (ret= Module32First(hSnap, &mod); ret; ret= Module32Next(hSnap, &mod)) |
580 | { |
581 | char *module_dir= mod.szExePath; |
582 | p= strrchr(module_dir,'\\'); |
583 | if (!p) |
584 | { |
585 | /* |
586 | Path separator was not found. Not known to happen, if ever happens, |
587 | will indicate current directory. |
588 | */ |
589 | module_dir[0]= '.'; |
590 | module_dir[1]= '\0'; |
591 | } |
592 | else |
593 | { |
594 | *p= '\0'; |
595 | } |
596 | add_to_symbol_path(path, size, module_dir,sizeof(mod.szExePath)); |
597 | } |
598 | CloseHandle(hSnap); |
599 | } |
600 | |
601 | |
602 | /* Add _NT_SYMBOL_PATH, if present. */ |
603 | envvar= getenv("_NT_SYMBOL_PATH" ); |
604 | if(envvar) |
605 | { |
606 | strcat_s(path, size, envvar); |
607 | } |
608 | } |
609 | |
610 | #define MAX_SYMBOL_PATH 32768 |
611 | |
612 | /* Platform SDK in VS2003 does not have definition for SYMOPT_NO_PROMPTS*/ |
613 | #ifndef SYMOPT_NO_PROMPTS |
614 | #define SYMOPT_NO_PROMPTS 0 |
615 | #endif |
616 | |
617 | void my_print_stacktrace(uchar* unused1, ulong unused2, my_bool silent) |
618 | { |
619 | HANDLE hProcess= GetCurrentProcess(); |
620 | HANDLE hThread= GetCurrentThread(); |
621 | static IMAGEHLP_MODULE64 module= {sizeof(module)}; |
622 | static IMAGEHLP_SYMBOL64_PACKAGE package; |
623 | DWORD64 addr; |
624 | DWORD machine; |
625 | int i; |
626 | CONTEXT context; |
627 | STACKFRAME64 frame={0}; |
628 | static char symbol_path[MAX_SYMBOL_PATH]; |
629 | |
630 | if(!exception_ptrs) |
631 | return; |
632 | |
633 | /* Copy context, as stackwalking on original will unwind the stack */ |
634 | context = *(exception_ptrs->ContextRecord); |
635 | /*Initialize symbols.*/ |
636 | SymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_NO_PROMPTS|SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG); |
637 | get_symbol_path(symbol_path, sizeof(symbol_path)); |
638 | SymInitialize(hProcess, symbol_path, TRUE); |
639 | |
640 | /*Prepare stackframe for the first StackWalk64 call*/ |
641 | frame.AddrFrame.Mode= frame.AddrPC.Mode= frame.AddrStack.Mode= AddrModeFlat; |
642 | #if (defined _M_IX86) |
643 | machine= IMAGE_FILE_MACHINE_I386; |
644 | frame.AddrFrame.Offset= context.Ebp; |
645 | frame.AddrPC.Offset= context.Eip; |
646 | frame.AddrStack.Offset= context.Esp; |
647 | #elif (defined _M_X64) |
648 | machine = IMAGE_FILE_MACHINE_AMD64; |
649 | frame.AddrFrame.Offset= context.Rbp; |
650 | frame.AddrPC.Offset= context.Rip; |
651 | frame.AddrStack.Offset= context.Rsp; |
652 | #else |
653 | /*There is currently no need to support IA64*/ |
654 | #pragma error ("unsupported architecture") |
655 | #endif |
656 | |
657 | package.sym.SizeOfStruct= sizeof(package.sym); |
658 | package.sym.MaxNameLength= sizeof(package.name); |
659 | |
660 | /*Walk the stack, output useful information*/ |
661 | for(i= 0; i< STACKWALK_MAX_FRAMES;i++) |
662 | { |
663 | DWORD64 function_offset= 0; |
664 | DWORD line_offset= 0; |
665 | IMAGEHLP_LINE64 line= {sizeof(line)}; |
666 | BOOL have_module= FALSE; |
667 | BOOL have_symbol= FALSE; |
668 | BOOL have_source= FALSE; |
669 | |
670 | if(!StackWalk64(machine, hProcess, hThread, &frame, &context, 0, 0, 0 ,0)) |
671 | break; |
672 | addr= frame.AddrPC.Offset; |
673 | |
674 | have_module= SymGetModuleInfo64(hProcess,addr,&module); |
675 | #ifdef _M_IX86 |
676 | if(!have_module) |
677 | { |
678 | /* |
679 | ModuleInfo structure has been "compatibly" extended in |
680 | releases after XP, and its size was increased. To make XP |
681 | dbghelp.dll function happy, pretend passing the old structure. |
682 | */ |
683 | module.SizeOfStruct= MODULE64_SIZE_WINXP; |
684 | have_module= SymGetModuleInfo64(hProcess, addr, &module); |
685 | } |
686 | #endif |
687 | |
688 | have_symbol= SymGetSymFromAddr64(hProcess, addr, &function_offset, |
689 | &(package.sym)); |
690 | have_source= SymGetLineFromAddr64(hProcess, addr, &line_offset, &line); |
691 | |
692 | if(have_module) |
693 | { |
694 | const char *base_image_name= my_basename(module.ImageName); |
695 | my_safe_printf_stderr("%s!" , base_image_name); |
696 | } |
697 | if(have_symbol) |
698 | my_safe_printf_stderr("%s()" , package.sym.Name); |
699 | |
700 | else if(have_module) |
701 | my_safe_printf_stderr("%s" , "???" ); |
702 | |
703 | if(have_source) |
704 | { |
705 | const char *base_file_name= my_basename(line.FileName); |
706 | my_safe_printf_stderr("[%s:%lu]" , |
707 | base_file_name, line.LineNumber); |
708 | } |
709 | my_safe_printf_stderr("%s" , "\n" ); |
710 | } |
711 | } |
712 | |
713 | |
714 | /* |
715 | Write dump. The dump is created in current directory, |
716 | file name is constructed from executable name plus |
717 | ".dmp" extension |
718 | */ |
719 | void my_write_core(int unused) |
720 | { |
721 | char path[MAX_PATH]; |
722 | char dump_fname[MAX_PATH]= "core.dmp" ; |
723 | MINIDUMP_EXCEPTION_INFORMATION info; |
724 | HANDLE hFile; |
725 | |
726 | if(!exception_ptrs) |
727 | return; |
728 | |
729 | info.ExceptionPointers= exception_ptrs; |
730 | info.ClientPointers= FALSE; |
731 | info.ThreadId= GetCurrentThreadId(); |
732 | |
733 | if(GetModuleFileName(NULL, path, sizeof(path))) |
734 | { |
735 | _splitpath(path, NULL, NULL,dump_fname,NULL); |
736 | strcat_s(dump_fname, sizeof(dump_fname), ".dmp" ); |
737 | } |
738 | |
739 | hFile= CreateFile(dump_fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, |
740 | FILE_ATTRIBUTE_NORMAL, 0); |
741 | if(hFile) |
742 | { |
743 | /* Create minidump */ |
744 | if(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), |
745 | hFile, MiniDumpNormal, &info, 0, 0)) |
746 | { |
747 | my_safe_printf_stderr("Minidump written to %s\n" , |
748 | _fullpath(path, dump_fname, sizeof(path)) ? |
749 | path : dump_fname); |
750 | } |
751 | else |
752 | { |
753 | my_safe_printf_stderr("MiniDumpWriteDump() failed, last error %u\n" , |
754 | (uint) GetLastError()); |
755 | } |
756 | CloseHandle(hFile); |
757 | } |
758 | else |
759 | { |
760 | my_safe_printf_stderr("CreateFile(%s) failed, last error %u\n" , |
761 | dump_fname, (uint) GetLastError()); |
762 | } |
763 | } |
764 | |
765 | |
766 | int my_safe_print_str(const char *val, size_t len) |
767 | { |
768 | __try |
769 | { |
770 | my_write_stderr(val, len); |
771 | } |
772 | __except(EXCEPTION_EXECUTE_HANDLER) |
773 | { |
774 | my_safe_printf_stderr("%s" , "is an invalid string pointer" ); |
775 | } |
776 | return 0; |
777 | } |
778 | #endif /*__WIN__*/ |
779 | |
780 | |
781 | size_t my_write_stderr(const void *buf, size_t count) |
782 | { |
783 | return (size_t) write(fileno(stderr), buf, (uint)count); |
784 | } |
785 | |
786 | |
787 | size_t my_safe_printf_stderr(const char* fmt, ...) |
788 | { |
789 | char to[512]; |
790 | size_t result; |
791 | va_list args; |
792 | va_start(args,fmt); |
793 | result= my_vsnprintf(to, sizeof(to), fmt, args); |
794 | va_end(args); |
795 | my_write_stderr(to, result); |
796 | return result; |
797 | } |
798 | |