1 : // Copyright 2013 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 : // Declares a utility class for getting and storing quick and dirty stack
16 : // captures.
17 :
18 : #ifndef SYZYGY_AGENT_ASAN_STACK_CAPTURE_H_
19 : #define SYZYGY_AGENT_ASAN_STACK_CAPTURE_H_
20 :
21 : #include <windows.h>
22 :
23 : #include "base/logging.h"
24 :
25 : namespace agent {
26 : namespace asan {
27 :
28 : // Computes the hash of a given stack trace. The hash function is simply an add
29 : // of all the stack trace pointers.
30 : uint32 ComputeStackTraceHash(void** stack_trace, uint8 stack_depth);
31 :
32 : // A simple class for holding a stack trace capture.
33 : class StackCapture {
34 : public:
35 : // From http://msdn.microsoft.com/en-us/library/bb204633.aspx,
36 : // The maximum number of frames which CaptureStackBackTrace can be asked
37 : // to traverse must be less than 63, so set it to 62.
38 : static const size_t kMaxNumFrames = 62;
39 :
40 : // The type used for reference counting. We use saturation arithmetic, so it
41 : // will top out at kMaxRefCount.
42 : typedef uint16 RefCount;
43 : static const RefCount kMaxRefCount = -1;
44 :
45 : // This corresponds to the the type used by ::CaptureStackBackTrace's hash
46 : // for a stack-trace.
47 : typedef ULONG StackId;
48 :
49 : StackCapture()
50 : : ref_count_(0), stack_id_(0), num_frames_(0),
51 E : max_num_frames_(kMaxNumFrames) {
52 E : }
53 :
54 E : explicit StackCapture(size_t max_num_frames)
55 : : ref_count_(0), stack_id_(0), num_frames_(0), max_num_frames_(0) {
56 E : DCHECK_LT(0u, max_num_frames);
57 E : DCHECK_GE(kMaxNumFrames, max_num_frames);
58 E : max_num_frames_ = max_num_frames;
59 E : }
60 :
61 : // Static initialisation of StackCapture context.
62 : static void Init();
63 :
64 : // Calculate the size necessary to store a StackCapture with the given
65 : // number of stack frames.
66 : // @param max_num_frames The maximum number of stack frames the object needs
67 : // to be able to hold.
68 : // @returns the size of a StackCapture object with the given number of frames.
69 : static size_t GetSize(size_t max_num_frames);
70 :
71 : // Calculate the max number of frames that can be fit into a memory region of
72 : // the given size.
73 : // @param bytes The number of bytes to be used.
74 : // @returns the maxmimum number of frames that will fit in the provided number
75 : // of bytes.
76 : static size_t GetMaxNumFrames(size_t bytes);
77 :
78 : // @returns the size of this initialized StackCapture object.
79 E : size_t Size() const { return GetSize(max_num_frames_); }
80 :
81 : // @returns true if this stack trace capture contains valid frame pointers.
82 E : bool IsValid() const { return num_frames_ != 0; }
83 :
84 : // Increments the reference count of this stack capture.
85 : void AddRef();
86 :
87 : // Decrements the reference count of this stack capture.
88 : void RemoveRef();
89 :
90 : // @returns true if the reference count is saturated, false otherwise. A
91 : // saturated reference count means that further calls to AddRef and
92 : // RemoveRef will be nops, and HasNoRefs will always return false.
93 E : bool RefCountIsSaturated() const { return ref_count_ == kMaxRefCount; }
94 :
95 : // @returns true if this stack capture is not referenced, false otherwise.
96 E : bool HasNoRefs() const { return ref_count_ == 0; }
97 :
98 : // @returns the reference count for this stack capture.
99 E : RefCount ref_count() const { return ref_count_; }
100 :
101 : // @returns the ID associated with this stack trace.
102 E : StackId stack_id() const { return stack_id_; }
103 :
104 : // @returns the number of valid frame pointers in this stack trace capture.
105 E : size_t num_frames() const { return num_frames_; }
106 :
107 : // @returns the maximum number of valid frame pointers in this stack trace
108 : // capture.
109 E : size_t max_num_frames() const { return max_num_frames_; }
110 :
111 : // @returns a pointer to the stack frames array, or NULL if the array has a
112 : // size of 0.
113 E : const void* const* frames() const {
114 E : return max_num_frames_ != 0 ? frames_ : NULL;
115 E : }
116 :
117 : // Sets the stack ID for a given trace.
118 : // @param The stack ID to set.
119 E : void set_stack_id(StackId stack_id) { stack_id_ = stack_id; }
120 :
121 : // Set the number of bottom frames to skip per stack trace. This is needed to
122 : // be able to improve the stack cache compression in Chrome's unittests where
123 : // the bottom of the stack traces is different for each test case.
124 : // @param bottom_frames_to_skip The number of bottom frames to skip.
125 E : static void set_bottom_frames_to_skip(size_t bottom_frames_to_skip) {
126 E : CHECK_LT(bottom_frames_to_skip, kMaxNumFrames);
127 E : bottom_frames_to_skip_ = bottom_frames_to_skip;
128 E : }
129 :
130 : // Get the number of bottom frames to skip per stack trace.
131 E : static size_t bottom_frames_to_skip() { return bottom_frames_to_skip_; }
132 :
133 : // Initializes a stack trace from an array of frame pointers, a count and
134 : // a StackId (such as returned by ::CaptureStackBackTrace).
135 : // @param stack_id The ID of the stack back trace.
136 : // @param frames an array of frame pointers.
137 : // @param num_frames the number of valid frame pointers in @frames. Note
138 : // that at most kMaxNumFrames frame pointers will be copied to this
139 : // stack trace capture.
140 : void InitFromBuffer(StackId stack_id,
141 : const void* const* frames,
142 : size_t num_frames);
143 :
144 : // Initializes a stack trace using ::CaptureStackBackTrace. This is inlined so
145 : // that it doesn't further pollute the stack trace, but rather makes it
146 : // reflect the actual point of the call.
147 E : __forceinline void InitFromStack() {
148 : num_frames_ = ::CaptureStackBackTrace(
149 E : 0, max_num_frames_, frames_, NULL);
150 E : if (num_frames_ > bottom_frames_to_skip_)
151 E : num_frames_ -= bottom_frames_to_skip_;
152 E : else
153 i : num_frames_ = 1;
154 E : stack_id_ = ComputeStackTraceHash(frames_, num_frames_);
155 E : }
156 :
157 : // The hash comparison functor for use with MSDN's stdext::hash_set.
158 : struct HashCompare {
159 : static const size_t bucket_size = 4;
160 : static const size_t min_buckets = 8;
161 : // Calculates a hash value for the given stack_capture.
162 : size_t operator()(const StackCapture* stack_capture) const;
163 : // Value comparison operator.
164 : bool operator()(const StackCapture* stack_capture1,
165 : const StackCapture* stack_capture2) const;
166 : };
167 :
168 : // Computes the hash of a stack trace using relative addresses of each stack
169 : // frame.
170 : // @returns the relative hash of this stack trace.
171 : StackId ComputeRelativeStackId();
172 :
173 : protected:
174 : // Don't skip any frames by default.
175 : static const size_t kDefaultBottomFramesToSkip_ = 0;
176 :
177 : // The number of bottom frames to skip on the stack traces.
178 : static size_t bottom_frames_to_skip_;
179 :
180 : // The unique ID of this hash. This is used for storing the hash in the set.
181 : StackId stack_id_;
182 :
183 : // The number of valid frames in this stack trace capture, and the maximum
184 : // number it can represent. We use uint8s here because we're limited to
185 : // kMaxNumFrames by the OS machinery and want this data structure to be as
186 : // compact as possible.
187 : uint8 num_frames_;
188 : uint8 max_num_frames_;
189 :
190 : // The reference count for this stack capture. We use saturation arithmetic
191 : // and something that is referenced 2^16 - 1 times will stay at that reference
192 : // count and never be removed from the stack cache.
193 : RefCount ref_count_;
194 :
195 : // The array or frame pointers comprising this stack trace capture.
196 : // This is a runtime dynamic array whose actual length is max_num_frames_, but
197 : // we use the maximum length here so that other users of StackCapture can
198 : // capture full stack traces if they so desire.
199 : // NOTE: This must be the last member of the class.
200 : void* frames_[kMaxNumFrames];
201 :
202 : private:
203 : DISALLOW_COPY_AND_ASSIGN(StackCapture);
204 : };
205 :
206 : } // namespace asan
207 : } // namespace agent
208 :
209 : #endif // SYZYGY_AGENT_ASAN_STACK_CAPTURE_H_
|