|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "PlatformMacros.h" |
|
7 #include "nsAutoPtr.h" |
|
8 |
|
9 #if !defined(SPS_OS_windows) |
|
10 # include "common/module.h" |
|
11 # include "processor/cfi_frame_info.h" |
|
12 #endif |
|
13 #include "google_breakpad/processor/code_module.h" |
|
14 #include "google_breakpad/processor/code_modules.h" |
|
15 #include "google_breakpad/processor/stack_frame.h" |
|
16 #include "common/logging.h" |
|
17 |
|
18 #if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \ |
|
19 || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android) |
|
20 # include "common/linux/dump_symbols.h" |
|
21 #elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) |
|
22 # include "shim_mac_dump_syms.h" |
|
23 #elif defined(SPS_OS_windows) |
|
24 /* This is all stubbed out anyway, so don't do anything. */ |
|
25 #else |
|
26 # error "Unknown platform" |
|
27 #endif |
|
28 |
|
29 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |
|
30 # include "mozilla/Types.h" |
|
31 # include "ElfLoader.h" |
|
32 # include <dlfcn.h> |
|
33 # include <sys/mman.h> |
|
34 # include "nsString.h" |
|
35 # include "nsDirectoryServiceUtils.h" |
|
36 # include "nsDirectoryServiceDefs.h" |
|
37 # include <sys/stat.h> |
|
38 # include <fcntl.h> |
|
39 #endif |
|
40 |
|
41 #include "local_debug_info_symbolizer.h" |
|
42 |
|
43 namespace google_breakpad { |
|
44 |
|
45 LocalDebugInfoSymbolizer::~LocalDebugInfoSymbolizer() { |
|
46 # if !defined(SPS_OS_windows) |
|
47 for (SymbolMap::iterator it = symbols_.begin(); |
|
48 it != symbols_.end(); |
|
49 ++it) { |
|
50 delete it->second; |
|
51 } |
|
52 # endif |
|
53 } |
|
54 |
|
55 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |
|
56 |
|
57 // Find out where the installation's lib directory is, since we'll |
|
58 // have to look in there to get hold of libmozglue.so. Returned |
|
59 // C string is heap allocated and the caller must deallocate it. |
|
60 static char* get_installation_lib_dir ( void ) |
|
61 { |
|
62 nsCOMPtr<nsIProperties> |
|
63 directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); |
|
64 if (!directoryService) return NULL; |
|
65 nsCOMPtr<nsIFile> greDir; |
|
66 nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), |
|
67 getter_AddRefs(greDir)); |
|
68 if (NS_FAILED(rv)) return NULL; |
|
69 nsCString path; |
|
70 rv = greDir->GetNativePath(path); |
|
71 if (NS_FAILED(rv)) return NULL; |
|
72 return strdup(path.get()); |
|
73 } |
|
74 |
|
75 // Read symbol data from a file on Android. OBJ_FILENAME has |
|
76 // three possible cases: |
|
77 // |
|
78 // (1) /foo/bar/xyzzy/blah.apk!/libwurble.so |
|
79 // We hand it as-is to faulty.lib and let it fish the relevant |
|
80 // bits out of the APK. |
|
81 // |
|
82 // (2) libmozglue.so |
|
83 // This is part of the Fennec installation, but is not in the |
|
84 // APK. Instead we have to figure out the installation path |
|
85 // and look for it there. Because of faulty.lib limitations, |
|
86 // we have to use regular open/mmap instead of faulty.lib. |
|
87 // |
|
88 // (3) libanythingelse.so |
|
89 // faulty.lib assumes this is a system library, and prepends |
|
90 // "/system/lib/" to the path. So as in (1), we can give it |
|
91 // as-is to faulty.lib. |
|
92 // |
|
93 // Hence only (2) requires special-casing here. |
|
94 // |
|
95 static bool ReadSymbolData_ANDROID(const string& obj_filename, |
|
96 const std::vector<string>& debug_dirs, |
|
97 SymbolData symbol_data, |
|
98 Module** module) |
|
99 { |
|
100 string obj_file_to_use = obj_filename; |
|
101 |
|
102 // Do (2) in the comment above. |
|
103 if (obj_file_to_use == "libmozglue.so") { |
|
104 char* libdir = get_installation_lib_dir(); |
|
105 if (libdir) { |
|
106 obj_file_to_use = string(libdir) + "/lib/" + obj_file_to_use; |
|
107 free(libdir); |
|
108 } |
|
109 |
|
110 // Use regular open/mmap here because of faulty.lib limitations |
|
111 int fd = open(obj_file_to_use.c_str(), O_RDONLY); |
|
112 if (fd == -1) { |
|
113 BPLOG(INFO) << "ReadSymbolData_APK: Failed to open \'" |
|
114 << obj_file_to_use << "\'"; |
|
115 return false; |
|
116 } |
|
117 |
|
118 struct stat st; |
|
119 if (fstat(fd, &st) != 0) { |
|
120 close(fd); |
|
121 BPLOG(INFO) << "ReadSymbolData_APK: Failed to fstat \'" |
|
122 << obj_file_to_use << "\'"; |
|
123 return false; |
|
124 } |
|
125 |
|
126 void* image = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); |
|
127 if (image == MAP_FAILED) { |
|
128 close(fd); |
|
129 BPLOG(INFO) << "ReadSymbolData_APK: Failed to mmap \'" |
|
130 << obj_file_to_use << "\'"; |
|
131 return false; |
|
132 } |
|
133 |
|
134 bool ok = ReadSymbolDataInternal((const uint8_t*)image, |
|
135 obj_file_to_use, debug_dirs, |
|
136 symbol_data, module); |
|
137 munmap(image, st.st_size); |
|
138 close(fd); |
|
139 return ok; |
|
140 } |
|
141 |
|
142 // Regardless of whether the file is inside an APK or not, we ask |
|
143 // faulty.lib to map it, then call ReadSymbolDataInternal, then |
|
144 // unmap and dlclose it. |
|
145 void* hdl = dlopen(obj_file_to_use.c_str(), RTLD_GLOBAL | RTLD_LAZY); |
|
146 if (!hdl) { |
|
147 BPLOG(INFO) << "ReadSymbolData_APK: Failed to get handle for ELF file \'" |
|
148 << obj_file_to_use << "\'"; |
|
149 return false; |
|
150 } |
|
151 |
|
152 size_t sz = __dl_get_mappable_length(hdl); |
|
153 if (sz == 0) { |
|
154 dlclose(hdl); |
|
155 BPLOG(INFO) << "ReadSymbolData_APK: Unable to get size for ELF file \'" |
|
156 << obj_file_to_use << "\'"; |
|
157 return false; |
|
158 } |
|
159 |
|
160 void* image = __dl_mmap(hdl, NULL, sz, 0); |
|
161 if (image == MAP_FAILED) { |
|
162 dlclose(hdl); |
|
163 BPLOG(INFO) << "ReadSymbolData_APK: Failed to mmap ELF file \'" |
|
164 << obj_file_to_use << "\'"; |
|
165 return false; |
|
166 } |
|
167 |
|
168 bool ok = ReadSymbolDataInternal((const uint8_t*)image, |
|
169 obj_file_to_use, debug_dirs, |
|
170 symbol_data, module); |
|
171 __dl_munmap(hdl, image, sz); |
|
172 dlclose(hdl); |
|
173 |
|
174 return ok; |
|
175 } |
|
176 #endif /* defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) */ |
|
177 |
|
178 |
|
179 StackFrameSymbolizer::SymbolizerResult |
|
180 LocalDebugInfoSymbolizer::FillSourceLineInfo(const CodeModules* modules, |
|
181 const SystemInfo* system_info, |
|
182 StackFrame* frame) { |
|
183 if (!modules) { |
|
184 return kError; |
|
185 } |
|
186 const CodeModule* module = modules->GetModuleForAddress(frame->instruction); |
|
187 if (!module) { |
|
188 return kError; |
|
189 } |
|
190 frame->module = module; |
|
191 |
|
192 # if !defined(SPS_OS_windows) |
|
193 Module* debug_info_module = NULL; |
|
194 SymbolMap::const_iterator it = symbols_.find(module->code_file()); |
|
195 if (it == symbols_.end()) { |
|
196 if (no_symbol_modules_.find(module->code_file()) != |
|
197 no_symbol_modules_.end()) { |
|
198 return kNoError; |
|
199 } |
|
200 |
|
201 bool ok = false; |
|
202 # if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |
|
203 ok = ReadSymbolData_ANDROID(module->code_file(), debug_dirs_, |
|
204 ONLY_CFI, &debug_info_module); |
|
205 # elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) |
|
206 ok = ReadSymbolData_DARWIN(module->code_file(), debug_dirs_, |
|
207 ONLY_CFI, &debug_info_module); |
|
208 # else |
|
209 ok = ReadSymbolData(module->code_file(), debug_dirs_, |
|
210 ONLY_CFI, &debug_info_module); |
|
211 # endif |
|
212 |
|
213 if (!ok) { |
|
214 if (debug_info_module) |
|
215 delete debug_info_module; |
|
216 no_symbol_modules_.insert(module->code_file()); |
|
217 return kNoError; |
|
218 } |
|
219 |
|
220 symbols_[module->code_file()] = debug_info_module; |
|
221 } else { |
|
222 debug_info_module = it->second; |
|
223 } |
|
224 |
|
225 u_int64_t address = frame->instruction - frame->module->base_address(); |
|
226 Module::Function* function = |
|
227 debug_info_module->FindFunctionByAddress(address); |
|
228 if (function) { |
|
229 frame->function_name = function->name; |
|
230 //TODO: line info: function->lines |
|
231 } else { |
|
232 Module::Extern* ex = debug_info_module->FindExternByAddress(address); |
|
233 if (ex) { |
|
234 frame->function_name = ex->name; |
|
235 } |
|
236 } |
|
237 # endif /* !defined(SPS_OS_windows) */ |
|
238 return kNoError; |
|
239 } |
|
240 |
|
241 |
|
242 WindowsFrameInfo* LocalDebugInfoSymbolizer::FindWindowsFrameInfo( |
|
243 const StackFrame* frame) { |
|
244 // Not currently implemented, would require PDBSourceLineWriter to |
|
245 // implement an API to return symbol data. |
|
246 return NULL; |
|
247 } |
|
248 |
|
249 #if !defined(SPS_OS_windows) |
|
250 // Taken wholesale from source_line_resolver_base.cc |
|
251 bool ParseCFIRuleSet(const string& rule_set, CFIFrameInfo* frame_info) { |
|
252 CFIFrameInfoParseHandler handler(frame_info); |
|
253 CFIRuleParser parser(&handler); |
|
254 return parser.Parse(rule_set); |
|
255 } |
|
256 |
|
257 static void ConvertCFI(const UniqueString* name, const Module::Expr& rule, |
|
258 CFIFrameInfo* frame_info) { |
|
259 if (name == ustr__ZDcfa()) frame_info->SetCFARule(rule); |
|
260 else if (name == ustr__ZDra()) frame_info->SetRARule(rule); |
|
261 else frame_info->SetRegisterRule(name, rule); |
|
262 } |
|
263 |
|
264 |
|
265 static void ConvertCFI(const Module::RuleMap& rule_map, |
|
266 CFIFrameInfo* frame_info) { |
|
267 for (Module::RuleMap::const_iterator it = rule_map.begin(); |
|
268 it != rule_map.end(); ++it) { |
|
269 ConvertCFI(it->first, it->second, frame_info); |
|
270 } |
|
271 } |
|
272 #endif |
|
273 |
|
274 CFIFrameInfo* LocalDebugInfoSymbolizer::FindCFIFrameInfo( |
|
275 const StackFrame* frame) { |
|
276 #if defined(SPS_OS_windows) |
|
277 return NULL; |
|
278 #else |
|
279 if (!frame || !frame->module) return NULL; |
|
280 |
|
281 SymbolMap::const_iterator it = symbols_.find(frame->module->code_file()); |
|
282 if (it == symbols_.end()) return NULL; |
|
283 |
|
284 Module* module = it->second; |
|
285 u_int64_t address = frame->instruction - frame->module->base_address(); |
|
286 Module::StackFrameEntry* entry = |
|
287 module->FindStackFrameEntryByAddress(address); |
|
288 if (!entry) |
|
289 return NULL; |
|
290 |
|
291 //TODO: can we cache this data per-address? does that make sense? |
|
292 // TODO: Maybe this should use google_breakpad::scoped_ptr, since we're in |
|
293 // "namespace google_breakpad". Not using scoped_ptr currently, because its |
|
294 // header triggers build warnings -- see bug 855010. |
|
295 nsAutoPtr<CFIFrameInfo> rules(new CFIFrameInfo()); |
|
296 ConvertCFI(entry->initial_rules, rules); |
|
297 for (Module::RuleChangeMap::const_iterator delta_it = |
|
298 entry->rule_changes.begin(); |
|
299 delta_it != entry->rule_changes.end() && delta_it->first < address; |
|
300 ++delta_it) { |
|
301 ConvertCFI(delta_it->second, rules); |
|
302 } |
|
303 return rules.forget(); |
|
304 #endif /* defined(SPS_OS_windows) */ |
|
305 } |
|
306 |
|
307 } // namespace google_breakpad |