Bumping gaia.json for 1 gaia revision(s) a=gaia-bump
[gecko.git] / tools / profiler / local_debug_info_symbolizer.cc
blob99cebad507e35563136e3ea53cb93c0c2f19f74f
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/. */
6 #include "PlatformMacros.h"
7 #include "nsAutoPtr.h"
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"
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
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
41 #include "local_debug_info_symbolizer.h"
43 namespace google_breakpad {
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;
52 # endif
55 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
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 )
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());
75 // Read symbol data from a file on Android. OBJ_FILENAME has
76 // three possible cases:
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.
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.
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.
93 // Hence only (2) requires special-casing here.
95 static bool ReadSymbolData_ANDROID(const string& obj_filename,
96 const std::vector<string>& debug_dirs,
97 SymbolData symbol_data,
98 Module** module)
100 string obj_file_to_use = obj_filename;
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);
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;
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;
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;
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;
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;
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;
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;
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);
174 return ok;
176 #endif /* defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) */
179 StackFrameSymbolizer::SymbolizerResult
180 LocalDebugInfoSymbolizer::FillSourceLineInfo(const CodeModules* modules,
181 const SystemInfo* system_info,
182 StackFrame* frame) {
183 if (!modules) {
184 return kError;
186 const CodeModule* module = modules->GetModuleForAddress(frame->instruction);
187 if (!module) {
188 return kError;
190 frame->module = module;
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;
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
213 if (!ok) {
214 if (debug_info_module)
215 delete debug_info_module;
216 no_symbol_modules_.insert(module->code_file());
217 return kNoError;
220 symbols_[module->code_file()] = debug_info_module;
221 } else {
222 debug_info_module = it->second;
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;
237 # endif /* !defined(SPS_OS_windows) */
238 return kNoError;
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;
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);
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);
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);
272 #endif
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;
281 SymbolMap::const_iterator it = symbols_.find(frame->module->code_file());
282 if (it == symbols_.end()) return NULL;
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;
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);
303 return rules.forget();
304 #endif /* defined(SPS_OS_windows) */
307 } // namespace google_breakpad