File: leak_detector_impl.h

package info (click to toggle)
chromium-browser 57.0.2987.98-1~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 2,637,852 kB
  • ctags: 2,544,394
  • sloc: cpp: 12,815,961; ansic: 3,676,222; python: 1,147,112; asm: 526,608; java: 523,212; xml: 286,794; perl: 92,654; sh: 86,408; objc: 73,271; makefile: 27,698; cs: 18,487; yacc: 13,031; tcl: 12,957; pascal: 4,875; ml: 4,716; lex: 3,904; sql: 3,862; ruby: 1,982; lisp: 1,508; php: 1,368; exp: 404; awk: 325; csh: 117; jsp: 39; sed: 37
file content (262 lines) | stat: -rw-r--r-- 9,804 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// 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_