microdump_writer.cc
1 // Copyright 2014 Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // This translation unit generates microdumps into the console (logcat on 30 // Android). See crbug.com/410294 for more info and design docs. 31 32 #ifdef HAVE_CONFIG_H 33 #include <config.h> // Must come first 34 #endif 35 36 #include "client/linux/microdump_writer/microdump_writer.h" 37 38 #include <limits> 39 40 #include <sys/utsname.h> 41 42 #include "client/linux/dump_writer_common/thread_info.h" 43 #include "client/linux/dump_writer_common/ucontext_reader.h" 44 #include "client/linux/handler/exception_handler.h" 45 #include "client/linux/handler/microdump_extra_info.h" 46 #include "client/linux/log/log.h" 47 #include "client/linux/minidump_writer/linux_ptrace_dumper.h" 48 #include "common/linux/file_id.h" 49 #include "common/linux/linux_libc_support.h" 50 #include "common/memory_allocator.h" 51 52 namespace { 53 54 using google_breakpad::auto_wasteful_vector; 55 using google_breakpad::elf::kDefaultBuildIdSize; 56 using google_breakpad::ExceptionHandler; 57 using google_breakpad::LinuxDumper; 58 using google_breakpad::LinuxPtraceDumper; 59 using google_breakpad::MappingInfo; 60 using google_breakpad::MappingList; 61 using google_breakpad::MicrodumpExtraInfo; 62 using google_breakpad::RawContextCPU; 63 using google_breakpad::ThreadInfo; 64 using google_breakpad::UContextReader; 65 66 const size_t kLineBufferSize = 2048; 67 68 #if !defined(__LP64__) 69 // The following are only used by DumpFreeSpace, so need to be compiled 70 // in conditionally in the same way. 71 72 template <typename Dst, typename Src> 73 Dst saturated_cast(Src src) { 74 if (src >= std::numeric_limits<Dst>::max()) 75 return std::numeric_limits<Dst>::max(); 76 if (src <= std::numeric_limits<Dst>::min()) 77 return std::numeric_limits<Dst>::min(); 78 return static_cast<Dst>(src); 79 } 80 81 int Log2Floor(uint64_t n) { 82 // Copied from chromium src/base/bits.h 83 if (n == 0) 84 return -1; 85 int log = 0; 86 uint64_t value = n; 87 for (int i = 5; i >= 0; --i) { 88 int shift = (1 << i); 89 uint64_t x = value >> shift; 90 if (x != 0) { 91 value = x; 92 log += shift; 93 } 94 } 95 assert(value == 1u); 96 return log; 97 } 98 99 bool MappingsAreAdjacent(const MappingInfo& a, const MappingInfo& b) { 100 // Because of load biasing, we can end up with a situation where two 101 // mappings actually overlap. So we will define adjacency to also include a 102 // b start address that lies within a's address range (including starting 103 // immediately after a). 104 // Because load biasing only ever moves the start address backwards, the end 105 // address should still increase. 106 return a.start_addr <= b.start_addr && a.start_addr + a.size >= b.start_addr; 107 } 108 109 bool MappingLessThan(const MappingInfo* a, const MappingInfo* b) { 110 // Return true if mapping a is before mapping b. 111 // For the same reason (load biasing) we compare end addresses, which - unlike 112 // start addresses - will not have been modified. 113 return a->start_addr + a->size < b->start_addr + b->size; 114 } 115 116 size_t NextOrderedMapping( 117 const google_breakpad::wasteful_vector<MappingInfo*>& mappings, 118 size_t curr) { 119 // Find the mapping that directly follows mappings[curr]. 120 // If no such mapping exists, return |invalid| to indicate this. 121 const size_t invalid = std::numeric_limits<size_t>::max(); 122 size_t best = invalid; 123 for (size_t next = 0; next < mappings.size(); ++next) { 124 if (MappingLessThan(mappings[curr], mappings[next]) && 125 (best == invalid || MappingLessThan(mappings[next], mappings[best]))) { 126 best = next; 127 } 128 } 129 return best; 130 } 131 132 #endif // !__LP64__ 133 134 class MicrodumpWriter { 135 public: 136 MicrodumpWriter(const ExceptionHandler::CrashContext* context, 137 const MappingList& mappings, 138 bool skip_dump_if_principal_mapping_not_referenced, 139 uintptr_t address_within_principal_mapping, 140 bool sanitize_stack, 141 const MicrodumpExtraInfo& microdump_extra_info, 142 LinuxDumper* dumper) 143 : ucontext_(context ? &context->context : NULL), 144 #if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE 145 float_state_(context ? &context->float_state : NULL), 146 #endif 147 dumper_(dumper), 148 mapping_list_(mappings), 149 skip_dump_if_principal_mapping_not_referenced_( 150 skip_dump_if_principal_mapping_not_referenced), 151 address_within_principal_mapping_(address_within_principal_mapping), 152 sanitize_stack_(sanitize_stack), 153 microdump_extra_info_(microdump_extra_info), 154 log_line_(NULL), 155 stack_copy_(NULL), 156 stack_len_(0), 157 stack_lower_bound_(0), 158 stack_pointer_(0) { 159 log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize)); 160 if (log_line_) 161 log_line_[0] = '\0'; // Clear out the log line buffer. 162 } 163 164 ~MicrodumpWriter() { dumper_->ThreadsResume(); } 165 166 bool Init() { 167 // In the exceptional case where the system was out of memory and there 168 // wasn't even room to allocate the line buffer, bail out. There is nothing 169 // useful we can possibly achieve without the ability to Log. At least let's 170 // try to not crash. 171 if (!dumper_->Init() || !log_line_) 172 return false; 173 return dumper_->ThreadsSuspend() && dumper_->LateInit(); 174 } 175 176 void Dump() { 177 CaptureResult stack_capture_result = CaptureCrashingThreadStack(-1); 178 if (stack_capture_result == CAPTURE_UNINTERESTING) { 179 LogLine("Microdump skipped (uninteresting)"); 180 return; 181 } 182 183 LogLine("-----BEGIN BREAKPAD MICRODUMP-----"); 184 DumpProductInformation(); 185 DumpOSInformation(); 186 DumpProcessType(); 187 DumpCrashReason(); 188 DumpGPUInformation(); 189 #if !defined(__LP64__) 190 DumpFreeSpace(); 191 #endif 192 if (stack_capture_result == CAPTURE_OK) 193 DumpThreadStack(); 194 DumpCPUState(); 195 DumpMappings(); 196 LogLine("-----END BREAKPAD MICRODUMP-----"); 197 } 198 199 private: 200 enum CaptureResult { CAPTURE_OK, CAPTURE_FAILED, CAPTURE_UNINTERESTING }; 201 202 // Writes one line to the system log. 203 void LogLine(const char* msg) { 204 #if defined(__ANDROID__) 205 logger::writeToCrashLog(msg); 206 #else 207 logger::write(msg, my_strlen(msg)); 208 logger::write("\n", 1); 209 #endif 210 } 211 212 // Stages the given string in the current line buffer. 213 void LogAppend(const char* str) { 214 my_strlcat(log_line_, str, kLineBufferSize); 215 } 216 217 // As above (required to take precedence over template specialization below). 218 void LogAppend(char* str) { 219 LogAppend(const_cast<const char*>(str)); 220 } 221 222 // Stages the hex repr. of the given int type in the current line buffer. 223 template<typename T> 224 void LogAppend(T value) { 225 // Make enough room to hex encode the largest int type + NUL. 226 static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 227 'A', 'B', 'C', 'D', 'E', 'F'}; 228 char hexstr[sizeof(T) * 2 + 1]; 229 for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4) 230 hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F]; 231 hexstr[sizeof(T) * 2] = '\0'; 232 LogAppend(hexstr); 233 } 234 235 // Stages the buffer content hex-encoded in the current line buffer. 236 void LogAppend(const void* buf, size_t length) { 237 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf); 238 for (size_t i = 0; i < length; ++i, ++ptr) 239 LogAppend(*ptr); 240 } 241 242 // Writes out the current line buffer on the system log. 243 void LogCommitLine() { 244 LogLine(log_line_); 245 log_line_[0] = 0; 246 } 247 248 CaptureResult CaptureCrashingThreadStack(int max_stack_len) { 249 stack_pointer_ = UContextReader::GetStackPointer(ucontext_); 250 251 if (!dumper_->GetStackInfo(reinterpret_cast<const void**>(&stack_lower_bound_), 252 &stack_len_, stack_pointer_)) { 253 return CAPTURE_FAILED; 254 } 255 256 if (max_stack_len >= 0 && 257 stack_len_ > static_cast<size_t>(max_stack_len)) { 258 stack_len_ = max_stack_len; 259 } 260 261 stack_copy_ = reinterpret_cast<uint8_t*>(Alloc(stack_len_)); 262 dumper_->CopyFromProcess(stack_copy_, dumper_->crash_thread(), 263 reinterpret_cast<const void*>(stack_lower_bound_), 264 stack_len_); 265 266 if (!skip_dump_if_principal_mapping_not_referenced_) return CAPTURE_OK; 267 268 const MappingInfo* principal_mapping = 269 dumper_->FindMappingNoBias(address_within_principal_mapping_); 270 if (!principal_mapping) return CAPTURE_UNINTERESTING; 271 272 uintptr_t low_addr = principal_mapping->system_mapping_info.start_addr; 273 uintptr_t high_addr = principal_mapping->system_mapping_info.end_addr; 274 uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_); 275 if (low_addr <= pc && pc <= high_addr) return CAPTURE_OK; 276 277 if (dumper_->StackHasPointerToMapping(stack_copy_, stack_len_, 278 stack_pointer_ - stack_lower_bound_, 279 *principal_mapping)) { 280 return CAPTURE_OK; 281 } 282 return CAPTURE_UNINTERESTING; 283 } 284 285 void DumpProductInformation() { 286 LogAppend("V "); 287 if (microdump_extra_info_.product_info) { 288 LogAppend(microdump_extra_info_.product_info); 289 } else { 290 LogAppend("UNKNOWN:0.0.0.0"); 291 } 292 LogCommitLine(); 293 } 294 295 void DumpProcessType() { 296 LogAppend("P "); 297 if (microdump_extra_info_.process_type) { 298 LogAppend(microdump_extra_info_.process_type); 299 } else { 300 LogAppend("UNKNOWN"); 301 } 302 LogCommitLine(); 303 } 304 305 void DumpCrashReason() { 306 LogAppend("R "); 307 LogAppend(dumper_->crash_signal()); 308 LogAppend(" "); 309 LogAppend(dumper_->GetCrashSignalString()); 310 LogAppend(" "); 311 LogAppend(dumper_->crash_address()); 312 LogCommitLine(); 313 } 314 315 void DumpOSInformation() { 316 const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF)); 317 318 #if defined(__ANDROID__) 319 const char kOSId[] = "A"; 320 #else 321 const char kOSId[] = "L"; 322 #endif 323 324 // Dump the runtime architecture. On multiarch devices it might not match the 325 // hw architecture (the one returned by uname()), for instance in the case of 326 // a 32-bit app running on a aarch64 device. 327 #if defined(__aarch64__) 328 const char kArch[] = "arm64"; 329 #elif defined(__ARMEL__) 330 const char kArch[] = "arm"; 331 #elif defined(__x86_64__) 332 const char kArch[] = "x86_64"; 333 #elif defined(__i386__) 334 const char kArch[] = "x86"; 335 #elif defined(__mips__) 336 # if _MIPS_SIM == _ABIO32 337 const char kArch[] = "mips"; 338 # elif _MIPS_SIM == _ABI64 339 const char kArch[] = "mips64"; 340 # else 341 # error "This mips ABI is currently not supported (n32)" 342 # endif 343 #elif defined(__riscv) 344 # if __riscv_xlen == 32 345 const char kArch[] = "riscv32"; 346 # elif __riscv_xlen == 64 347 const char kArch[] = "riscv64"; 348 # else 349 # error "Unexpected __riscv_xlen" 350 # endif 351 #else 352 # error "This code has not been ported to your platform yet" 353 #endif 354 355 LogAppend("O "); 356 LogAppend(kOSId); 357 LogAppend(" "); 358 LogAppend(kArch); 359 LogAppend(" "); 360 LogAppend(n_cpus); 361 LogAppend(" "); 362 363 // Dump the HW architecture (e.g., armv7l, aarch64). 364 struct utsname uts; 365 const bool has_uts_info = (uname(&uts) == 0); 366 const char* hwArch = has_uts_info ? uts.machine : "unknown_hw_arch"; 367 LogAppend(hwArch); 368 LogAppend(" "); 369 370 // If the client has attached a build fingerprint to the MinidumpDescriptor 371 // use that one. Otherwise try to get some basic info from uname(). 372 if (microdump_extra_info_.build_fingerprint) { 373 LogAppend(microdump_extra_info_.build_fingerprint); 374 } else if (has_uts_info) { 375 LogAppend(uts.release); 376 LogAppend(" "); 377 LogAppend(uts.version); 378 } else { 379 LogAppend("no build fingerprint available"); 380 } 381 LogCommitLine(); 382 } 383 384 void DumpGPUInformation() { 385 LogAppend("G "); 386 if (microdump_extra_info_.gpu_fingerprint) { 387 LogAppend(microdump_extra_info_.gpu_fingerprint); 388 } else { 389 LogAppend("UNKNOWN"); 390 } 391 LogCommitLine(); 392 } 393 394 void DumpThreadStack() { 395 if (sanitize_stack_) { 396 dumper_->SanitizeStackCopy(stack_copy_, stack_len_, stack_pointer_, 397 stack_pointer_ - stack_lower_bound_); 398 } 399 400 LogAppend("S 0 "); 401 LogAppend(stack_pointer_); 402 LogAppend(" "); 403 LogAppend(stack_lower_bound_); 404 LogAppend(" "); 405 LogAppend(stack_len_); 406 LogCommitLine(); 407 408 const size_t STACK_DUMP_CHUNK_SIZE = 384; 409 for (size_t stack_off = 0; stack_off < stack_len_; 410 stack_off += STACK_DUMP_CHUNK_SIZE) { 411 LogAppend("S "); 412 LogAppend(stack_lower_bound_ + stack_off); 413 LogAppend(" "); 414 LogAppend(stack_copy_ + stack_off, 415 std::min(STACK_DUMP_CHUNK_SIZE, stack_len_ - stack_off)); 416 LogCommitLine(); 417 } 418 } 419 420 void DumpCPUState() { 421 RawContextCPU cpu; 422 my_memset(&cpu, 0, sizeof(RawContextCPU)); 423 #if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE 424 UContextReader::FillCPUContext(&cpu, ucontext_, float_state_); 425 #else 426 UContextReader::FillCPUContext(&cpu, ucontext_); 427 #endif 428 LogAppend("C "); 429 LogAppend(&cpu, sizeof(cpu)); 430 LogCommitLine(); 431 } 432 433 // If there is caller-provided information about this mapping 434 // in the mapping_list_ list, return true. Otherwise, return false. 435 bool HaveMappingInfo(const MappingInfo& mapping) { 436 for (MappingList::const_iterator iter = mapping_list_.begin(); 437 iter != mapping_list_.end(); 438 ++iter) { 439 // Ignore any mappings that are wholly contained within 440 // mappings in the mapping_info_ list. 441 if (mapping.start_addr >= iter->first.start_addr && 442 (mapping.start_addr + mapping.size) <= 443 (iter->first.start_addr + iter->first.size)) { 444 return true; 445 } 446 } 447 return false; 448 } 449 450 // Dump information about the provided |mapping|. If |identifier| is non-NULL, 451 // use it instead of calculating a file ID from the mapping. 452 void DumpModule(const MappingInfo& mapping, 453 bool member, 454 unsigned int mapping_id, 455 const uint8_t* identifier) { 456 457 auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes( 458 dumper_->allocator()); 459 460 if (identifier) { 461 // GUID was provided by caller. 462 identifier_bytes.insert(identifier_bytes.end(), 463 identifier, 464 identifier + sizeof(MDGUID)); 465 } else { 466 dumper_->ElfFileIdentifierForMapping( 467 mapping, 468 member, 469 mapping_id, 470 identifier_bytes); 471 } 472 473 // Copy as many bytes of |identifier| as will fit into a MDGUID 474 MDGUID module_identifier = {0}; 475 memcpy(&module_identifier, &identifier_bytes[0], 476 std::min(sizeof(MDGUID), identifier_bytes.size())); 477 478 char file_name[NAME_MAX]; 479 char file_path[NAME_MAX]; 480 dumper_->GetMappingEffectiveNameAndPath( 481 mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); 482 483 LogAppend("M "); 484 LogAppend(static_cast<uintptr_t>(mapping.start_addr)); 485 LogAppend(" "); 486 LogAppend(mapping.offset); 487 LogAppend(" "); 488 LogAppend(mapping.size); 489 LogAppend(" "); 490 LogAppend(module_identifier.data1); 491 LogAppend(module_identifier.data2); 492 LogAppend(module_identifier.data3); 493 LogAppend(module_identifier.data4[0]); 494 LogAppend(module_identifier.data4[1]); 495 LogAppend(module_identifier.data4[2]); 496 LogAppend(module_identifier.data4[3]); 497 LogAppend(module_identifier.data4[4]); 498 LogAppend(module_identifier.data4[5]); 499 LogAppend(module_identifier.data4[6]); 500 LogAppend(module_identifier.data4[7]); 501 LogAppend("0 "); // Age is always 0 on Linux. 502 LogAppend(file_name); 503 LogCommitLine(); 504 } 505 506 #if !defined(__LP64__) 507 void DumpFreeSpace() { 508 const MappingInfo* stack_mapping = nullptr; 509 ThreadInfo info; 510 if (dumper_->GetThreadInfoByIndex(dumper_->GetMainThreadIndex(), &info)) { 511 stack_mapping = dumper_->FindMappingNoBias(info.stack_pointer); 512 } 513 514 const google_breakpad::wasteful_vector<MappingInfo*>& mappings = 515 dumper_->mappings(); 516 if (mappings.size() == 0) return; 517 518 // This is complicated by the fact that mappings is not in order. It should 519 // be mostly in order, however the mapping that contains the entry point for 520 // the process is always at the front of the vector. 521 522 static const int HBITS = sizeof(size_t) * 8; 523 size_t hole_histogram[HBITS]; 524 my_memset(hole_histogram, 0, sizeof(hole_histogram)); 525 526 // Find the lowest address mapping. 527 size_t curr = 0; 528 for (size_t i = 1; i < mappings.size(); ++i) { 529 if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i; 530 } 531 532 uintptr_t lo_addr = mappings[curr]->start_addr; 533 534 size_t hole_cnt = 0; 535 size_t hole_max = 0; 536 size_t hole_sum = 0; 537 538 while (true) { 539 // Skip to the end of an adjacent run of mappings. This is an optimization 540 // for the fact that mappings is mostly sorted. 541 while (curr != mappings.size() - 1 && 542 MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) { 543 ++curr; 544 } 545 546 if (mappings[curr] == stack_mapping) { 547 // Because we can't determine the top of userspace mappable 548 // memory we treat the start of the process stack as the top 549 // of the allocatable address space. Once we reach 550 // |stack_mapping| we are done scanning for free space regions. 551 break; 552 } 553 554 size_t next = NextOrderedMapping(mappings, curr); 555 if (next == std::numeric_limits<size_t>::max()) 556 break; 557 558 uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size; 559 uintptr_t hole_hi = mappings[next]->start_addr; 560 561 if (hole_hi > hole_lo) { 562 size_t hole_sz = hole_hi - hole_lo; 563 hole_sum += hole_sz; 564 hole_max = std::max(hole_sz, hole_max); 565 ++hole_cnt; 566 ++hole_histogram[Log2Floor(hole_sz)]; 567 } 568 curr = next; 569 } 570 571 uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size; 572 573 LogAppend("H "); 574 LogAppend(lo_addr); 575 LogAppend(" "); 576 LogAppend(hi_addr); 577 LogAppend(" "); 578 LogAppend(saturated_cast<uint16_t>(hole_cnt)); 579 LogAppend(" "); 580 LogAppend(hole_max); 581 LogAppend(" "); 582 LogAppend(hole_sum); 583 for (unsigned int i = 0; i < HBITS; ++i) { 584 if (!hole_histogram[i]) continue; 585 LogAppend(" "); 586 LogAppend(saturated_cast<uint8_t>(i)); 587 LogAppend(":"); 588 LogAppend(saturated_cast<uint8_t>(hole_histogram[i])); 589 } 590 LogCommitLine(); 591 } 592 #endif 593 594 // Write information about the mappings in effect. 595 void DumpMappings() { 596 // First write all the mappings from the dumper 597 for (unsigned i = 0; i < dumper_->mappings().size(); ++i) { 598 const MappingInfo& mapping = *dumper_->mappings()[i]; 599 if (mapping.name[0] == 0 || // only want modules with filenames. 600 !mapping.exec || // only want executable mappings. 601 mapping.size < 4096 || // too small to get a signature for. 602 HaveMappingInfo(mapping)) { 603 continue; 604 } 605 606 DumpModule(mapping, true, i, NULL); 607 } 608 // Next write all the mappings provided by the caller 609 for (MappingList::const_iterator iter = mapping_list_.begin(); 610 iter != mapping_list_.end(); 611 ++iter) { 612 DumpModule(iter->first, false, 0, iter->second); 613 } 614 } 615 616 void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); } 617 618 const ucontext_t* const ucontext_; 619 #if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE 620 const google_breakpad::fpstate_t* const float_state_; 621 #endif 622 LinuxDumper* dumper_; 623 const MappingList& mapping_list_; 624 bool skip_dump_if_principal_mapping_not_referenced_; 625 uintptr_t address_within_principal_mapping_; 626 bool sanitize_stack_; 627 const MicrodumpExtraInfo microdump_extra_info_; 628 char* log_line_; 629 630 // The local copy of crashed process stack memory, beginning at 631 // |stack_lower_bound_|. 632 uint8_t* stack_copy_; 633 634 // The length of crashed process stack copy. 635 size_t stack_len_; 636 637 // The address of the page containing the stack pointer in the 638 // crashed process. |stack_lower_bound_| <= |stack_pointer_| 639 uintptr_t stack_lower_bound_; 640 641 // The stack pointer of the crashed thread. 642 uintptr_t stack_pointer_; 643 }; 644 } // namespace 645 646 namespace google_breakpad { 647 648 bool WriteMicrodump(pid_t crashing_process, 649 const void* blob, 650 size_t blob_size, 651 const MappingList& mappings, 652 bool skip_dump_if_principal_mapping_not_referenced, 653 uintptr_t address_within_principal_mapping, 654 bool sanitize_stack, 655 const MicrodumpExtraInfo& microdump_extra_info) { 656 LinuxPtraceDumper dumper(crashing_process); 657 const ExceptionHandler::CrashContext* context = NULL; 658 if (blob) { 659 if (blob_size != sizeof(ExceptionHandler::CrashContext)) 660 return false; 661 context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob); 662 dumper.SetCrashInfoFromSigInfo(context->siginfo); 663 dumper.set_crash_thread(context->tid); 664 } 665 MicrodumpWriter writer(context, mappings, 666 skip_dump_if_principal_mapping_not_referenced, 667 address_within_principal_mapping, sanitize_stack, 668 microdump_extra_info, &dumper); 669 if (!writer.Init()) 670 return false; 671 writer.Dump(); 672 return true; 673 } 674 675 } // namespace google_breakpad