xref: /aosp_15_r20/external/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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>
saturated_cast(Src 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 
Log2Floor(uint64_t n)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 
MappingsAreAdjacent(const MappingInfo & a,const MappingInfo & b)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 
MappingLessThan(const MappingInfo * a,const MappingInfo * b)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 
NextOrderedMapping(const google_breakpad::wasteful_vector<MappingInfo * > & mappings,size_t curr)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:
MicrodumpWriter(const ExceptionHandler::CrashContext * context,const MappingList & mappings,bool skip_dump_if_principal_mapping_not_referenced,uintptr_t address_within_principal_mapping,bool sanitize_stack,const MicrodumpExtraInfo & microdump_extra_info,LinuxDumper * dumper)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 
~MicrodumpWriter()164   ~MicrodumpWriter() { dumper_->ThreadsResume(); }
165 
Init()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 
Dump()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.
LogLine(const char * msg)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.
LogAppend(const char * str)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).
LogAppend(char * str)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>
LogAppend(T value)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.
LogAppend(const void * buf,size_t length)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.
LogCommitLine()243   void LogCommitLine() {
244     LogLine(log_line_);
245     log_line_[0] = 0;
246   }
247 
CaptureCrashingThreadStack(int max_stack_len)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 
DumpProductInformation()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 
DumpProcessType()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 
DumpCrashReason()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 
DumpOSInformation()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 
DumpGPUInformation()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 
DumpThreadStack()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 
DumpCPUState()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.
HaveMappingInfo(const MappingInfo & mapping)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.
DumpModule(const MappingInfo & mapping,bool member,unsigned int mapping_id,const uint8_t * identifier)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__)
DumpFreeSpace()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.
DumpMappings()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 
Alloc(unsigned bytes)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 
WriteMicrodump(pid_t crashing_process,const void * blob,size_t blob_size,const MappingList & mappings,bool skip_dump_if_principal_mapping_not_referenced,uintptr_t address_within_principal_mapping,bool sanitize_stack,const MicrodumpExtraInfo & microdump_extra_info)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
676