blob: 64048fa7e58ea4c575390c87183caab41eaba406 [file] [log] [blame]
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -07001//===-- sanitizer_symbolizer_mac.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 various sanitizers' runtime libraries.
11//
12// Implementation of Mac-specific "atos" symbolizer.
13//===----------------------------------------------------------------------===//
14
15#include "sanitizer_platform.h"
16#if SANITIZER_MAC
17
18#include "sanitizer_allocator_internal.h"
19#include "sanitizer_mac.h"
20#include "sanitizer_symbolizer_mac.h"
21
22namespace __sanitizer {
23
24#include <dlfcn.h>
25#include <errno.h>
26#include <stdlib.h>
27#include <sys/wait.h>
28#include <unistd.h>
29#include <util.h>
30
31bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
32 Dl_info info;
33 int result = dladdr((const void *)addr, &info);
34 if (!result) return false;
35 const char *demangled = DemangleCXXABI(info.dli_sname);
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080036 stack->info.function = demangled ? internal_strdup(demangled) : nullptr;
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070037 return true;
38}
39
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080040bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
41 Dl_info info;
42 int result = dladdr((const void *)addr, &info);
43 if (!result) return false;
44 const char *demangled = DemangleCXXABI(info.dli_sname);
45 datainfo->name = internal_strdup(demangled);
46 datainfo->start = (uptr)info.dli_saddr;
47 return true;
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070048}
49
50class AtosSymbolizerProcess : public SymbolizerProcess {
51 public:
52 explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080053 : SymbolizerProcess(path, /*use_forkpty*/ true) {
54 // Put the string command line argument in the object so that it outlives
55 // the call to GetArgV.
56 internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
57 }
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070058
59 private:
60 bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
61 return (length >= 1 && buffer[length - 1] == '\n');
62 }
63
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080064 void GetArgV(const char *path_to_binary,
65 const char *(&argv)[kArgVMax]) const override {
66 int i = 0;
67 argv[i++] = path_to_binary;
68 argv[i++] = "-p";
69 argv[i++] = &pid_str_[0];
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070070 if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
71 // On Mavericks atos prints a deprecation warning which we suppress by
72 // passing -d. The warning isn't present on other OSX versions, even the
73 // newer ones.
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080074 argv[i++] = "-d";
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070075 }
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080076 argv[i++] = nullptr;
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070077 }
78
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080079 char pid_str_[16];
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070080};
81
82static const char *kAtosErrorMessages[] = {
83 "atos cannot examine process",
84 "unable to get permission to examine process",
85 "An admin user name and password is required",
86 "could not load inserted library",
87 "architecture mismatch between analysis process",
88};
89
90static bool IsAtosErrorMessage(const char *str) {
91 for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
92 if (internal_strstr(str, kAtosErrorMessages[i])) {
93 return true;
94 }
95 }
96 return false;
97}
98
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080099static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
100 char **out_module, char **out_file, uptr *line,
101 uptr *start_address) {
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700102 // Trim ending newlines.
103 char *trim;
104 ExtractTokenUpToDelimiter(str, "\n", &trim);
105
106 // The line from `atos` is in one of these formats:
107 // myfunction (in library.dylib) (sourcefile.c:17)
108 // myfunction (in library.dylib) + 0x1fe
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800109 // myfunction (in library.dylib) + 15
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700110 // 0xdeadbeef (in library.dylib) + 0x1fe
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800111 // 0xdeadbeef (in library.dylib) + 15
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700112 // 0xdeadbeef (in library.dylib)
113 // 0xdeadbeef
114
115 if (IsAtosErrorMessage(trim)) {
116 Report("atos returned an error: %s\n", trim);
117 InternalFree(trim);
118 return false;
119 }
120
121 const char *rest = trim;
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800122 char *symbol_name;
123 rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
124 if (rest[0] == '\0') {
125 InternalFree(symbol_name);
126 InternalFree(trim);
127 return false;
128 }
129
130 if (internal_strncmp(symbol_name, "0x", 2) != 0)
131 *out_name = symbol_name;
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700132 else
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800133 InternalFree(symbol_name);
134 rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700135
136 if (rest[0] == '(') {
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800137 if (out_file) {
138 rest++;
139 rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
140 char *extracted_line_number;
141 rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
142 if (line) *line = (uptr)internal_atoll(extracted_line_number);
143 InternalFree(extracted_line_number);
144 }
145 } else if (rest[0] == '+') {
146 rest += 2;
147 uptr offset = internal_atoll(rest);
148 if (start_address) *start_address = addr - offset;
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700149 }
150
151 InternalFree(trim);
152 return true;
153}
154
155AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
156 : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
157
158bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
159 if (!process_) return false;
160 char command[32];
161 internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
162 const char *buf = process_->SendCommand(command);
163 if (!buf) return false;
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800164 uptr line;
165 if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
166 &stack->info.file, &line, nullptr)) {
167 process_ = nullptr;
168 return false;
169 }
170 stack->info.line = (int)line;
171 return true;
172}
173
174bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
175 if (!process_) return false;
176 char command[32];
177 internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
178 const char *buf = process_->SendCommand(command);
179 if (!buf) return false;
180 if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
181 nullptr, &info->start)) {
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700182 process_ = nullptr;
183 return false;
184 }
185 return true;
186}
187
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700188} // namespace __sanitizer
189
190#endif // SANITIZER_MAC