blob: ebbca87a0584039844c38149710893a0b859ff08 [file] [log] [blame]
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sys/stat.h>
#include <map>
#include <string>
#include <iostream>
#include <fstream>
#include <utility>
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/minidump.h"
#include "google_breakpad/processor/system_info.h"
#include "processor/pathname_stripper.h"
#include "on_demand_symbol_supplier.h"
#include "common/mac/dump_syms.h"
using std::map;
using std::string;
using google_breakpad::OnDemandSymbolSupplier;
using google_breakpad::PathnameStripper;
using google_breakpad::SymbolSupplier;
using google_breakpad::SystemInfo;
OnDemandSymbolSupplier::OnDemandSymbolSupplier(const string &search_dir,
const string &symbol_search_dir)
: search_dir_(search_dir) {
NSFileManager *mgr = [NSFileManager defaultManager];
size_t length = symbol_search_dir.length();
if (length) {
// Load all sym files in symbol_search_dir into our module_file_map
// A symbol file always starts with a line like this:
// MODULE mac x86 BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon
// or
// MODULE mac ppc BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon
const char *symbolSearchStr = symbol_search_dir.c_str();
NSString *symbolSearchPath =
[mgr stringWithFileSystemRepresentation:symbolSearchStr
length:strlen(symbolSearchStr)];
NSDirectoryEnumerator *dirEnum = [mgr enumeratorAtPath:symbolSearchPath];
NSString *fileName;
NSCharacterSet *hexSet =
[NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEF"];
NSCharacterSet *newlineSet =
[NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
while ((fileName = [dirEnum nextObject])) {
// Check to see what type of file we have
NSDictionary *attrib = [dirEnum fileAttributes];
NSString *fileType = [attrib objectForKey:NSFileType];
if ([fileType isEqualToString:NSFileTypeDirectory]) {
// Skip subdirectories
[dirEnum skipDescendents];
} else {
NSString *filePath = [symbolSearchPath stringByAppendingPathComponent:fileName];
NSString *dataStr = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
if (dataStr) {
// Check file to see if it is of appropriate type, and grab module
// name.
NSScanner *scanner = [NSScanner scannerWithString:dataStr];
BOOL goodScan = [scanner scanString:@"MODULE mac " intoString:nil];
if (goodScan) {
goodScan = ([scanner scanString:@"x86 " intoString:nil] ||
[scanner scanString:@"x86_64 " intoString:nil] ||
[scanner scanString:@"ppc " intoString:nil]);
if (goodScan) {
NSString *moduleID;
goodScan = [scanner scanCharactersFromSet:hexSet
intoString:&moduleID];
if (goodScan) {
// Module IDs are always 33 chars long
goodScan = [moduleID length] == 33;
if (goodScan) {
NSString *moduleName;
goodScan = [scanner scanUpToCharactersFromSet:newlineSet
intoString:&moduleName];
if (goodScan) {
goodScan = [moduleName length] > 0;
if (goodScan) {
const char *moduleNameStr = [moduleName UTF8String];
const char *filePathStr = [filePath fileSystemRepresentation];
// Map our file
module_file_map_[moduleNameStr] = filePathStr;
}
}
}
}
}
}
}
}
}
}
}
SymbolSupplier::SymbolResult
OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file) {
string path(GetModuleSymbolFile(module));
if (path.empty()) {
if (!GenerateSymbolFile(module, system_info))
return NOT_FOUND;
path = GetModuleSymbolFile(module);
}
if (path.empty())
return NOT_FOUND;
*symbol_file = path;
return FOUND;
}
SymbolSupplier::SymbolResult
OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
string *symbol_data) {
SymbolSupplier::SymbolResult s = GetSymbolFile(module,
system_info,
symbol_file);
if (s == FOUND) {
std::ifstream in(symbol_file->c_str());
getline(in, *symbol_data, std::string::traits_type::to_char_type(
std::string::traits_type::eof()));
in.close();
}
return s;
}
SymbolSupplier::SymbolResult
OnDemandSymbolSupplier::GetCStringSymbolData(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
char **symbol_data,
size_t *symbol_data_size) {
std::string symbol_data_string;
SymbolSupplier::SymbolResult result = GetSymbolFile(module,
system_info,
symbol_file,
&symbol_data_string);
if (result == FOUND) {
*symbol_data_size = symbol_data_string.size() + 1;
*symbol_data = new char[*symbol_data_size];
if (*symbol_data == NULL) {
// Should return INTERRUPT on memory allocation failure.
return INTERRUPT;
}
memcpy(*symbol_data, symbol_data_string.c_str(), symbol_data_string.size());
(*symbol_data)[symbol_data_string.size()] = '\0';
memory_buffers_.insert(make_pair(module->code_file(), *symbol_data));
}
return result;
}
void OnDemandSymbolSupplier::FreeSymbolData(const CodeModule *module) {
map<string, char *>::iterator it = memory_buffers_.find(module->code_file());
if (it != memory_buffers_.end()) {
delete [] it->second;
memory_buffers_.erase(it);
}
}
string OnDemandSymbolSupplier::GetLocalModulePath(const CodeModule *module) {
NSFileManager *mgr = [NSFileManager defaultManager];
const char *moduleStr = module->code_file().c_str();
NSString *modulePath =
[mgr stringWithFileSystemRepresentation:moduleStr length:strlen(moduleStr)];
const char *searchStr = search_dir_.c_str();
NSString *searchDir =
[mgr stringWithFileSystemRepresentation:searchStr length:strlen(searchStr)];
if ([mgr fileExistsAtPath:modulePath])
return module->code_file();
// If the module is not found, try to start appending the components to the
// search string and stop if a file (not dir) is found or all components
// have been appended
NSArray *pathComponents = [modulePath componentsSeparatedByString:@"/"];
size_t count = [pathComponents count];
NSMutableString *path = [NSMutableString string];
for (size_t i = 0; i < count; ++i) {
[path setString:searchDir];
for (size_t j = 0; j < i + 1; ++j) {
size_t idx = count - 1 - i + j;
[path appendFormat:@"/%@", [pathComponents objectAtIndex:idx]];
}
BOOL isDir;
if ([mgr fileExistsAtPath:path isDirectory:&isDir] && (!isDir)) {
return [path fileSystemRepresentation];
}
}
return "";
}
string OnDemandSymbolSupplier::GetModulePath(const CodeModule *module) {
return module->code_file();
}
string OnDemandSymbolSupplier::GetNameForModule(const CodeModule *module) {
return PathnameStripper::File(module->code_file());
}
string OnDemandSymbolSupplier::GetModuleSymbolFile(const CodeModule *module) {
string name(GetNameForModule(module));
map<string, string>::iterator result = module_file_map_.find(name);
return (result == module_file_map_.end()) ? "" : (*result).second;
}
static float GetFileModificationTime(const char *path) {
float result = 0;
struct stat file_stat;
if (stat(path, &file_stat) == 0)
result = (float)file_stat.st_mtimespec.tv_sec +
(float)file_stat.st_mtimespec.tv_nsec / 1.0e9f;
return result;
}
bool OnDemandSymbolSupplier::GenerateSymbolFile(const CodeModule *module,
const SystemInfo *system_info) {
bool result = true;
string name = GetNameForModule(module);
string module_path = GetLocalModulePath(module);
NSString *symbol_path = [NSString stringWithFormat:@"/tmp/%s.%s.sym",
name.c_str(), system_info->cpu.c_str()];
if (module_path.empty())
return false;
// Check if there's already a symbol file cached. Ensure that the file is
// newer than the module. Otherwise, generate a new one.
BOOL generate_file = YES;
if ([[NSFileManager defaultManager] fileExistsAtPath:symbol_path]) {
// Check if the module file is newer than the saved symbols
float cache_time =
GetFileModificationTime([symbol_path fileSystemRepresentation]);
float module_time =
GetFileModificationTime(module_path.c_str());
if (cache_time > module_time)
generate_file = NO;
}
if (generate_file) {
NSString *module_str = [[NSFileManager defaultManager]
stringWithFileSystemRepresentation:module_path.c_str()
length:module_path.length()];
DumpSymbols dump(ALL_SYMBOL_DATA, false);
if (dump.Read(module_str)) {
// What Breakpad calls "x86" should be given to the system as "i386".
std::string architecture;
if (system_info->cpu.compare("x86") == 0) {
architecture = "i386";
} else {
architecture = system_info->cpu;
}
if (dump.SetArchitecture(architecture)) {
std::fstream file([symbol_path fileSystemRepresentation],
std::ios_base::out | std::ios_base::trunc);
dump.WriteSymbolFile(file);
} else {
printf("Architecture %s not available for %s\n",
system_info->cpu.c_str(), name.c_str());
result = false;
}
} else {
printf("Unable to open %s\n", [module_str UTF8String]);
result = false;
}
}
// Add the mapping
if (result)
module_file_map_[name] = [symbol_path fileSystemRepresentation];
return result;
}