Line data Source code
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 "linux/microdump_writer/microdump_writer.h"
34 :
35 : #include <limits>
36 :
37 : #include <sys/utsname.h>
38 :
39 : #include "linux/dump_writer_common/thread_info.h"
40 : #include "linux/dump_writer_common/ucontext_reader.h"
41 : #include "linux/handler/exception_handler.h"
42 : #include "linux/handler/microdump_extra_info.h"
43 : #include "linux/log/log.h"
44 : #include "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.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>
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 :
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 :
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 :
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 :
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:
133 0 : MicrodumpWriter(const ExceptionHandler::CrashContext* context,
134 : const MappingList& mappings,
135 : const MicrodumpExtraInfo& microdump_extra_info,
136 : LinuxDumper* dumper)
137 0 : : ucontext_(context ? &context->context : NULL),
138 : #if !defined(__ARM_EABI__) && !defined(__mips__)
139 0 : float_state_(context ? &context->float_state : NULL),
140 : #endif
141 : dumper_(dumper),
142 : mapping_list_(mappings),
143 : microdump_extra_info_(microdump_extra_info),
144 0 : log_line_(NULL) {
145 0 : log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
146 0 : if (log_line_)
147 0 : log_line_[0] = '\0'; // Clear out the log line buffer.
148 0 : }
149 :
150 0 : ~MicrodumpWriter() { dumper_->ThreadsResume(); }
151 :
152 0 : bool Init() {
153 : // In the exceptional case where the system was out of memory and there
154 : // wasn't even room to allocate the line buffer, bail out. There is nothing
155 : // useful we can possibly achieve without the ability to Log. At least let's
156 : // try to not crash.
157 0 : if (!dumper_->Init() || !log_line_)
158 0 : return false;
159 0 : return dumper_->ThreadsSuspend() && dumper_->LateInit();
160 : }
161 :
162 0 : bool Dump() {
163 : bool success;
164 0 : LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
165 0 : DumpProductInformation();
166 0 : DumpOSInformation();
167 0 : DumpProcessType();
168 0 : DumpGPUInformation();
169 : #if !defined(__LP64__)
170 : DumpFreeSpace();
171 : #endif
172 0 : success = DumpCrashingThread();
173 0 : if (success)
174 0 : success = DumpMappings();
175 0 : LogLine("-----END BREAKPAD MICRODUMP-----");
176 0 : dumper_->ThreadsResume();
177 0 : return success;
178 : }
179 :
180 : private:
181 : // Writes one line to the system log.
182 0 : void LogLine(const char* msg) {
183 : #if defined(__ANDROID__)
184 : logger::writeToCrashLog(msg);
185 : #else
186 0 : logger::write(msg, my_strlen(msg));
187 0 : logger::write("\n", 1);
188 : #endif
189 0 : }
190 :
191 : // Stages the given string in the current line buffer.
192 0 : void LogAppend(const char* str) {
193 0 : my_strlcat(log_line_, str, kLineBufferSize);
194 0 : }
195 :
196 : // As above (required to take precedence over template specialization below).
197 0 : void LogAppend(char* str) {
198 0 : LogAppend(const_cast<const char*>(str));
199 0 : }
200 :
201 : // Stages the hex repr. of the given int type in the current line buffer.
202 : template<typename T>
203 0 : void LogAppend(T value) {
204 : // Make enough room to hex encode the largest int type + NUL.
205 : static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
206 : 'A', 'B', 'C', 'D', 'E', 'F'};
207 : char hexstr[sizeof(T) * 2 + 1];
208 0 : for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4)
209 0 : hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F];
210 0 : hexstr[sizeof(T) * 2] = '\0';
211 0 : LogAppend(hexstr);
212 0 : }
213 :
214 : // Stages the buffer content hex-encoded in the current line buffer.
215 0 : void LogAppend(const void* buf, size_t length) {
216 0 : const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf);
217 0 : for (size_t i = 0; i < length; ++i, ++ptr)
218 0 : LogAppend(*ptr);
219 0 : }
220 :
221 : // Writes out the current line buffer on the system log.
222 0 : void LogCommitLine() {
223 0 : LogLine(log_line_);
224 0 : my_strlcpy(log_line_, "", kLineBufferSize);
225 0 : }
226 :
227 0 : void DumpProductInformation() {
228 0 : LogAppend("V ");
229 0 : if (microdump_extra_info_.product_info) {
230 0 : LogAppend(microdump_extra_info_.product_info);
231 : } else {
232 0 : LogAppend("UNKNOWN:0.0.0.0");
233 : }
234 0 : LogCommitLine();
235 0 : }
236 :
237 0 : void DumpProcessType() {
238 0 : LogAppend("P ");
239 0 : if (microdump_extra_info_.process_type) {
240 0 : LogAppend(microdump_extra_info_.process_type);
241 : } else {
242 0 : LogAppend("UNKNOWN");
243 : }
244 0 : LogCommitLine();
245 0 : }
246 :
247 0 : void DumpOSInformation() {
248 0 : const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
249 :
250 : #if defined(__ANDROID__)
251 : const char kOSId[] = "A";
252 : #else
253 0 : const char kOSId[] = "L";
254 : #endif
255 :
256 : // Dump the runtime architecture. On multiarch devices it might not match the
257 : // hw architecture (the one returned by uname()), for instance in the case of
258 : // a 32-bit app running on a aarch64 device.
259 : #if defined(__aarch64__)
260 : const char kArch[] = "arm64";
261 : #elif defined(__ARMEL__)
262 : const char kArch[] = "arm";
263 : #elif defined(__x86_64__)
264 0 : const char kArch[] = "x86_64";
265 : #elif defined(__i386__)
266 : const char kArch[] = "x86";
267 : #elif defined(__mips__)
268 : # if _MIPS_SIM == _ABIO32
269 : const char kArch[] = "mips";
270 : # elif _MIPS_SIM == _ABI64
271 : const char kArch[] = "mips64";
272 : # else
273 : # error "This mips ABI is currently not supported (n32)"
274 : #endif
275 : #else
276 : #error "This code has not been ported to your platform yet"
277 : #endif
278 :
279 0 : LogAppend("O ");
280 0 : LogAppend(kOSId);
281 0 : LogAppend(" ");
282 0 : LogAppend(kArch);
283 0 : LogAppend(" ");
284 0 : LogAppend(n_cpus);
285 0 : LogAppend(" ");
286 :
287 : // Dump the HW architecture (e.g., armv7l, aarch64).
288 : struct utsname uts;
289 0 : const bool has_uts_info = (uname(&uts) == 0);
290 0 : const char* hwArch = has_uts_info ? uts.machine : "unknown_hw_arch";
291 0 : LogAppend(hwArch);
292 0 : LogAppend(" ");
293 :
294 : // If the client has attached a build fingerprint to the MinidumpDescriptor
295 : // use that one. Otherwise try to get some basic info from uname().
296 0 : if (microdump_extra_info_.build_fingerprint) {
297 0 : LogAppend(microdump_extra_info_.build_fingerprint);
298 0 : } else if (has_uts_info) {
299 0 : LogAppend(uts.release);
300 0 : LogAppend(" ");
301 0 : LogAppend(uts.version);
302 : } else {
303 0 : LogAppend("no build fingerprint available");
304 : }
305 0 : LogCommitLine();
306 0 : }
307 :
308 0 : void DumpGPUInformation() {
309 0 : LogAppend("G ");
310 0 : if (microdump_extra_info_.gpu_fingerprint) {
311 0 : LogAppend(microdump_extra_info_.gpu_fingerprint);
312 : } else {
313 0 : LogAppend("UNKNOWN");
314 : }
315 0 : LogCommitLine();
316 0 : }
317 :
318 0 : bool DumpThreadStack(uint32_t thread_id,
319 : uintptr_t stack_pointer,
320 : int max_stack_len,
321 : uint8_t** stack_copy) {
322 0 : *stack_copy = NULL;
323 : const void* stack;
324 : size_t stack_len;
325 :
326 0 : if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
327 : // The stack pointer might not be available. In this case we don't hard
328 : // fail, just produce a (almost useless) microdump w/o a stack section.
329 0 : return true;
330 : }
331 :
332 0 : LogAppend("S 0 ");
333 0 : LogAppend(stack_pointer);
334 0 : LogAppend(" ");
335 0 : LogAppend(reinterpret_cast<uintptr_t>(stack));
336 0 : LogAppend(" ");
337 0 : LogAppend(stack_len);
338 0 : LogCommitLine();
339 :
340 0 : if (max_stack_len >= 0 &&
341 0 : stack_len > static_cast<unsigned int>(max_stack_len)) {
342 0 : stack_len = max_stack_len;
343 : }
344 :
345 0 : *stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
346 0 : dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len);
347 :
348 : // Dump the content of the stack, splicing it into chunks which size is
349 : // compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD).
350 0 : const size_t STACK_DUMP_CHUNK_SIZE = 384;
351 0 : for (size_t stack_off = 0; stack_off < stack_len;
352 0 : stack_off += STACK_DUMP_CHUNK_SIZE) {
353 0 : LogAppend("S ");
354 0 : LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off);
355 0 : LogAppend(" ");
356 0 : LogAppend(*stack_copy + stack_off,
357 0 : std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off));
358 0 : LogCommitLine();
359 : }
360 0 : return true;
361 : }
362 :
363 : // Write information about the crashing thread.
364 0 : bool DumpCrashingThread() {
365 0 : const unsigned num_threads = dumper_->threads().size();
366 :
367 0 : for (unsigned i = 0; i < num_threads; ++i) {
368 : MDRawThread thread;
369 0 : my_memset(&thread, 0, sizeof(thread));
370 0 : thread.thread_id = dumper_->threads()[i];
371 :
372 : // Dump only the crashing thread.
373 0 : if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread())
374 0 : continue;
375 :
376 0 : assert(ucontext_);
377 0 : assert(!dumper_->IsPostMortem());
378 :
379 : uint8_t* stack_copy;
380 0 : const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
381 0 : if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy))
382 0 : return false;
383 :
384 : RawContextCPU cpu;
385 0 : my_memset(&cpu, 0, sizeof(RawContextCPU));
386 : #if !defined(__ARM_EABI__) && !defined(__mips__)
387 0 : UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
388 : #else
389 : UContextReader::FillCPUContext(&cpu, ucontext_);
390 : #endif
391 0 : DumpCPUState(&cpu);
392 : }
393 0 : return true;
394 : }
395 :
396 0 : void DumpCPUState(RawContextCPU* cpu) {
397 0 : LogAppend("C ");
398 0 : LogAppend(cpu, sizeof(*cpu));
399 0 : LogCommitLine();
400 0 : }
401 :
402 : // If there is caller-provided information about this mapping
403 : // in the mapping_list_ list, return true. Otherwise, return false.
404 0 : bool HaveMappingInfo(const MappingInfo& mapping) {
405 0 : for (MappingList::const_iterator iter = mapping_list_.begin();
406 0 : iter != mapping_list_.end();
407 : ++iter) {
408 : // Ignore any mappings that are wholly contained within
409 : // mappings in the mapping_info_ list.
410 0 : if (mapping.start_addr >= iter->first.start_addr &&
411 0 : (mapping.start_addr + mapping.size) <=
412 0 : (iter->first.start_addr + iter->first.size)) {
413 0 : return true;
414 : }
415 : }
416 0 : return false;
417 : }
418 :
419 : // Dump information about the provided |mapping|. If |identifier| is non-NULL,
420 : // use it instead of calculating a file ID from the mapping.
421 0 : void DumpModule(const MappingInfo& mapping,
422 : bool member,
423 : unsigned int mapping_id,
424 : const uint8_t* identifier) {
425 :
426 : auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes(
427 0 : dumper_->allocator());
428 :
429 0 : if (identifier) {
430 : // GUID was provided by caller.
431 0 : identifier_bytes.insert(identifier_bytes.end(),
432 : identifier,
433 0 : identifier + sizeof(MDGUID));
434 : } else {
435 0 : dumper_->ElfFileIdentifierForMapping(
436 : mapping,
437 : member,
438 : mapping_id,
439 0 : identifier_bytes);
440 : }
441 :
442 : // Copy as many bytes of |identifier| as will fit into a MDGUID
443 0 : MDGUID module_identifier = {0};
444 0 : memcpy(&module_identifier, &identifier_bytes[0],
445 0 : std::min(sizeof(MDGUID), identifier_bytes.size()));
446 :
447 : char file_name[NAME_MAX];
448 : char file_path[NAME_MAX];
449 0 : dumper_->GetMappingEffectiveNameAndPath(
450 0 : mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
451 :
452 0 : LogAppend("M ");
453 0 : LogAppend(static_cast<uintptr_t>(mapping.start_addr));
454 0 : LogAppend(" ");
455 0 : LogAppend(mapping.offset);
456 0 : LogAppend(" ");
457 0 : LogAppend(mapping.size);
458 0 : LogAppend(" ");
459 0 : LogAppend(module_identifier.data1);
460 0 : LogAppend(module_identifier.data2);
461 0 : LogAppend(module_identifier.data3);
462 0 : LogAppend(module_identifier.data4[0]);
463 0 : LogAppend(module_identifier.data4[1]);
464 0 : LogAppend(module_identifier.data4[2]);
465 0 : LogAppend(module_identifier.data4[3]);
466 0 : LogAppend(module_identifier.data4[4]);
467 0 : LogAppend(module_identifier.data4[5]);
468 0 : LogAppend(module_identifier.data4[6]);
469 0 : LogAppend(module_identifier.data4[7]);
470 0 : LogAppend("0 "); // Age is always 0 on Linux.
471 0 : LogAppend(file_name);
472 0 : LogCommitLine();
473 0 : }
474 :
475 : #if !defined(__LP64__)
476 : void DumpFreeSpace() {
477 : const google_breakpad::wasteful_vector<MappingInfo*>& mappings =
478 : dumper_->mappings();
479 : if (mappings.size() == 0) return;
480 :
481 : // This is complicated by the fact that mappings is not in order. It should
482 : // be mostly in order, however the mapping that contains the entry point for
483 : // the process is always at the front of the vector.
484 :
485 : static const int HBITS = sizeof(size_t) * 8;
486 : size_t hole_histogram[HBITS];
487 : my_memset(hole_histogram, 0, sizeof(hole_histogram));
488 :
489 : // Find the lowest address mapping.
490 : size_t curr = 0;
491 : for (size_t i = 1; i < mappings.size(); ++i) {
492 : if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i;
493 : }
494 :
495 : uintptr_t lo_addr = mappings[curr]->start_addr;
496 :
497 : size_t hole_cnt = 0;
498 : size_t hole_max = 0;
499 : size_t hole_sum = 0;
500 :
501 : while (true) {
502 : // Skip to the end of an adjacent run of mappings. This is an optimization
503 : // for the fact that mappings is mostly sorted.
504 : while (curr != mappings.size() - 1 &&
505 : MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) {
506 : ++curr;
507 : }
508 :
509 : size_t next = NextOrderedMapping(mappings, curr);
510 : if (next == std::numeric_limits<size_t>::max())
511 : break;
512 :
513 : uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size;
514 : uintptr_t hole_hi = mappings[next]->start_addr;
515 :
516 : if (hole_hi > hole_lo) {
517 : size_t hole_sz = hole_hi - hole_lo;
518 : hole_sum += hole_sz;
519 : hole_max = std::max(hole_sz, hole_max);
520 : ++hole_cnt;
521 : ++hole_histogram[Log2Floor(hole_sz)];
522 : }
523 : curr = next;
524 : }
525 :
526 : uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size;
527 :
528 : LogAppend("H ");
529 : LogAppend(lo_addr);
530 : LogAppend(" ");
531 : LogAppend(hi_addr);
532 : LogAppend(" ");
533 : LogAppend(saturated_cast<uint16_t>(hole_cnt));
534 : LogAppend(" ");
535 : LogAppend(hole_max);
536 : LogAppend(" ");
537 : LogAppend(hole_sum);
538 : for (unsigned int i = 0; i < HBITS; ++i) {
539 : if (!hole_histogram[i]) continue;
540 : LogAppend(" ");
541 : LogAppend(saturated_cast<uint8_t>(i));
542 : LogAppend(":");
543 : LogAppend(saturated_cast<uint8_t>(hole_histogram[i]));
544 : }
545 : LogCommitLine();
546 : }
547 : #endif
548 :
549 : // Write information about the mappings in effect.
550 0 : bool DumpMappings() {
551 : // First write all the mappings from the dumper
552 0 : for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
553 0 : const MappingInfo& mapping = *dumper_->mappings()[i];
554 0 : if (mapping.name[0] == 0 || // only want modules with filenames.
555 0 : !mapping.exec || // only want executable mappings.
556 0 : mapping.size < 4096 || // too small to get a signature for.
557 0 : HaveMappingInfo(mapping)) {
558 0 : continue;
559 : }
560 :
561 0 : DumpModule(mapping, true, i, NULL);
562 : }
563 : // Next write all the mappings provided by the caller
564 0 : for (MappingList::const_iterator iter = mapping_list_.begin();
565 0 : iter != mapping_list_.end();
566 : ++iter) {
567 0 : DumpModule(iter->first, false, 0, iter->second);
568 : }
569 0 : return true;
570 : }
571 :
572 0 : void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
573 :
574 : const struct ucontext* const ucontext_;
575 : #if !defined(__ARM_EABI__) && !defined(__mips__)
576 : const google_breakpad::fpstate_t* const float_state_;
577 : #endif
578 : LinuxDumper* dumper_;
579 : const MappingList& mapping_list_;
580 : const MicrodumpExtraInfo microdump_extra_info_;
581 : char* log_line_;
582 : };
583 : } // namespace
584 :
585 : namespace google_breakpad {
586 :
587 0 : bool WriteMicrodump(pid_t crashing_process,
588 : const void* blob,
589 : size_t blob_size,
590 : const MappingList& mappings,
591 : const MicrodumpExtraInfo& microdump_extra_info) {
592 0 : LinuxPtraceDumper dumper(crashing_process);
593 0 : const ExceptionHandler::CrashContext* context = NULL;
594 0 : if (blob) {
595 0 : if (blob_size != sizeof(ExceptionHandler::CrashContext))
596 0 : return false;
597 0 : context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
598 0 : dumper.set_crash_address(
599 0 : reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
600 0 : dumper.set_crash_signal(context->siginfo.si_signo);
601 0 : dumper.set_crash_thread(context->tid);
602 : }
603 0 : MicrodumpWriter writer(context, mappings, microdump_extra_info, &dumper);
604 0 : if (!writer.Init())
605 0 : return false;
606 0 : return writer.Dump();
607 : }
608 :
609 : } // namespace google_breakpad
|