blob: 65c93d9781d72dec2eab9c37f3e55be9282880dd [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_METRICS_LEAK_DETECTOR_LEAK_DETECTOR_IMPL_H_
#define COMPONENTS_METRICS_LEAK_DETECTOR_LEAK_DETECTOR_IMPL_H_
#include <stddef.h>
#include <stdint.h>
#include <list>
#include <vector>
#include "base/containers/hash_tables.h"
#include "base/macros.h"
#include "components/metrics/leak_detector/call_stack_manager.h"
#include "components/metrics/leak_detector/custom_allocator.h"
#include "components/metrics/leak_detector/leak_analyzer.h"
#include "components/metrics/leak_detector/stl_allocator.h"
namespace metrics {
namespace leak_detector {
class CallStackTable;
// Class that contains the actual leak detection mechanism.
// Not thread-safe.
class LeakDetectorImpl {
public:
// STL types that are safe to use within the memory leak detector. They use
// CustomAllocator to avoid recursive malloc hook invocation when analyzing
// allocs and frees.
template <typename T>
using InternalList = std::list<T, STLAllocator<T, CustomAllocator>>;
template <typename T>
using InternalVector = std::vector<T, STLAllocator<T, CustomAllocator>>;
// Leak report generated by LeakDetectorImpl.
class LeakReport {
public:
// Stores a record of the allocation bookkeeping taken at a single moment in
// time.
struct AllocationBreakdown {
AllocationBreakdown();
AllocationBreakdown(const AllocationBreakdown& other);
~AllocationBreakdown();
// The contents of |LeakDetectorImpl::size_breakdown_history_| when this
// report was generated. See comment description of that variable.
InternalVector<uint32_t> counts_by_size;
// The net number of allocations with alloc size of
// |Leakreport::alloc_size_bytes_| allocated from |call_stack_|, at the
// time this record was generated.
uint32_t count_for_call_stack;
};
LeakReport();
LeakReport(const LeakReport& other);
~LeakReport();
size_t alloc_size_bytes() const { return alloc_size_bytes_; }
const InternalVector<uintptr_t>& call_stack() const { return call_stack_; }
const InternalVector<AllocationBreakdown>& alloc_breakdown_history() const {
return alloc_breakdown_history_;
}
size_t num_rising_intervals() const { return num_rising_intervals_; }
uint32_t num_allocs_increase() const { return num_allocs_increase_; }
void set_num_rising_intervals(size_t num_rising_intervals) {
num_rising_intervals_ = num_rising_intervals;
}
// Used to compare the contents of two leak reports.
bool operator<(const LeakReport& other) const;
private:
// LeakDetectorImpl needs access to class members when creating a new leak
// report.
friend class LeakDetectorImpl;
// Number of bytes allocated by the leak site during each allocation.
size_t alloc_size_bytes_;
// Number of intervals in the last uptrend.
size_t num_rising_intervals_;
// Net number of bytes allocated in the last uptrend.
uint32_t num_allocs_increase_;
// Unlike the CallStack struct, which consists of addresses, this call stack
// will contain offsets in the executable binary.
InternalVector<uintptr_t> call_stack_;
// Records of allocation bookkeeping over time. The first element is the
// oldest entry and the last element is the newest.
InternalVector<AllocationBreakdown> alloc_breakdown_history_;
};
LeakDetectorImpl(uintptr_t mapping_addr,
size_t mapping_size,
int size_suspicion_threshold,
int call_stack_suspicion_threshold);
~LeakDetectorImpl();
// Indicates whether the given allocation size has an associated call stack
// table, and thus requires a stack unwind.
bool ShouldGetStackTraceForSize(size_t size) const;
// Record allocs and frees.
void RecordAlloc(const void* ptr,
size_t size,
int stack_depth,
const void* const call_stack[]);
void RecordFree(const void* ptr);
// Run check for possible leaks based on the current profiling data.
void TestForLeaks(InternalVector<LeakReport>* reports, size_t timestamp);
private:
// A record of allocations for a particular size.
struct AllocSizeEntry {
// Number of allocations and frees for this size.
uint32_t num_allocs;
uint32_t num_frees;
// A stack table, if this size is being profiled for stack as well.
CallStackTable* stack_table;
// Historical records of allocation breakdown by call site, for a particular
// allocation size. Each entry in the list is a RankedSet containing the top
// call sites ordered by most number of call sites, collected during a leak
// analysis. The oldest record is at the head and the newest record is at
// the tail.
InternalList<RankedSet> call_site_breakdown_history;
AllocSizeEntry();
~AllocSizeEntry();
// Returns net number of allocs.
uint32_t GetNetAllocs() const { return num_allocs - num_frees; }
};
// Info for a single allocation.
struct AllocInfo {
AllocInfo() : call_stack(nullptr) {}
// Number of bytes in this allocation.
size_t size;
// Points to a unique call stack.
const CallStack* call_stack;
};
// Allocator class for allocation entry map. Maps allocated addresses to
// AllocInfo objects.
using AllocationEntryAllocator =
STLAllocator<std::pair<const uintptr_t, AllocInfo>, CustomAllocator>;
// Hash class for addresses.
struct AddressHash {
size_t operator()(uintptr_t addr) const;
};
// Returns the offset of |ptr| within the current binary. If it is not in the
// current binary, return |UINTPTR_MAX|.
uintptr_t GetOffset(const void* ptr) const;
// Record some of the current allocation bookkeeping. The net number of allocs
// per size is recorded in |size_breakdown_history_|. The net number of allocs
// per call site for each size is recorded in
// |AllocSizeEntry::call_site_breakdown_history|.
// Argument |timestamp| is used to update information about drops in
// allocation number for each stored call stack.
//
// Not all the net alloc counts are recorded. And the number of historical
// records kept is capped. If adding a new record exceeds that limit, the
// oldest record is discarded. See the function definition for more details.
void RecordCurrentAllocationDataInHistory(size_t timestamp);
// Store the data collected by RecordCurrentAllocationDataInHistory() in
// |*report|. Not all net alloc counts per call site will be stored, only the
// count for size=|size| and made from |call_site|. Also information
// about the last uptrend in net allocations for |size| and |call_site|
// is recorded with help of |timestamp|.
void StoreHistoricalDataInReport(size_t size, const CallStack* call_site,
LeakReport* report, size_t timestamp);
// Decrements the cooldown counter (value) for each entry in
// |cooldowns_per_leak_|. If the cooldown counter reaches 0, the entry is
// removed. Thus, all extantentries in |cooldowns_per_leak_| maintain a
// positive count.
void UpdateLeakCooldowns();
// Returns true if a particular leak signature (alloc size + call site) does
// not have an active cooldown counter (i.e. does not have an entry in
// |cooldowns_per_leak_|.
bool ReadyToGenerateReport(size_t size, const CallStack* call_stack) const;
// Resets the counter for a leak signature (alloc size + call site) in
// |cooldowns_per_leak_| to the max cooldown value. Creates a new entry in the
// container if none exists for this leak signature.
void ResetLeakCooldown(size_t size, const CallStack* call_stack);
// Owns all unique call stack objects, which are allocated on the heap. Any
// other class or function that references a call stack must get it from here,
// but may not take ownership of the call stack object.
CallStackManager call_stack_manager_;
// Allocation stats.
uint64_t num_allocs_;
uint64_t num_frees_;
uint64_t alloc_size_;
uint64_t free_size_;
uint32_t num_allocs_with_call_stack_;
uint32_t num_stack_tables_;
// Stores all individual recorded allocations.
base::hash_map<uintptr_t,
AllocInfo,
AddressHash,
std::equal_to<uintptr_t>,
AllocationEntryAllocator> address_map_;
// Used to analyze potential leak patterns in the allocation sizes.
LeakAnalyzer size_leak_analyzer_;
// Allocation stats for each size.
InternalVector<AllocSizeEntry> size_entries_;
// Tracks the net number of allocations per size over time. Each list item is
// a vector containing the allocation counts for each size. The vector element
// with index i corresponds to sizes |i * 4| to |i * 4 + 3|. The oldest size
// breakdowns is at the head of the list, and new size breakdowns should be
// added to the tail of the list.
InternalList<InternalVector<uint32_t>> size_breakdown_history_;
// Key: leak signature (alloc size + call site)
// Value: number of leak analyses before another leak report can be generated
// for that leak.
std::map<std::pair<size_t, const CallStack*>, size_t> cooldowns_per_leak_;
// Address mapping info of the current binary.
uintptr_t mapping_addr_;
size_t mapping_size_;
// Number of consecutive times a call stack must trigger suspicion to be
// considered a leak suspect.
int call_stack_suspicion_threshold_;
DISALLOW_COPY_AND_ASSIGN(LeakDetectorImpl);
};
} // namespace leak_detector
} // namespace metrics
#endif // COMPONENTS_METRICS_LEAK_DETECTOR_LEAK_DETECTOR_IMPL_H_