Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 1 | //===-- sanitizer_stacktrace.cc -------------------------------------------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file is shared between AddressSanitizer and ThreadSanitizer |
| 11 | // run-time libraries. |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
Kostya Serebryany | 9ada1f3 | 2012-08-28 14:48:28 +0000 | [diff] [blame] | 14 | #include "sanitizer_common.h" |
Alexey Samsonov | 90b0f1e | 2013-10-04 08:55:03 +0000 | [diff] [blame] | 15 | #include "sanitizer_flags.h" |
Kostya Serebryany | 9ada1f3 | 2012-08-28 14:48:28 +0000 | [diff] [blame] | 16 | #include "sanitizer_procmaps.h" |
| 17 | #include "sanitizer_stacktrace.h" |
| 18 | #include "sanitizer_symbolizer.h" |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 19 | |
| 20 | namespace __sanitizer { |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 21 | |
Alexey Samsonov | d2f08ff | 2012-12-18 09:57:34 +0000 | [diff] [blame] | 22 | uptr StackTrace::GetPreviousInstructionPc(uptr pc) { |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 23 | #ifdef __arm__ |
| 24 | // Cancel Thumb bit. |
| 25 | pc = pc & (~1); |
| 26 | #endif |
Kostya Serebryany | d7d4650 | 2012-11-20 07:00:42 +0000 | [diff] [blame] | 27 | #if defined(__powerpc__) || defined(__powerpc64__) |
| 28 | // PCs are always 4 byte aligned. |
| 29 | return pc - 4; |
Kostya Serebryany | 68aad43 | 2012-12-03 18:39:21 +0000 | [diff] [blame] | 30 | #elif defined(__sparc__) |
| 31 | return pc - 8; |
Kostya Serebryany | d7d4650 | 2012-11-20 07:00:42 +0000 | [diff] [blame] | 32 | #else |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 33 | return pc - 1; |
Kostya Serebryany | d7d4650 | 2012-11-20 07:00:42 +0000 | [diff] [blame] | 34 | #endif |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 35 | } |
| 36 | |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 37 | static void PrintStackFramePrefix(InternalScopedString *buffer, uptr frame_num, |
| 38 | uptr pc) { |
| 39 | buffer->append(" #%zu 0x%zx", frame_num, pc); |
Alexey Samsonov | 1ca5357 | 2012-10-02 12:11:17 +0000 | [diff] [blame] | 40 | } |
| 41 | |
Alexey Samsonov | 7996a2e | 2013-10-29 05:31:25 +0000 | [diff] [blame] | 42 | void StackTrace::PrintStack(const uptr *addr, uptr size, |
Alexey Samsonov | 90b0f1e | 2013-10-04 08:55:03 +0000 | [diff] [blame] | 43 | SymbolizeCallback symbolize_callback) { |
Alexey Samsonov | a96c4dc | 2013-11-01 00:19:46 +0000 | [diff] [blame] | 44 | if (addr == 0) { |
| 45 | Printf("<empty stack>\n\n"); |
| 46 | return; |
| 47 | } |
Alexander Potapenko | 9ae2883 | 2013-03-26 10:34:37 +0000 | [diff] [blame] | 48 | MemoryMappingLayout proc_maps(/*cache_enabled*/true); |
Kostya Serebryany | e89f184 | 2012-11-24 05:03:11 +0000 | [diff] [blame] | 49 | InternalScopedBuffer<char> buff(GetPageSizeCached() * 2); |
Kostya Serebryany | 4fa111c | 2012-08-29 08:40:36 +0000 | [diff] [blame] | 50 | InternalScopedBuffer<AddressInfo> addr_frames(64); |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 51 | InternalScopedString frame_desc(GetPageSizeCached() * 2); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 52 | uptr frame_num = 0; |
| 53 | for (uptr i = 0; i < size && addr[i]; i++) { |
Alexey Samsonov | d2f08ff | 2012-12-18 09:57:34 +0000 | [diff] [blame] | 54 | // PCs in stack traces are actually the return addresses, that is, |
| 55 | // addresses of the next instructions after the call. |
| 56 | uptr pc = GetPreviousInstructionPc(addr[i]); |
Alexey Samsonov | 1ca5357 | 2012-10-02 12:11:17 +0000 | [diff] [blame] | 57 | uptr addr_frames_num = 0; // The number of stack frames for current |
| 58 | // instruction address. |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 59 | if (symbolize_callback) { |
Alexey Samsonov | 1ca5357 | 2012-10-02 12:11:17 +0000 | [diff] [blame] | 60 | if (symbolize_callback((void*)pc, buff.data(), buff.size())) { |
| 61 | addr_frames_num = 1; |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 62 | frame_desc.clear(); |
| 63 | PrintStackFramePrefix(&frame_desc, frame_num, pc); |
Alexey Samsonov | 1ca5357 | 2012-10-02 12:11:17 +0000 | [diff] [blame] | 64 | // We can't know anything about the string returned by external |
| 65 | // symbolizer, but if it starts with filename, try to strip path prefix |
| 66 | // from it. |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 67 | frame_desc.append( |
| 68 | " %s", |
| 69 | StripPathPrefix(buff.data(), common_flags()->strip_path_prefix)); |
| 70 | Printf("%s\n", frame_desc.data()); |
Alexey Samsonov | 1ca5357 | 2012-10-02 12:11:17 +0000 | [diff] [blame] | 71 | frame_num++; |
| 72 | } |
Alexey Samsonov | d5951e6 | 2012-10-18 11:46:22 +0000 | [diff] [blame] | 73 | } |
Alexey Samsonov | 7996a2e | 2013-10-29 05:31:25 +0000 | [diff] [blame] | 74 | if (common_flags()->symbolize && addr_frames_num == 0) { |
Alexey Samsonov | 1ca5357 | 2012-10-02 12:11:17 +0000 | [diff] [blame] | 75 | // Use our own (online) symbolizer, if necessary. |
Peter Collingbourne | c1a1ed6 | 2013-10-25 23:03:29 +0000 | [diff] [blame] | 76 | if (Symbolizer *sym = Symbolizer::GetOrNull()) |
| 77 | addr_frames_num = |
| 78 | sym->SymbolizeCode(pc, addr_frames.data(), addr_frames.size()); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 79 | for (uptr j = 0; j < addr_frames_num; j++) { |
| 80 | AddressInfo &info = addr_frames[j]; |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 81 | frame_desc.clear(); |
| 82 | PrintStackFramePrefix(&frame_desc, frame_num, pc); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 83 | if (info.function) { |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 84 | frame_desc.append(" in %s", info.function); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 85 | } |
| 86 | if (info.file) { |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 87 | frame_desc.append(" "); |
| 88 | PrintSourceLocation(&frame_desc, info.file, info.line, info.column); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 89 | } else if (info.module) { |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 90 | frame_desc.append(" "); |
| 91 | PrintModuleAndOffset(&frame_desc, info.module, info.module_offset); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 92 | } |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 93 | Printf("%s\n", frame_desc.data()); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 94 | frame_num++; |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 95 | info.Clear(); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 96 | } |
Alexey Samsonov | 1ca5357 | 2012-10-02 12:11:17 +0000 | [diff] [blame] | 97 | } |
| 98 | if (addr_frames_num == 0) { |
| 99 | // If online symbolization failed, try to output at least module and |
| 100 | // offset for instruction. |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 101 | frame_desc.clear(); |
| 102 | PrintStackFramePrefix(&frame_desc, frame_num, pc); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 103 | uptr offset; |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 104 | if (proc_maps.GetObjectNameAndOffset(pc, &offset, |
Alexey Samsonov | 45717c9 | 2013-03-13 06:51:02 +0000 | [diff] [blame] | 105 | buff.data(), buff.size(), |
| 106 | /* protection */0)) { |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 107 | frame_desc.append(" "); |
| 108 | PrintModuleAndOffset(&frame_desc, buff.data(), offset); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 109 | } |
Alexey Samsonov | bb4697f | 2013-11-14 09:41:24 +0000 | [diff] [blame^] | 110 | Printf("%s\n", frame_desc.data()); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 111 | frame_num++; |
| 112 | } |
| 113 | } |
Alexey Samsonov | a96c4dc | 2013-11-01 00:19:46 +0000 | [diff] [blame] | 114 | // Always print a trailing empty line after stack trace. |
| 115 | Printf("\n"); |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | uptr StackTrace::GetCurrentPc() { |
| 119 | return GET_CALLER_PC(); |
| 120 | } |
| 121 | |
| 122 | void StackTrace::FastUnwindStack(uptr pc, uptr bp, |
Alexey Samsonov | 064da32 | 2013-10-11 09:58:30 +0000 | [diff] [blame] | 123 | uptr stack_top, uptr stack_bottom, |
| 124 | uptr max_depth) { |
Alexey Samsonov | 3e0b8ff | 2013-10-12 12:40:47 +0000 | [diff] [blame] | 125 | if (max_depth == 0) { |
Alexey Samsonov | 064da32 | 2013-10-11 09:58:30 +0000 | [diff] [blame] | 126 | size = 0; |
| 127 | return; |
| 128 | } |
| 129 | trace[0] = pc; |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 130 | size = 1; |
Kostya Serebryany | 574618a | 2012-11-19 10:31:00 +0000 | [diff] [blame] | 131 | uhwptr *frame = (uhwptr *)bp; |
Reid Kleckner | 20aed57 | 2013-02-20 20:29:48 +0000 | [diff] [blame] | 132 | uhwptr *prev_frame = frame - 1; |
Kostya Serebryany | 8301c73 | 2013-06-18 14:47:40 +0000 | [diff] [blame] | 133 | if (stack_top < 4096) return; // Sanity check for stack top. |
Reid Kleckner | 20aed57 | 2013-02-20 20:29:48 +0000 | [diff] [blame] | 134 | // Avoid infinite loop when frame == frame[0] by using frame > prev_frame. |
| 135 | while (frame > prev_frame && |
Kostya Serebryany | 574618a | 2012-11-19 10:31:00 +0000 | [diff] [blame] | 136 | frame < (uhwptr *)stack_top - 2 && |
| 137 | frame > (uhwptr *)stack_bottom && |
Kostya Serebryany | 583025d | 2013-04-04 06:52:40 +0000 | [diff] [blame] | 138 | IsAligned((uptr)frame, sizeof(*frame)) && |
Alexey Samsonov | 3e0b8ff | 2013-10-12 12:40:47 +0000 | [diff] [blame] | 139 | size < max_depth) { |
Kostya Serebryany | 574618a | 2012-11-19 10:31:00 +0000 | [diff] [blame] | 140 | uhwptr pc1 = frame[1]; |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 141 | if (pc1 != pc) { |
Kostya Serebryany | 574618a | 2012-11-19 10:31:00 +0000 | [diff] [blame] | 142 | trace[size++] = (uptr) pc1; |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 143 | } |
| 144 | prev_frame = frame; |
Kostya Serebryany | 574618a | 2012-11-19 10:31:00 +0000 | [diff] [blame] | 145 | frame = (uhwptr *)frame[0]; |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 146 | } |
| 147 | } |
| 148 | |
Kostya Serebryany | d7d4650 | 2012-11-20 07:00:42 +0000 | [diff] [blame] | 149 | void StackTrace::PopStackFrames(uptr count) { |
Evgeniy Stepanov | 6523fe2 | 2012-11-21 13:00:04 +0000 | [diff] [blame] | 150 | CHECK(size >= count); |
Kostya Serebryany | d7d4650 | 2012-11-20 07:00:42 +0000 | [diff] [blame] | 151 | size -= count; |
| 152 | for (uptr i = 0; i < size; i++) { |
| 153 | trace[i] = trace[i + count]; |
| 154 | } |
| 155 | } |
| 156 | |
Timur Iskhodzhanov | 7177d2b | 2013-11-09 13:59:12 +0000 | [diff] [blame] | 157 | static bool MatchPc(uptr cur_pc, uptr trace_pc, uptr threshold) { |
| 158 | return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold; |
| 159 | } |
| 160 | |
| 161 | uptr |
Alexey Samsonov | 24d775e | 2013-11-13 15:20:10 +0000 | [diff] [blame] | 162 | StackTrace::LocatePcInTrace(uptr pc, uptr max_pc_depth) { |
| 163 | // Use threshold to find PC in stack trace, as PC we want to unwind from may |
| 164 | // slightly differ from return address in the actual unwinded stack trace. |
| 165 | const int kPcThreshold = 64; |
Timur Iskhodzhanov | 7177d2b | 2013-11-09 13:59:12 +0000 | [diff] [blame] | 166 | for (uptr i = 0; i < max_pc_depth && i < size; ++i) { |
Alexey Samsonov | 24d775e | 2013-11-13 15:20:10 +0000 | [diff] [blame] | 167 | if (MatchPc(pc, trace[i], kPcThreshold)) |
Timur Iskhodzhanov | 7177d2b | 2013-11-09 13:59:12 +0000 | [diff] [blame] | 168 | return i; |
| 169 | } |
| 170 | return 0; |
| 171 | } |
| 172 | |
Kostya Serebryany | c7be407 | 2012-08-28 14:27:06 +0000 | [diff] [blame] | 173 | } // namespace __sanitizer |