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/logging.h"
18 : #include "base/stringprintf.h"
19 : #include "syzygy/agent/asan/asan_logger.h"
20 : #include "syzygy/agent/asan/stack_capture.h"
21 :
22 : namespace agent {
23 : namespace asan {
24 :
25 : size_t StackCaptureCache::compression_reporting_period_ =
26 : StackCaptureCache::kDefaultCompressionReportingPeriod;
27 :
28 E : StackCaptureCache::CachePage::~CachePage() {
29 E : if (next_page_ != NULL)
30 i : delete next_page_;
31 E : }
32 :
33 : StackCapture* StackCaptureCache::CachePage::GetNextStackCapture(
34 E : size_t max_num_frames) {
35 E : size_t size = StackCapture::GetSize(max_num_frames);
36 E : if (bytes_used_ + size > kCachePageSize)
37 E : return NULL;
38 :
39 : // Use placement new.
40 E : StackCapture* stack = new(data_ + bytes_used_) StackCapture(max_num_frames);
41 E : bytes_used_ += size;
42 :
43 E : return stack;
44 E : }
45 :
46 : void StackCaptureCache::CachePage::ReleaseStackCapture(
47 E : StackCapture* stack_capture) {
48 E : DCHECK(stack_capture != NULL);
49 :
50 E : uint8* stack = reinterpret_cast<uint8*>(stack_capture);
51 E : size_t size = stack_capture->Size();
52 E : DCHECK_EQ(data_ + bytes_used_, stack + size);
53 E : bytes_used_ -= size;
54 E : }
55 :
56 : StackCaptureCache::StackCaptureCache(AsanLogger* logger)
57 : : logger_(logger),
58 : max_num_frames_(StackCapture::kMaxNumFrames),
59 : current_page_(new CachePage(NULL)),
60 : total_allocations_(0),
61 E : cached_allocations_(0) {
62 E : CHECK(current_page_ != NULL);
63 E : DCHECK(logger_ != NULL);
64 E : }
65 :
66 : StackCaptureCache::StackCaptureCache(AsanLogger* logger, size_t max_num_frames)
67 : : logger_(logger),
68 : max_num_frames_(0),
69 : current_page_(new CachePage(NULL)),
70 : total_allocations_(0),
71 E : cached_allocations_(0) {
72 E : CHECK(current_page_ != NULL);
73 E : DCHECK(logger_ != NULL);
74 E : DCHECK_LT(0u, max_num_frames);
75 : max_num_frames_ = static_cast<uint8>(
76 E : std::min(max_num_frames, StackCapture::kMaxNumFrames));
77 E : }
78 :
79 E : StackCaptureCache::~StackCaptureCache() {
80 E : if (current_page_ != NULL)
81 E : delete current_page_;
82 E : }
83 :
84 : const StackCapture* StackCaptureCache::SaveStackTrace(
85 E : StackId stack_id, const void* const* frames, size_t num_frames) {
86 E : DCHECK(frames != NULL);
87 E : DCHECK(num_frames != 0);
88 E : DCHECK(current_page_ != NULL);
89 :
90 E : bool must_log_ratio = false;
91 E : double compression_ratio = 1.0;
92 E : const StackCapture* stack_trace = NULL;
93 :
94 : {
95 : // Get or insert the current stack trace while under the lock.
96 E : base::AutoLock auto_lock(lock_);
97 :
98 : // If the current page has been entirely consumed, allocate a new page
99 : // that links to the current page.
100 : StackCapture* unused_trace = current_page_->GetNextStackCapture(
101 E : max_num_frames_);
102 E : if (unused_trace == NULL) {
103 i : current_page_ = new CachePage(current_page_);
104 i : CHECK(current_page_ != NULL);
105 i : unused_trace = current_page_->GetNextStackCapture(max_num_frames_);
106 : }
107 E : DCHECK(unused_trace != NULL);
108 :
109 : // Attempt to insert it into the known stacks map.
110 E : unused_trace->set_stack_id(stack_id);
111 : std::pair<StackSet::const_iterator, bool> result = known_stacks_.insert(
112 E : unused_trace);
113 :
114 : // If the insertion was successful, then this capture has not already been
115 : // cached and we have to initialize the data.
116 E : if (result.second) {
117 E : DCHECK_EQ(unused_trace, *result.first);
118 E : unused_trace->InitFromBuffer(stack_id, frames, num_frames);
119 E : ++cached_allocations_;
120 E : } else {
121 : // If we didn't need the stack capture then return it.
122 E : current_page_->ReleaseStackCapture(unused_trace);
123 E : unused_trace = NULL;
124 : }
125 :
126 E : ++total_allocations_;
127 E : stack_trace = *result.first;
128 :
129 : if (compression_reporting_period_ != 0 &&
130 E : total_allocations_ % compression_reporting_period_ == 0) {
131 i : must_log_ratio = true;
132 i : compression_ratio = GetCompressionRatioUnlocked();
133 : }
134 E : }
135 :
136 E : DCHECK(stack_trace != NULL);
137 :
138 E : if (must_log_ratio)
139 i : LogCompressionRatioImpl(compression_ratio);
140 :
141 : // Return the stack trace pointer that is now in the cache.
142 E : return stack_trace;
143 E : }
144 :
145 : const StackCapture* StackCaptureCache::SaveStackTrace(
146 E : const StackCapture& stack_capture) {
147 : return SaveStackTrace(stack_capture.stack_id(),
148 : stack_capture.frames(),
149 E : stack_capture.num_frames());
150 E : }
151 :
152 E : void StackCaptureCache::LogCompressionRatio() const {
153 E : double compression_ratio = 0.0;
154 :
155 : {
156 E : base::AutoLock auto_lock(lock_);
157 E : compression_ratio = GetCompressionRatioUnlocked();
158 E : }
159 :
160 E : LogCompressionRatioImpl(compression_ratio);
161 E : }
162 :
163 E : double StackCaptureCache::GetCompressionRatioUnlocked() const {
164 E : lock_.AssertAcquired();
165 E : if (total_allocations_ == 0)
166 E : return 1.0;
167 E : return static_cast<double>(cached_allocations_) / total_allocations_;
168 E : }
169 :
170 E : void StackCaptureCache::LogCompressionRatioImpl(double ratio) const {
171 E : DCHECK_LE(0.0, ratio);
172 E : DCHECK_GE(1.0, ratio);
173 : logger_->Write(base::StringPrintf(
174 E : "Allocation stack cache compression: %.2f%%.\n", (1.0 - ratio) * 100.0));
175 E : }
176 :
177 : } // namespace asan
178 : } // namespace agent
|