Sat, 03 Jan 2015 20:18:00 +0100
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 | } |