1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/profiler/local_debug_info_symbolizer.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,307 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "PlatformMacros.h" 1.10 +#include "nsAutoPtr.h" 1.11 + 1.12 +#if !defined(SPS_OS_windows) 1.13 +# include "common/module.h" 1.14 +# include "processor/cfi_frame_info.h" 1.15 +#endif 1.16 +#include "google_breakpad/processor/code_module.h" 1.17 +#include "google_breakpad/processor/code_modules.h" 1.18 +#include "google_breakpad/processor/stack_frame.h" 1.19 +#include "common/logging.h" 1.20 + 1.21 +#if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \ 1.22 + || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android) 1.23 +# include "common/linux/dump_symbols.h" 1.24 +#elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) 1.25 +# include "shim_mac_dump_syms.h" 1.26 +#elif defined(SPS_OS_windows) 1.27 + /* This is all stubbed out anyway, so don't do anything. */ 1.28 +#else 1.29 +# error "Unknown platform" 1.30 +#endif 1.31 + 1.32 +#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) 1.33 +# include "mozilla/Types.h" 1.34 +# include "ElfLoader.h" 1.35 +# include <dlfcn.h> 1.36 +# include <sys/mman.h> 1.37 +# include "nsString.h" 1.38 +# include "nsDirectoryServiceUtils.h" 1.39 +# include "nsDirectoryServiceDefs.h" 1.40 +# include <sys/stat.h> 1.41 +# include <fcntl.h> 1.42 +#endif 1.43 + 1.44 +#include "local_debug_info_symbolizer.h" 1.45 + 1.46 +namespace google_breakpad { 1.47 + 1.48 +LocalDebugInfoSymbolizer::~LocalDebugInfoSymbolizer() { 1.49 +# if !defined(SPS_OS_windows) 1.50 + for (SymbolMap::iterator it = symbols_.begin(); 1.51 + it != symbols_.end(); 1.52 + ++it) { 1.53 + delete it->second; 1.54 + } 1.55 +# endif 1.56 +} 1.57 + 1.58 +#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) 1.59 + 1.60 +// Find out where the installation's lib directory is, since we'll 1.61 +// have to look in there to get hold of libmozglue.so. Returned 1.62 +// C string is heap allocated and the caller must deallocate it. 1.63 +static char* get_installation_lib_dir ( void ) 1.64 +{ 1.65 + nsCOMPtr<nsIProperties> 1.66 + directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); 1.67 + if (!directoryService) return NULL; 1.68 + nsCOMPtr<nsIFile> greDir; 1.69 + nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), 1.70 + getter_AddRefs(greDir)); 1.71 + if (NS_FAILED(rv)) return NULL; 1.72 + nsCString path; 1.73 + rv = greDir->GetNativePath(path); 1.74 + if (NS_FAILED(rv)) return NULL; 1.75 + return strdup(path.get()); 1.76 +} 1.77 + 1.78 +// Read symbol data from a file on Android. OBJ_FILENAME has 1.79 +// three possible cases: 1.80 +// 1.81 +// (1) /foo/bar/xyzzy/blah.apk!/libwurble.so 1.82 +// We hand it as-is to faulty.lib and let it fish the relevant 1.83 +// bits out of the APK. 1.84 +// 1.85 +// (2) libmozglue.so 1.86 +// This is part of the Fennec installation, but is not in the 1.87 +// APK. Instead we have to figure out the installation path 1.88 +// and look for it there. Because of faulty.lib limitations, 1.89 +// we have to use regular open/mmap instead of faulty.lib. 1.90 +// 1.91 +// (3) libanythingelse.so 1.92 +// faulty.lib assumes this is a system library, and prepends 1.93 +// "/system/lib/" to the path. So as in (1), we can give it 1.94 +// as-is to faulty.lib. 1.95 +// 1.96 +// Hence only (2) requires special-casing here. 1.97 +// 1.98 +static bool ReadSymbolData_ANDROID(const string& obj_filename, 1.99 + const std::vector<string>& debug_dirs, 1.100 + SymbolData symbol_data, 1.101 + Module** module) 1.102 +{ 1.103 + string obj_file_to_use = obj_filename; 1.104 + 1.105 + // Do (2) in the comment above. 1.106 + if (obj_file_to_use == "libmozglue.so") { 1.107 + char* libdir = get_installation_lib_dir(); 1.108 + if (libdir) { 1.109 + obj_file_to_use = string(libdir) + "/lib/" + obj_file_to_use; 1.110 + free(libdir); 1.111 + } 1.112 + 1.113 + // Use regular open/mmap here because of faulty.lib limitations 1.114 + int fd = open(obj_file_to_use.c_str(), O_RDONLY); 1.115 + if (fd == -1) { 1.116 + BPLOG(INFO) << "ReadSymbolData_APK: Failed to open \'" 1.117 + << obj_file_to_use << "\'"; 1.118 + return false; 1.119 + } 1.120 + 1.121 + struct stat st; 1.122 + if (fstat(fd, &st) != 0) { 1.123 + close(fd); 1.124 + BPLOG(INFO) << "ReadSymbolData_APK: Failed to fstat \'" 1.125 + << obj_file_to_use << "\'"; 1.126 + return false; 1.127 + } 1.128 + 1.129 + void* image = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 1.130 + if (image == MAP_FAILED) { 1.131 + close(fd); 1.132 + BPLOG(INFO) << "ReadSymbolData_APK: Failed to mmap \'" 1.133 + << obj_file_to_use << "\'"; 1.134 + return false; 1.135 + } 1.136 + 1.137 + bool ok = ReadSymbolDataInternal((const uint8_t*)image, 1.138 + obj_file_to_use, debug_dirs, 1.139 + symbol_data, module); 1.140 + munmap(image, st.st_size); 1.141 + close(fd); 1.142 + return ok; 1.143 + } 1.144 + 1.145 + // Regardless of whether the file is inside an APK or not, we ask 1.146 + // faulty.lib to map it, then call ReadSymbolDataInternal, then 1.147 + // unmap and dlclose it. 1.148 + void* hdl = dlopen(obj_file_to_use.c_str(), RTLD_GLOBAL | RTLD_LAZY); 1.149 + if (!hdl) { 1.150 + BPLOG(INFO) << "ReadSymbolData_APK: Failed to get handle for ELF file \'" 1.151 + << obj_file_to_use << "\'"; 1.152 + return false; 1.153 + } 1.154 + 1.155 + size_t sz = __dl_get_mappable_length(hdl); 1.156 + if (sz == 0) { 1.157 + dlclose(hdl); 1.158 + BPLOG(INFO) << "ReadSymbolData_APK: Unable to get size for ELF file \'" 1.159 + << obj_file_to_use << "\'"; 1.160 + return false; 1.161 + } 1.162 + 1.163 + void* image = __dl_mmap(hdl, NULL, sz, 0); 1.164 + if (image == MAP_FAILED) { 1.165 + dlclose(hdl); 1.166 + BPLOG(INFO) << "ReadSymbolData_APK: Failed to mmap ELF file \'" 1.167 + << obj_file_to_use << "\'"; 1.168 + return false; 1.169 + } 1.170 + 1.171 + bool ok = ReadSymbolDataInternal((const uint8_t*)image, 1.172 + obj_file_to_use, debug_dirs, 1.173 + symbol_data, module); 1.174 + __dl_munmap(hdl, image, sz); 1.175 + dlclose(hdl); 1.176 + 1.177 + return ok; 1.178 +} 1.179 +#endif /* defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) */ 1.180 + 1.181 + 1.182 +StackFrameSymbolizer::SymbolizerResult 1.183 +LocalDebugInfoSymbolizer::FillSourceLineInfo(const CodeModules* modules, 1.184 + const SystemInfo* system_info, 1.185 + StackFrame* frame) { 1.186 + if (!modules) { 1.187 + return kError; 1.188 + } 1.189 + const CodeModule* module = modules->GetModuleForAddress(frame->instruction); 1.190 + if (!module) { 1.191 + return kError; 1.192 + } 1.193 + frame->module = module; 1.194 + 1.195 +# if !defined(SPS_OS_windows) 1.196 + Module* debug_info_module = NULL; 1.197 + SymbolMap::const_iterator it = symbols_.find(module->code_file()); 1.198 + if (it == symbols_.end()) { 1.199 + if (no_symbol_modules_.find(module->code_file()) != 1.200 + no_symbol_modules_.end()) { 1.201 + return kNoError; 1.202 + } 1.203 + 1.204 + bool ok = false; 1.205 +# if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) 1.206 + ok = ReadSymbolData_ANDROID(module->code_file(), debug_dirs_, 1.207 + ONLY_CFI, &debug_info_module); 1.208 +# elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) 1.209 + ok = ReadSymbolData_DARWIN(module->code_file(), debug_dirs_, 1.210 + ONLY_CFI, &debug_info_module); 1.211 +# else 1.212 + ok = ReadSymbolData(module->code_file(), debug_dirs_, 1.213 + ONLY_CFI, &debug_info_module); 1.214 +# endif 1.215 + 1.216 + if (!ok) { 1.217 + if (debug_info_module) 1.218 + delete debug_info_module; 1.219 + no_symbol_modules_.insert(module->code_file()); 1.220 + return kNoError; 1.221 + } 1.222 + 1.223 + symbols_[module->code_file()] = debug_info_module; 1.224 + } else { 1.225 + debug_info_module = it->second; 1.226 + } 1.227 + 1.228 + u_int64_t address = frame->instruction - frame->module->base_address(); 1.229 + Module::Function* function = 1.230 + debug_info_module->FindFunctionByAddress(address); 1.231 + if (function) { 1.232 + frame->function_name = function->name; 1.233 + //TODO: line info: function->lines 1.234 + } else { 1.235 + Module::Extern* ex = debug_info_module->FindExternByAddress(address); 1.236 + if (ex) { 1.237 + frame->function_name = ex->name; 1.238 + } 1.239 + } 1.240 +# endif /* !defined(SPS_OS_windows) */ 1.241 + return kNoError; 1.242 +} 1.243 + 1.244 + 1.245 +WindowsFrameInfo* LocalDebugInfoSymbolizer::FindWindowsFrameInfo( 1.246 + const StackFrame* frame) { 1.247 + // Not currently implemented, would require PDBSourceLineWriter to 1.248 + // implement an API to return symbol data. 1.249 + return NULL; 1.250 +} 1.251 + 1.252 +#if !defined(SPS_OS_windows) 1.253 +// Taken wholesale from source_line_resolver_base.cc 1.254 +bool ParseCFIRuleSet(const string& rule_set, CFIFrameInfo* frame_info) { 1.255 + CFIFrameInfoParseHandler handler(frame_info); 1.256 + CFIRuleParser parser(&handler); 1.257 + return parser.Parse(rule_set); 1.258 +} 1.259 + 1.260 +static void ConvertCFI(const UniqueString* name, const Module::Expr& rule, 1.261 + CFIFrameInfo* frame_info) { 1.262 + if (name == ustr__ZDcfa()) frame_info->SetCFARule(rule); 1.263 + else if (name == ustr__ZDra()) frame_info->SetRARule(rule); 1.264 + else frame_info->SetRegisterRule(name, rule); 1.265 +} 1.266 + 1.267 + 1.268 +static void ConvertCFI(const Module::RuleMap& rule_map, 1.269 + CFIFrameInfo* frame_info) { 1.270 + for (Module::RuleMap::const_iterator it = rule_map.begin(); 1.271 + it != rule_map.end(); ++it) { 1.272 + ConvertCFI(it->first, it->second, frame_info); 1.273 + } 1.274 +} 1.275 +#endif 1.276 + 1.277 +CFIFrameInfo* LocalDebugInfoSymbolizer::FindCFIFrameInfo( 1.278 + const StackFrame* frame) { 1.279 +#if defined(SPS_OS_windows) 1.280 + return NULL; 1.281 +#else 1.282 + if (!frame || !frame->module) return NULL; 1.283 + 1.284 + SymbolMap::const_iterator it = symbols_.find(frame->module->code_file()); 1.285 + if (it == symbols_.end()) return NULL; 1.286 + 1.287 + Module* module = it->second; 1.288 + u_int64_t address = frame->instruction - frame->module->base_address(); 1.289 + Module::StackFrameEntry* entry = 1.290 + module->FindStackFrameEntryByAddress(address); 1.291 + if (!entry) 1.292 + return NULL; 1.293 + 1.294 + //TODO: can we cache this data per-address? does that make sense? 1.295 + // TODO: Maybe this should use google_breakpad::scoped_ptr, since we're in 1.296 + // "namespace google_breakpad". Not using scoped_ptr currently, because its 1.297 + // header triggers build warnings -- see bug 855010. 1.298 + nsAutoPtr<CFIFrameInfo> rules(new CFIFrameInfo()); 1.299 + ConvertCFI(entry->initial_rules, rules); 1.300 + for (Module::RuleChangeMap::const_iterator delta_it = 1.301 + entry->rule_changes.begin(); 1.302 + delta_it != entry->rule_changes.end() && delta_it->first < address; 1.303 + ++delta_it) { 1.304 + ConvertCFI(delta_it->second, rules); 1.305 + } 1.306 + return rules.forget(); 1.307 +#endif /* defined(SPS_OS_windows) */ 1.308 +} 1.309 + 1.310 +} // namespace google_breakpad