1// Copyright (c) 2006, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30#include <sys/stat.h> 31#include <map> 32#include <string> 33#include <iostream> 34#include <fstream> 35#include <utility> 36 37#include "google_breakpad/processor/basic_source_line_resolver.h" 38#include "google_breakpad/processor/minidump.h" 39#include "google_breakpad/processor/system_info.h" 40#include "processor/pathname_stripper.h" 41 42#include "on_demand_symbol_supplier.h" 43#include "common/mac/dump_syms.h" 44 45using std::map; 46using std::string; 47 48using google_breakpad::OnDemandSymbolSupplier; 49using google_breakpad::PathnameStripper; 50using google_breakpad::SymbolSupplier; 51using google_breakpad::SystemInfo; 52 53OnDemandSymbolSupplier::OnDemandSymbolSupplier(const string &search_dir, 54 const string &symbol_search_dir) 55 : search_dir_(search_dir) { 56 NSFileManager *mgr = [NSFileManager defaultManager]; 57 size_t length = symbol_search_dir.length(); 58 if (length) { 59 // Load all sym files in symbol_search_dir into our module_file_map 60 // A symbol file always starts with a line like this: 61 // MODULE mac x86 BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon 62 // or 63 // MODULE mac ppc BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon 64 const char *symbolSearchStr = symbol_search_dir.c_str(); 65 NSString *symbolSearchPath = 66 [mgr stringWithFileSystemRepresentation:symbolSearchStr 67 length:strlen(symbolSearchStr)]; 68 NSDirectoryEnumerator *dirEnum = [mgr enumeratorAtPath:symbolSearchPath]; 69 NSString *fileName; 70 NSCharacterSet *hexSet = 71 [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEF"]; 72 NSCharacterSet *newlineSet = 73 [NSCharacterSet characterSetWithCharactersInString:@"\r\n"]; 74 while ((fileName = [dirEnum nextObject])) { 75 // Check to see what type of file we have 76 NSDictionary *attrib = [dirEnum fileAttributes]; 77 NSString *fileType = [attrib objectForKey:NSFileType]; 78 if ([fileType isEqualToString:NSFileTypeDirectory]) { 79 // Skip subdirectories 80 [dirEnum skipDescendents]; 81 } else { 82 NSString *filePath = [symbolSearchPath stringByAppendingPathComponent:fileName]; 83 NSString *dataStr = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL]; 84 if (dataStr) { 85 // Check file to see if it is of appropriate type, and grab module 86 // name. 87 NSScanner *scanner = [NSScanner scannerWithString:dataStr]; 88 BOOL goodScan = [scanner scanString:@"MODULE mac " intoString:nil]; 89 if (goodScan) { 90 goodScan = ([scanner scanString:@"x86 " intoString:nil] || 91 [scanner scanString:@"x86_64 " intoString:nil] || 92 [scanner scanString:@"ppc " intoString:nil]); 93 if (goodScan) { 94 NSString *moduleID; 95 goodScan = [scanner scanCharactersFromSet:hexSet 96 intoString:&moduleID]; 97 if (goodScan) { 98 // Module IDs are always 33 chars long 99 goodScan = [moduleID length] == 33; 100 if (goodScan) { 101 NSString *moduleName; 102 goodScan = [scanner scanUpToCharactersFromSet:newlineSet 103 intoString:&moduleName]; 104 if (goodScan) { 105 goodScan = [moduleName length] > 0; 106 if (goodScan) { 107 const char *moduleNameStr = [moduleName UTF8String]; 108 const char *filePathStr = [filePath fileSystemRepresentation]; 109 // Map our file 110 module_file_map_[moduleNameStr] = filePathStr; 111 } 112 } 113 } 114 } 115 } 116 } 117 } 118 } 119 } 120 } 121} 122 123SymbolSupplier::SymbolResult 124OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module, 125 const SystemInfo *system_info, 126 string *symbol_file) { 127 string path(GetModuleSymbolFile(module)); 128 129 if (path.empty()) { 130 if (!GenerateSymbolFile(module, system_info)) 131 return NOT_FOUND; 132 133 path = GetModuleSymbolFile(module); 134 } 135 136 if (path.empty()) 137 return NOT_FOUND; 138 139 *symbol_file = path; 140 return FOUND; 141} 142 143SymbolSupplier::SymbolResult 144OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module, 145 const SystemInfo *system_info, 146 string *symbol_file, 147 string *symbol_data) { 148 SymbolSupplier::SymbolResult s = GetSymbolFile(module, 149 system_info, 150 symbol_file); 151 152 153 if (s == FOUND) { 154 std::ifstream in(symbol_file->c_str()); 155 getline(in, *symbol_data, std::string::traits_type::to_char_type( 156 std::string::traits_type::eof())); 157 in.close(); 158 } 159 160 return s; 161} 162 163SymbolSupplier::SymbolResult 164OnDemandSymbolSupplier::GetCStringSymbolData(const CodeModule *module, 165 const SystemInfo *system_info, 166 string *symbol_file, 167 char **symbol_data, 168 size_t *symbol_data_size) { 169 std::string symbol_data_string; 170 SymbolSupplier::SymbolResult result = GetSymbolFile(module, 171 system_info, 172 symbol_file, 173 &symbol_data_string); 174 if (result == FOUND) { 175 *symbol_data_size = symbol_data_string.size() + 1; 176 *symbol_data = new char[*symbol_data_size]; 177 if (*symbol_data == NULL) { 178 // Should return INTERRUPT on memory allocation failure. 179 return INTERRUPT; 180 } 181 memcpy(*symbol_data, symbol_data_string.c_str(), symbol_data_string.size()); 182 (*symbol_data)[symbol_data_string.size()] = '\0'; 183 memory_buffers_.insert(make_pair(module->code_file(), *symbol_data)); 184 } 185 return result; 186} 187 188void OnDemandSymbolSupplier::FreeSymbolData(const CodeModule *module) { 189 map<string, char *>::iterator it = memory_buffers_.find(module->code_file()); 190 if (it != memory_buffers_.end()) { 191 delete [] it->second; 192 memory_buffers_.erase(it); 193 } 194} 195 196string OnDemandSymbolSupplier::GetLocalModulePath(const CodeModule *module) { 197 NSFileManager *mgr = [NSFileManager defaultManager]; 198 const char *moduleStr = module->code_file().c_str(); 199 NSString *modulePath = 200 [mgr stringWithFileSystemRepresentation:moduleStr length:strlen(moduleStr)]; 201 const char *searchStr = search_dir_.c_str(); 202 NSString *searchDir = 203 [mgr stringWithFileSystemRepresentation:searchStr length:strlen(searchStr)]; 204 205 if ([mgr fileExistsAtPath:modulePath]) 206 return module->code_file(); 207 208 // If the module is not found, try to start appending the components to the 209 // search string and stop if a file (not dir) is found or all components 210 // have been appended 211 NSArray *pathComponents = [modulePath componentsSeparatedByString:@"/"]; 212 size_t count = [pathComponents count]; 213 NSMutableString *path = [NSMutableString string]; 214 215 for (size_t i = 0; i < count; ++i) { 216 [path setString:searchDir]; 217 218 for (size_t j = 0; j < i + 1; ++j) { 219 size_t idx = count - 1 - i + j; 220 [path appendFormat:@"/%@", [pathComponents objectAtIndex:idx]]; 221 } 222 223 BOOL isDir; 224 if ([mgr fileExistsAtPath:path isDirectory:&isDir] && (!isDir)) { 225 return [path fileSystemRepresentation]; 226 } 227 } 228 229 return ""; 230} 231 232string OnDemandSymbolSupplier::GetModulePath(const CodeModule *module) { 233 return module->code_file(); 234} 235 236string OnDemandSymbolSupplier::GetNameForModule(const CodeModule *module) { 237 return PathnameStripper::File(module->code_file()); 238} 239 240string OnDemandSymbolSupplier::GetModuleSymbolFile(const CodeModule *module) { 241 string name(GetNameForModule(module)); 242 map<string, string>::iterator result = module_file_map_.find(name); 243 244 return (result == module_file_map_.end()) ? "" : (*result).second; 245} 246 247static float GetFileModificationTime(const char *path) { 248 float result = 0; 249 struct stat file_stat; 250 if (stat(path, &file_stat) == 0) 251 result = (float)file_stat.st_mtimespec.tv_sec + 252 (float)file_stat.st_mtimespec.tv_nsec / 1.0e9f; 253 254 return result; 255} 256 257bool OnDemandSymbolSupplier::GenerateSymbolFile(const CodeModule *module, 258 const SystemInfo *system_info) { 259 bool result = true; 260 string name = GetNameForModule(module); 261 string module_path = GetLocalModulePath(module); 262 NSString *symbol_path = [NSString stringWithFormat:@"/tmp/%s.%s.sym", 263 name.c_str(), system_info->cpu.c_str()]; 264 265 if (module_path.empty()) 266 return false; 267 268 // Check if there's already a symbol file cached. Ensure that the file is 269 // newer than the module. Otherwise, generate a new one. 270 BOOL generate_file = YES; 271 if ([[NSFileManager defaultManager] fileExistsAtPath:symbol_path]) { 272 // Check if the module file is newer than the saved symbols 273 float cache_time = 274 GetFileModificationTime([symbol_path fileSystemRepresentation]); 275 float module_time = 276 GetFileModificationTime(module_path.c_str()); 277 278 if (cache_time > module_time) 279 generate_file = NO; 280 } 281 282 if (generate_file) { 283 NSString *module_str = [[NSFileManager defaultManager] 284 stringWithFileSystemRepresentation:module_path.c_str() 285 length:module_path.length()]; 286 DumpSymbols dump(ALL_SYMBOL_DATA, false); 287 if (dump.Read(module_str)) { 288 // What Breakpad calls "x86" should be given to the system as "i386". 289 std::string architecture; 290 if (system_info->cpu.compare("x86") == 0) { 291 architecture = "i386"; 292 } else { 293 architecture = system_info->cpu; 294 } 295 296 if (dump.SetArchitecture(architecture)) { 297 std::fstream file([symbol_path fileSystemRepresentation], 298 std::ios_base::out | std::ios_base::trunc); 299 dump.WriteSymbolFile(file); 300 } else { 301 printf("Architecture %s not available for %s\n", 302 system_info->cpu.c_str(), name.c_str()); 303 result = false; 304 } 305 } else { 306 printf("Unable to open %s\n", [module_str UTF8String]); 307 result = false; 308 } 309 } 310 311 // Add the mapping 312 if (result) 313 module_file_map_[name] = [symbol_path fileSystemRepresentation]; 314 315 return result; 316} 317