1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #ifndef SYZYGY_AGENT_ASAN_STACK_CAPTURE_CACHE_H_
16 : #define SYZYGY_AGENT_ASAN_STACK_CAPTURE_CACHE_H_
17 :
18 : #include "base/hash_tables.h"
19 : #include "base/synchronization/lock.h"
20 : #include "syzygy/agent/asan/stack_capture.h"
21 :
22 : namespace agent {
23 : namespace asan {
24 :
25 : // Forward declaration.
26 : class AsanLogger;
27 : class StackCapture;
28 :
29 : // A class which manages a thread-safe cache of unique stack traces, by ID.
30 : class StackCaptureCache {
31 : public:
32 : // The size of a page of stack captures, in bytes. This should be in the
33 : // hundreds of KB or low MBs so that we have an efficient pooled allocator
34 : // that can store hundreds to thousands of stack captures, yet whose
35 : // incremental growth is not too large.
36 : static const size_t kCachePageSize = 1024 * 1024;
37 :
38 : // The type used to uniquely identify a stack.
39 : typedef StackCapture::StackId StackId;
40 :
41 : // Forward declaration.
42 : class CachePage;
43 :
44 : // TODO(chrisha): Plumb a command-line parameter through to control the
45 : // max depth of stack traces in the StackCaptureCache. This should get us
46 : // significant memory savings in the stack trace cache.
47 :
48 : // Initializes a new stack capture cache.
49 : // @param logger The logger to use.
50 : // @param max_num_frames The maximum number of frames to be used by the
51 : // StackCapture objects in this cache.
52 : explicit StackCaptureCache(AsanLogger* logger);
53 : StackCaptureCache(AsanLogger* logger, size_t max_num_frames);
54 :
55 : // Destroys a stack capture cache.
56 : ~StackCaptureCache();
57 :
58 : // @returns the current maximum number of frames supported by saved stack
59 : // traces.
60 E : size_t max_num_frames() const { return max_num_frames_; }
61 :
62 : // Sets the current maximum number of frames supported by saved stack traces.
63 : // @param max_num_frames The maximum number of frames to set.
64 E : void set_max_num_frames(size_t max_num_frames) {
65 E : max_num_frames_ = max_num_frames;
66 E : }
67 :
68 : // @returns the default compression reporting period value.
69 E : static size_t GetDefaultCompressionReportingPeriod() {
70 E : return kDefaultCompressionReportingPeriod;
71 E : }
72 :
73 : // Sets a new (global) compression reporting period value. Note that this
74 : // method is not thread safe. It is expected to be called once at startup,
75 : // or not at all.
76 E : static void set_compression_reporting_period(size_t period) {
77 E : compression_reporting_period_ = period;
78 E : }
79 :
80 : // @returns the current (global) compression reporting period value. It is
81 : // expected that this value is a constant after initialization.
82 E : static size_t compression_reporting_period() {
83 E : return compression_reporting_period_;
84 E : }
85 :
86 : // Save (or retrieve) the stack capture (the first @p num_frames elements
87 : // from @p frames) into the cache using @p stack_id as the key.
88 : // @param stack_id a unique identifier for this stack trace. It is expected
89 : // that identical stack traces will have the same @p stack_id.
90 : // @param frames an array of stack frame pointers.
91 : // @param num_frames the number of valid elements in @p frames. Note that
92 : // at most StackCapture::kMaxNumFrames will be saved.
93 : // @param stack_capture The initialized stack capture to save.
94 : // @returns a pointer to the saved stack capture.
95 : const StackCapture* SaveStackTrace(StackId stack_id,
96 : const void* const* frames,
97 : size_t num_frames);
98 : const StackCapture* SaveStackTrace(const StackCapture& stack_capture);
99 :
100 : // Logs the current stack capture cache compression ratio. This method is
101 : // thread safe.
102 : void LogCompressionRatio() const;
103 :
104 : protected:
105 : // The container type in which we store the cached stacks. This enforces
106 : // uniqueness based on their hash value, nothing more.
107 : typedef base::hash_set<const StackCapture*,
108 : StackCapture::HashCompare> StackSet;
109 :
110 : // @returns The compression ratio achieved by the stack capture cache. This
111 : // is the percentage of total allocation stack traces actually stored in
112 : // the cache. This method must be called while holding lock_.
113 : double GetCompressionRatioUnlocked() const;
114 :
115 : // Implementation function for logging the compression ratio.
116 : void LogCompressionRatioImpl(double ratio) const;
117 :
118 : // The default number of iterations between each compression ratio report.
119 : // Zero (0) means do not report.
120 : static const size_t kDefaultCompressionReportingPeriod = 0;
121 :
122 : // The number of allocations between reports of the stack trace cache
123 : // compression ratio. Zero (0) means do not report. Values like 1 million
124 : // seem to be pretty good with Chrome.
125 : static size_t compression_reporting_period_;
126 :
127 : // Logger instance to which to report the compression ratio.
128 : AsanLogger* const logger_;
129 :
130 : // A lock to protect the known stacks set from concurrent access.
131 : mutable base::Lock lock_;
132 :
133 : // The max depth of the stack traces to allocate. This can change, but it
134 : // doesn't really make sense to do so.
135 : size_t max_num_frames_;
136 :
137 : // The set of known stacks. Accessed under lock_.
138 : StackSet known_stacks_;
139 :
140 : // The current page from which new stack captures are allocated.
141 : // Accessed under lock_.
142 : CachePage* current_page_;
143 :
144 : // The total number of stack allocations requested. Accessed under lock_.
145 : uint64 total_allocations_;
146 :
147 : // The total number of stack allocations requested. Accessed under lock_.
148 : uint64 cached_allocations_;
149 :
150 : private:
151 : DISALLOW_COPY_AND_ASSIGN(StackCaptureCache);
152 : };
153 :
154 : // A page of preallocated stack trace capture objects to be populated
155 : // and stored in the known stacks cache set.
156 : class StackCaptureCache::CachePage {
157 : public:
158 E : explicit CachePage(CachePage* link) : next_page_(link), bytes_used_(0) {
159 E : }
160 :
161 : ~CachePage();
162 :
163 : // Allocates a stack capture from this cache page if possible.
164 : // @param max_num_frames The maximum number of frames the object needs to be
165 : // able to store.
166 : // @returns a new StackCapture, or NULL if the page is full.
167 : StackCapture* GetNextStackCapture(size_t max_num_frames);
168 :
169 : // Releases the most recently allocated stack capture back to the page.
170 : // @param stack_capture The stack capture to return. This must be the most
171 : // recently allocated capture as returned by GetNextStackCapture.
172 : void ReleaseStackCapture(StackCapture* stack_capture);
173 :
174 : // @returns the number of bytes used in this page. This is mainly a hook
175 : // for unittesting.
176 E : size_t bytes_used() const { return bytes_used_; }
177 :
178 : protected:
179 : // The cache pages from a linked list, which allows for easy cleanup
180 : // when the cache is destroyed.
181 : CachePage* next_page_;
182 :
183 : // The number of bytes used, also equal to the byte offset of the next
184 : // StackCapture object to be allocated.
185 : size_t bytes_used_;
186 :
187 : // A page's worth of data, which will be allocated as StackCapture objects.
188 : uint8 data_[kCachePageSize];
189 :
190 : private:
191 : DISALLOW_COPY_AND_ASSIGN(CachePage);
192 : };
193 :
194 : } // namespace asan
195 : } // namespace agent
196 :
197 : #endif // SYZYGY_AGENT_ASAN_STACK_CAPTURE_CACHE_H_
|