Issue 181: Add version info for Mac OS X modules. Found by iterating over load commands until I found LC_ID_DYLIB. Also modified crash_report to generate version number. Also added suspend/resume capability to exception handler, necessary because exception handling can behave strangely across fork() calls. Also added fix for filtering out functions with no line number information, and for filtering out some multiple inheritance glue the compiler generates.

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@291 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
nealsid 2008-10-22 05:08:50 +00:00
parent c5f46b2f4b
commit 32441cc060
7 changed files with 173 additions and 95 deletions

View File

@ -187,7 +187,7 @@ void* ReadTaskMemory(task_port_t target_task,
//==============================================================================
// Initializes vmaddr_, vmsize_, and slide_
void DynamicImage::CalculateMemoryInfo() {
void DynamicImage::CalculateMemoryAndVersionInfo() {
breakpad_mach_header *header = GetMachHeader();
// unless we can process the header, ensure that calls to
@ -195,6 +195,10 @@ void DynamicImage::CalculateMemoryInfo() {
vmaddr_ = 0;
vmsize_ = 0;
slide_ = 0;
version_ = 0;
bool foundTextSection = false;
bool foundDylibIDCommand = false;
#if __LP64__
if(header->magic != MH_MAGIC_64) {
@ -206,30 +210,48 @@ void DynamicImage::CalculateMemoryInfo() {
}
#endif
#ifdef __LP64__
const uint32_t segmentLoadCommand = LC_SEGMENT_64;
#else
const uint32_t segmentLoadCommand = LC_SEGMENT;
#endif
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
#ifdef __LP64__
if (cmd->cmd == LC_SEGMENT_64) {
#else
if (cmd->cmd == LC_SEGMENT) {
#endif
const breakpad_mach_segment_command *seg =
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
if (!foundTextSection) {
if (cmd->cmd == segmentLoadCommand) {
const breakpad_mach_segment_command *seg =
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
vmaddr_ = seg->vmaddr;
vmsize_ = seg->vmsize;
slide_ = 0;
if (!strcmp(seg->segname, "__TEXT")) {
vmaddr_ = seg->vmaddr;
vmsize_ = seg->vmsize;
slide_ = 0;
if (seg->fileoff == 0 && seg->filesize != 0) {
slide_ = (uintptr_t)GetLoadAddress() - (uintptr_t)seg->vmaddr;
if (seg->fileoff == 0 && seg->filesize != 0) {
slide_ = (uintptr_t)GetLoadAddress() - (uintptr_t)seg->vmaddr;
}
foundTextSection = true;
}
return;
}
}
if (!foundDylibIDCommand) {
if (cmd->cmd == LC_ID_DYLIB) {
const struct dylib_command *dc =
reinterpret_cast<const struct dylib_command *>(cmd);
version_ = dc->dylib.current_version;
foundDylibIDCommand = true;
}
}
if (foundDylibIDCommand && foundTextSection) {
return;
}
cmd = reinterpret_cast<const struct load_command *>
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
}

View File

@ -114,7 +114,7 @@ class DynamicImage {
file_mod_date_(image_mod_date),
task_(task) {
InitializeFilePath(inFilePath);
CalculateMemoryInfo();
CalculateMemoryAndVersionInfo();
}
~DynamicImage() {
@ -150,6 +150,7 @@ class DynamicImage {
// Task owning this loaded image
mach_port_t GetTask() {return task_;}
uint32_t GetVersion() {return version_;}
// For sorting
bool operator<(const DynamicImage &inInfo) {
return GetLoadAddress() < inInfo.GetLoadAddress();
@ -176,29 +177,7 @@ class DynamicImage {
}
// Initializes vmaddr_, vmsize_, and slide_
void CalculateMemoryInfo();
#if 0 // currently not needed
// Copy constructor: we don't want this to be invoked,
// but here's the code in case we need to make it public some day.
DynamicImage(DynamicImage &inInfo)
: load_address_(inInfo.load_address_),
vmaddr_(inInfo.vmaddr_),
vmsize_(inInfo.vmsize_),
slide_(inInfo.slide_),
file_mod_date_(inInfo.file_mod_date_),
task_(inInfo.task_) {
// copy file path string
InitializeFilePath(inInfo.GetFilePath());
// copy mach_header and load commands
void *headerBuffer = malloc(inInfo.header_size_);
header_ = reinterpret_cast<breakpad_mach_header*>(headerBuffer);
memcpy(header_, inInfo.header_, inInfo.header_size_);
header_size_ = inInfo.header_size_;
}
#endif
void CalculateMemoryAndVersionInfo();
breakpad_mach_header *header_; // our local copy of the header
int header_size_; // mach_header plus load commands
@ -206,7 +185,7 @@ class DynamicImage {
mach_vm_address_t vmaddr_;
mach_vm_size_t vmsize_;
ptrdiff_t slide_;
uint32_t version_; // Dylib version
char *file_path_; // path dyld used to load the image
uintptr_t file_mod_date_; // time_t of image file

View File

@ -221,7 +221,7 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
callback_context_(callback_context),
directCallback_(NULL),
handler_thread_(NULL),
handler_port_(0),
handler_port_(MACH_PORT_NULL),
previous_(NULL),
installed_exception_handler_(false),
is_in_teardown_(false),
@ -244,7 +244,7 @@ ExceptionHandler::ExceptionHandler(DirectCallback callback,
callback_context_(callback_context),
directCallback_(callback),
handler_thread_(NULL),
handler_port_(0),
handler_port_(MACH_PORT_NULL),
previous_(NULL),
installed_exception_handler_(false),
is_in_teardown_(false),
@ -600,6 +600,36 @@ bool ExceptionHandler::UninstallHandler(bool in_exception) {
return result == KERN_SUCCESS;
}
bool ExceptionHandler::SuspendExceptionHandling() {
if (!installed_exception_handler_) {
return false;
}
return UninstallHandler(false);
}
bool ExceptionHandler::ResumeExceptionHandling() {
if (installed_exception_handler_) {
return false;
}
// This conditional means that Setup() has never been
// called, but since it's called from the constructor
// we should never hit this.
assert(handler_port_);
if (handler_port_ == MACH_PORT_NULL) {
return false;
}
return InstallHandler();
}
bool ExceptionHandler::ExceptionHandlerIsSuspended() {
return handler_port_ != MACH_PORT_NULL && !installed_exception_handler_;
}
bool ExceptionHandler::Setup(bool install_handler) {
if (pthread_mutex_init(&minidump_write_mutex_, NULL))
return false;

View File

@ -114,6 +114,21 @@ class ExceptionHandler {
static bool WriteMinidump(const string &dump_path, MinidumpCallback callback,
void *callback_context);
// Temporarily stop this class from receiving exceptions
// Returns true if exception handling was successfully suspended
// It's an error to call this function if exception handling is
// not installed(we return false)
bool SuspendExceptionHandling();
// Resume this class from receiving exceptions
// Returns true if exception handling was successfully resumed
// It's an error to call this function if exception handling is
// already installed
bool ResumeExceptionHandling();
// Tell caller whether we're setup to handle exceptions or not
bool ExceptionHandlerIsSuspended();
private:
// Install the mach exception handler
bool InstallHandler();

View File

@ -680,6 +680,26 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
module->size_of_image = image->GetVMSize();
module->module_name_rva = string_location.rva;
// We'll skip the executable module, because they don't have
// LC_ID_DYLIB load commands, and the crash processing server gets
// version information from the Plist file, anyway.
if (index != (uint32_t)FindExecutableModule()) {
module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
// Convert MAC dylib version format, which is a 32 bit number, to the
// format used by minidump. The mac format is <16 bits>.<8 bits>.<8 bits>
// so it fits nicely into the windows version with some massaging
// The mapping is:
// 1) upper 16 bits of MAC version go to lower 16 bits of product HI
// 2) Next most significant 8 bits go to upper 16 bits of product LO
// 3) Least significant 8 bits go to lower 16 bits of product LO
uint32_t modVersion = image->GetVersion();
module->version_info.file_version_hi = 0;
module->version_info.file_version_hi = modVersion >> 16;
module->version_info.file_version_lo |= (modVersion & 0xff00) << 8;
module->version_info.file_version_lo |= (modVersion & 0xff);
}
if (!WriteCVRecord(module, cpu_type, name)) {
return false;
}

View File

@ -502,7 +502,7 @@ void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) {
}
// set line number for beginning of function
if (![dict objectForKey:kAddressSourceLineKey])
if (iter->second->line && ![dict objectForKey:kAddressSourceLineKey])
[dict setObject:[NSNumber numberWithUnsignedInt:iter->second->line]
forKey:kAddressSourceLineKey];
@ -534,7 +534,7 @@ void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) {
[dict release];
}
if (![dict objectForKey:kAddressSourceLineKey]) {
if (iter->second.second && ![dict objectForKey:kAddressSourceLineKey]) {
[dict setObject:[NSNumber numberWithUnsignedInt:iter->second.second]
forKey:kAddressSourceLineKey];
}
@ -1024,6 +1024,12 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
if ([symbol hasPrefix:@"GCC_except_table"])
continue;
if ([symbol hasPrefix:@"__tcf"])
continue;
if ([symbol hasPrefix:@"non-virtual thunk"])
continue;
// Find the source file (if any) that contains this address
while (sourceCount && (addressVal >= nextSourceFileAddress)) {
fileIdx = nextFileIdx;

View File

@ -99,6 +99,12 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
int maxStr = 20;
buffer[maxStr] = 0;
printf("%-*s", maxStr, buffer);
strcpy(buffer, module->version().c_str());
buffer[maxStr] = 0;
printf("%-*s",maxStr, buffer);
u_int64_t instruction = frame->instruction;
// PPC only: Adjust the instruction to match that of Crash reporter. The