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#import <Foundation/Foundation.h> 31#include <sys/stat.h> 32#include <map> 33#include <string> 34#include <iostream> 35#include <fstream> 36#include <utility> 37 38#include "google_breakpad/processor/basic_source_line_resolver.h" 39#include "google_breakpad/processor/minidump.h" 40#include "google_breakpad/processor/system_info.h" 41#include "processor/pathname_stripper.h" 42 43#include "on_demand_symbol_supplier.h" 44#include "common/mac/dump_syms.h" 45 46using std::map; 47using std::string; 48 49using google_breakpad::OnDemandSymbolSupplier; 50using google_breakpad::PathnameStripper; 51using google_breakpad::SymbolSupplier; 52using google_breakpad::SystemInfo; 53 54OnDemandSymbolSupplier::OnDemandSymbolSupplier(const string &search_dir, 55 const string &symbol_search_dir) 56 : search_dir_(search_dir) { 57 NSFileManager *mgr = [NSFileManager defaultManager]; 58 size_t length = symbol_search_dir.length(); 59 if (length) { 60 // Load all sym files in symbol_search_dir into our module_file_map 61 // A symbol file always starts with a line like this: 62 // MODULE mac x86 BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon 63 // or 64 // MODULE mac ppc BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon 65 const char *symbolSearchStr = symbol_search_dir.c_str(); 66 NSString *symbolSearchPath = 67 [mgr stringWithFileSystemRepresentation:symbolSearchStr 68 length:strlen(symbolSearchStr)]; 69 NSDirectoryEnumerator *dirEnum = [mgr enumeratorAtPath:symbolSearchPath]; 70 NSString *fileName; 71 NSCharacterSet *hexSet = 72 [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEF"]; 73 NSCharacterSet *newlineSet = 74 [NSCharacterSet characterSetWithCharactersInString:@"\r\n"]; 75 while ((fileName = [dirEnum nextObject])) { 76 // Check to see what type of file we have 77 NSDictionary *attrib = [dirEnum fileAttributes]; 78 NSString *fileType = [attrib objectForKey:NSFileType]; 79 if ([fileType isEqualToString:NSFileTypeDirectory]) { 80 // Skip subdirectories 81 [dirEnum skipDescendents]; 82 } else { 83 NSString *filePath = [symbolSearchPath stringByAppendingPathComponent:fileName]; 84 NSString *dataStr = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL]; 85 if (dataStr) { 86 // Check file to see if it is of appropriate type, and grab module 87 // name. 88 NSScanner *scanner = [NSScanner scannerWithString:dataStr]; 89 BOOL goodScan = [scanner scanString:@"MODULE mac " intoString:nil]; 90 if (goodScan) { 91 goodScan = ([scanner scanString:@"x86 " intoString:nil] || 92 [scanner scanString:@"x86_64 " intoString:nil] || 93 [scanner scanString:@"ppc " intoString:nil]); 94 if (goodScan) { 95 NSString *moduleID; 96 goodScan = [scanner scanCharactersFromSet:hexSet 97 intoString:&moduleID]; 98 if (goodScan) { 99 // Module IDs are always 33 chars long 100 goodScan = [moduleID length] == 33; 101 if (goodScan) { 102 NSString *moduleName; 103 goodScan = [scanner scanUpToCharactersFromSet:newlineSet 104 intoString:&moduleName]; 105 if (goodScan) { 106 goodScan = [moduleName length] > 0; 107 if (goodScan) { 108 const char *moduleNameStr = [moduleName UTF8String]; 109 const char *filePathStr = [filePath fileSystemRepresentation]; 110 // Map our file 111 module_file_map_[moduleNameStr] = filePathStr; 112 } 113 } 114 } 115 } 116 } 117 } 118 } 119 } 120 } 121 } 122} 123 124SymbolSupplier::SymbolResult 125OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module, 126 const SystemInfo *system_info, 127 string *symbol_file) { 128 string path(GetModuleSymbolFile(module)); 129 130 if (path.empty()) { 131 if (!GenerateSymbolFile(module, system_info)) 132 return NOT_FOUND; 133 134 path = GetModuleSymbolFile(module); 135 } 136 137 if (path.empty()) 138 return NOT_FOUND; 139 140 *symbol_file = path; 141 return FOUND; 142} 143 144SymbolSupplier::SymbolResult 145OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module, 146 const SystemInfo *system_info, 147 string *symbol_file, 148 string *symbol_data) { 149 SymbolSupplier::SymbolResult s = GetSymbolFile(module, 150 system_info, 151 symbol_file); 152 153 154 if (s == FOUND) { 155 std::ifstream in(symbol_file->c_str()); 156 getline(in, *symbol_data, std::string::traits_type::to_char_type( 157 std::string::traits_type::eof())); 158 in.close(); 159 } 160 161 return s; 162} 163 164SymbolSupplier::SymbolResult 165OnDemandSymbolSupplier::GetCStringSymbolData(const CodeModule *module, 166 const SystemInfo *system_info, 167 string *symbol_file, 168 char **symbol_data, 169 size_t *symbol_data_size) { 170 std::string symbol_data_string; 171 SymbolSupplier::SymbolResult result = GetSymbolFile(module, 172 system_info, 173 symbol_file, 174 &symbol_data_string); 175 if (result == FOUND) { 176 *symbol_data_size = symbol_data_string.size() + 1; 177 *symbol_data = new char[*symbol_data_size]; 178 if (*symbol_data == NULL) { 179 // Should return INTERRUPT on memory allocation failure. 180 return INTERRUPT; 181 } 182 memcpy(*symbol_data, symbol_data_string.c_str(), symbol_data_string.size()); 183 (*symbol_data)[symbol_data_string.size()] = '\0'; 184 memory_buffers_.insert(make_pair(module->code_file(), *symbol_data)); 185 } 186 return result; 187} 188 189void OnDemandSymbolSupplier::FreeSymbolData(const CodeModule *module) { 190 map<string, char *>::iterator it = memory_buffers_.find(module->code_file()); 191 if (it != memory_buffers_.end()) { 192 delete [] it->second; 193 memory_buffers_.erase(it); 194 } 195} 196 197string OnDemandSymbolSupplier::GetLocalModulePath(const CodeModule *module) { 198 NSFileManager *mgr = [NSFileManager defaultManager]; 199 const char *moduleStr = module->code_file().c_str(); 200 NSString *modulePath = 201 [mgr stringWithFileSystemRepresentation:moduleStr length:strlen(moduleStr)]; 202 const char *searchStr = search_dir_.c_str(); 203 NSString *searchDir = 204 [mgr stringWithFileSystemRepresentation:searchStr length:strlen(searchStr)]; 205 206 if ([mgr fileExistsAtPath:modulePath]) 207 return module->code_file(); 208 209 // If the module is not found, try to start appending the components to the 210 // search string and stop if a file (not dir) is found or all components 211 // have been appended 212 NSArray *pathComponents = [modulePath componentsSeparatedByString:@"/"]; 213 size_t count = [pathComponents count]; 214 NSMutableString *path = [NSMutableString string]; 215 216 for (size_t i = 0; i < count; ++i) { 217 [path setString:searchDir]; 218 219 for (size_t j = 0; j < i + 1; ++j) { 220 size_t idx = count - 1 - i + j; 221 [path appendFormat:@"/%@", [pathComponents objectAtIndex:idx]]; 222 } 223 224 BOOL isDir; 225 if ([mgr fileExistsAtPath:path isDirectory:&isDir] && (!isDir)) { 226 return [path fileSystemRepresentation]; 227 } 228 } 229 230 return ""; 231} 232 233string OnDemandSymbolSupplier::GetModulePath(const CodeModule *module) { 234 return module->code_file(); 235} 236 237string OnDemandSymbolSupplier::GetNameForModule(const CodeModule *module) { 238 return PathnameStripper::File(module->code_file()); 239} 240 241string OnDemandSymbolSupplier::GetModuleSymbolFile(const CodeModule *module) { 242 string name(GetNameForModule(module)); 243 map<string, string>::iterator result = module_file_map_.find(name); 244 245 return (result == module_file_map_.end()) ? "" : (*result).second; 246} 247 248static float GetFileModificationTime(const char *path) { 249 float result = 0; 250 struct stat file_stat; 251 if (stat(path, &file_stat) == 0) 252 result = (float)file_stat.st_mtimespec.tv_sec + 253 (float)file_stat.st_mtimespec.tv_nsec / 1.0e9f; 254 255 return result; 256} 257 258bool OnDemandSymbolSupplier::GenerateSymbolFile(const CodeModule *module, 259 const SystemInfo *system_info) { 260 bool result = true; 261 string name = GetNameForModule(module); 262 string module_path = GetLocalModulePath(module); 263 NSString *symbol_path = [NSString stringWithFormat:@"/tmp/%s.%s.sym", 264 name.c_str(), system_info->cpu.c_str()]; 265 266 if (module_path.empty()) 267 return false; 268 269 // Check if there's already a symbol file cached. Ensure that the file is 270 // newer than the module. Otherwise, generate a new one. 271 BOOL generate_file = YES; 272 if ([[NSFileManager defaultManager] fileExistsAtPath:symbol_path]) { 273 // Check if the module file is newer than the saved symbols 274 float cache_time = 275 GetFileModificationTime([symbol_path fileSystemRepresentation]); 276 float module_time = 277 GetFileModificationTime(module_path.c_str()); 278 279 if (cache_time > module_time) 280 generate_file = NO; 281 } 282 283 if (generate_file) { 284 DumpSymbols dump(ALL_SYMBOL_DATA, false); 285 if (dump.Read(module_path)) { 286 // What Breakpad calls "x86" should be given to the system as "i386". 287 std::string architecture; 288 if (system_info->cpu.compare("x86") == 0) { 289 architecture = "i386"; 290 } else { 291 architecture = system_info->cpu; 292 } 293 294 if (dump.SetArchitecture(architecture)) { 295 std::fstream file([symbol_path fileSystemRepresentation], 296 std::ios_base::out | std::ios_base::trunc); 297 dump.WriteSymbolFile(file); 298 } else { 299 printf("Architecture %s not available for %s\n", 300 system_info->cpu.c_str(), name.c_str()); 301 result = false; 302 } 303 } else { 304 printf("Unable to open %s\n", module_path.c_str()); 305 result = false; 306 } 307 } 308 309 // Add the mapping 310 if (result) 311 module_file_map_[name] = [symbol_path fileSystemRepresentation]; 312 313 return result; 314} 315