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 : #include "syzygy/agent/common/stack_capture.h"
16 :
17 : #include <algorithm>
18 :
19 : #include "base/logging.h"
20 : #include "base/process/memory.h"
21 :
22 : // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
23 : extern "C" IMAGE_DOS_HEADER __ImageBase;
24 :
25 : namespace agent {
26 : namespace common {
27 :
28 E : uint32 ComputeStackTraceHash(void** stack_trace, uint8 stack_depth) {
29 E : uint32 hash_value = 0;
30 E : for (uint8 i = 0; i < stack_depth; ++i) {
31 E : hash_value += reinterpret_cast<uint32>(stack_trace[i]);
32 E : }
33 E : return hash_value;
34 E : }
35 :
36 : // The number of bottom frames to skip per stack trace.
37 : size_t StackCapture::bottom_frames_to_skip_ =
38 E : ::common::kDefaultBottomFramesToSkip;
39 :
40 E : size_t StackCapture::GetSize(size_t max_num_frames) {
41 E : DCHECK_LT(0u, max_num_frames);
42 E : max_num_frames = std::min(max_num_frames, kMaxNumFrames);
43 E : return offsetof(StackCapture, frames_) + max_num_frames * sizeof(void*);
44 E : }
45 :
46 i : size_t StackCapture::GetMaxNumFrames(size_t bytes) {
47 i : if (bytes < offsetof(StackCapture, frames_))
48 i : return 0;
49 i : bytes -= offsetof(StackCapture, frames_);
50 i : bytes /= sizeof(void*);
51 i : return bytes;
52 i : }
53 :
54 E : void StackCapture::AddRef() {
55 E : if (RefCountIsSaturated())
56 i : return;
57 E : DCHECK_GT(kMaxRefCount, ref_count_);
58 E : ++ref_count_;
59 E : }
60 :
61 E : void StackCapture::RemoveRef() {
62 E : DCHECK_LT(0u, ref_count_);
63 E : if (RefCountIsSaturated())
64 E : return;
65 E : --ref_count_;
66 E : }
67 :
68 E : void StackCapture::Init() {
69 E : bottom_frames_to_skip_ = ::common::kDefaultBottomFramesToSkip;
70 E : }
71 :
72 : void StackCapture::InitFromBuffer(StackId stack_id,
73 : const void* const* frames,
74 E : size_t num_frames) {
75 E : DCHECK(frames != NULL);
76 E : DCHECK_LT(0U, num_frames);
77 E : stack_id_ = stack_id;
78 E : num_frames_ = std::min<uint8>(num_frames, max_num_frames_);
79 E : ::memcpy(frames_, frames, num_frames_ * sizeof(void*));
80 E : }
81 :
82 E : StackCapture::StackId StackCapture::ComputeRelativeStackId() {
83 : // We want to ignore the frames relative to our module to be able to get the
84 : // same trace id even if we update our runtime.
85 E : HANDLE asan_handle = reinterpret_cast<HANDLE>(&__ImageBase);
86 E : DCHECK(asan_handle != NULL);
87 :
88 : // Use a simple hash with reasonable properties.
89 : // This is effectively the same as base::SuperFastHash, but we can't use it
90 : // as there's no API for updating an in-progress hash.
91 : // http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time
92 :
93 E : StackId stack_id = num_frames_;
94 E : for (size_t i = 0; i < num_frames_; ++i) {
95 : // NULL stack frames may be returned from ::CaptureStackBackTrace.
96 : // This has been observed on Windows 8.
97 E : if (frames_[i] == NULL)
98 i : continue;
99 E : HMODULE module = base::GetModuleFromAddress(frames_[i]);
100 E : if (module == NULL || module == asan_handle)
101 E : continue;
102 : stack_id += reinterpret_cast<size_t>(frames_[i]) -
103 E : reinterpret_cast<size_t>(module);
104 E : stack_id += stack_id << 10;
105 E : stack_id ^= stack_id >> 6;
106 E : }
107 :
108 E : stack_id += stack_id << 3;
109 E : stack_id ^= stack_id >> 11;
110 E : stack_id += stack_id << 15;
111 :
112 E : return stack_id;
113 E : }
114 :
115 : size_t StackCapture::HashCompare::operator()(
116 E : const StackCapture* stack_capture) const {
117 E : DCHECK(stack_capture != NULL);
118 : // We're assuming that the StackId and size_t have the same size, so let's
119 : // make sure that's the case.
120 : COMPILE_ASSERT(sizeof(StackId) == sizeof(size_t),
121 : stack_id_and_size_t_not_same_size);
122 E : return stack_capture->stack_id_;
123 E : }
124 :
125 : bool StackCapture::HashCompare::operator()(
126 : const StackCapture* stack_capture1,
127 E : const StackCapture* stack_capture2) const {
128 E : DCHECK(stack_capture1 != NULL);
129 E : DCHECK(stack_capture2 != NULL);
130 E : return stack_capture1->stack_id_ < stack_capture2->stack_id_;
131 E : }
132 :
133 : } // namespace common
134 : } // namespace agent
|