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 : #include "syzygy/agent/asan/stack_capture_cache.h"
16 :
17 : #include "base/memory/scoped_ptr.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/agent/asan/asan_logger.h"
20 :
21 : namespace agent {
22 : namespace asan {
23 :
24 : namespace {
25 :
26 : class TestStackCaptureCache : public StackCaptureCache {
27 : public:
28 E : explicit TestStackCaptureCache(AsanLogger* logger)
29 : : StackCaptureCache(logger) {
30 E : }
31 E : TestStackCaptureCache(AsanLogger* logger, size_t max_num_frames)
32 : : StackCaptureCache(logger, max_num_frames) {
33 E : }
34 :
35 : using StackCaptureCache::Statistics;
36 :
37 E : void GetStatistics(Statistics* s) {
38 E : DCHECK(s != NULL);
39 E : base::AutoLock auto_lock(stats_lock_);
40 E : GetStatisticsUnlocked(s);
41 E : }
42 : };
43 :
44 : class StackCaptureCacheTest : public testing::Test {
45 : public:
46 E : void SetUp() OVERRIDE {
47 : // Setup the "global" state.
48 E : StackCapture::Init();
49 E : StackCaptureCache::Init();
50 E : }
51 : };
52 :
53 : } // namespace
54 :
55 E : TEST_F(StackCaptureCacheTest, CachePageTest) {
56 : static const size_t kFrameCounts[] =
57 : { 5, 10, 30, StackCapture::kMaxNumFrames };
58 :
59 E : for (size_t i = 0; i < arraysize(kFrameCounts); ++i) {
60 E : size_t max_num_frames = kFrameCounts[i];
61 : scoped_ptr<TestStackCaptureCache::CachePage> page(
62 E : new TestStackCaptureCache::CachePage(NULL));
63 :
64 : // Ensure that returning a page works.
65 E : EXPECT_EQ(0u, page->bytes_used());
66 E : StackCapture* s1 = page->GetNextStackCapture(max_num_frames);
67 E : ASSERT_TRUE(s1 != NULL);
68 E : EXPECT_EQ(max_num_frames, s1->max_num_frames());
69 E : EXPECT_EQ(s1->Size(), page->bytes_used());
70 E : page->ReturnStackCapture(s1);
71 E : EXPECT_EQ(0u, page->bytes_used());
72 :
73 : // Reallocating should get us the same page as the one we just returned.
74 E : StackCapture* s2 = page->GetNextStackCapture(max_num_frames);
75 E : EXPECT_EQ(s1, s2);
76 :
77 : // Figure out how many more allocations the page should give us.
78 E : size_t bytes_left = page->bytes_left();
79 E : size_t allocations_left = bytes_left / s2->Size();
80 :
81 : // Ensure we get exactly that many.
82 E : for (size_t j = 0; j < allocations_left; ++j) {
83 E : EXPECT_TRUE(page->GetNextStackCapture(max_num_frames) != NULL);
84 E : }
85 :
86 : // And no more than that.
87 E : EXPECT_TRUE(page->GetNextStackCapture(max_num_frames) == NULL);
88 E : }
89 E : }
90 :
91 E : TEST_F(StackCaptureCacheTest, SaveStackTrace) {
92 E : AsanLogger logger;
93 E : TestStackCaptureCache cache(&logger);
94 E : EXPECT_EQ(StackCapture::kMaxNumFrames, cache.max_num_frames());
95 :
96 : // Capture a stack trace.
97 E : ULONG stack_id = 0;
98 E : void* frames[StackCapture::kMaxNumFrames] = { 0 };
99 : size_t num_frames = ::CaptureStackBackTrace(
100 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
101 :
102 : // We should be able to save the captures stack trace.
103 E : const StackCapture* s1 = cache.SaveStackTrace(stack_id, frames, num_frames);
104 E : ASSERT_TRUE(s1 != NULL);
105 E : EXPECT_EQ(num_frames, s1->max_num_frames());
106 :
107 : // We should get a pointer to the initial stack capture object if we attempt
108 : // to save the same trace again.
109 E : const StackCapture* s2 = cache.SaveStackTrace(stack_id, frames, num_frames);
110 E : EXPECT_EQ(s1, s2);
111 :
112 : // Capture a new stack trace.
113 : num_frames = ::CaptureStackBackTrace(
114 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
115 :
116 : // We should get a pointer to a new stack capture object when we attempt
117 : // to save a different trace.
118 E : const StackCapture* s3 = cache.SaveStackTrace(stack_id, frames, num_frames);
119 E : EXPECT_NE(s1, s3);
120 E : EXPECT_EQ(num_frames, s3->max_num_frames());
121 E : }
122 :
123 E : TEST_F(StackCaptureCacheTest, RestrictedStackTraces) {
124 E : AsanLogger logger;
125 E : TestStackCaptureCache cache(&logger, 20);
126 E : EXPECT_EQ(20u, cache.max_num_frames());
127 :
128 : // Capture a stack trace.
129 E : ULONG stack_id = 0;
130 E : void* frames[StackCapture::kMaxNumFrames] = { 0 };
131 : size_t num_frames = ::CaptureStackBackTrace(
132 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
133 :
134 : // We should be able to save the captures stack trace.
135 E : const StackCapture* s1 = cache.SaveStackTrace(stack_id, frames, num_frames);
136 E : ASSERT_TRUE(s1 != NULL);
137 E : EXPECT_EQ(num_frames, s1->max_num_frames());
138 :
139 : // We should get a pointer to the initial stack capture object if we attempt
140 : // to save the same trace again.
141 E : const StackCapture* s2 = cache.SaveStackTrace(stack_id, frames, num_frames);
142 E : EXPECT_EQ(s1, s2);
143 :
144 : // Capture a new stack trace.
145 : num_frames = ::CaptureStackBackTrace(
146 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
147 :
148 : // We should get a pointer to a new stack capture object when we attempt
149 : // to save a different trace.
150 E : const StackCapture* s3 = cache.SaveStackTrace(stack_id, frames, num_frames);
151 E : EXPECT_NE(s1, s3);
152 E : EXPECT_EQ(num_frames, s1->max_num_frames());
153 E : }
154 :
155 E : TEST_F(StackCaptureCacheTest, MaxNumFrames) {
156 E : AsanLogger logger;
157 E : TestStackCaptureCache cache(&logger);
158 E : size_t max_num_frames = cache.max_num_frames() + 1;
159 E : cache.set_max_num_frames(max_num_frames);
160 E : ASSERT_EQ(max_num_frames, cache.max_num_frames());
161 E : }
162 :
163 E : TEST_F(StackCaptureCacheTest, ReclaimedStackCapture) {
164 E : AsanLogger logger;
165 E : TestStackCaptureCache cache(&logger);
166 :
167 : // Grab a stack capture and insert it.
168 E : StackCapture stack_capture;
169 E : stack_capture.InitFromStack();
170 E : const StackCapture* s1 = cache.SaveStackTrace(stack_capture);
171 E : ASSERT_TRUE(s1 != NULL);
172 :
173 : // Grab another one and insert it.
174 E : stack_capture.InitFromStack();
175 E : const StackCapture* s2 = cache.SaveStackTrace(stack_capture);
176 E : ASSERT_TRUE(s2 != NULL);
177 :
178 : // Return the first one.
179 E : cache.ReleaseStackTrace(s1);
180 :
181 : // Grab another one and insert it.
182 E : stack_capture.InitFromStack();
183 E : const StackCapture* s3 = cache.SaveStackTrace(stack_capture);
184 E : ASSERT_TRUE(s3 != NULL);
185 :
186 : // We expect this third one to have been reclaimed.
187 E : EXPECT_EQ(s1, s3);
188 E : }
189 :
190 E : TEST_F(StackCaptureCacheTest, Statistics) {
191 E : AsanLogger logger;
192 E : TestStackCaptureCache cache(&logger);
193 E : cache.set_compression_reporting_period(1U);
194 E : TestStackCaptureCache::Statistics s = {};
195 :
196 E : cache.GetStatistics(&s);
197 E : EXPECT_EQ(0u, s.cached);
198 E : EXPECT_EQ(0u, s.saturated);
199 E : EXPECT_EQ(0u, s.unreferenced);
200 E : EXPECT_EQ(0u, s.requested);
201 E : EXPECT_EQ(0u, s.allocated);
202 E : EXPECT_EQ(0u, s.references);
203 E : EXPECT_EQ(0u, s.frames_stored);
204 E : EXPECT_EQ(0u, s.frames_alive);
205 E : EXPECT_EQ(0u, s.frames_dead);
206 :
207 : // Grab a stack capture and insert it.
208 E : StackCapture stack_capture;
209 E : stack_capture.InitFromStack();
210 E : const StackCapture* s1 = cache.SaveStackTrace(stack_capture);
211 E : ASSERT_TRUE(s1 != NULL);
212 E : size_t s1_frames = s1->num_frames();
213 E : cache.GetStatistics(&s);
214 E : EXPECT_EQ(1u, s.cached);
215 E : EXPECT_EQ(0u, s.saturated);
216 E : EXPECT_EQ(0u, s.unreferenced);
217 E : EXPECT_EQ(1u, s.requested);
218 E : EXPECT_EQ(1u, s.allocated);
219 E : EXPECT_EQ(1u, s.references);
220 E : EXPECT_EQ(s1_frames, s.frames_stored);
221 E : EXPECT_EQ(s1_frames, s.frames_alive);
222 E : EXPECT_EQ(0u, s.frames_dead);
223 :
224 : // Reinsert the same stack. We expect to get the same pointer back.
225 E : const StackCapture* s2 = cache.SaveStackTrace(stack_capture);
226 E : ASSERT_TRUE(s2 != NULL);
227 E : cache.GetStatistics(&s);
228 E : EXPECT_EQ(s1, s2);
229 E : EXPECT_EQ(1u, s.cached);
230 E : EXPECT_EQ(0u, s.saturated);
231 E : EXPECT_EQ(0u, s.unreferenced);
232 E : EXPECT_EQ(2u, s.requested);
233 E : EXPECT_EQ(1u, s.allocated);
234 E : EXPECT_EQ(2u, s.references);
235 E : EXPECT_EQ(2 * s1_frames, s.frames_stored);
236 E : EXPECT_EQ(s1_frames, s.frames_alive);
237 E : EXPECT_EQ(0u, s.frames_dead);
238 :
239 : // Insert a new stack.
240 E : stack_capture.InitFromStack();
241 E : const StackCapture* s3 = cache.SaveStackTrace(stack_capture);
242 E : ASSERT_TRUE(s3 != NULL);
243 E : size_t s3_frames = s3->num_frames();
244 E : cache.GetStatistics(&s);
245 E : EXPECT_EQ(2u, s.cached);
246 E : EXPECT_EQ(0u, s.saturated);
247 E : EXPECT_EQ(0u, s.unreferenced);
248 E : EXPECT_EQ(3u, s.requested);
249 E : EXPECT_EQ(2u, s.allocated);
250 E : EXPECT_EQ(3u, s.references);
251 E : EXPECT_EQ(2 * s1_frames + s3_frames, s.frames_stored);
252 E : EXPECT_EQ(s1_frames + s3_frames, s.frames_alive);
253 E : EXPECT_EQ(0u, s.frames_dead);
254 :
255 : // Return the first stack. This should decrement the total reference count.
256 E : cache.ReleaseStackTrace(s1);
257 E : s1 = NULL;
258 E : cache.GetStatistics(&s);
259 E : EXPECT_EQ(2u, s.cached);
260 E : EXPECT_EQ(0u, s.saturated);
261 E : EXPECT_EQ(0u, s.unreferenced);
262 E : EXPECT_EQ(3u, s.requested);
263 E : EXPECT_EQ(2u, s.allocated);
264 E : EXPECT_EQ(2u, s.references);
265 E : EXPECT_EQ(s1_frames + s3_frames, s.frames_stored);
266 E : EXPECT_EQ(s1_frames + s3_frames, s.frames_alive);
267 E : EXPECT_EQ(0u, s.frames_dead);
268 :
269 : // Return the 2nd stack. This should decrement the reference count, and leave
270 : // a stack unreferenced (and its frames dead).
271 E : cache.ReleaseStackTrace(s2);
272 E : s2 = NULL;
273 E : cache.GetStatistics(&s);
274 E : EXPECT_EQ(1u, s.cached);
275 E : EXPECT_EQ(0u, s.saturated);
276 E : EXPECT_EQ(1u, s.unreferenced);
277 E : EXPECT_EQ(3u, s.requested);
278 E : EXPECT_EQ(2u, s.allocated);
279 E : EXPECT_EQ(1u, s.references);
280 E : EXPECT_EQ(s3_frames, s.frames_stored);
281 E : EXPECT_EQ(s3_frames, s.frames_alive);
282 E : EXPECT_EQ(s1_frames, s.frames_dead);
283 :
284 : // Insert the 3rd stack over and over again. We'll eventually saturate the
285 : // reference counter and it'll be a permanent part of the cache.
286 E : size_t kEnoughTimesToSaturate = StackCapture::kMaxRefCount;
287 E : for (size_t i = 0; i < kEnoughTimesToSaturate; ++i) {
288 E : const StackCapture* s4 = cache.SaveStackTrace(stack_capture);
289 E : ASSERT_TRUE(s4 != NULL);
290 E : EXPECT_EQ(s3, s4);
291 E : }
292 E : cache.GetStatistics(&s);
293 E : EXPECT_EQ(1u, s.cached);
294 E : EXPECT_EQ(1u, s.saturated);
295 E : EXPECT_EQ(1u, s.unreferenced);
296 E : EXPECT_EQ(3u + kEnoughTimesToSaturate, s.requested);
297 E : EXPECT_EQ(2u, s.allocated);
298 E : EXPECT_EQ(1u + kEnoughTimesToSaturate, s.references);
299 E : EXPECT_EQ((1u + kEnoughTimesToSaturate) * s3_frames, s.frames_stored);
300 E : EXPECT_EQ(s3_frames, s.frames_alive);
301 E : EXPECT_EQ(s1_frames, s.frames_dead);
302 :
303 : // Return the 3rd stack as many times as it was referenced. It should still
304 : // be saturated. None of its frames should be stored (there are no active
305 : // references), but it should still be 'alive' as it remains in the cache.
306 E : for (size_t i = 0; i < kEnoughTimesToSaturate + 1; ++i)
307 E : cache.ReleaseStackTrace(s3);
308 E : s3 = NULL;
309 E : cache.GetStatistics(&s);
310 E : EXPECT_EQ(1u, s.cached);
311 E : EXPECT_EQ(1u, s.saturated);
312 E : EXPECT_EQ(1u, s.unreferenced);
313 E : EXPECT_EQ(3u + kEnoughTimesToSaturate, s.requested);
314 E : EXPECT_EQ(2u, s.allocated);
315 E : EXPECT_EQ(0u, s.references);
316 E : EXPECT_EQ(0u, s.frames_stored);
317 E : EXPECT_EQ(s3_frames, s.frames_alive);
318 E : EXPECT_EQ(s1_frames, s.frames_dead);
319 E : }
320 :
321 E : TEST_F(StackCaptureCacheTest, CachePagesArePoisoned) {
322 : scoped_ptr<TestStackCaptureCache::CachePage> page(
323 E : new TestStackCaptureCache::CachePage(NULL));
324 E : void* cache_page_ptr = reinterpret_cast<void*>(page.get());
325 E : EXPECT_FALSE(Shadow::IsAccessible(cache_page_ptr));
326 E : page.reset(NULL);
327 E : EXPECT_TRUE(Shadow::IsAccessible(cache_page_ptr));
328 E : }
329 :
330 : } // namespace asan
331 : } // namespace agent
|