From b65dce60f1b911e23bda2a02be36fd991c14815e Mon Sep 17 00:00:00 2001 From: waylonis Date: Mon, 18 Dec 2006 23:59:48 +0000 Subject: [PATCH] Add crash_report tool that will: - Generate local symbol file for module - Output minidump report in a format similar to Apple's crash reporter git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@93 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/tools/mac/crash_report/crash_report.mm | 330 ++++++++++++++++ .../crash_report.xcodeproj/project.pbxproj | 358 ++++++++++++++++++ .../crash_report/on_demand_symbol_supplier.h | 92 +++++ .../crash_report/on_demand_symbol_supplier.mm | 178 +++++++++ 4 files changed, 958 insertions(+) create mode 100644 src/tools/mac/crash_report/crash_report.mm create mode 100644 src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj create mode 100644 src/tools/mac/crash_report/on_demand_symbol_supplier.h create mode 100644 src/tools/mac/crash_report/on_demand_symbol_supplier.mm diff --git a/src/tools/mac/crash_report/crash_report.mm b/src/tools/mac/crash_report/crash_report.mm new file mode 100644 index 00000000..bd026cf1 --- /dev/null +++ b/src/tools/mac/crash_report/crash_report.mm @@ -0,0 +1,330 @@ +// +// 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. + +// crash_report.mm: Convert the contents of a minidump into a format that +// looks more like Apple's CrashReporter format + +#include + +#include +#include + +#include + +#include + +#include "google_airbag/processor/basic_source_line_resolver.h" +#include "google_airbag/processor/call_stack.h" +#include "google_airbag/processor/code_module.h" +#include "google_airbag/processor/minidump.h" +#include "google_airbag/processor/minidump_processor.h" +#include "google_airbag/processor/process_state.h" +#include "google_airbag/processor/stack_frame_cpu.h" +#include "processor/pathname_stripper.h" +#include "processor/scoped_ptr.h" +#include "processor/simple_symbol_supplier.h" + +#include "on_demand_symbol_supplier.h" + +using std::string; + +using google_airbag::BasicSourceLineResolver; +using google_airbag::CallStack; +using google_airbag::CodeModule; +using google_airbag::CodeModules; +using google_airbag::MinidumpProcessor; +using google_airbag::OnDemandSymbolSupplier; +using google_airbag::PathnameStripper; +using google_airbag::ProcessState; +using google_airbag::scoped_ptr; +using google_airbag::StackFrame; +using google_airbag::StackFramePPC; +using google_airbag::StackFrameX86; + +typedef struct { + NSString *minidumpPath; + NSString *searchDir; +} Options; + +//============================================================================= +static int PrintRegister(const char *name, u_int32_t value, int sequence) { + if (sequence % 4 == 0) { + printf("\n"); + } + printf("%6s = 0x%08x ", name, value); + return ++sequence; +} + +//============================================================================= +static void PrintStack(const CallStack *stack, const string &cpu) { + int frame_count = stack->frames()->size(); + char buffer[1024]; + for (int frame_index = 0; frame_index < frame_count; ++frame_index) { + const StackFrame *frame = stack->frames()->at(frame_index); + const CodeModule *module = frame->module; + printf("%2d ", frame_index); + + if (module) { + // Module name (20 chars max) + strcpy(buffer, PathnameStripper::File(module->code_file()).c_str()); + int maxStr = 20; + 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 + // instruction listed is actually the return address. See the detailed + // comments in stackwalker_ppc.cc for more information. + if (cpu == "ppc" && frame_index) + instruction += 4; + + printf(" 0x%08llx ", instruction); + + // Function name + if (!frame->function_name.empty()) { + printf("%s", frame->function_name.c_str()); + if (!frame->source_file_name.empty()) { + string source_file = PathnameStripper::File(frame->source_file_name); + printf(" + 0x%llx (%s:%d)", + instruction - frame->source_line_base, + source_file.c_str(), frame->source_line); + } else { + printf(" + 0x%llx", instruction - frame->function_base); + } + } + } + printf("\n"); + } +} + +//============================================================================= +static void PrintRegisters(const CallStack *stack, const string &cpu) { + int sequence = 0; + const StackFrame *frame = stack->frames()->at(0); + if (cpu == "x86") { + const StackFrameX86 *frame_x86 = + reinterpret_cast(frame); + + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP) + sequence = PrintRegister("eip", frame_x86->context.eip, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP) + sequence = PrintRegister("esp", frame_x86->context.esp, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP) + sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX) + sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI) + sequence = PrintRegister("esi", frame_x86->context.esi, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI) + sequence = PrintRegister("edi", frame_x86->context.edi, sequence); + if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) { + sequence = PrintRegister("eax", frame_x86->context.eax, sequence); + sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence); + sequence = PrintRegister("edx", frame_x86->context.edx, sequence); + sequence = PrintRegister("efl", frame_x86->context.eflags, sequence); + } + } else if (cpu == "ppc") { + const StackFramePPC *frame_ppc = + reinterpret_cast(frame); + + if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_ALL == + StackFramePPC::CONTEXT_VALID_ALL) { + sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence); + sequence = PrintRegister("srr1", frame_ppc->context.srr1, sequence); + sequence = PrintRegister("cr", frame_ppc->context.cr, sequence); + sequence = PrintRegister("xer", frame_ppc->context.xer, sequence); + sequence = PrintRegister("lr", frame_ppc->context.lr, sequence); + sequence = PrintRegister("ctr", frame_ppc->context.ctr, sequence); + sequence = PrintRegister("mq", frame_ppc->context.mq, sequence); + sequence = PrintRegister("vrsave", frame_ppc->context.vrsave, sequence); + + sequence = 0; + char buffer[5]; + for (int i = 0; i < MD_CONTEXT_PPC_GPR_COUNT; ++i) { + sprintf(buffer, "r%d", i); + sequence = PrintRegister(buffer, frame_ppc->context.gpr[i], sequence); + } + } else { + if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0) + sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence); + if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1) + sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence); + } + } + + printf("\n"); +} + +//============================================================================= +static void Start(Options *options) { + string minidump_file([options->minidumpPath fileSystemRepresentation]); + + // Guess that the symbols are for our default architecture + const NXArchInfo *localArchInfo = NXGetLocalArchInfo(); + string arch = "ppc"; + + if (!localArchInfo) + return; + + if (localArchInfo->cputype & CPU_ARCH_ABI64) + arch = ((localArchInfo->cputype & ~CPU_ARCH_ABI64) == CPU_TYPE_X86) ? + "x86_64" : "ppc64"; + else + arch = (localArchInfo->cputype == CPU_TYPE_X86) ? "x86" : "ppc"; + + BasicSourceLineResolver resolver; + string search_dir = options->searchDir ? + [options->searchDir fileSystemRepresentation] : ""; + scoped_ptr symbol_supplier( + new OnDemandSymbolSupplier(arch, search_dir)); + scoped_ptr + minidump_processor(new MinidumpProcessor(symbol_supplier.get(), &resolver)); + ProcessState process_state; + if (minidump_processor->Process(minidump_file, &process_state) != + MinidumpProcessor::PROCESS_OK) { + fprintf(stderr, "MinidumpProcessor::Process failed\n"); + return; + } + + string cpu = process_state.cpu(); + // If the minidump is different than the default architecture + if (cpu != arch) { + symbol_supplier.reset(new OnDemandSymbolSupplier(cpu, search_dir)); + minidump_processor.reset(new MinidumpProcessor(symbol_supplier.get(), + &resolver)); + + if (minidump_processor->Process(minidump_file, &process_state) != + MinidumpProcessor::PROCESS_OK) { + fprintf(stderr, "MinidumpProcessor::Process failed\n"); + return; + } + } + + // Convert the time to a string + u_int32_t time_date_stamp = process_state.time_date_stamp(); + struct tm timestruct; + gmtime_r(reinterpret_cast(&time_date_stamp), ×truct); + char timestr[20]; + strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", ×truct); + printf("Date: %s GMT\n", timestr); + + string cpu_info = process_state.cpu_info(); + printf("Operating system: %s (%s)\n", process_state.os().c_str(), + process_state.os_version().c_str()); + printf("Architecture: %s\n", cpu.c_str()); + + if (process_state.crashed()) { + printf("Crash reason: %s\n", process_state.crash_reason().c_str()); + printf("Crash address: 0x%llx\n", process_state.crash_address()); + } else { + printf("No crash\n"); + } + + int requesting_thread = process_state.requesting_thread(); + if (requesting_thread != -1) { + printf("\n"); + printf("Thread %d (%s)\n", + requesting_thread, + process_state.crashed() ? "crashed" : + "requested dump, did not crash"); + PrintStack(process_state.threads()->at(requesting_thread), cpu); + } + + // Print all of the threads in the dump. + int thread_count = process_state.threads()->size(); + for (int thread_index = 0; thread_index < thread_count; ++thread_index) { + if (thread_index != requesting_thread) { + // Don't print the crash thread again, it was already printed. + printf("\n"); + printf("Thread %d\n", thread_index); + PrintStack(process_state.threads()->at(thread_index), cpu); + } + } + + // Print the crashed registers + if (requesting_thread != -1) { + printf("\nThread %d:", requesting_thread); + PrintRegisters(process_state.threads()->at(requesting_thread), cpu); + } +} + +//============================================================================= +static void Usage(int argc, const char *argv[]) { + fprintf(stderr, "Convert a minidump to a crash report. Airbag symbol files\n"); + fprintf(stderr, "will be used (or created if missing) in /tmp.\n"); + fprintf(stderr, "Usage: %s [-s search-dir] minidump-file\n", argv[0]); + fprintf(stderr, "\t-s: Specify a search directory to use for missing modules\n"); + fprintf(stderr, "\t-h: Usage\n"); + fprintf(stderr, "\t-?: Usage\n"); +} + +//============================================================================= +static void SetupOptions(int argc, const char *argv[], Options *options) { + extern int optind; + char ch; + + while ((ch = getopt(argc, (char * const *)argv, "s:h?")) != -1) { + switch (ch) { + case 's': + options->searchDir = [[NSFileManager defaultManager] + stringWithFileSystemRepresentation:optarg + length:strlen(optarg)]; + break; + + case 'h': + case '?': + Usage(argc, argv); + exit(1); + break; + } + } + + if ((argc - optind) != 1) { + fprintf(stderr, "%s: Missing minidump file\n", argv[0]); + Usage(argc, argv); + } + + options->minidumpPath = [[NSFileManager defaultManager] + stringWithFileSystemRepresentation:argv[optind] + length:strlen(argv[optind])]; +} + +//============================================================================= +int main (int argc, const char * argv[]) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Options options; + + bzero(&options, sizeof(Options)); + SetupOptions(argc, argv, &options); + Start(&options); + [pool release]; + + return 0; +} diff --git a/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj b/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj new file mode 100644 index 00000000..2c1eccc1 --- /dev/null +++ b/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj @@ -0,0 +1,358 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXBuildFile section */ + 8DD76F9A0486AA7600D96B5E /* crash_report.mm in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* crash_report.mm */; settings = {ATTRIBUTES = (); }; }; + 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; + 9B35FEE40B2675F9008DE8C7 /* code_module.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FEE20B2675F9008DE8C7 /* code_module.h */; }; + 9B35FEE50B2675F9008DE8C7 /* code_modules.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FEE30B2675F9008DE8C7 /* code_modules.h */; }; + 9B35FEE90B26761C008DE8C7 /* basic_code_module.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FEE60B26761C008DE8C7 /* basic_code_module.h */; }; + 9B35FEEA0B26761C008DE8C7 /* basic_code_modules.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FEE70B26761C008DE8C7 /* basic_code_modules.cc */; }; + 9B35FEEB0B26761C008DE8C7 /* basic_code_modules.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FEE80B26761C008DE8C7 /* basic_code_modules.h */; }; + 9B3904960B2E52D90059FABE /* basic_source_line_resolver.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B3904940B2E52D90059FABE /* basic_source_line_resolver.h */; }; + 9B3904970B2E52D90059FABE /* source_line_resolver_interface.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B3904950B2E52D90059FABE /* source_line_resolver_interface.h */; }; + 9B3904990B2E52FD0059FABE /* basic_source_line_resolver.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B3904980B2E52FD0059FABE /* basic_source_line_resolver.cc */; }; + 9BDF172C0B1B8B2400F8391B /* call_stack.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF172A0B1B8B2400F8391B /* call_stack.cc */; }; + 9BDF172D0B1B8B2400F8391B /* minidump_processor.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF172B0B1B8B2400F8391B /* minidump_processor.cc */; }; + 9BDF17410B1B8B9A00F8391B /* minidump.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF173F0B1B8B9A00F8391B /* minidump.cc */; }; + 9BDF17540B1B8BF900F8391B /* stackwalker_ppc.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF17510B1B8BF900F8391B /* stackwalker_ppc.cc */; }; + 9BDF17550B1B8BF900F8391B /* stackwalker_x86.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF17520B1B8BF900F8391B /* stackwalker_x86.cc */; }; + 9BDF17560B1B8BF900F8391B /* stackwalker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF17530B1B8BF900F8391B /* stackwalker.cc */; }; + 9BDF175D0B1B8C1B00F8391B /* process_state.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF175B0B1B8C1B00F8391B /* process_state.cc */; }; + 9BDF176D0B1B8CB100F8391B /* on_demand_symbol_supplier.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF176B0B1B8CB100F8391B /* on_demand_symbol_supplier.h */; }; + 9BDF176E0B1B8CB100F8391B /* on_demand_symbol_supplier.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF176C0B1B8CB100F8391B /* on_demand_symbol_supplier.mm */; }; + 9BDF1A280B1BD58200F8391B /* pathname_stripper.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF1A270B1BD58200F8391B /* pathname_stripper.cc */; }; + 9BDF1AB90B1BE70C00F8391B /* range_map.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF1A7B0B1BE30100F8391B /* range_map.h */; }; + 9BDF1ABA0B1BE70D00F8391B /* range_map-inl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF1A7A0B1BE30100F8391B /* range_map-inl.h */; }; + 9BDF1AFC0B1BEB6300F8391B /* address_map-inl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF1AFA0B1BEB6300F8391B /* address_map-inl.h */; }; + 9BDF1AFD0B1BEB6300F8391B /* address_map.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF1AFB0B1BEB6300F8391B /* address_map.h */; }; + 9BDF21A60B1E825400F8391B /* dump_syms.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF192D0B1BC15D00F8391B /* dump_syms.h */; }; + 9BDF21A70B1E825400F8391B /* dump_syms.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF192E0B1BC15D00F8391B /* dump_syms.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 9BDF176D0B1B8CB100F8391B /* on_demand_symbol_supplier.h in CopyFiles */, + 9BDF1AB90B1BE70C00F8391B /* range_map.h in CopyFiles */, + 9BDF1ABA0B1BE70D00F8391B /* range_map-inl.h in CopyFiles */, + 9BDF1AFC0B1BEB6300F8391B /* address_map-inl.h in CopyFiles */, + 9BDF1AFD0B1BEB6300F8391B /* address_map.h in CopyFiles */, + 9BDF21A60B1E825400F8391B /* dump_syms.h in CopyFiles */, + 9B35FEE40B2675F9008DE8C7 /* code_module.h in CopyFiles */, + 9B35FEE50B2675F9008DE8C7 /* code_modules.h in CopyFiles */, + 9B35FEE90B26761C008DE8C7 /* basic_code_module.h in CopyFiles */, + 9B35FEEB0B26761C008DE8C7 /* basic_code_modules.h in CopyFiles */, + 9B3904960B2E52D90059FABE /* basic_source_line_resolver.h in CopyFiles */, + 9B3904970B2E52D90059FABE /* source_line_resolver_interface.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 08FB7796FE84155DC02AAC07 /* crash_report.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = crash_report.mm; sourceTree = ""; }; + 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 8DD76FA10486AA7600D96B5E /* crash_report */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = crash_report; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B35FEE20B2675F9008DE8C7 /* code_module.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = code_module.h; path = ../../../google_airbag/processor/code_module.h; sourceTree = SOURCE_ROOT; }; + 9B35FEE30B2675F9008DE8C7 /* code_modules.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = code_modules.h; path = ../../../google_airbag/processor/code_modules.h; sourceTree = SOURCE_ROOT; }; + 9B35FEE60B26761C008DE8C7 /* basic_code_module.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = basic_code_module.h; path = ../../../processor/basic_code_module.h; sourceTree = SOURCE_ROOT; }; + 9B35FEE70B26761C008DE8C7 /* basic_code_modules.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = basic_code_modules.cc; path = ../../../processor/basic_code_modules.cc; sourceTree = SOURCE_ROOT; }; + 9B35FEE80B26761C008DE8C7 /* basic_code_modules.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = basic_code_modules.h; path = ../../../processor/basic_code_modules.h; sourceTree = SOURCE_ROOT; }; + 9B3904940B2E52D90059FABE /* basic_source_line_resolver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = basic_source_line_resolver.h; sourceTree = ""; }; + 9B3904950B2E52D90059FABE /* source_line_resolver_interface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = source_line_resolver_interface.h; sourceTree = ""; }; + 9B3904980B2E52FD0059FABE /* basic_source_line_resolver.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = basic_source_line_resolver.cc; path = ../../../processor/basic_source_line_resolver.cc; sourceTree = SOURCE_ROOT; }; + 9BDF16F90B1B8ACD00F8391B /* airbag_types.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = airbag_types.h; sourceTree = ""; }; + 9BDF16FA0B1B8ACD00F8391B /* minidump_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = minidump_format.h; sourceTree = ""; }; + 9BDF16FC0B1B8ACD00F8391B /* call_stack.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = call_stack.h; sourceTree = ""; }; + 9BDF16FD0B1B8ACD00F8391B /* memory_region.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = memory_region.h; sourceTree = ""; }; + 9BDF16FE0B1B8ACD00F8391B /* minidump.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = minidump.h; sourceTree = ""; }; + 9BDF16FF0B1B8ACD00F8391B /* minidump_processor.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = minidump_processor.h; sourceTree = ""; }; + 9BDF17000B1B8ACD00F8391B /* process_state.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = process_state.h; sourceTree = ""; }; + 9BDF17010B1B8ACD00F8391B /* stack_frame.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = stack_frame.h; sourceTree = ""; }; + 9BDF17020B1B8ACD00F8391B /* stack_frame_cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = stack_frame_cpu.h; sourceTree = ""; }; + 9BDF17030B1B8ACD00F8391B /* stackwalker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = stackwalker.h; sourceTree = ""; }; + 9BDF17040B1B8ACD00F8391B /* symbol_supplier.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = symbol_supplier.h; sourceTree = ""; }; + 9BDF172A0B1B8B2400F8391B /* call_stack.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = call_stack.cc; path = ../../../processor/call_stack.cc; sourceTree = SOURCE_ROOT; }; + 9BDF172B0B1B8B2400F8391B /* minidump_processor.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_processor.cc; path = ../../../processor/minidump_processor.cc; sourceTree = SOURCE_ROOT; }; + 9BDF173F0B1B8B9A00F8391B /* minidump.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = minidump.cc; path = ../../../processor/minidump.cc; sourceTree = SOURCE_ROOT; }; + 9BDF17510B1B8BF900F8391B /* stackwalker_ppc.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = stackwalker_ppc.cc; path = ../../../processor/stackwalker_ppc.cc; sourceTree = SOURCE_ROOT; }; + 9BDF17520B1B8BF900F8391B /* stackwalker_x86.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = stackwalker_x86.cc; path = ../../../processor/stackwalker_x86.cc; sourceTree = SOURCE_ROOT; }; + 9BDF17530B1B8BF900F8391B /* stackwalker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = stackwalker.cc; path = ../../../processor/stackwalker.cc; sourceTree = SOURCE_ROOT; }; + 9BDF175B0B1B8C1B00F8391B /* process_state.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = process_state.cc; path = ../../../processor/process_state.cc; sourceTree = SOURCE_ROOT; }; + 9BDF176B0B1B8CB100F8391B /* on_demand_symbol_supplier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = on_demand_symbol_supplier.h; sourceTree = ""; }; + 9BDF176C0B1B8CB100F8391B /* on_demand_symbol_supplier.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = on_demand_symbol_supplier.mm; sourceTree = ""; }; + 9BDF192D0B1BC15D00F8391B /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; }; + 9BDF192E0B1BC15D00F8391B /* dump_syms.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = dump_syms.m; path = ../../../common/mac/dump_syms.m; sourceTree = SOURCE_ROOT; }; + 9BDF1A270B1BD58200F8391B /* pathname_stripper.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = pathname_stripper.cc; path = ../../../processor/pathname_stripper.cc; sourceTree = SOURCE_ROOT; }; + 9BDF1A7A0B1BE30100F8391B /* range_map-inl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "range_map-inl.h"; path = "../../../processor/range_map-inl.h"; sourceTree = SOURCE_ROOT; }; + 9BDF1A7B0B1BE30100F8391B /* range_map.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = range_map.h; path = ../../../processor/range_map.h; sourceTree = SOURCE_ROOT; }; + 9BDF1AFA0B1BEB6300F8391B /* address_map-inl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "address_map-inl.h"; path = "../../../processor/address_map-inl.h"; sourceTree = SOURCE_ROOT; }; + 9BDF1AFB0B1BEB6300F8391B /* address_map.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = address_map.h; path = ../../../processor/address_map.h; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* crash_report */ = { + isa = PBXGroup; + children = ( + 9BDF192D0B1BC15D00F8391B /* dump_syms.h */, + 9BDF192E0B1BC15D00F8391B /* dump_syms.m */, + 08FB7796FE84155DC02AAC07 /* crash_report.mm */, + 9BDF176B0B1B8CB100F8391B /* on_demand_symbol_supplier.h */, + 9BDF176C0B1B8CB100F8391B /* on_demand_symbol_supplier.mm */, + 08FB7795FE84155DC02AAC07 /* airbag */, + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = crash_report; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* airbag */ = { + isa = PBXGroup; + children = ( + 9BDF17280B1B8B0200F8391B /* processor */, + 9BDF16F70B1B8ACD00F8391B /* google_airbag */, + ); + name = airbag; + sourceTree = ""; + }; + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 08FB779EFE84155DC02AAC07 /* Foundation.framework */, + ); + name = "External Frameworks and Libraries"; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76FA10486AA7600D96B5E /* crash_report */, + ); + name = Products; + sourceTree = ""; + }; + 9BDF16F70B1B8ACD00F8391B /* google_airbag */ = { + isa = PBXGroup; + children = ( + 9BDF16F80B1B8ACD00F8391B /* common */, + 9BDF16FB0B1B8ACD00F8391B /* processor */, + ); + name = google_airbag; + path = ../../../google_airbag; + sourceTree = SOURCE_ROOT; + }; + 9BDF16F80B1B8ACD00F8391B /* common */ = { + isa = PBXGroup; + children = ( + 9BDF16F90B1B8ACD00F8391B /* airbag_types.h */, + 9BDF16FA0B1B8ACD00F8391B /* minidump_format.h */, + ); + path = common; + sourceTree = ""; + }; + 9BDF16FB0B1B8ACD00F8391B /* processor */ = { + isa = PBXGroup; + children = ( + 9B3904940B2E52D90059FABE /* basic_source_line_resolver.h */, + 9B3904950B2E52D90059FABE /* source_line_resolver_interface.h */, + 9BDF16FC0B1B8ACD00F8391B /* call_stack.h */, + 9B35FEE20B2675F9008DE8C7 /* code_module.h */, + 9B35FEE30B2675F9008DE8C7 /* code_modules.h */, + 9BDF16FD0B1B8ACD00F8391B /* memory_region.h */, + 9BDF16FE0B1B8ACD00F8391B /* minidump.h */, + 9BDF16FF0B1B8ACD00F8391B /* minidump_processor.h */, + 9BDF17000B1B8ACD00F8391B /* process_state.h */, + 9BDF17010B1B8ACD00F8391B /* stack_frame.h */, + 9BDF17020B1B8ACD00F8391B /* stack_frame_cpu.h */, + 9BDF17030B1B8ACD00F8391B /* stackwalker.h */, + 9BDF17040B1B8ACD00F8391B /* symbol_supplier.h */, + ); + path = processor; + sourceTree = ""; + }; + 9BDF17280B1B8B0200F8391B /* processor */ = { + isa = PBXGroup; + children = ( + 9B3904980B2E52FD0059FABE /* basic_source_line_resolver.cc */, + 9BDF1AFA0B1BEB6300F8391B /* address_map-inl.h */, + 9BDF1AFB0B1BEB6300F8391B /* address_map.h */, + 9B35FEE60B26761C008DE8C7 /* basic_code_module.h */, + 9B35FEE70B26761C008DE8C7 /* basic_code_modules.cc */, + 9B35FEE80B26761C008DE8C7 /* basic_code_modules.h */, + 9BDF172A0B1B8B2400F8391B /* call_stack.cc */, + 9BDF173F0B1B8B9A00F8391B /* minidump.cc */, + 9BDF172B0B1B8B2400F8391B /* minidump_processor.cc */, + 9BDF1A270B1BD58200F8391B /* pathname_stripper.cc */, + 9BDF175B0B1B8C1B00F8391B /* process_state.cc */, + 9BDF1A7A0B1BE30100F8391B /* range_map-inl.h */, + 9BDF1A7B0B1BE30100F8391B /* range_map.h */, + 9BDF17530B1B8BF900F8391B /* stackwalker.cc */, + 9BDF17510B1B8BF900F8391B /* stackwalker_ppc.cc */, + 9BDF17520B1B8BF900F8391B /* stackwalker_x86.cc */, + ); + name = processor; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DD76F960486AA7600D96B5E /* crash_report */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "crash_report" */; + buildPhases = ( + 8DD76F990486AA7600D96B5E /* Sources */, + 8DD76F9B0486AA7600D96B5E /* Frameworks */, + 8DD76F9E0486AA7600D96B5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = crash_report; + productInstallPath = "$(HOME)/bin"; + productName = crash_report; + productReference = 8DD76FA10486AA7600D96B5E /* crash_report */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "crash_report" */; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* crash_report */; + projectDirPath = ""; + targets = ( + 8DD76F960486AA7600D96B5E /* crash_report */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DD76F990486AA7600D96B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DD76F9A0486AA7600D96B5E /* crash_report.mm in Sources */, + 9BDF172C0B1B8B2400F8391B /* call_stack.cc in Sources */, + 9BDF172D0B1B8B2400F8391B /* minidump_processor.cc in Sources */, + 9BDF17410B1B8B9A00F8391B /* minidump.cc in Sources */, + 9BDF17540B1B8BF900F8391B /* stackwalker_ppc.cc in Sources */, + 9BDF17550B1B8BF900F8391B /* stackwalker_x86.cc in Sources */, + 9BDF17560B1B8BF900F8391B /* stackwalker.cc in Sources */, + 9BDF175D0B1B8C1B00F8391B /* process_state.cc in Sources */, + 9BDF176E0B1B8CB100F8391B /* on_demand_symbol_supplier.mm in Sources */, + 9BDF1A280B1BD58200F8391B /* pathname_stripper.cc in Sources */, + 9BDF21A70B1E825400F8391B /* dump_syms.m in Sources */, + 9B35FEEA0B26761C008DE8C7 /* basic_code_modules.cc in Sources */, + 9B3904990B2E52FD0059FABE /* basic_source_line_resolver.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB927508733DD40010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + INSTALL_PATH = "$(HOME)/bin"; + PRODUCT_NAME = crash_report; + USER_HEADER_SEARCH_PATHS = "../../../../** $(inherited)"; + ZERO_LINK = NO; + }; + name = Debug; + }; + 1DEB927608733DD40010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + GCC_C_LANGUAGE_STANDARD = c99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + INSTALL_PATH = "$(HOME)/bin"; + PRODUCT_NAME = crash_report; + USER_HEADER_SEARCH_PATHS = "../../../../** $(inherited)"; + ZERO_LINK = NO; + }; + name = Release; + }; + 1DEB927908733DD40010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Debug; + }; + 1DEB927A08733DD40010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "crash_report" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB927508733DD40010E9CD /* Debug */, + 1DEB927608733DD40010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "crash_report" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB927908733DD40010E9CD /* Debug */, + 1DEB927A08733DD40010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/src/tools/mac/crash_report/on_demand_symbol_supplier.h b/src/tools/mac/crash_report/on_demand_symbol_supplier.h new file mode 100644 index 00000000..16ab7a49 --- /dev/null +++ b/src/tools/mac/crash_report/on_demand_symbol_supplier.h @@ -0,0 +1,92 @@ +// 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. + +// on_demand_symbol_supplier.h: Provides a Symbol Supplier that will create +// an airbag symbol file on demand. + +#ifndef TOOLS_MAC_CRASH_REPORT_ON_DEMAND_SYMBOL_SUPPLIER_H__ +#define TOOLS_MAC_CRASH_REPORT_ON_DEMAND_SYMBOL_SUPPLIER_H__ + +#include +#include +#include "google_airbag/processor/symbol_supplier.h" + +namespace google_airbag { + +using std::map; +using std::string; +class MinidumpModule; + +class OnDemandSymbolSupplier : public SymbolSupplier { + public: + // |architecture| should be one of ppc, i386, x86, ppc64, x86_64 + // |search_dir| is the directory to search for alternative symbols with + // the same name as the module in the minidump + OnDemandSymbolSupplier(const string &architecture, const string &search_dir); + virtual ~OnDemandSymbolSupplier() {} + + // Returns the path to the symbol file for the given module. + virtual SymbolResult GetSymbolFile(const CodeModule *module, + string *symbol_file); + + protected: + // Return symbols for this architecture + string architecture_; + + // Search directory + string search_dir_; + + // When we create a symbol file for a module, save the name of the module + // and the path to that module's symbol file. + map module_file_map_; + + // Return the name for |module| This will be the value used as the key + // to the |module_file_map_|. + string GetNameForModule(const CodeModule *module); + + // Find the module on local system. If the module resides in a different + // location than the full path in the minidump, this will be the location + // used. + string GetLocalModulePath(const CodeModule *module); + + // Return the full path for |module|. + string GetModulePath(const CodeModule *module); + + // Return the path to the symbol file for |module|. If an empty string is + // returned, then |module| doesn't have a symbol file. + string GetModuleSymbolFile(const CodeModule *module); + + // Generate the airbag symbol file for |module|. Return true if successful. + // File is generated in /tmp. + bool GenerateSymbolFile(const CodeModule *module); +}; + +} // namespace google_airbag + +#endif // TOOLS_MAC_CRASH_REPORT_ON_DEMAND_SYMBOL_SUPPLIER_H__ diff --git a/src/tools/mac/crash_report/on_demand_symbol_supplier.mm b/src/tools/mac/crash_report/on_demand_symbol_supplier.mm new file mode 100644 index 00000000..d37313d7 --- /dev/null +++ b/src/tools/mac/crash_report/on_demand_symbol_supplier.mm @@ -0,0 +1,178 @@ +// 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 +#include +#include + +#include "google_airbag/processor/basic_source_line_resolver.h" +#include "google_airbag/processor/minidump.h" +#include "processor/pathname_stripper.h" + +#include "on_demand_symbol_supplier.h" +#include "dump_syms.h" + +using std::map; +using std::string; + +using google_airbag::OnDemandSymbolSupplier; +using google_airbag::PathnameStripper; +using google_airbag::SymbolSupplier; + +OnDemandSymbolSupplier::OnDemandSymbolSupplier(const string &architecture, + const string &search_dir) + : architecture_(architecture), + search_dir_(search_dir) { +} + +SymbolSupplier::SymbolResult +OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module, + string *symbol_file) { + string path(GetModuleSymbolFile(module)); + + if (path.empty()) { + if (!GenerateSymbolFile(module)) + return NOT_FOUND; + + path = GetModuleSymbolFile(module); + } + + if (path.empty()) + return NOT_FOUND; + + *symbol_file = path; + return FOUND; +} + +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:@"/"]; + int count = [pathComponents count]; + NSMutableString *path = [NSMutableString string]; + + for (int i = 0; i < count; ++i) { + [path setString:searchDir]; + + for (int j = 0; j < i + 1; ++j) { + int 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::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.0e9; + + return result; +} + +bool OnDemandSymbolSupplier::GenerateSymbolFile(const CodeModule *module) { + bool result = true; + string name = GetNameForModule(module); + string module_path = GetLocalModulePath(module); + NSString *symbol_path = [NSString stringWithFormat:@"/tmp/%s.%s.sym", + name.c_str(), architecture_.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 = [[DumpSymbols alloc] initWithContentsOfFile:module_str]; + const char *archStr = architecture_.c_str(); + if ([dump setArchitecture:[NSString stringWithUTF8String:archStr]]) { + [dump writeSymbolFile:symbol_path]; + } else { + printf("Architecture %s not available for %s\n", archStr, name.c_str()); + result = false; + } + [dump release]; + } + + // Add the mapping + if (result) + module_file_map_[name] = [symbol_path fileSystemRepresentation]; + + return result; +}