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