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 E : double GetCompressionRatio() {
36 E : base::AutoLock auto_lock(lock_);
37 E : return GetCompressionRatioUnlocked();
38 E : }
39 : };
40 :
41 : } // namespace
42 :
43 E : TEST(StackCaptureCacheTest, CachePageTest) {
44 : static const size_t kFrameCounts[] =
45 : { 5, 10, 30, StackCapture::kMaxNumFrames };
46 :
47 E : for (size_t i = 0; i < arraysize(kFrameCounts); ++i) {
48 E : size_t max_num_frames = kFrameCounts[i];
49 : scoped_ptr<TestStackCaptureCache::CachePage> page(
50 E : new TestStackCaptureCache::CachePage(NULL));
51 :
52 : // Ensure that returning a page works.
53 E : EXPECT_EQ(0u, page->bytes_used());
54 E : StackCapture* s1 = page->GetNextStackCapture(max_num_frames);
55 E : ASSERT_TRUE(s1 != NULL);
56 E : EXPECT_EQ(max_num_frames, s1->max_num_frames());
57 E : EXPECT_EQ(s1->Size(), page->bytes_used());
58 E : page->ReleaseStackCapture(s1);
59 E : EXPECT_EQ(0u, page->bytes_used());
60 :
61 : // Reallocating should get us the same page as the one we just returned.
62 E : StackCapture* s2 = page->GetNextStackCapture(max_num_frames);
63 E : EXPECT_EQ(s1, s2);
64 :
65 : // Figure out how many more allocations the page should give us.
66 E : size_t bytes_left = StackCaptureCache::kCachePageSize - page->bytes_used();
67 E : size_t allocations_left = bytes_left / s2->Size();
68 :
69 : // Ensure we get exactly that many.
70 E : for (size_t j = 0; j < allocations_left; ++j) {
71 E : EXPECT_TRUE(page->GetNextStackCapture(max_num_frames) != NULL);
72 E : }
73 :
74 : // And no more than that.
75 E : EXPECT_TRUE(page->GetNextStackCapture(max_num_frames) == NULL);
76 E : }
77 E : }
78 :
79 E : TEST(StackCaptureCacheTest, SaveStackTrace) {
80 E : AsanLogger logger;
81 E : TestStackCaptureCache cache(&logger);
82 E : EXPECT_EQ(StackCapture::kMaxNumFrames, cache.max_num_frames());
83 :
84 : // Capture a stack trace.
85 E : ULONG stack_id = 0;
86 E : void* frames[StackCapture::kMaxNumFrames] = { 0 };
87 : size_t num_frames = ::CaptureStackBackTrace(
88 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
89 :
90 : // We should be able to save the captures stack trace.
91 E : const StackCapture* s1 = cache.SaveStackTrace(stack_id, frames, num_frames);
92 E : ASSERT_TRUE(s1 != NULL);
93 E : EXPECT_EQ(StackCapture::kMaxNumFrames, s1->max_num_frames());
94 E : EXPECT_EQ(sizeof(StackCapture), s1->Size());
95 :
96 : // We should get a pointer to the initial stack capture object if we attempt
97 : // to save the same trace again.
98 E : const StackCapture* s2 = cache.SaveStackTrace(stack_id, frames, num_frames);
99 E : EXPECT_EQ(s1, s2);
100 :
101 : // Capture a new stack trace.
102 : num_frames = ::CaptureStackBackTrace(
103 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
104 :
105 : // We should get a pointer to a new stack capture object when we attempt
106 : // to save a different trace.
107 E : const StackCapture* s3 = cache.SaveStackTrace(stack_id, frames, num_frames);
108 E : EXPECT_NE(s1, s3);
109 E : EXPECT_EQ(StackCapture::kMaxNumFrames, s3->max_num_frames());
110 E : EXPECT_EQ(sizeof(StackCapture), s1->Size());
111 E : }
112 :
113 E : TEST(StackCaptureCacheTest, RestrictedStackTraces) {
114 E : AsanLogger logger;
115 E : TestStackCaptureCache cache(&logger, 20);
116 E : EXPECT_EQ(20u, cache.max_num_frames());
117 :
118 : // Capture a stack trace.
119 E : ULONG stack_id = 0;
120 E : void* frames[StackCapture::kMaxNumFrames] = { 0 };
121 : size_t num_frames = ::CaptureStackBackTrace(
122 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
123 :
124 : // We should be able to save the captures stack trace.
125 E : const StackCapture* s1 = cache.SaveStackTrace(stack_id, frames, num_frames);
126 E : ASSERT_TRUE(s1 != NULL);
127 E : EXPECT_EQ(20u, s1->max_num_frames());
128 E : EXPECT_GT(sizeof(StackCapture), s1->Size());
129 :
130 : // We should get a pointer to the initial stack capture object if we attempt
131 : // to save the same trace again.
132 E : const StackCapture* s2 = cache.SaveStackTrace(stack_id, frames, num_frames);
133 E : EXPECT_EQ(s1, s2);
134 :
135 : // Capture a new stack trace.
136 : num_frames = ::CaptureStackBackTrace(
137 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
138 :
139 : // We should get a pointer to a new stack capture object when we attempt
140 : // to save a different trace.
141 E : const StackCapture* s3 = cache.SaveStackTrace(stack_id, frames, num_frames);
142 E : EXPECT_NE(s1, s3);
143 E : EXPECT_EQ(20u, s1->max_num_frames());
144 E : EXPECT_GT(sizeof(StackCapture), s1->Size());
145 E : }
146 :
147 E : TEST(StackCaptureCacheTest, GetCompressionRatio) {
148 E : AsanLogger logger;
149 E : TestStackCaptureCache cache(&logger);
150 :
151 E : ULONG stack_id = 0;
152 E : void* frames[StackCapture::kMaxNumFrames] = { 0 };
153 E : size_t num_frames = 0;
154 :
155 E : ASSERT_NEAR(1.0, cache.GetCompressionRatio(), 0.001);
156 :
157 : // Insert 4 identical stack frames.
158 E : for (int i = 0; i < 4; ++i) {
159 : num_frames = ::CaptureStackBackTrace(
160 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
161 E : ASSERT_TRUE(cache.SaveStackTrace(stack_id, frames, num_frames) != NULL);
162 E : }
163 :
164 : // There should now be a compression ration of 25%.
165 E : ASSERT_NEAR(0.25, cache.GetCompressionRatio(), 0.001);
166 :
167 : // Insert a new unique stack frame. Taking the ratio to 40%.
168 : num_frames = ::CaptureStackBackTrace(
169 E : 0, StackCapture::kMaxNumFrames, frames, &stack_id);
170 E : ASSERT_TRUE(cache.SaveStackTrace(stack_id, frames, num_frames) != NULL);
171 E : ASSERT_NEAR(0.40, cache.GetCompressionRatio(), 0.001);
172 E : }
173 :
174 E : TEST(StackCaptureCacheTest, MaxNumFrames) {
175 E : AsanLogger logger;
176 E : TestStackCaptureCache cache(&logger);
177 E : size_t max_num_frames = cache.max_num_frames() + 1;
178 E : cache.set_max_num_frames(max_num_frames);
179 E : ASSERT_EQ(max_num_frames, cache.max_num_frames());
180 E : }
181 :
182 : } // namespace asan
183 : } // namespace agent
|