/ src / client / linux / microdump_writer / microdump_writer.cc
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