Line data Source code
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 "HangAnnotations.h"
9 : #include "mozilla/HangAnnotations.h"
10 : #include "jsapi.h"
11 :
12 : namespace mozilla {
13 : namespace Telemetry {
14 :
15 : // The maximum number of chrome hangs stacks that we're keeping.
16 : const size_t kMaxChromeStacksKept = 50;
17 :
18 9 : CombinedStacks::CombinedStacks()
19 9 : : CombinedStacks(kMaxChromeStacksKept)
20 9 : {}
21 :
22 9 : CombinedStacks::CombinedStacks(size_t aMaxStacksCount)
23 : : mNextIndex(0)
24 9 : , mMaxStacksCount(aMaxStacksCount)
25 9 : {}
26 :
27 : size_t
28 0 : CombinedStacks::GetModuleCount() const {
29 0 : return mModules.size();
30 : }
31 :
32 : const Telemetry::ProcessedStack::Module&
33 0 : CombinedStacks::GetModule(unsigned aIndex) const {
34 0 : return mModules[aIndex];
35 : }
36 :
37 : size_t
38 0 : CombinedStacks::AddStack(const Telemetry::ProcessedStack& aStack) {
39 : // Advance the indices of the circular queue holding the stacks.
40 0 : size_t index = mNextIndex++ % mMaxStacksCount;
41 : // Grow the vector up to the maximum size, if needed.
42 0 : if (mStacks.size() < mMaxStacksCount) {
43 0 : mStacks.resize(mStacks.size() + 1);
44 : }
45 : // Get a reference to the location holding the new stack.
46 0 : CombinedStacks::Stack& adjustedStack = mStacks[index];
47 : // If we're using an old stack to hold aStack, clear it.
48 0 : adjustedStack.clear();
49 :
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 : uint16_t modIndex;
54 0 : if (frame.mModIndex == std::numeric_limits<uint16_t>::max()) {
55 0 : modIndex = frame.mModIndex;
56 : } else {
57 : const Telemetry::ProcessedStack::Module& module =
58 0 : aStack.GetModule(frame.mModIndex);
59 : 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 : } else {
65 0 : modIndex = modIterator - mModules.begin();
66 : }
67 : }
68 0 : Telemetry::ProcessedStack::Frame adjustedFrame = { frame.mOffset, modIndex };
69 0 : adjustedStack.push_back(adjustedFrame);
70 : }
71 0 : return index;
72 : }
73 :
74 : const CombinedStacks::Stack&
75 0 : CombinedStacks::GetStack(unsigned aIndex) const {
76 0 : return mStacks[aIndex];
77 : }
78 :
79 : size_t
80 0 : CombinedStacks::GetStackCount() const {
81 0 : return mStacks.size();
82 : }
83 :
84 : size_t
85 0 : CombinedStacks::SizeOfExcludingThis() const {
86 : // This is a crude approximation. We would like to do something like
87 : // aMallocSizeOf(&mModules[0]), but on linux aMallocSizeOf will call
88 : // malloc_usable_size which is only safe on the pointers returned by malloc.
89 : // While it works on current libstdc++, it is better to be safe and not assume
90 : // that &vec[0] points to one. We could use a custom allocator, but
91 : // 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 : }
98 0 : return n;
99 : }
100 :
101 : #if defined(MOZ_GECKO_PROFILER)
102 : void
103 0 : CombinedStacks::Clear() {
104 0 : mNextIndex = 0;
105 0 : mStacks.clear();
106 0 : mModules.clear();
107 0 : }
108 : #endif
109 :
110 : JSObject *
111 0 : CreateJSStackObject(JSContext *cx, const CombinedStacks &stacks) {
112 0 : JS::Rooted<JSObject*> ret(cx, JS_NewPlainObject(cx));
113 0 : if (!ret) {
114 0 : return nullptr;
115 : }
116 :
117 0 : JS::Rooted<JSObject*> moduleArray(cx, JS_NewArrayObject(cx, 0));
118 0 : if (!moduleArray) {
119 0 : return nullptr;
120 : }
121 0 : bool ok = JS_DefineProperty(cx, ret, "memoryMap", moduleArray,
122 0 : JSPROP_ENUMERATE);
123 0 : if (!ok) {
124 0 : return nullptr;
125 : }
126 :
127 0 : const size_t moduleCount = stacks.GetModuleCount();
128 0 : for (size_t moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
129 : // Current module
130 : const Telemetry::ProcessedStack::Module& module =
131 0 : stacks.GetModule(moduleIndex);
132 :
133 0 : JS::Rooted<JSObject*> moduleInfoArray(cx, JS_NewArrayObject(cx, 0));
134 0 : if (!moduleInfoArray) {
135 0 : return nullptr;
136 : }
137 0 : if (!JS_DefineElement(cx, moduleArray, moduleIndex, moduleInfoArray,
138 : JSPROP_ENUMERATE)) {
139 0 : return nullptr;
140 : }
141 :
142 0 : unsigned index = 0;
143 :
144 : // Module name
145 0 : JS::Rooted<JSString*> str(cx, JS_NewUCStringCopyZ(cx, module.mName.get()));
146 0 : if (!str || !JS_DefineElement(cx, moduleInfoArray, index++, str, JSPROP_ENUMERATE)) {
147 0 : return nullptr;
148 : }
149 :
150 : // Module breakpad identifier
151 0 : JS::Rooted<JSString*> id(cx, JS_NewStringCopyZ(cx, module.mBreakpadId.c_str()));
152 0 : if (!id || !JS_DefineElement(cx, moduleInfoArray, index++, id, JSPROP_ENUMERATE)) {
153 0 : return nullptr;
154 : }
155 : }
156 :
157 0 : JS::Rooted<JSObject*> reportArray(cx, JS_NewArrayObject(cx, 0));
158 0 : if (!reportArray) {
159 0 : return nullptr;
160 : }
161 0 : ok = JS_DefineProperty(cx, ret, "stacks", reportArray, JSPROP_ENUMERATE);
162 0 : if (!ok) {
163 0 : return nullptr;
164 : }
165 :
166 0 : const size_t length = stacks.GetStackCount();
167 0 : for (size_t i = 0; i < length; ++i) {
168 : // Represent call stack PCs as (module index, offset) pairs.
169 0 : JS::Rooted<JSObject*> pcArray(cx, JS_NewArrayObject(cx, 0));
170 0 : if (!pcArray) {
171 0 : return nullptr;
172 : }
173 :
174 0 : if (!JS_DefineElement(cx, reportArray, i, pcArray, JSPROP_ENUMERATE)) {
175 0 : return nullptr;
176 : }
177 :
178 0 : const CombinedStacks::Stack& stack = stacks.GetStack(i);
179 0 : const uint32_t pcCount = stack.size();
180 0 : for (size_t pcIndex = 0; pcIndex < pcCount; ++pcIndex) {
181 0 : const Telemetry::ProcessedStack::Frame& frame = stack[pcIndex];
182 0 : JS::Rooted<JSObject*> framePair(cx, JS_NewArrayObject(cx, 0));
183 0 : if (!framePair) {
184 0 : return nullptr;
185 : }
186 0 : int modIndex = (std::numeric_limits<uint16_t>::max() == frame.mModIndex) ?
187 0 : -1 : frame.mModIndex;
188 0 : if (!JS_DefineElement(cx, framePair, 0, modIndex, JSPROP_ENUMERATE)) {
189 0 : return nullptr;
190 : }
191 0 : if (!JS_DefineElement(cx, framePair, 1, static_cast<double>(frame.mOffset),
192 : JSPROP_ENUMERATE)) {
193 0 : return nullptr;
194 : }
195 0 : if (!JS_DefineElement(cx, pcArray, pcIndex, framePair, JSPROP_ENUMERATE)) {
196 0 : return nullptr;
197 : }
198 : }
199 : }
200 :
201 0 : return ret;
202 : }
203 :
204 : } // namespace Telemetry
205 : } // namespace mozilla
|