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/logger.h"
20 : #include "syzygy/agent/asan/memory_notifiers/null_memory_notifier.h"
21 : #include "testing/gmock/include/gmock/gmock.h"
22 :
23 : namespace agent {
24 : namespace asan {
25 :
26 : namespace {
27 :
28 : using ::testing::_;
29 : using ::testing::Eq;
30 : using ::testing::Pointee;
31 : using ::testing::Property;
32 :
33 : using agent::common::StackCapture;
34 :
35 E : memory_notifiers::NullMemoryNotifier null_memory_notifier;
36 :
37 : class TestStackCaptureCache : public StackCaptureCache {
38 : public:
39 E : explicit TestStackCaptureCache(AsanLogger* logger)
40 : : StackCaptureCache(logger, &null_memory_notifier) {
41 E : }
42 E : TestStackCaptureCache(AsanLogger* logger, size_t max_num_frames)
43 : : StackCaptureCache(logger, &null_memory_notifier, max_num_frames) {
44 E : }
45 :
46 : using StackCaptureCache::Statistics;
47 :
48 E : void GetStatistics(Statistics* s) {
49 E : DCHECK(s != NULL);
50 E : base::AutoLock auto_lock(stats_lock_);
51 E : GetStatisticsUnlocked(s);
52 E : }
53 :
54 E : CachePage* current_page() { return current_page_; }
55 :
56 : private:
57 : using StackCaptureCache::current_page_;
58 : };
59 :
60 : class StackCaptureCacheTest : public testing::Test {
61 : public:
62 E : void SetUp() override {
63 : // Setup the "global" state.
64 E : StackCapture::Init();
65 E : StackCaptureCache::Init();
66 E : }
67 : };
68 :
69 : class TestStackCaptureCacheObserver : public StackCaptureCache::Observer {
70 : public:
71 E : TestStackCaptureCacheObserver() {}
72 :
73 E : MOCK_METHOD1(OnNewStack, void(common::StackCapture* new_stack));
74 : };
75 :
76 : } // namespace
77 :
78 E : TEST_F(StackCaptureCacheTest, CachePageTest) {
79 : static const size_t kFrameCounts[] =
80 : { 5, 10, 30, StackCapture::kMaxNumFrames };
81 :
82 : void* alloc = ::VirtualAlloc(nullptr,
83 E : sizeof(TestStackCaptureCache::CachePage), MEM_COMMIT, PAGE_READWRITE);
84 :
85 E : for (size_t i = 0; i < arraysize(kFrameCounts); ++i) {
86 E : size_t max_num_frames = kFrameCounts[i];
87 : TestStackCaptureCache::CachePage* page =
88 E : TestStackCaptureCache::CachePage::CreateInPlace(alloc, nullptr);
89 :
90 : // Ensure that returning a stack to a page works.
91 E : EXPECT_EQ(0u, page->bytes_used());
92 E : StackCapture* s1 = page->GetNextStackCapture(max_num_frames);
93 E : ASSERT_TRUE(s1 != NULL);
94 E : EXPECT_EQ(max_num_frames, s1->max_num_frames());
95 E : EXPECT_EQ(s1->Size(), page->bytes_used());
96 E : page->ReturnStackCapture(s1);
97 E : EXPECT_EQ(0u, page->bytes_used());
98 :
99 : // Reallocating should get us the same page as the one we just returned.
100 E : StackCapture* s2 = page->GetNextStackCapture(max_num_frames);
101 E : EXPECT_EQ(s1, s2);
102 :
103 : // Figure out how many more allocations the page should give us.
104 E : size_t bytes_left = page->bytes_left();
105 E : size_t allocations_left = bytes_left / s2->Size();
106 :
107 : // Ensure we get exactly that many.
108 E : for (size_t j = 0; j < allocations_left; ++j) {
109 E : EXPECT_TRUE(page->GetNextStackCapture(max_num_frames) != NULL);
110 E : }
111 :
112 : // And no more than that.
113 E : EXPECT_TRUE(page->GetNextStackCapture(max_num_frames) == NULL);
114 E : }
115 :
116 E : ::VirtualFree(alloc, 0, MEM_RELEASE);
117 E : }
118 :
119 E : TEST_F(StackCaptureCacheTest, SaveStackTrace) {
120 E : AsanLogger logger;
121 E : TestStackCaptureCache cache(&logger);
122 E : EXPECT_EQ(StackCapture::kMaxNumFrames, cache.max_num_frames());
123 :
124 : // Capture a stack trace.
125 E : StackCapture capture;
126 E : capture.InitFromStack();
127 :
128 : // We should be able to save the captures stack trace.
129 E : const StackCapture* s1 = cache.SaveStackTrace(capture);
130 E : ASSERT_TRUE(s1 != NULL);
131 E : EXPECT_EQ(capture.num_frames(), s1->max_num_frames());
132 :
133 : // We should get a pointer to the initial stack capture object if we attempt
134 : // to save the same trace again.
135 E : const StackCapture* s2 = cache.SaveStackTrace(capture);
136 E : EXPECT_EQ(s1, s2);
137 :
138 E : capture.InitFromStack();
139 :
140 : // We should get a pointer to a new stack capture object when we attempt
141 : // to save a different trace.
142 E : const StackCapture* s3 = cache.SaveStackTrace(capture);
143 E : EXPECT_NE(s1, s3);
144 E : EXPECT_EQ(capture.num_frames(), s3->max_num_frames());
145 E : }
146 :
147 E : TEST_F(StackCaptureCacheTest, RestrictedStackTraces) {
148 E : AsanLogger logger;
149 E : TestStackCaptureCache cache(&logger, 20);
150 E : EXPECT_EQ(20u, cache.max_num_frames());
151 :
152 : // Capture a stack trace.
153 E : StackCapture capture;
154 E : capture.InitFromStack();
155 :
156 : // We should be able to save the captures stack trace.
157 E : const StackCapture* s1 = cache.SaveStackTrace(capture);
158 E : ASSERT_TRUE(s1 != NULL);
159 E : EXPECT_EQ(capture.num_frames(), s1->max_num_frames());
160 :
161 : // We should get a pointer to the initial stack capture object if we attempt
162 : // to save the same trace again.
163 E : const StackCapture* s2 = cache.SaveStackTrace(capture);
164 E : EXPECT_EQ(s1, s2);
165 :
166 : // Capture a new stack trace.
167 E : capture.InitFromStack();
168 :
169 : // We should get a pointer to a new stack capture object when we attempt
170 : // to save a different trace.
171 E : const StackCapture* s3 = cache.SaveStackTrace(capture);
172 E : EXPECT_NE(s1, s3);
173 E : EXPECT_EQ(capture.num_frames(), s1->max_num_frames());
174 E : }
175 :
176 E : TEST_F(StackCaptureCacheTest, MaxNumFrames) {
177 E : AsanLogger logger;
178 E : TestStackCaptureCache cache(&logger);
179 E : size_t max_num_frames = cache.max_num_frames() + 1;
180 E : cache.set_max_num_frames(max_num_frames);
181 E : ASSERT_EQ(max_num_frames, cache.max_num_frames());
182 E : }
183 :
184 E : TEST_F(StackCaptureCacheTest, ReclaimedStackCapture) {
185 E : AsanLogger logger;
186 E : TestStackCaptureCache cache(&logger);
187 :
188 : // Grab a stack capture and insert it.
189 E : StackCapture stack_capture;
190 E : stack_capture.InitFromStack();
191 E : const StackCapture* s1 = cache.SaveStackTrace(stack_capture);
192 E : ASSERT_TRUE(s1 != NULL);
193 :
194 : // Grab another one and insert it.
195 E : stack_capture.InitFromStack();
196 E : const StackCapture* s2 = cache.SaveStackTrace(stack_capture);
197 E : ASSERT_TRUE(s2 != NULL);
198 :
199 : // Return the first one.
200 E : cache.ReleaseStackTrace(s1);
201 :
202 : // Grab another one and insert it.
203 E : stack_capture.InitFromStack();
204 E : const StackCapture* s3 = cache.SaveStackTrace(stack_capture);
205 E : ASSERT_TRUE(s3 != NULL);
206 :
207 : // We expect this third one to have been reclaimed.
208 E : EXPECT_EQ(s1, s3);
209 E : }
210 :
211 E : TEST_F(StackCaptureCacheTest, Statistics) {
212 E : AsanLogger logger;
213 E : TestStackCaptureCache cache(&logger);
214 E : cache.set_compression_reporting_period(1U);
215 E : TestStackCaptureCache::Statistics s = {};
216 :
217 E : cache.GetStatistics(&s);
218 E : EXPECT_EQ(0u, s.cached);
219 E : EXPECT_EQ(0u, s.saturated);
220 E : EXPECT_EQ(0u, s.unreferenced);
221 E : EXPECT_EQ(0u, s.requested);
222 E : EXPECT_EQ(0u, s.allocated);
223 E : EXPECT_EQ(0u, s.references);
224 E : EXPECT_EQ(0u, s.frames_stored);
225 E : EXPECT_EQ(0u, s.frames_alive);
226 E : EXPECT_EQ(0u, s.frames_dead);
227 :
228 : // Grab a stack capture and insert it.
229 E : StackCapture stack_capture;
230 E : stack_capture.InitFromStack();
231 E : const StackCapture* s1 = cache.SaveStackTrace(stack_capture);
232 E : ASSERT_TRUE(s1 != NULL);
233 E : size_t s1_frames = s1->num_frames();
234 E : cache.GetStatistics(&s);
235 E : EXPECT_EQ(1u, s.cached);
236 E : EXPECT_EQ(0u, s.saturated);
237 E : EXPECT_EQ(0u, s.unreferenced);
238 E : EXPECT_EQ(1u, s.requested);
239 E : EXPECT_EQ(1u, s.allocated);
240 E : EXPECT_EQ(1u, s.references);
241 E : EXPECT_EQ(s1_frames, s.frames_stored);
242 E : EXPECT_EQ(s1_frames, s.frames_alive);
243 E : EXPECT_EQ(0u, s.frames_dead);
244 :
245 : // Reinsert the same stack. We expect to get the same pointer back.
246 E : const StackCapture* s2 = cache.SaveStackTrace(stack_capture);
247 E : ASSERT_TRUE(s2 != NULL);
248 E : cache.GetStatistics(&s);
249 E : EXPECT_EQ(s1, s2);
250 E : EXPECT_EQ(1u, s.cached);
251 E : EXPECT_EQ(0u, s.saturated);
252 E : EXPECT_EQ(0u, s.unreferenced);
253 E : EXPECT_EQ(2u, s.requested);
254 E : EXPECT_EQ(1u, s.allocated);
255 E : EXPECT_EQ(2u, s.references);
256 E : EXPECT_EQ(2 * s1_frames, s.frames_stored);
257 E : EXPECT_EQ(s1_frames, s.frames_alive);
258 E : EXPECT_EQ(0u, s.frames_dead);
259 :
260 : // Insert a new stack.
261 E : stack_capture.InitFromStack();
262 E : const StackCapture* s3 = cache.SaveStackTrace(stack_capture);
263 E : ASSERT_TRUE(s3 != NULL);
264 E : size_t s3_frames = s3->num_frames();
265 E : cache.GetStatistics(&s);
266 E : EXPECT_EQ(2u, s.cached);
267 E : EXPECT_EQ(0u, s.saturated);
268 E : EXPECT_EQ(0u, s.unreferenced);
269 E : EXPECT_EQ(3u, s.requested);
270 E : EXPECT_EQ(2u, s.allocated);
271 E : EXPECT_EQ(3u, s.references);
272 E : EXPECT_EQ(2 * s1_frames + s3_frames, s.frames_stored);
273 E : EXPECT_EQ(s1_frames + s3_frames, s.frames_alive);
274 E : EXPECT_EQ(0u, s.frames_dead);
275 :
276 : // Return the first stack. This should decrement the total reference count.
277 E : cache.ReleaseStackTrace(s1);
278 E : s1 = NULL;
279 E : cache.GetStatistics(&s);
280 E : EXPECT_EQ(2u, s.cached);
281 E : EXPECT_EQ(0u, s.saturated);
282 E : EXPECT_EQ(0u, s.unreferenced);
283 E : EXPECT_EQ(3u, s.requested);
284 E : EXPECT_EQ(2u, s.allocated);
285 E : EXPECT_EQ(2u, s.references);
286 E : EXPECT_EQ(s1_frames + s3_frames, s.frames_stored);
287 E : EXPECT_EQ(s1_frames + s3_frames, s.frames_alive);
288 E : EXPECT_EQ(0u, s.frames_dead);
289 :
290 : // Return the 2nd stack. This should decrement the reference count, and leave
291 : // a stack unreferenced (and its frames dead).
292 E : cache.ReleaseStackTrace(s2);
293 E : s2 = NULL;
294 E : cache.GetStatistics(&s);
295 E : EXPECT_EQ(1u, s.cached);
296 E : EXPECT_EQ(0u, s.saturated);
297 E : EXPECT_EQ(1u, s.unreferenced);
298 E : EXPECT_EQ(3u, s.requested);
299 E : EXPECT_EQ(2u, s.allocated);
300 E : EXPECT_EQ(1u, s.references);
301 E : EXPECT_EQ(s3_frames, s.frames_stored);
302 E : EXPECT_EQ(s3_frames, s.frames_alive);
303 E : EXPECT_EQ(s1_frames, s.frames_dead);
304 :
305 : // Insert the 3rd stack over and over again. We'll eventually saturate the
306 : // reference counter and it'll be a permanent part of the cache.
307 E : size_t kEnoughTimesToSaturate = StackCapture::kMaxRefCount;
308 E : for (size_t i = 0; i < kEnoughTimesToSaturate; ++i) {
309 E : const StackCapture* s4 = cache.SaveStackTrace(stack_capture);
310 E : ASSERT_TRUE(s4 != NULL);
311 E : EXPECT_EQ(s3, s4);
312 E : }
313 E : cache.GetStatistics(&s);
314 E : EXPECT_EQ(1u, s.cached);
315 E : EXPECT_EQ(1u, s.saturated);
316 E : EXPECT_EQ(1u, s.unreferenced);
317 E : EXPECT_EQ(3u + kEnoughTimesToSaturate, s.requested);
318 E : EXPECT_EQ(2u, s.allocated);
319 E : EXPECT_EQ(1u + kEnoughTimesToSaturate, s.references);
320 E : EXPECT_EQ((1u + kEnoughTimesToSaturate) * s3_frames, s.frames_stored);
321 E : EXPECT_EQ(s3_frames, s.frames_alive);
322 E : EXPECT_EQ(s1_frames, s.frames_dead);
323 :
324 : // Return the 3rd stack as many times as it was referenced. It should still
325 : // be saturated. None of its frames should be stored (there are no active
326 : // references), but it should still be 'alive' as it remains in the cache.
327 E : for (size_t i = 0; i < kEnoughTimesToSaturate + 1; ++i)
328 E : cache.ReleaseStackTrace(s3);
329 E : s3 = NULL;
330 E : cache.GetStatistics(&s);
331 E : EXPECT_EQ(1u, s.cached);
332 E : EXPECT_EQ(1u, s.saturated);
333 E : EXPECT_EQ(1u, s.unreferenced);
334 E : EXPECT_EQ(3u + kEnoughTimesToSaturate, s.requested);
335 E : EXPECT_EQ(2u, s.allocated);
336 E : EXPECT_EQ(0u, s.references);
337 E : EXPECT_EQ(0u, s.frames_stored);
338 E : EXPECT_EQ(s3_frames, s.frames_alive);
339 E : EXPECT_EQ(s1_frames, s.frames_dead);
340 E : }
341 :
342 E : TEST_F(StackCaptureCacheTest, StackCapturePointerIsValid) {
343 E : AsanLogger logger;
344 E : TestStackCaptureCache cache(&logger);
345 :
346 : // Capture and save a stack trace.
347 E : StackCapture stack_capture;
348 E : stack_capture.InitFromStack();
349 E : const StackCapture* s1 = cache.SaveStackTrace(stack_capture);
350 E : ASSERT_TRUE(s1 != NULL);
351 :
352 : // This pointer should be valid.
353 E : EXPECT_TRUE(cache.StackCapturePointerIsValid(s1));
354 :
355 : // An address after the current page should be invalid.
356 : const StackCapture* invalid_stack_capture_1 =
357 : reinterpret_cast<const StackCapture*>(cache.current_page()->data() +
358 E : cache.current_page()->data_size());
359 E : EXPECT_FALSE(cache.StackCapturePointerIsValid(invalid_stack_capture_1));
360 :
361 : // An address before the current page should be invalid.
362 : const StackCapture* invalid_stack_capture_2 =
363 E : reinterpret_cast<const StackCapture*>(cache.current_page()->data() - 1);
364 E : EXPECT_FALSE(cache.StackCapturePointerIsValid(invalid_stack_capture_2));
365 :
366 : // A null pointer should be invalid.
367 : EXPECT_FALSE(cache.StackCapturePointerIsValid(
368 E : reinterpret_cast<const StackCapture*>(NULL)));
369 E : }
370 :
371 E : TEST_F(StackCaptureCacheTest, StackCaptureObserver) {
372 E : AsanLogger logger;
373 E : TestStackCaptureCache cache(&logger);
374 E : TestStackCaptureCacheObserver observer;
375 E : cache.AddObserver(&observer);
376 :
377 : // Capture a stack trace.
378 E : StackCapture stack;
379 E : stack.InitFromStack();
380 : // Expect one callback with the right stack id value.
381 : EXPECT_CALL(observer,
382 : OnNewStack(Pointee(Property(&StackCapture::absolute_stack_id,
383 : Eq(stack.absolute_stack_id())))))
384 E : .Times(1);
385 E : cache.SaveStackTrace(stack);
386 :
387 : // Capture another stack trace.
388 E : StackCapture stack2;
389 E : stack2.InitFromStack();
390 : // Expect one callback with the right stack id value.
391 : EXPECT_CALL(observer,
392 : OnNewStack(Pointee(Property(&StackCapture::absolute_stack_id,
393 : Eq(stack2.absolute_stack_id())))))
394 E : .Times(1);
395 E : cache.SaveStackTrace(stack2);
396 :
397 : // Save again the first stack, which should not call back our observer.
398 E : cache.SaveStackTrace(stack);
399 E : }
400 :
401 E : TEST_F(StackCaptureCacheTest, AllocateMultiplePages) {
402 E : AsanLogger logger;
403 E : TestStackCaptureCache cache(&logger);
404 :
405 : static const size_t kMaxFrames = 32;
406 E : void* dummy_frames[kMaxFrames] = {};
407 :
408 E : StackCapture stack;
409 E : stack.InitFromStack();
410 E : cache.SaveStackTrace(stack);
411 E : auto page = cache.current_page();
412 :
413 E : for (size_t i = 0; i < 10000; ++i) {
414 E : StackCapture stack;
415 : // Make sure each stack is unique.
416 E : dummy_frames[0] = reinterpret_cast<void*>(i);
417 E : stack.InitFromBuffer(dummy_frames, kMaxFrames);
418 E : cache.SaveStackTrace(stack);
419 E : }
420 :
421 E : EXPECT_NE(page, cache.current_page());
422 E : }
423 :
424 : } // namespace asan
425 : } // namespace agent
|