]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc
MFV r308987: 7180 potential race between zfs_suspend_fs+zfs_resume_fs
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / sanitizer_common / sanitizer_symbolizer_mac.cc
1 //===-- 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
22 namespace __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
31 bool 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);
36   stack->info.function = demangled ? internal_strdup(demangled) : nullptr;
37   return true;
38 }
39
40 bool 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;
48 }
49
50 class AtosSymbolizerProcess : public SymbolizerProcess {
51  public:
52   explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
53       : 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   }
58
59  private:
60   bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
61     return (length >= 1 && buffer[length - 1] == '\n');
62   }
63
64   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];
70     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.
74       argv[i++] = "-d";
75     }
76     argv[i++] = nullptr;
77   }
78
79   char pid_str_[16];
80 };
81
82 static 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
90 static 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
99 static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
100                                char **out_module, char **out_file, uptr *line,
101                                uptr *start_address) {
102   // 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
109   //   myfunction (in library.dylib) + 15
110   //   0xdeadbeef (in library.dylib) + 0x1fe
111   //   0xdeadbeef (in library.dylib) + 15
112   //   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;
122   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;
132   else
133     InternalFree(symbol_name);
134   rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
135
136   if (rest[0] == '(') {
137     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;
149   }
150
151   InternalFree(trim);
152   return true;
153 }
154
155 AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
156     : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
157
158 bool 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;
164   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
174 bool 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)) {
182     process_ = nullptr;
183     return false;
184   }
185   return true;
186 }
187
188 }  // namespace __sanitizer
189
190 #endif  // SANITIZER_MAC