Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "components/metrics/leak_detector/leak_detector.h" | 5 #include "components/metrics/leak_detector/leak_detector.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include <algorithm> | |
| 10 | |
| 9 #include "base/allocator/allocator_extension.h" | 11 #include "base/allocator/allocator_extension.h" |
| 10 #include "base/bind.h" | 12 #include "base/bind.h" |
| 11 #include "base/lazy_instance.h" | 13 #include "base/lazy_instance.h" |
| 12 #include "base/logging.h" | 14 #include "base/logging.h" |
| 13 #include "base/numerics/safe_conversions.h" | 15 #include "base/numerics/safe_conversions.h" |
| 14 #include "base/threading/thread_local.h" | 16 #include "base/threading/thread_local.h" |
| 15 #include "components/metrics/leak_detector/custom_allocator.h" | 17 #include "components/metrics/leak_detector/custom_allocator.h" |
| 16 #include "components/metrics/leak_detector/leak_detector_impl.h" | 18 #include "components/metrics/leak_detector/leak_detector_impl.h" |
| 17 #include "content/public/browser/browser_thread.h" | 19 #include "content/public/browser/browser_thread.h" |
| 18 | 20 |
| 19 #if defined(OS_CHROMEOS) | 21 #if defined(OS_CHROMEOS) |
| 20 #include <link.h> // for dl_iterate_phdr | 22 #include <link.h> // for dl_iterate_phdr |
| 21 #else | 23 #else |
| 22 #error "Getting binary mapping info is not supported on this platform." | 24 #error "Getting binary mapping info is not supported on this platform." |
| 23 #endif // defined(OS_CHROMEOS) | 25 #endif // defined(OS_CHROMEOS) |
| 24 | 26 |
| 25 namespace metrics { | 27 namespace metrics { |
| 26 | 28 |
| 27 using LeakReport = LeakDetector::LeakReport; | |
| 28 using InternalLeakReport = leak_detector::LeakDetectorImpl::LeakReport; | 29 using InternalLeakReport = leak_detector::LeakDetectorImpl::LeakReport; |
| 29 template <typename T> | 30 template <typename T> |
| 30 using InternalVector = leak_detector::LeakDetectorImpl::InternalVector<T>; | 31 using InternalVector = leak_detector::LeakDetectorImpl::InternalVector<T>; |
| 31 | 32 |
| 32 namespace { | 33 namespace { |
| 33 | 34 |
| 34 // Add the thread-local alloc size count to the shared alloc size count | 35 // Add the thread-local alloc size count to the shared alloc size count |
| 35 // (LeakDetector::total_alloc_size_) whenever the local counter reaches | 36 // (LeakDetector::total_alloc_size_) whenever the local counter reaches |
| 36 // |LeakDetector::analysis_interval_bytes_| divided by this value. Choose a | 37 // |LeakDetector::analysis_interval_bytes_| divided by this value. Choose a |
| 37 // high enough value that there is plenty of granularity, but low enough that a | 38 // high enough value that there is plenty of granularity, but low enough that a |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 93 // Convert a pointer to a hash value. Returns only the upper eight bits. | 94 // Convert a pointer to a hash value. Returns only the upper eight bits. |
| 94 inline uint64_t PointerToHash(const void* ptr) { | 95 inline uint64_t PointerToHash(const void* ptr) { |
| 95 // The input data is the pointer address, not the location in memory pointed | 96 // The input data is the pointer address, not the location in memory pointed |
| 96 // to by the pointer. | 97 // to by the pointer. |
| 97 // The multiplier is taken from Farmhash code: | 98 // The multiplier is taken from Farmhash code: |
| 98 // https://github.com/google/farmhash/blob/master/src/farmhash.cc | 99 // https://github.com/google/farmhash/blob/master/src/farmhash.cc |
| 99 const uint64_t kMultiplier = 0x9ddfea08eb382d69ULL; | 100 const uint64_t kMultiplier = 0x9ddfea08eb382d69ULL; |
| 100 return reinterpret_cast<uint64_t>(ptr) * kMultiplier; | 101 return reinterpret_cast<uint64_t>(ptr) * kMultiplier; |
| 101 } | 102 } |
| 102 | 103 |
| 103 // Converts a vector of leak reports generated by LeakDetectorImpl | 104 // Converts an memory leak report generated by LeakDetectorImpl |
| 104 // (InternalLeakReport) to a vector of leak reports suitable for sending to | 105 // (InternalLeakReport) to protobuf format (MemoryLeakReportProto). |
| 105 // LeakDetector's observers (LeakReport). | 106 MemoryLeakReportProto ConvertLeakReportToProto( |
| 106 void GetReportsForObservers( | 107 const InternalLeakReport& report) { |
| 107 const InternalVector<InternalLeakReport>& leak_reports, | 108 MemoryLeakReportProto proto; |
| 108 std::vector<LeakReport>* reports_for_observers) { | |
| 109 reports_for_observers->clear(); | |
| 110 reports_for_observers->reserve(leak_reports.size()); | |
| 111 for (const InternalLeakReport& report : leak_reports) { | |
| 112 reports_for_observers->push_back(LeakReport()); | |
| 113 LeakReport* new_report = &reports_for_observers->back(); | |
| 114 | 109 |
| 115 new_report->alloc_size_bytes = report.alloc_size_bytes(); | 110 proto.set_size_bytes(report.alloc_size_bytes()); |
| 116 if (!report.call_stack().empty()) { | 111 for (auto call_stack_entry : report.call_stack()) { |
| 117 new_report->call_stack.resize(report.call_stack().size()); | 112 proto.add_call_stack(call_stack_entry); |
| 118 memcpy(new_report->call_stack.data(), report.call_stack().data(), | |
| 119 report.call_stack().size() * sizeof(report.call_stack()[0])); | |
| 120 } | |
| 121 } | 113 } |
| 114 | |
| 115 for (const auto& entry : report.alloc_breakdown_history()) { | |
| 116 auto* proto_entry = proto.add_alloc_breakdown_history(); | |
| 117 for (const uint32_t count : entry.counts_by_size) | |
| 118 proto_entry->add_counts_by_size(count); | |
|
Ilya Sherman
2016/04/14 02:30:04
nit: Curly braces here too
Simon Que
2016/04/14 03:04:49
Done.
| |
| 119 proto_entry->set_count_for_call_stack(entry.count_for_call_stack); | |
| 120 } | |
| 121 | |
| 122 return proto; | |
| 122 } | 123 } |
| 123 | 124 |
| 124 // The only instance of LeakDetector that should be used. | 125 // The only instance of LeakDetector that should be used. |
| 125 base::LazyInstance<LeakDetector>::Leaky g_instance = LAZY_INSTANCE_INITIALIZER; | 126 base::LazyInstance<LeakDetector>::Leaky g_instance = LAZY_INSTANCE_INITIALIZER; |
| 126 | 127 |
| 127 // Thread-specific data to be used by hook functions. | 128 // Thread-specific data to be used by hook functions. |
| 128 base::LazyInstance<base::ThreadLocalPointer<void>>::Leaky g_hook_data_tls = | 129 base::LazyInstance<base::ThreadLocalPointer<void>>::Leaky g_hook_data_tls = |
| 129 LAZY_INSTANCE_INITIALIZER; | 130 LAZY_INSTANCE_INITIALIZER; |
| 130 | 131 |
| 131 // Returns the contents of |g_hook_data_tls| as a HookData structure. | 132 // Returns the contents of |g_hook_data_tls| as a HookData structure. |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 146 inline void StoreHookDataToTLS(HookData hook_data) { | 147 inline void StoreHookDataToTLS(HookData hook_data) { |
| 147 // NOTE: |alloc_size| loses its upper bit when it gets stored in the TLS here. | 148 // NOTE: |alloc_size| loses its upper bit when it gets stored in the TLS here. |
| 148 // The effective max value of |alloc_size| is thus half its nominal max value. | 149 // The effective max value of |alloc_size| is thus half its nominal max value. |
| 149 uintptr_t ptr_value = | 150 uintptr_t ptr_value = |
| 150 (hook_data.entered_hook ? 1 : 0) | (hook_data.alloc_size << 1); | 151 (hook_data.entered_hook ? 1 : 0) | (hook_data.alloc_size << 1); |
| 151 g_hook_data_tls.Get().Set(reinterpret_cast<void*>(ptr_value)); | 152 g_hook_data_tls.Get().Set(reinterpret_cast<void*>(ptr_value)); |
| 152 } | 153 } |
| 153 | 154 |
| 154 } // namespace | 155 } // namespace |
| 155 | 156 |
| 156 LeakDetector::LeakReport::LeakReport() {} | |
| 157 | |
| 158 LeakDetector::LeakReport::LeakReport(const LeakReport& other) = default; | |
| 159 | |
| 160 LeakDetector::LeakReport::~LeakReport() {} | |
| 161 | |
| 162 // static | 157 // static |
| 163 LeakDetector* LeakDetector::GetInstance() { | 158 LeakDetector* LeakDetector::GetInstance() { |
| 164 return g_instance.Pointer(); | 159 return g_instance.Pointer(); |
| 165 } | 160 } |
| 166 | 161 |
| 167 void LeakDetector::Init(float sampling_rate, | 162 void LeakDetector::Init(float sampling_rate, |
| 168 size_t max_call_stack_unwind_depth, | 163 size_t max_call_stack_unwind_depth, |
| 169 uint64_t analysis_interval_bytes, | 164 uint64_t analysis_interval_bytes, |
| 170 uint32_t size_suspicion_threshold, | 165 uint32_t size_suspicion_threshold, |
| 171 uint32_t call_stack_suspicion_threshold) { | 166 uint32_t call_stack_suspicion_threshold) { |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 267 if (total_alloc_size > | 262 if (total_alloc_size > |
| 268 detector->last_analysis_alloc_size_ + analysis_interval_bytes) { | 263 detector->last_analysis_alloc_size_ + analysis_interval_bytes) { |
| 269 // Try to maintain regular intervals of size |analysis_interval_bytes_|. | 264 // Try to maintain regular intervals of size |analysis_interval_bytes_|. |
| 270 detector->last_analysis_alloc_size_ = | 265 detector->last_analysis_alloc_size_ = |
| 271 total_alloc_size - total_alloc_size % analysis_interval_bytes; | 266 total_alloc_size - total_alloc_size % analysis_interval_bytes; |
| 272 | 267 |
| 273 InternalVector<InternalLeakReport> leak_reports; | 268 InternalVector<InternalLeakReport> leak_reports; |
| 274 detector->impl_->TestForLeaks(&leak_reports); | 269 detector->impl_->TestForLeaks(&leak_reports); |
| 275 | 270 |
| 276 // Pass leak reports to observers. | 271 // Pass leak reports to observers. |
| 277 std::vector<LeakReport> leak_reports_for_observers; | 272 std::vector<MemoryLeakReportProto> leak_report_protos; |
| 278 GetReportsForObservers(leak_reports, &leak_reports_for_observers); | 273 std::transform(leak_reports.begin(), leak_reports.end(), |
| 279 detector->NotifyObservers(leak_reports_for_observers); | 274 leak_report_protos.begin(), &ConvertLeakReportToProto); |
| 275 detector->NotifyObservers(leak_report_protos); | |
| 280 } | 276 } |
| 281 } | 277 } |
| 282 | 278 |
| 283 { | 279 { |
| 284 // The internal memory of |stack| should be freed before setting | 280 // The internal memory of |stack| should be freed before setting |
| 285 // |entered_hook| to false at the end of this function. Free it here by | 281 // |entered_hook| to false at the end of this function. Free it here by |
| 286 // moving the internal memory to a temporary variable that will go out of | 282 // moving the internal memory to a temporary variable that will go out of |
| 287 // scope. | 283 // scope. |
| 288 std::vector<void*> dummy_stack; | 284 std::vector<void*> dummy_stack; |
| 289 dummy_stack.swap(stack); | 285 dummy_stack.swap(stack); |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 312 } | 308 } |
| 313 | 309 |
| 314 hook_data.entered_hook = false; | 310 hook_data.entered_hook = false; |
| 315 StoreHookDataToTLS(hook_data); | 311 StoreHookDataToTLS(hook_data); |
| 316 } | 312 } |
| 317 | 313 |
| 318 inline bool LeakDetector::ShouldSample(const void* ptr) const { | 314 inline bool LeakDetector::ShouldSample(const void* ptr) const { |
| 319 return PointerToHash(ptr) < sampling_factor_; | 315 return PointerToHash(ptr) < sampling_factor_; |
| 320 } | 316 } |
| 321 | 317 |
| 322 void LeakDetector::NotifyObservers(const std::vector<LeakReport>& reports) { | 318 void LeakDetector::NotifyObservers( |
| 319 const std::vector<MemoryLeakReportProto>& reports) { | |
| 323 if (reports.empty()) | 320 if (reports.empty()) |
| 324 return; | 321 return; |
| 325 | 322 |
| 326 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { | 323 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { |
| 327 content::BrowserThread::PostTask( | 324 content::BrowserThread::PostTask( |
| 328 content::BrowserThread::UI, FROM_HERE, | 325 content::BrowserThread::UI, FROM_HERE, |
| 329 base::Bind(&LeakDetector::NotifyObservers, base::Unretained(this), | 326 base::Bind(&LeakDetector::NotifyObservers, base::Unretained(this), |
| 330 reports)); | 327 reports)); |
| 331 return; | 328 return; |
| 332 } | 329 } |
| 333 | 330 |
| 334 for (const LeakReport& report : reports) { | 331 { |
| 335 base::AutoLock lock(observers_lock_); | 332 base::AutoLock lock(observers_lock_); |
| 336 FOR_EACH_OBSERVER(Observer, observers_, OnLeakFound(report)); | 333 FOR_EACH_OBSERVER(Observer, observers_, OnLeaksFound(reports)); |
| 337 } | 334 } |
| 338 } | 335 } |
| 339 | 336 |
| 340 } // namespace metrics | 337 } // namespace metrics |
| OLD | NEW |