• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
49 namespace {
50 
51 using google_breakpad::auto_wasteful_vector;
52 using google_breakpad::ExceptionHandler;
53 using google_breakpad::kDefaultBuildIdSize;
54 using google_breakpad::LinuxDumper;
55 using google_breakpad::LinuxPtraceDumper;
56 using google_breakpad::MappingInfo;
57 using google_breakpad::MappingList;
58 using google_breakpad::MicrodumpExtraInfo;
59 using google_breakpad::RawContextCPU;
60 using google_breakpad::ThreadInfo;
61 using google_breakpad::UContextReader;
62 
63 const 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 
69 template <typename Dst, typename Src>
saturated_cast(Src src)70 Dst 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 
Log2Floor(uint64_t n)78 int 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 
MappingsAreAdjacent(const MappingInfo & a,const MappingInfo & b)96 bool 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 
MappingLessThan(const MappingInfo * a,const MappingInfo * b)106 bool 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 
NextOrderedMapping(const google_breakpad::wasteful_vector<MappingInfo * > & mappings,size_t curr)113 size_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 
131 class MicrodumpWriter {
132  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)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 
~MicrodumpWriter()161   ~MicrodumpWriter() { dumper_->ThreadsResume(); }
162 
Init()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 
Dump()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.
LogLine(const char * msg)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.
LogAppend(const char * str)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).
LogAppend(char * str)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>
LogAppend(T value)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.
LogAppend(const void * buf,size_t length)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.
LogCommitLine()240   void LogCommitLine() {
241     LogLine(log_line_);
242     log_line_[0] = 0;
243   }
244 
CaptureCrashingThreadStack(int max_stack_len)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 
DumpProductInformation()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 
DumpProcessType()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 
DumpCrashReason()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 
DumpOSInformation()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 
DumpGPUInformation()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 
DumpThreadStack()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 
DumpCPUState()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.
HaveMappingInfo(const MappingInfo & mapping)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.
DumpModule(const MappingInfo & mapping,bool member,unsigned int mapping_id,const uint8_t * identifier)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 = {};
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__)
DumpFreeSpace()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.
DumpMappings()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 
Alloc(unsigned bytes)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 
635 namespace google_breakpad {
636 
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)637 bool 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