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 ID associated with this stack trace.
99 E : StackId stack_id() const { return stack_id_; }
100 :
101 : // @returns the number of valid frame pointers in this stack trace capture.
102 E : size_t num_frames() const { return num_frames_; }
103 :
104 : // @returns the maximum number of valid frame pointers in this stack trace
105 : // capture.
106 E : size_t max_num_frames() const { return max_num_frames_; }
107 :
108 : // @returns a pointer to the captured stack frames, or NULL if no stack
109 : // frames have been captured.
110 E : const void* const* frames() const { return IsValid() ? frames_ : NULL; }
111 :
112 : // Sets the stack ID for a given trace.
113 : // @param The stack ID to set.
114 E : void set_stack_id(StackId stack_id) { stack_id_ = stack_id; }
115 :
116 : // Set the number of bottom frames to skip per stack trace. This is needed to
117 : // be able to improve the stack cache compression in Chrome's unittests where
118 : // the bottom of the stack traces is different for each test case.
119 : // @param bottom_frames_to_skip The number of bottom frames to skip.
120 E : static void set_bottom_frames_to_skip(size_t bottom_frames_to_skip) {
121 E : CHECK_LT(bottom_frames_to_skip, kMaxNumFrames);
122 E : bottom_frames_to_skip_ = bottom_frames_to_skip;
123 E : }
124 :
125 : // Get the number of bottom frames to skip per stack trace.
126 E : static size_t bottom_frames_to_skip() { return bottom_frames_to_skip_; }
127 :
128 : // Initializes a stack trace from an array of frame pointers, a count and
129 : // a StackId (such as returned by ::CaptureStackBackTrace).
130 : // @param stack_id The ID of the stack back trace.
131 : // @param frames an array of frame pointers.
132 : // @param num_frames the number of valid frame pointers in @frames. Note
133 : // that at most kMaxNumFrames frame pointers will be copied to this
134 : // stack trace capture.
135 : void InitFromBuffer(StackId stack_id,
136 : const void* const* frames,
137 : size_t num_frames);
138 :
139 : // Initializes a stack trace using ::CaptureStackBackTrace. This is inlined so
140 : // that it doesn't further pollute the stack trace, but rather makes it
141 : // reflect the actual point of the call.
142 E : __forceinline void InitFromStack() {
143 : num_frames_ = ::CaptureStackBackTrace(
144 E : 0, max_num_frames_, frames_, NULL);
145 E : if (num_frames_ > bottom_frames_to_skip_)
146 E : num_frames_ -= bottom_frames_to_skip_;
147 E : else
148 i : num_frames_ = 1;
149 E : stack_id_ = ComputeStackTraceHash(frames_, num_frames_);
150 E : }
151 :
152 : // The hash comparison functor for use with MSDN's stdext::hash_set.
153 : struct HashCompare {
154 : static const size_t bucket_size = 4;
155 : static const size_t min_buckets = 8;
156 : // Calculates a hash value for the given stack_capture.
157 : size_t operator()(const StackCapture* stack_capture) const;
158 : // Value comparison operator.
159 : bool operator()(const StackCapture* stack_capture1,
160 : const StackCapture* stack_capture2) const;
161 : };
162 :
163 : // Computes the hash of a stack trace using relative addresses of each stack
164 : // frame.
165 : // @returns the relative hash of this stack trace.
166 : StackId ComputeRelativeStackId();
167 :
168 : protected:
169 : // Don't skip any frames by default.
170 : static const size_t kDefaultBottomFramesToSkip_ = 0;
171 :
172 : // The number of bottom frames to skip on the stack traces.
173 : static size_t bottom_frames_to_skip_;
174 :
175 : // The unique ID of this hash. This is used for storing the hash in the set.
176 : StackId stack_id_;
177 :
178 : // The number of valid frames in this stack trace capture, and the maximum
179 : // number it can represent. We use uint8s here because we're limited to
180 : // kMaxNumFrames by the OS machinery and want this data structure to be as
181 : // compact as possible.
182 : uint8 num_frames_;
183 : uint8 max_num_frames_;
184 :
185 : // The reference count for this stack capture. We use saturation arithmetic
186 : // and something that is referenced 2^16 - 1 times will stay at that reference
187 : // count and never be removed from the stack cache.
188 : RefCount ref_count_;
189 :
190 : // The array or frame pointers comprising this stack trace capture.
191 : // This is a runtime dynamic array whose actual length is max_num_frames_, but
192 : // we use the maximum length here so that other users of StackCapture can
193 : // capture full stack traces if they so desire.
194 : // NOTE: This must be the last member of the class.
195 : void* frames_[kMaxNumFrames];
196 :
197 : private:
198 : DISALLOW_COPY_AND_ASSIGN(StackCapture);
199 : };
200 :
201 : } // namespace asan
202 : } // namespace agent
203 :
204 : #endif // SYZYGY_AGENT_ASAN_STACK_CAPTURE_H_
|