toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 // Copyright (c) 2010 Google Inc.
michael@0 2 // All rights reserved.
michael@0 3 //
michael@0 4 // Redistribution and use in source and binary forms, with or without
michael@0 5 // modification, are permitted provided that the following conditions are
michael@0 6 // met:
michael@0 7 //
michael@0 8 // * Redistributions of source code must retain the above copyright
michael@0 9 // notice, this list of conditions and the following disclaimer.
michael@0 10 // * Redistributions in binary form must reproduce the above
michael@0 11 // copyright notice, this list of conditions and the following disclaimer
michael@0 12 // in the documentation and/or other materials provided with the
michael@0 13 // distribution.
michael@0 14 // * Neither the name of Google Inc. nor the names of its
michael@0 15 // contributors may be used to endorse or promote products derived from
michael@0 16 // this software without specific prior written permission.
michael@0 17 //
michael@0 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 29
michael@0 30 // minidump_stackwalk.cc: Process a minidump with MinidumpProcessor, printing
michael@0 31 // the results, including stack traces.
michael@0 32 //
michael@0 33 // Author: Mark Mentovai
michael@0 34
michael@0 35 #include <stdio.h>
michael@0 36 #include <stdlib.h>
michael@0 37 #include <string.h>
michael@0 38
michael@0 39 #include <string>
michael@0 40 #include <vector>
michael@0 41
michael@0 42 #include "common/scoped_ptr.h"
michael@0 43 #include "common/using_std_string.h"
michael@0 44 #include "google_breakpad/processor/basic_source_line_resolver.h"
michael@0 45 #include "google_breakpad/processor/call_stack.h"
michael@0 46 #include "google_breakpad/processor/code_module.h"
michael@0 47 #include "google_breakpad/processor/code_modules.h"
michael@0 48 #include "google_breakpad/processor/minidump.h"
michael@0 49 #include "google_breakpad/processor/minidump_processor.h"
michael@0 50 #include "google_breakpad/processor/process_state.h"
michael@0 51 #include "google_breakpad/processor/stack_frame_cpu.h"
michael@0 52 #include "processor/logging.h"
michael@0 53 #include "processor/pathname_stripper.h"
michael@0 54 #include "processor/simple_symbol_supplier.h"
michael@0 55
michael@0 56 namespace {
michael@0 57
michael@0 58 using std::vector;
michael@0 59 using google_breakpad::BasicSourceLineResolver;
michael@0 60 using google_breakpad::CallStack;
michael@0 61 using google_breakpad::CodeModule;
michael@0 62 using google_breakpad::CodeModules;
michael@0 63 using google_breakpad::MinidumpModule;
michael@0 64 using google_breakpad::MinidumpProcessor;
michael@0 65 using google_breakpad::PathnameStripper;
michael@0 66 using google_breakpad::ProcessState;
michael@0 67 using google_breakpad::scoped_ptr;
michael@0 68 using google_breakpad::SimpleSymbolSupplier;
michael@0 69 using google_breakpad::StackFrame;
michael@0 70 using google_breakpad::StackFramePPC;
michael@0 71 using google_breakpad::StackFrameSPARC;
michael@0 72 using google_breakpad::StackFrameX86;
michael@0 73 using google_breakpad::StackFrameAMD64;
michael@0 74 using google_breakpad::StackFrameARM;
michael@0 75
michael@0 76 // Separator character for machine readable output.
michael@0 77 static const char kOutputSeparator = '|';
michael@0 78
michael@0 79 // PrintRegister prints a register's name and value to stdout. It will
michael@0 80 // print four registers on a line. For the first register in a set,
michael@0 81 // pass 0 for |start_col|. For registers in a set, pass the most recent
michael@0 82 // return value of PrintRegister.
michael@0 83 // The caller is responsible for printing the final newline after a set
michael@0 84 // of registers is completely printed, regardless of the number of calls
michael@0 85 // to PrintRegister.
michael@0 86 static const int kMaxWidth = 80; // optimize for an 80-column terminal
michael@0 87 static int PrintRegister(const char *name, uint32_t value, int start_col) {
michael@0 88 char buffer[64];
michael@0 89 snprintf(buffer, sizeof(buffer), " %5s = 0x%08x", name, value);
michael@0 90
michael@0 91 if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
michael@0 92 start_col = 0;
michael@0 93 printf("\n ");
michael@0 94 }
michael@0 95 fputs(buffer, stdout);
michael@0 96
michael@0 97 return start_col + strlen(buffer);
michael@0 98 }
michael@0 99
michael@0 100 // PrintRegister64 does the same thing, but for 64-bit registers.
michael@0 101 static int PrintRegister64(const char *name, uint64_t value, int start_col) {
michael@0 102 char buffer[64];
michael@0 103 snprintf(buffer, sizeof(buffer), " %5s = 0x%016" PRIx64 , name, value);
michael@0 104
michael@0 105 if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
michael@0 106 start_col = 0;
michael@0 107 printf("\n ");
michael@0 108 }
michael@0 109 fputs(buffer, stdout);
michael@0 110
michael@0 111 return start_col + strlen(buffer);
michael@0 112 }
michael@0 113
michael@0 114 // StripSeparator takes a string |original| and returns a copy
michael@0 115 // of the string with all occurences of |kOutputSeparator| removed.
michael@0 116 static string StripSeparator(const string &original) {
michael@0 117 string result = original;
michael@0 118 string::size_type position = 0;
michael@0 119 while ((position = result.find(kOutputSeparator, position)) != string::npos) {
michael@0 120 result.erase(position, 1);
michael@0 121 }
michael@0 122 position = 0;
michael@0 123 while ((position = result.find('\n', position)) != string::npos) {
michael@0 124 result.erase(position, 1);
michael@0 125 }
michael@0 126 return result;
michael@0 127 }
michael@0 128
michael@0 129 // PrintStack prints the call stack in |stack| to stdout, in a reasonably
michael@0 130 // useful form. Module, function, and source file names are displayed if
michael@0 131 // they are available. The code offset to the base code address of the
michael@0 132 // source line, function, or module is printed, preferring them in that
michael@0 133 // order. If no source line, function, or module information is available,
michael@0 134 // an absolute code offset is printed.
michael@0 135 //
michael@0 136 // If |cpu| is a recognized CPU name, relevant register state for each stack
michael@0 137 // frame printed is also output, if available.
michael@0 138 static void PrintStack(const CallStack *stack, const string &cpu) {
michael@0 139 int frame_count = stack->frames()->size();
michael@0 140 if (frame_count == 0) {
michael@0 141 printf(" <no frames>\n");
michael@0 142 }
michael@0 143 for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
michael@0 144 const StackFrame *frame = stack->frames()->at(frame_index);
michael@0 145 printf("%2d ", frame_index);
michael@0 146
michael@0 147 uint64_t instruction_address = frame->ReturnAddress();
michael@0 148
michael@0 149 if (frame->module) {
michael@0 150 printf("%s", PathnameStripper::File(frame->module->code_file()).c_str());
michael@0 151 if (!frame->function_name.empty()) {
michael@0 152 printf("!%s", frame->function_name.c_str());
michael@0 153 if (!frame->source_file_name.empty()) {
michael@0 154 string source_file = PathnameStripper::File(frame->source_file_name);
michael@0 155 printf(" [%s : %d + 0x%" PRIx64 "]",
michael@0 156 source_file.c_str(),
michael@0 157 frame->source_line,
michael@0 158 instruction_address - frame->source_line_base);
michael@0 159 } else {
michael@0 160 printf(" + 0x%" PRIx64, instruction_address - frame->function_base);
michael@0 161 }
michael@0 162 } else {
michael@0 163 printf(" + 0x%" PRIx64,
michael@0 164 instruction_address - frame->module->base_address());
michael@0 165 }
michael@0 166 } else {
michael@0 167 printf("0x%" PRIx64, instruction_address);
michael@0 168 }
michael@0 169 printf("\n ");
michael@0 170
michael@0 171 int sequence = 0;
michael@0 172 if (cpu == "x86") {
michael@0 173 const StackFrameX86 *frame_x86 =
michael@0 174 reinterpret_cast<const StackFrameX86*>(frame);
michael@0 175
michael@0 176 if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP)
michael@0 177 sequence = PrintRegister("eip", frame_x86->context.eip, sequence);
michael@0 178 if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)
michael@0 179 sequence = PrintRegister("esp", frame_x86->context.esp, sequence);
michael@0 180 if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP)
michael@0 181 sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence);
michael@0 182 if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX)
michael@0 183 sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence);
michael@0 184 if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI)
michael@0 185 sequence = PrintRegister("esi", frame_x86->context.esi, sequence);
michael@0 186 if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI)
michael@0 187 sequence = PrintRegister("edi", frame_x86->context.edi, sequence);
michael@0 188 if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
michael@0 189 sequence = PrintRegister("eax", frame_x86->context.eax, sequence);
michael@0 190 sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence);
michael@0 191 sequence = PrintRegister("edx", frame_x86->context.edx, sequence);
michael@0 192 sequence = PrintRegister("efl", frame_x86->context.eflags, sequence);
michael@0 193 }
michael@0 194 } else if (cpu == "ppc") {
michael@0 195 const StackFramePPC *frame_ppc =
michael@0 196 reinterpret_cast<const StackFramePPC*>(frame);
michael@0 197
michael@0 198 if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0)
michael@0 199 sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence);
michael@0 200 if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1)
michael@0 201 sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence);
michael@0 202 } else if (cpu == "amd64") {
michael@0 203 const StackFrameAMD64 *frame_amd64 =
michael@0 204 reinterpret_cast<const StackFrameAMD64*>(frame);
michael@0 205
michael@0 206 if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX)
michael@0 207 sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence);
michael@0 208 if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12)
michael@0 209 sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence);
michael@0 210 if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13)
michael@0 211 sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence);
michael@0 212 if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14)
michael@0 213 sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence);
michael@0 214 if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15)
michael@0 215 sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence);
michael@0 216 if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP)
michael@0 217 sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence);
michael@0 218 if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP)
michael@0 219 sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence);
michael@0 220 if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP)
michael@0 221 sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence);
michael@0 222 } else if (cpu == "sparc") {
michael@0 223 const StackFrameSPARC *frame_sparc =
michael@0 224 reinterpret_cast<const StackFrameSPARC*>(frame);
michael@0 225
michael@0 226 if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP)
michael@0 227 sequence = PrintRegister("sp", frame_sparc->context.g_r[14], sequence);
michael@0 228 if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP)
michael@0 229 sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence);
michael@0 230 if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC)
michael@0 231 sequence = PrintRegister("pc", frame_sparc->context.pc, sequence);
michael@0 232 } else if (cpu == "arm") {
michael@0 233 const StackFrameARM *frame_arm =
michael@0 234 reinterpret_cast<const StackFrameARM*>(frame);
michael@0 235
michael@0 236 // Argument registers (caller-saves), which will likely only be valid
michael@0 237 // for the youngest frame.
michael@0 238 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0)
michael@0 239 sequence = PrintRegister("r0", frame_arm->context.iregs[0], sequence);
michael@0 240 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1)
michael@0 241 sequence = PrintRegister("r1", frame_arm->context.iregs[1], sequence);
michael@0 242 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2)
michael@0 243 sequence = PrintRegister("r2", frame_arm->context.iregs[2], sequence);
michael@0 244 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3)
michael@0 245 sequence = PrintRegister("r3", frame_arm->context.iregs[3], sequence);
michael@0 246
michael@0 247 // General-purpose callee-saves registers.
michael@0 248 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
michael@0 249 sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
michael@0 250 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
michael@0 251 sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
michael@0 252 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
michael@0 253 sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
michael@0 254 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
michael@0 255 sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
michael@0 256 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
michael@0 257 sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
michael@0 258 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
michael@0 259 sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
michael@0 260 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
michael@0 261 sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence);
michael@0 262
michael@0 263 // Registers with a dedicated or conventional purpose.
michael@0 264 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
michael@0 265 sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence);
michael@0 266 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
michael@0 267 sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence);
michael@0 268 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
michael@0 269 sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
michael@0 270 if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
michael@0 271 sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
michael@0 272 }
michael@0 273 printf("\n Found by: %s\n", frame->trust_description().c_str());
michael@0 274 }
michael@0 275 }
michael@0 276
michael@0 277 // PrintStackMachineReadable prints the call stack in |stack| to stdout,
michael@0 278 // in the following machine readable pipe-delimited text format:
michael@0 279 // thread number|frame number|module|function|source file|line|offset
michael@0 280 //
michael@0 281 // Module, function, source file, and source line may all be empty
michael@0 282 // depending on availability. The code offset follows the same rules as
michael@0 283 // PrintStack above.
michael@0 284 static void PrintStackMachineReadable(int thread_num, const CallStack *stack) {
michael@0 285 int frame_count = stack->frames()->size();
michael@0 286 for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
michael@0 287 const StackFrame *frame = stack->frames()->at(frame_index);
michael@0 288 printf("%d%c%d%c", thread_num, kOutputSeparator, frame_index,
michael@0 289 kOutputSeparator);
michael@0 290
michael@0 291 uint64_t instruction_address = frame->ReturnAddress();
michael@0 292
michael@0 293 if (frame->module) {
michael@0 294 assert(!frame->module->code_file().empty());
michael@0 295 printf("%s", StripSeparator(PathnameStripper::File(
michael@0 296 frame->module->code_file())).c_str());
michael@0 297 if (!frame->function_name.empty()) {
michael@0 298 printf("%c%s", kOutputSeparator,
michael@0 299 StripSeparator(frame->function_name).c_str());
michael@0 300 if (!frame->source_file_name.empty()) {
michael@0 301 printf("%c%s%c%d%c0x%" PRIx64,
michael@0 302 kOutputSeparator,
michael@0 303 StripSeparator(frame->source_file_name).c_str(),
michael@0 304 kOutputSeparator,
michael@0 305 frame->source_line,
michael@0 306 kOutputSeparator,
michael@0 307 instruction_address - frame->source_line_base);
michael@0 308 } else {
michael@0 309 printf("%c%c%c0x%" PRIx64,
michael@0 310 kOutputSeparator, // empty source file
michael@0 311 kOutputSeparator, // empty source line
michael@0 312 kOutputSeparator,
michael@0 313 instruction_address - frame->function_base);
michael@0 314 }
michael@0 315 } else {
michael@0 316 printf("%c%c%c%c0x%" PRIx64,
michael@0 317 kOutputSeparator, // empty function name
michael@0 318 kOutputSeparator, // empty source file
michael@0 319 kOutputSeparator, // empty source line
michael@0 320 kOutputSeparator,
michael@0 321 instruction_address - frame->module->base_address());
michael@0 322 }
michael@0 323 } else {
michael@0 324 // the printf before this prints a trailing separator for module name
michael@0 325 printf("%c%c%c%c0x%" PRIx64,
michael@0 326 kOutputSeparator, // empty function name
michael@0 327 kOutputSeparator, // empty source file
michael@0 328 kOutputSeparator, // empty source line
michael@0 329 kOutputSeparator,
michael@0 330 instruction_address);
michael@0 331 }
michael@0 332 printf("\n");
michael@0 333 }
michael@0 334 }
michael@0 335
michael@0 336 // ContainsModule checks whether a given |module| is in the vector
michael@0 337 // |modules_without_symbols|.
michael@0 338 static bool ContainsModule(
michael@0 339 const vector<const CodeModule*> *modules,
michael@0 340 const CodeModule *module) {
michael@0 341 assert(modules);
michael@0 342 assert(module);
michael@0 343 vector<const CodeModule*>::const_iterator iter;
michael@0 344 for (iter = modules->begin(); iter != modules->end(); ++iter) {
michael@0 345 if (module->debug_file().compare((*iter)->debug_file()) == 0 &&
michael@0 346 module->debug_identifier().compare((*iter)->debug_identifier()) == 0) {
michael@0 347 return true;
michael@0 348 }
michael@0 349 }
michael@0 350 return false;
michael@0 351 }
michael@0 352
michael@0 353 // PrintModule prints a single |module| to stdout.
michael@0 354 // |modules_without_symbols| should contain the list of modules that were
michael@0 355 // confirmed to be missing their symbols during the stack walk.
michael@0 356 static void PrintModule(
michael@0 357 const CodeModule *module,
michael@0 358 const vector<const CodeModule*> *modules_without_symbols,
michael@0 359 uint64_t main_address) {
michael@0 360 string missing_symbols;
michael@0 361 if (ContainsModule(modules_without_symbols, module)) {
michael@0 362 missing_symbols = " (WARNING: No symbols, " +
michael@0 363 PathnameStripper::File(module->debug_file()) + ", " +
michael@0 364 module->debug_identifier() + ")";
michael@0 365 }
michael@0 366 uint64_t base_address = module->base_address();
michael@0 367 printf("0x%08" PRIx64 " - 0x%08" PRIx64 " %s %s%s%s\n",
michael@0 368 base_address, base_address + module->size() - 1,
michael@0 369 PathnameStripper::File(module->code_file()).c_str(),
michael@0 370 module->version().empty() ? "???" : module->version().c_str(),
michael@0 371 main_address != 0 && base_address == main_address ? " (main)" : "",
michael@0 372 missing_symbols.c_str());
michael@0 373 }
michael@0 374
michael@0 375 // PrintModules prints the list of all loaded |modules| to stdout.
michael@0 376 // |modules_without_symbols| should contain the list of modules that were
michael@0 377 // confirmed to be missing their symbols during the stack walk.
michael@0 378 static void PrintModules(
michael@0 379 const CodeModules *modules,
michael@0 380 const vector<const CodeModule*> *modules_without_symbols) {
michael@0 381 if (!modules)
michael@0 382 return;
michael@0 383
michael@0 384 printf("\n");
michael@0 385 printf("Loaded modules:\n");
michael@0 386
michael@0 387 uint64_t main_address = 0;
michael@0 388 const CodeModule *main_module = modules->GetMainModule();
michael@0 389 if (main_module) {
michael@0 390 main_address = main_module->base_address();
michael@0 391 }
michael@0 392
michael@0 393 unsigned int module_count = modules->module_count();
michael@0 394 for (unsigned int module_sequence = 0;
michael@0 395 module_sequence < module_count;
michael@0 396 ++module_sequence) {
michael@0 397 const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
michael@0 398 PrintModule(module, modules_without_symbols, main_address);
michael@0 399 }
michael@0 400 }
michael@0 401
michael@0 402 // PrintModulesMachineReadable outputs a list of loaded modules,
michael@0 403 // one per line, in the following machine-readable pipe-delimited
michael@0 404 // text format:
michael@0 405 // Module|{Module Filename}|{Version}|{Debug Filename}|{Debug Identifier}|
michael@0 406 // {Base Address}|{Max Address}|{Main}
michael@0 407 static void PrintModulesMachineReadable(const CodeModules *modules) {
michael@0 408 if (!modules)
michael@0 409 return;
michael@0 410
michael@0 411 uint64_t main_address = 0;
michael@0 412 const CodeModule *main_module = modules->GetMainModule();
michael@0 413 if (main_module) {
michael@0 414 main_address = main_module->base_address();
michael@0 415 }
michael@0 416
michael@0 417 unsigned int module_count = modules->module_count();
michael@0 418 for (unsigned int module_sequence = 0;
michael@0 419 module_sequence < module_count;
michael@0 420 ++module_sequence) {
michael@0 421 const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
michael@0 422 uint64_t base_address = module->base_address();
michael@0 423 printf("Module%c%s%c%s%c%s%c%s%c0x%08" PRIx64 "%c0x%08" PRIx64 "%c%d\n",
michael@0 424 kOutputSeparator,
michael@0 425 StripSeparator(PathnameStripper::File(module->code_file())).c_str(),
michael@0 426 kOutputSeparator, StripSeparator(module->version()).c_str(),
michael@0 427 kOutputSeparator,
michael@0 428 StripSeparator(PathnameStripper::File(module->debug_file())).c_str(),
michael@0 429 kOutputSeparator,
michael@0 430 StripSeparator(module->debug_identifier()).c_str(),
michael@0 431 kOutputSeparator, base_address,
michael@0 432 kOutputSeparator, base_address + module->size() - 1,
michael@0 433 kOutputSeparator,
michael@0 434 main_module != NULL && base_address == main_address ? 1 : 0);
michael@0 435 }
michael@0 436 }
michael@0 437
michael@0 438 static void PrintProcessState(const ProcessState& process_state) {
michael@0 439 // Print OS and CPU information.
michael@0 440 string cpu = process_state.system_info()->cpu;
michael@0 441 string cpu_info = process_state.system_info()->cpu_info;
michael@0 442 printf("Operating system: %s\n", process_state.system_info()->os.c_str());
michael@0 443 printf(" %s\n",
michael@0 444 process_state.system_info()->os_version.c_str());
michael@0 445 printf("CPU: %s\n", cpu.c_str());
michael@0 446 if (!cpu_info.empty()) {
michael@0 447 // This field is optional.
michael@0 448 printf(" %s\n", cpu_info.c_str());
michael@0 449 }
michael@0 450 printf(" %d CPU%s\n",
michael@0 451 process_state.system_info()->cpu_count,
michael@0 452 process_state.system_info()->cpu_count != 1 ? "s" : "");
michael@0 453 printf("\n");
michael@0 454
michael@0 455 // Print crash information.
michael@0 456 if (process_state.crashed()) {
michael@0 457 printf("Crash reason: %s\n", process_state.crash_reason().c_str());
michael@0 458 printf("Crash address: 0x%" PRIx64 "\n", process_state.crash_address());
michael@0 459 } else {
michael@0 460 printf("No crash\n");
michael@0 461 }
michael@0 462
michael@0 463 string assertion = process_state.assertion();
michael@0 464 if (!assertion.empty()) {
michael@0 465 printf("Assertion: %s\n", assertion.c_str());
michael@0 466 }
michael@0 467
michael@0 468 // If the thread that requested the dump is known, print it first.
michael@0 469 int requesting_thread = process_state.requesting_thread();
michael@0 470 if (requesting_thread != -1) {
michael@0 471 printf("\n");
michael@0 472 printf("Thread %d (%s)\n",
michael@0 473 requesting_thread,
michael@0 474 process_state.crashed() ? "crashed" :
michael@0 475 "requested dump, did not crash");
michael@0 476 PrintStack(process_state.threads()->at(requesting_thread), cpu);
michael@0 477 }
michael@0 478
michael@0 479 // Print all of the threads in the dump.
michael@0 480 int thread_count = process_state.threads()->size();
michael@0 481 for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
michael@0 482 if (thread_index != requesting_thread) {
michael@0 483 // Don't print the crash thread again, it was already printed.
michael@0 484 printf("\n");
michael@0 485 printf("Thread %d\n", thread_index);
michael@0 486 PrintStack(process_state.threads()->at(thread_index), cpu);
michael@0 487 }
michael@0 488 }
michael@0 489
michael@0 490 PrintModules(process_state.modules(),
michael@0 491 process_state.modules_without_symbols());
michael@0 492 }
michael@0 493
michael@0 494 static void PrintProcessStateMachineReadable(const ProcessState& process_state)
michael@0 495 {
michael@0 496 // Print OS and CPU information.
michael@0 497 // OS|{OS Name}|{OS Version}
michael@0 498 // CPU|{CPU Name}|{CPU Info}|{Number of CPUs}
michael@0 499 printf("OS%c%s%c%s\n", kOutputSeparator,
michael@0 500 StripSeparator(process_state.system_info()->os).c_str(),
michael@0 501 kOutputSeparator,
michael@0 502 StripSeparator(process_state.system_info()->os_version).c_str());
michael@0 503 printf("CPU%c%s%c%s%c%d\n", kOutputSeparator,
michael@0 504 StripSeparator(process_state.system_info()->cpu).c_str(),
michael@0 505 kOutputSeparator,
michael@0 506 // this may be empty
michael@0 507 StripSeparator(process_state.system_info()->cpu_info).c_str(),
michael@0 508 kOutputSeparator,
michael@0 509 process_state.system_info()->cpu_count);
michael@0 510
michael@0 511 int requesting_thread = process_state.requesting_thread();
michael@0 512
michael@0 513 // Print crash information.
michael@0 514 // Crash|{Crash Reason}|{Crash Address}|{Crashed Thread}
michael@0 515 printf("Crash%c", kOutputSeparator);
michael@0 516 if (process_state.crashed()) {
michael@0 517 printf("%s%c0x%" PRIx64 "%c",
michael@0 518 StripSeparator(process_state.crash_reason()).c_str(),
michael@0 519 kOutputSeparator, process_state.crash_address(), kOutputSeparator);
michael@0 520 } else {
michael@0 521 // print assertion info, if available, in place of crash reason,
michael@0 522 // instead of the unhelpful "No crash"
michael@0 523 string assertion = process_state.assertion();
michael@0 524 if (!assertion.empty()) {
michael@0 525 printf("%s%c%c", StripSeparator(assertion).c_str(),
michael@0 526 kOutputSeparator, kOutputSeparator);
michael@0 527 } else {
michael@0 528 printf("No crash%c%c", kOutputSeparator, kOutputSeparator);
michael@0 529 }
michael@0 530 }
michael@0 531
michael@0 532 if (requesting_thread != -1) {
michael@0 533 printf("%d\n", requesting_thread);
michael@0 534 } else {
michael@0 535 printf("\n");
michael@0 536 }
michael@0 537
michael@0 538 PrintModulesMachineReadable(process_state.modules());
michael@0 539
michael@0 540 // blank line to indicate start of threads
michael@0 541 printf("\n");
michael@0 542
michael@0 543 // If the thread that requested the dump is known, print it first.
michael@0 544 if (requesting_thread != -1) {
michael@0 545 PrintStackMachineReadable(requesting_thread,
michael@0 546 process_state.threads()->at(requesting_thread));
michael@0 547 }
michael@0 548
michael@0 549 // Print all of the threads in the dump.
michael@0 550 int thread_count = process_state.threads()->size();
michael@0 551 for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
michael@0 552 if (thread_index != requesting_thread) {
michael@0 553 // Don't print the crash thread again, it was already printed.
michael@0 554 PrintStackMachineReadable(thread_index,
michael@0 555 process_state.threads()->at(thread_index));
michael@0 556 }
michael@0 557 }
michael@0 558 }
michael@0 559
michael@0 560 // Processes |minidump_file| using MinidumpProcessor. |symbol_path|, if
michael@0 561 // non-empty, is the base directory of a symbol storage area, laid out in
michael@0 562 // the format required by SimpleSymbolSupplier. If such a storage area
michael@0 563 // is specified, it is made available for use by the MinidumpProcessor.
michael@0 564 //
michael@0 565 // Returns the value of MinidumpProcessor::Process. If processing succeeds,
michael@0 566 // prints identifying OS and CPU information from the minidump, crash
michael@0 567 // information if the minidump was produced as a result of a crash, and
michael@0 568 // call stacks for each thread contained in the minidump. All information
michael@0 569 // is printed to stdout.
michael@0 570 static bool PrintMinidumpProcess(const string &minidump_file,
michael@0 571 const vector<string> &symbol_paths,
michael@0 572 bool machine_readable) {
michael@0 573 scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
michael@0 574 if (!symbol_paths.empty()) {
michael@0 575 // TODO(mmentovai): check existence of symbol_path if specified?
michael@0 576 symbol_supplier.reset(new SimpleSymbolSupplier(symbol_paths));
michael@0 577 }
michael@0 578
michael@0 579 BasicSourceLineResolver resolver;
michael@0 580 MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver);
michael@0 581
michael@0 582 // Process the minidump.
michael@0 583 ProcessState process_state;
michael@0 584 if (minidump_processor.Process(minidump_file, &process_state) !=
michael@0 585 google_breakpad::PROCESS_OK) {
michael@0 586 BPLOG(ERROR) << "MinidumpProcessor::Process failed";
michael@0 587 return false;
michael@0 588 }
michael@0 589
michael@0 590 if (machine_readable) {
michael@0 591 PrintProcessStateMachineReadable(process_state);
michael@0 592 } else {
michael@0 593 PrintProcessState(process_state);
michael@0 594 }
michael@0 595
michael@0 596 return true;
michael@0 597 }
michael@0 598
michael@0 599 } // namespace
michael@0 600
michael@0 601 static void usage(const char *program_name) {
michael@0 602 fprintf(stderr, "usage: %s [-m] <minidump-file> [symbol-path ...]\n"
michael@0 603 " -m : Output in machine-readable format\n",
michael@0 604 program_name);
michael@0 605 }
michael@0 606
michael@0 607 int main(int argc, char **argv) {
michael@0 608 BPLOG_INIT(&argc, &argv);
michael@0 609
michael@0 610 if (argc < 2) {
michael@0 611 usage(argv[0]);
michael@0 612 return 1;
michael@0 613 }
michael@0 614
michael@0 615 const char *minidump_file;
michael@0 616 bool machine_readable;
michael@0 617 int symbol_path_arg;
michael@0 618
michael@0 619 if (strcmp(argv[1], "-m") == 0) {
michael@0 620 if (argc < 3) {
michael@0 621 usage(argv[0]);
michael@0 622 return 1;
michael@0 623 }
michael@0 624
michael@0 625 machine_readable = true;
michael@0 626 minidump_file = argv[2];
michael@0 627 symbol_path_arg = 3;
michael@0 628 } else {
michael@0 629 machine_readable = false;
michael@0 630 minidump_file = argv[1];
michael@0 631 symbol_path_arg = 2;
michael@0 632 }
michael@0 633
michael@0 634 // extra arguments are symbol paths
michael@0 635 std::vector<string> symbol_paths;
michael@0 636 if (argc > symbol_path_arg) {
michael@0 637 for (int argi = symbol_path_arg; argi < argc; ++argi)
michael@0 638 symbol_paths.push_back(argv[argi]);
michael@0 639 }
michael@0 640
michael@0 641 return PrintMinidumpProcess(minidump_file,
michael@0 642 symbol_paths,
michael@0 643 machine_readable) ? 0 : 1;
michael@0 644 }

mercurial