summaryrefslogtreecommitdiff
path: root/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
blob: 747a4a70172817d60337f9eb50ab6c3e25dad707 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//===-- sanitizer_stacktrace_libcdep.cc -----------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries.
//===----------------------------------------------------------------------===//

#include "sanitizer_common.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_stacktrace.h"
#include "sanitizer_stacktrace_printer.h"
#include "sanitizer_symbolizer.h"

namespace __sanitizer {

void StackTrace::Print() const {
  if (trace == nullptr || size == 0) {
    Printf("    <empty stack>\n\n");
    return;
  }
  InternalScopedString frame_desc(GetPageSizeCached() * 2);
  InternalScopedString dedup_token(GetPageSizeCached());
  int dedup_frames = common_flags()->dedup_token_length;
  uptr frame_num = 0;
  for (uptr i = 0; i < size && trace[i]; i++) {
    // PCs in stack traces are actually the return addresses, that is,
    // addresses of the next instructions after the call.
    uptr pc = GetPreviousInstructionPc(trace[i]);
    SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
    CHECK(frames);
    for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
      frame_desc.clear();
      RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
                  cur->info, common_flags()->symbolize_vs_style,
                  common_flags()->strip_path_prefix);
      Printf("%s\n", frame_desc.data());
      if (dedup_frames-- > 0) {
        if (dedup_token.length())
          dedup_token.append("--");
        if (cur->info.function != nullptr)
          dedup_token.append(cur->info.function);
      }
    }
    frames->ClearAll();
  }
  // Always print a trailing empty line after stack trace.
  Printf("\n");
  if (dedup_token.length())
    Printf("DEDUP_TOKEN: %s\n", dedup_token.data());
}

void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
                                uptr stack_top, uptr stack_bottom,
                                bool request_fast_unwind) {
  top_frame_bp = (max_depth > 0) ? bp : 0;
  // Avoid doing any work for small max_depth.
  if (max_depth == 0) {
    size = 0;
    return;
  }
  if (max_depth == 1) {
    size = 1;
    trace_buffer[0] = pc;
    return;
  }
  if (!WillUseFastUnwind(request_fast_unwind)) {
#if SANITIZER_CAN_SLOW_UNWIND
    if (context)
      SlowUnwindStackWithContext(pc, context, max_depth);
    else
      SlowUnwindStack(pc, max_depth);
#else
    UNREACHABLE("slow unwind requested but not available");
#endif
  } else {
    FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth);
  }
}

static int GetModuleAndOffsetForPc(uptr pc, char *module_name,
                                   uptr module_name_len, uptr *pc_offset) {
  const char *found_module_name = nullptr;
  bool ok = Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(
      pc, &found_module_name, pc_offset);

  if (!ok) return false;

  if (module_name && module_name_len) {
    internal_strncpy(module_name, found_module_name, module_name_len);
    module_name[module_name_len - 1] = '\x00';
  }
  return true;
}

}  // namespace __sanitizer
using namespace __sanitizer;

extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
                              uptr out_buf_size) {
  if (!out_buf_size) return;
  pc = StackTrace::GetPreviousInstructionPc(pc);
  SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
  if (!frame) {
    internal_strncpy(out_buf, "<can't symbolize>", out_buf_size);
    out_buf[out_buf_size - 1] = 0;
    return;
  }
  InternalScopedString frame_desc(GetPageSizeCached());
  RenderFrame(&frame_desc, fmt, 0, frame->info,
              common_flags()->symbolize_vs_style,
              common_flags()->strip_path_prefix);
  internal_strncpy(out_buf, frame_desc.data(), out_buf_size);
  out_buf[out_buf_size - 1] = 0;
}

SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_symbolize_global(uptr data_addr, const char *fmt,
                                  char *out_buf, uptr out_buf_size) {
  if (!out_buf_size) return;
  out_buf[0] = 0;
  DataInfo DI;
  if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr, &DI)) return;
  InternalScopedString data_desc(GetPageSizeCached());
  RenderData(&data_desc, fmt, &DI, common_flags()->strip_path_prefix);
  internal_strncpy(out_buf, data_desc.data(), out_buf_size);
  out_buf[out_buf_size - 1] = 0;
}

SANITIZER_INTERFACE_ATTRIBUTE
int __sanitizer_get_module_and_offset_for_pc( // NOLINT
    uptr pc, char *module_name, uptr module_name_len, uptr *pc_offset) {
  return __sanitizer::GetModuleAndOffsetForPc(pc, module_name, module_name_len,
                                              pc_offset);
}
}  // extern "C"