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