Chromium Code Reviews| Index: src/client/linux/microdump_writer/microdump_writer.cc |
| diff --git a/src/client/linux/microdump_writer/microdump_writer.cc b/src/client/linux/microdump_writer/microdump_writer.cc |
| index d459d9ec441370c2e06e2e2f9a365bedf10e6353..2091429c62f2b27a1c693204092fcaa782fb9344 100644 |
| --- a/src/client/linux/microdump_writer/microdump_writer.cc |
| +++ b/src/client/linux/microdump_writer/microdump_writer.cc |
| @@ -32,9 +32,9 @@ |
| #include "client/linux/microdump_writer/microdump_writer.h" |
| -#include <sys/utsname.h> |
| +#include <limits> |
| -#include <algorithm> |
| +#include <sys/utsname.h> |
| #include "client/linux/dump_writer_common/thread_info.h" |
| #include "client/linux/dump_writer_common/ucontext_reader.h" |
| @@ -44,6 +44,7 @@ |
| #include "client/linux/minidump_writer/linux_ptrace_dumper.h" |
| #include "common/linux/file_id.h" |
| #include "common/linux/linux_libc_support.h" |
| +#include "common/memory.h" |
| namespace { |
| @@ -61,6 +62,66 @@ using google_breakpad::UContextReader; |
| const size_t kLineBufferSize = 2048; |
| +template <typename Dst, typename Src> |
| +Dst saturated_cast(Src src) { |
| + if (src >= std::numeric_limits<Dst>::max()) |
| + return std::numeric_limits<Dst>::max(); |
| + if (src <= std::numeric_limits<Dst>::min()) |
| + return std::numeric_limits<Dst>::min(); |
| + return static_cast<Dst>(src); |
| +} |
| + |
| +int Log2Floor(uint64_t n) { |
| + // Copied from chromium src/base/bits.h |
| + if (n == 0) |
| + return -1; |
| + int log = 0; |
| + uint64_t value = n; |
| + for (int i = 5; i >= 0; --i) { |
| + int shift = (1 << i); |
| + uint64_t x = value >> shift; |
| + if (x != 0) { |
| + value = x; |
| + log += shift; |
| + } |
| + } |
| + assert(value == 1u); |
| + return log; |
| +} |
| + |
| +bool MappingsAreAdjacent(const MappingInfo& a, const MappingInfo& b) { |
| + // Because of load biasing, we can end up with a situation where two |
| + // mappings actually overlap. So we will define adjacency to also include a |
| + // b start address that lies within a's address range (including starting |
| + // immediately after a). |
| + // Because load biasing only ever moves the start address backwards, the end |
| + // address should still increase. |
| + return a.start_addr <= b.start_addr && a.start_addr + a.size >= b.start_addr; |
| +} |
| + |
| +bool MappingLessThan(const MappingInfo* a, const MappingInfo* b) { |
| + // Return true if mapping a is before mapping b. |
| + // For the same reason (load biasing) we compare end addresses, which - unlike |
| + // start addresses - will not have been modified. |
| + return a->start_addr + a->size < b->start_addr + b->size; |
| +} |
| + |
| +size_t NextOrderedMapping( |
| + const google_breakpad::wasteful_vector<MappingInfo*>& mappings, |
| + size_t curr) { |
| + // Find the mapping that directly follows mappings[curr]. |
| + // If no such mapping exists, return |invalid| to indicate this. |
| + const size_t invalid = std::numeric_limits<size_t>::max(); |
| + size_t best = invalid; |
| + for (size_t next = 0; next < mappings.size(); ++next) { |
| + if (MappingLessThan(mappings[curr], mappings[next]) && |
| + (best == invalid || MappingLessThan(mappings[next], mappings[best]))) { |
| + best = next; |
| + } |
| + } |
| + return best; |
| +} |
| + |
| class MicrodumpWriter { |
| public: |
| MicrodumpWriter(const ExceptionHandler::CrashContext* context, |
| @@ -98,6 +159,9 @@ class MicrodumpWriter { |
| DumpProductInformation(); |
| DumpOSInformation(); |
| DumpGPUInformation(); |
| +#if !defined(__LP64__) |
| + DumpFreeSpace(); |
| +#endif |
| success = DumpCrashingThread(); |
| if (success) |
| success = DumpMappings(); |
| @@ -391,6 +455,81 @@ class MicrodumpWriter { |
| LogCommitLine(); |
| } |
| +#if !defined(__LP64__) |
| + void DumpFreeSpace() { |
| + const google_breakpad::wasteful_vector<MappingInfo*>& mappings = |
| + dumper_->mappings(); |
| + if (mappings.size() == 0) return; |
| + |
| + // This is complicated by the fact that mappings is not in order. It should |
| + // be mostly in order, however the mapping that contains the entry point for |
| + // the process is always at the front of the vector. |
| + |
| + static const int HBITS = sizeof(size_t) * 8; |
| + size_t hole_histogram[HBITS]; |
| + my_memset(hole_histogram, 0, sizeof(hole_histogram)); |
| + |
| + // Find the lowest address mapping. |
| + size_t curr = 0; |
| + for (size_t i = 1; i < mappings.size(); ++i) { |
| + if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i; |
| + } |
| + |
| + uintptr_t lo_addr = mappings[curr]->start_addr; |
| + |
| + size_t hole_cnt = 0; |
| + size_t hole_max = 0; |
| + size_t hole_sum = 0; |
| + |
| + while (true) { |
| + // Skip to the end of an adjacent run of mappings. This is an optimization |
| + // for the fact that mappings is mostly sorted. |
| + while (curr != mappings.size() - 1 && |
| + MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) { |
| + ++curr; |
| + } |
| + |
| + size_t next = NextOrderedMapping(mappings, curr); |
| + if (next == std::numeric_limits<size_t>::max()) |
| + break; |
| + |
| + uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size; |
| + uintptr_t hole_hi = mappings[next]->start_addr; |
| + |
| + if (hole_hi > hole_lo) { |
| + size_t hole_sz = hole_hi - hole_lo; |
| + int log2_hole_sz = Log2Floor(hole_sz); |
|
Primiano Tucci (use gerrit)
2016/05/24 13:23:12
Somebody just reported that this variable is unuse
|
| + hole_sum += hole_sz; |
| + hole_max = std::max(hole_sz, hole_max); |
| + ++hole_cnt; |
| + ++hole_histogram[Log2Floor(hole_sz)]; |
| + } |
| + curr = next; |
| + } |
| + |
| + uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size; |
| + |
| + LogAppend("H "); |
| + LogAppend(lo_addr); |
| + LogAppend(" "); |
| + LogAppend(hi_addr); |
| + LogAppend(" "); |
| + LogAppend(saturated_cast<uint16_t>(hole_cnt)); |
| + LogAppend(" "); |
| + LogAppend(hole_max); |
| + LogAppend(" "); |
| + LogAppend(hole_sum); |
| + for (unsigned int i = 0; i < HBITS; ++i) { |
| + if (!hole_histogram[i]) continue; |
| + LogAppend(" "); |
| + LogAppend(saturated_cast<uint8_t>(i)); |
| + LogAppend(":"); |
| + LogAppend(saturated_cast<uint8_t>(hole_histogram[i])); |
|
Tobias Sargeant
2016/05/23 10:51:59
And added a saturated cast here too, to simplify t
Primiano Tucci (use gerrit)
2016/05/23 14:15:45
makes sense. thanks.
|
| + } |
| + LogCommitLine(); |
| + } |
| +#endif |
| + |
| // Write information about the mappings in effect. |
| bool DumpMappings() { |
| // First write all the mappings from the dumper |