/src/mozilla-central/toolkit/components/telemetry/other/CombinedStacks.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "CombinedStacks.h" |
8 | | #include "mozilla/HangAnnotations.h" |
9 | | #include "jsapi.h" |
10 | | |
11 | | namespace mozilla { |
12 | | namespace Telemetry { |
13 | | |
14 | | // The maximum number of chrome hangs stacks that we're keeping. |
15 | | const size_t kMaxChromeStacksKept = 50; |
16 | | |
17 | | CombinedStacks::CombinedStacks() |
18 | | : CombinedStacks(kMaxChromeStacksKept) |
19 | 6 | {} |
20 | | |
21 | | CombinedStacks::CombinedStacks(size_t aMaxStacksCount) |
22 | | : mNextIndex(0) |
23 | | , mMaxStacksCount(aMaxStacksCount) |
24 | 6 | {} |
25 | | |
26 | | size_t |
27 | 0 | CombinedStacks::GetModuleCount() const { |
28 | 0 | return mModules.size(); |
29 | 0 | } |
30 | | |
31 | | const Telemetry::ProcessedStack::Module& |
32 | 0 | CombinedStacks::GetModule(unsigned aIndex) const { |
33 | 0 | return mModules[aIndex]; |
34 | 0 | } |
35 | | |
36 | | size_t |
37 | 0 | CombinedStacks::AddStack(const Telemetry::ProcessedStack& aStack) { |
38 | 0 | size_t index = mNextIndex; |
39 | 0 | // Advance the indices of the circular queue holding the stacks. |
40 | 0 | mNextIndex = (mNextIndex + 1) % mMaxStacksCount; |
41 | 0 | // Grow the vector up to the maximum size, if needed. |
42 | 0 | if (mStacks.size() < mMaxStacksCount) { |
43 | 0 | mStacks.resize(mStacks.size() + 1); |
44 | 0 | } |
45 | 0 | // Get a reference to the location holding the new stack. |
46 | 0 | CombinedStacks::Stack& adjustedStack = mStacks[index]; |
47 | 0 | // If we're using an old stack to hold aStack, clear it. |
48 | 0 | adjustedStack.clear(); |
49 | 0 |
|
50 | 0 | size_t stackSize = aStack.GetStackSize(); |
51 | 0 | for (size_t i = 0; i < stackSize; ++i) { |
52 | 0 | const Telemetry::ProcessedStack::Frame& frame = aStack.GetFrame(i); |
53 | 0 | uint16_t modIndex; |
54 | 0 | if (frame.mModIndex == std::numeric_limits<uint16_t>::max()) { |
55 | 0 | modIndex = frame.mModIndex; |
56 | 0 | } else { |
57 | 0 | const Telemetry::ProcessedStack::Module& module = |
58 | 0 | aStack.GetModule(frame.mModIndex); |
59 | 0 | std::vector<Telemetry::ProcessedStack::Module>::iterator modIterator = |
60 | 0 | std::find(mModules.begin(), mModules.end(), module); |
61 | 0 | if (modIterator == mModules.end()) { |
62 | 0 | mModules.push_back(module); |
63 | 0 | modIndex = mModules.size() - 1; |
64 | 0 | } else { |
65 | 0 | modIndex = modIterator - mModules.begin(); |
66 | 0 | } |
67 | 0 | } |
68 | 0 | Telemetry::ProcessedStack::Frame adjustedFrame = { frame.mOffset, modIndex }; |
69 | 0 | adjustedStack.push_back(adjustedFrame); |
70 | 0 | } |
71 | 0 | return index; |
72 | 0 | } |
73 | | |
74 | | const CombinedStacks::Stack& |
75 | 0 | CombinedStacks::GetStack(unsigned aIndex) const { |
76 | 0 | return mStacks[aIndex]; |
77 | 0 | } |
78 | | |
79 | | size_t |
80 | 0 | CombinedStacks::GetStackCount() const { |
81 | 0 | return mStacks.size(); |
82 | 0 | } |
83 | | |
84 | | size_t |
85 | 0 | CombinedStacks::SizeOfExcludingThis() const { |
86 | 0 | // This is a crude approximation. We would like to do something like |
87 | 0 | // aMallocSizeOf(&mModules[0]), but on linux aMallocSizeOf will call |
88 | 0 | // malloc_usable_size which is only safe on the pointers returned by malloc. |
89 | 0 | // While it works on current libstdc++, it is better to be safe and not assume |
90 | 0 | // that &vec[0] points to one. We could use a custom allocator, but |
91 | 0 | // it doesn't seem worth it. |
92 | 0 | size_t n = 0; |
93 | 0 | n += mModules.capacity() * sizeof(Telemetry::ProcessedStack::Module); |
94 | 0 | n += mStacks.capacity() * sizeof(Stack); |
95 | 0 | for (const auto & s : mStacks) { |
96 | 0 | n += s.capacity() * sizeof(Telemetry::ProcessedStack::Frame); |
97 | 0 | } |
98 | 0 | return n; |
99 | 0 | } |
100 | | |
101 | | void |
102 | 0 | CombinedStacks::RemoveStack(unsigned aIndex) { |
103 | 0 | MOZ_ASSERT(aIndex < mStacks.size()); |
104 | 0 |
|
105 | 0 | mStacks.erase(mStacks.begin() + aIndex); |
106 | 0 |
|
107 | 0 | if (aIndex < mNextIndex) { |
108 | 0 | if (mNextIndex == 0) { |
109 | 0 | mNextIndex = mStacks.size(); |
110 | 0 | } else { |
111 | 0 | mNextIndex--; |
112 | 0 | } |
113 | 0 | } |
114 | 0 |
|
115 | 0 | if (mNextIndex > mStacks.size()) { |
116 | 0 | mNextIndex = mStacks.size(); |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | | #if defined(MOZ_GECKO_PROFILER) |
121 | | void |
122 | 0 | CombinedStacks::Clear() { |
123 | 0 | mNextIndex = 0; |
124 | 0 | mStacks.clear(); |
125 | 0 | mModules.clear(); |
126 | 0 | } |
127 | | #endif |
128 | | |
129 | | JSObject * |
130 | 0 | CreateJSStackObject(JSContext *cx, const CombinedStacks &stacks) { |
131 | 0 | JS::Rooted<JSObject*> ret(cx, JS_NewPlainObject(cx)); |
132 | 0 | if (!ret) { |
133 | 0 | return nullptr; |
134 | 0 | } |
135 | 0 | |
136 | 0 | JS::Rooted<JSObject*> moduleArray(cx, JS_NewArrayObject(cx, 0)); |
137 | 0 | if (!moduleArray) { |
138 | 0 | return nullptr; |
139 | 0 | } |
140 | 0 | bool ok = JS_DefineProperty(cx, ret, "memoryMap", moduleArray, |
141 | 0 | JSPROP_ENUMERATE); |
142 | 0 | if (!ok) { |
143 | 0 | return nullptr; |
144 | 0 | } |
145 | 0 | |
146 | 0 | const size_t moduleCount = stacks.GetModuleCount(); |
147 | 0 | for (size_t moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) { |
148 | 0 | // Current module |
149 | 0 | const Telemetry::ProcessedStack::Module& module = |
150 | 0 | stacks.GetModule(moduleIndex); |
151 | 0 |
|
152 | 0 | JS::Rooted<JSObject*> moduleInfoArray(cx, JS_NewArrayObject(cx, 0)); |
153 | 0 | if (!moduleInfoArray) { |
154 | 0 | return nullptr; |
155 | 0 | } |
156 | 0 | if (!JS_DefineElement(cx, moduleArray, moduleIndex, moduleInfoArray, |
157 | 0 | JSPROP_ENUMERATE)) { |
158 | 0 | return nullptr; |
159 | 0 | } |
160 | 0 | |
161 | 0 | unsigned index = 0; |
162 | 0 |
|
163 | 0 | // Module name |
164 | 0 | JS::Rooted<JSString*> str(cx, JS_NewUCStringCopyZ(cx, module.mName.get())); |
165 | 0 | if (!str || !JS_DefineElement(cx, moduleInfoArray, index++, str, JSPROP_ENUMERATE)) { |
166 | 0 | return nullptr; |
167 | 0 | } |
168 | 0 | |
169 | 0 | // Module breakpad identifier |
170 | 0 | JS::Rooted<JSString*> id(cx, JS_NewStringCopyZ(cx, module.mBreakpadId.get())); |
171 | 0 | if (!id || !JS_DefineElement(cx, moduleInfoArray, index, id, JSPROP_ENUMERATE)) { |
172 | 0 | return nullptr; |
173 | 0 | } |
174 | 0 | } |
175 | 0 |
|
176 | 0 | JS::Rooted<JSObject*> reportArray(cx, JS_NewArrayObject(cx, 0)); |
177 | 0 | if (!reportArray) { |
178 | 0 | return nullptr; |
179 | 0 | } |
180 | 0 | ok = JS_DefineProperty(cx, ret, "stacks", reportArray, JSPROP_ENUMERATE); |
181 | 0 | if (!ok) { |
182 | 0 | return nullptr; |
183 | 0 | } |
184 | 0 | |
185 | 0 | const size_t length = stacks.GetStackCount(); |
186 | 0 | for (size_t i = 0; i < length; ++i) { |
187 | 0 | // Represent call stack PCs as (module index, offset) pairs. |
188 | 0 | JS::Rooted<JSObject*> pcArray(cx, JS_NewArrayObject(cx, 0)); |
189 | 0 | if (!pcArray) { |
190 | 0 | return nullptr; |
191 | 0 | } |
192 | 0 | |
193 | 0 | if (!JS_DefineElement(cx, reportArray, i, pcArray, JSPROP_ENUMERATE)) { |
194 | 0 | return nullptr; |
195 | 0 | } |
196 | 0 | |
197 | 0 | const CombinedStacks::Stack& stack = stacks.GetStack(i); |
198 | 0 | const uint32_t pcCount = stack.size(); |
199 | 0 | for (size_t pcIndex = 0; pcIndex < pcCount; ++pcIndex) { |
200 | 0 | const Telemetry::ProcessedStack::Frame& frame = stack[pcIndex]; |
201 | 0 | JS::Rooted<JSObject*> framePair(cx, JS_NewArrayObject(cx, 0)); |
202 | 0 | if (!framePair) { |
203 | 0 | return nullptr; |
204 | 0 | } |
205 | 0 | int modIndex = (std::numeric_limits<uint16_t>::max() == frame.mModIndex) ? |
206 | 0 | -1 : frame.mModIndex; |
207 | 0 | if (!JS_DefineElement(cx, framePair, 0, modIndex, JSPROP_ENUMERATE)) { |
208 | 0 | return nullptr; |
209 | 0 | } |
210 | 0 | if (!JS_DefineElement(cx, framePair, 1, static_cast<double>(frame.mOffset), |
211 | 0 | JSPROP_ENUMERATE)) { |
212 | 0 | return nullptr; |
213 | 0 | } |
214 | 0 | if (!JS_DefineElement(cx, pcArray, pcIndex, framePair, JSPROP_ENUMERATE)) { |
215 | 0 | return nullptr; |
216 | 0 | } |
217 | 0 | } |
218 | 0 | } |
219 | 0 |
|
220 | 0 | return ret; |
221 | 0 | } |
222 | | |
223 | | } // namespace Telemetry |
224 | | } // namespace mozilla |