| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/trace_event/heap_profiler_heap_dump_writer.h" | 5 #include "base/trace_event/heap_profiler_heap_dump_writer.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <iterator> | 10 #include <iterator> |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 122 | 122 |
| 123 // Ensure that |buckets| is a max-heap (the data structure, not memory heap), | 123 // Ensure that |buckets| is a max-heap (the data structure, not memory heap), |
| 124 // so its front contains the largest bucket. Buckets should be iterated | 124 // so its front contains the largest bucket. Buckets should be iterated |
| 125 // ordered by size, but sorting the vector is overkill because the long tail | 125 // ordered by size, but sorting the vector is overkill because the long tail |
| 126 // of small buckets will be discarded. By using a max-heap, the optimal case | 126 // of small buckets will be discarded. By using a max-heap, the optimal case |
| 127 // where all but the first bucket are discarded is O(n). The worst case where | 127 // where all but the first bucket are discarded is O(n). The worst case where |
| 128 // no bucket is discarded is doing a heap sort, which is O(n log n). | 128 // no bucket is discarded is doing a heap sort, which is O(n log n). |
| 129 std::make_heap(buckets.begin(), buckets.end()); | 129 std::make_heap(buckets.begin(), buckets.end()); |
| 130 | 130 |
| 131 // Keep including buckets until adding one would increase the number of | 131 // Keep including buckets until adding one would increase the number of |
| 132 // bytes accounted for by less than 0.8 percent. This simple heuristic works | 132 // bytes accounted for by less than 0.8% (1/125). This simple heuristic works |
| 133 // quite well. The large buckets end up in [it, end()), [begin(), it) is the | 133 // quite well. The large buckets end up in [it, end()), [begin(), it) is the |
| 134 // part that contains the max-heap of small buckets. | 134 // part that contains the max-heap of small buckets. |
| 135 size_t accounted_for = 0; | 135 size_t accounted_for = 0; |
| 136 std::vector<Bucket>::iterator it; | 136 std::vector<Bucket>::iterator it; |
| 137 for (it = buckets.end(); it != buckets.begin(); --it) { | 137 for (it = buckets.end(); it != buckets.begin(); --it) { |
| 138 // Compute the contribution to the number of bytes accounted for as a | |
| 139 // fraction of 125 (in increments of 0.8 percent). Anything less than 1/125 | |
| 140 // is rounded down to 0 due to integer division. Buckets are iterated by | |
| 141 // descending size, so later buckets cannot have a larger contribution than | |
| 142 // this one. | |
| 143 accounted_for += buckets.front().size; | 138 accounted_for += buckets.front().size; |
| 144 size_t contribution = buckets.front().size * 125 / accounted_for; | 139 if (buckets.front().size < (accounted_for / 125)) |
| 145 if (contribution == 0) | |
| 146 break; | 140 break; |
| 147 | 141 |
| 148 // Put the largest bucket in [begin, it) at |it - 1| and max-heapify | 142 // Put the largest bucket in [begin, it) at |it - 1| and max-heapify |
| 149 // [begin, it - 1). This puts the next largest bucket at |buckets.front()|. | 143 // [begin, it - 1). This puts the next largest bucket at |buckets.front()|. |
| 150 std::pop_heap(buckets.begin(), it); | 144 std::pop_heap(buckets.begin(), it); |
| 151 } | 145 } |
| 152 | 146 |
| 153 // At this point, |buckets| looks like this (numbers are bucket sizes): | 147 // At this point, |buckets| looks like this (numbers are bucket sizes): |
| 154 // | 148 // |
| 155 // <-- max-heap of small buckets ---> | 149 // <-- max-heap of small buckets ---> |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 227 BreakDown(subbucket); | 221 BreakDown(subbucket); |
| 228 } | 222 } |
| 229 | 223 |
| 230 const std::set<Entry>& HeapDumpWriter::Summarize( | 224 const std::set<Entry>& HeapDumpWriter::Summarize( |
| 231 const hash_map<AllocationContext, size_t>& bytes_by_context) { | 225 const hash_map<AllocationContext, size_t>& bytes_by_context) { |
| 232 // Start with one bucket that represents the entire heap. Iterate by | 226 // Start with one bucket that represents the entire heap. Iterate by |
| 233 // reference, because the allocation contexts are going to point to allocation | 227 // reference, because the allocation contexts are going to point to allocation |
| 234 // contexts stored in |bytes_by_context|. | 228 // contexts stored in |bytes_by_context|. |
| 235 Bucket root_bucket; | 229 Bucket root_bucket; |
| 236 for (const auto& context_and_size : bytes_by_context) { | 230 for (const auto& context_and_size : bytes_by_context) { |
| 231 DCHECK_GT(context_and_size.second, 0u); |
| 237 const AllocationContext* context = &context_and_size.first; | 232 const AllocationContext* context = &context_and_size.first; |
| 238 const size_t size = context_and_size.second; | 233 const size_t size = context_and_size.second; |
| 239 root_bucket.bytes_by_context.push_back(std::make_pair(context, size)); | 234 root_bucket.bytes_by_context.push_back(std::make_pair(context, size)); |
| 240 root_bucket.size += size; | 235 root_bucket.size += size; |
| 241 } | 236 } |
| 242 | 237 |
| 243 AddEntryForBucket(root_bucket); | 238 AddEntryForBucket(root_bucket); |
| 244 | 239 |
| 245 // Recursively break down the heap and fill |entries_| with entries to dump. | 240 // Recursively break down the heap and fill |entries_| with entries to dump. |
| 246 BreakDown(root_bucket); | 241 BreakDown(root_bucket); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 293 const hash_map<AllocationContext, size_t>& bytes_by_size, | 288 const hash_map<AllocationContext, size_t>& bytes_by_size, |
| 294 StackFrameDeduplicator* stack_frame_deduplicator, | 289 StackFrameDeduplicator* stack_frame_deduplicator, |
| 295 TypeNameDeduplicator* type_name_deduplicator) { | 290 TypeNameDeduplicator* type_name_deduplicator) { |
| 296 internal::HeapDumpWriter writer(stack_frame_deduplicator, | 291 internal::HeapDumpWriter writer(stack_frame_deduplicator, |
| 297 type_name_deduplicator); | 292 type_name_deduplicator); |
| 298 return Serialize(writer.Summarize(bytes_by_size)); | 293 return Serialize(writer.Summarize(bytes_by_size)); |
| 299 } | 294 } |
| 300 | 295 |
| 301 } // namespace trace_event | 296 } // namespace trace_event |
| 302 } // namespace base | 297 } // namespace base |
| OLD | NEW |