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/. */
7 #include "CombinedStacks.h"
10 #include "js/Array.h" // JS::NewArrayObject
11 #include "js/PropertyAndElement.h" // JS_DefineElement, JS_DefineProperty
12 #include "js/String.h"
14 namespace mozilla::Telemetry
{
16 // The maximum number of chrome hangs stacks that we're keeping.
17 const size_t kMaxChromeStacksKept
= 50;
19 CombinedStacks::CombinedStacks() : CombinedStacks(kMaxChromeStacksKept
) {}
21 CombinedStacks::CombinedStacks(size_t aMaxStacksCount
)
22 : mNextIndex(0), mMaxStacksCount(aMaxStacksCount
) {}
24 size_t CombinedStacks::GetMaxStacksCount() const { return mMaxStacksCount
; }
25 size_t CombinedStacks::GetModuleCount() const { return mModules
.size(); }
27 const Telemetry::ProcessedStack::Module
& CombinedStacks::GetModule(
28 unsigned aIndex
) const {
29 return mModules
[aIndex
];
32 void CombinedStacks::AddFrame(
33 size_t aStackIndex
, const ProcessedStack::Frame
& aFrame
,
34 const std::function
<const ProcessedStack::Module
&(int)>& aModuleGetter
) {
36 if (aFrame
.mModIndex
== std::numeric_limits
<uint16_t>::max()) {
37 modIndex
= aFrame
.mModIndex
;
39 const ProcessedStack::Module
& module
= aModuleGetter(aFrame
.mModIndex
);
40 auto modIterator
= std::find(mModules
.begin(), mModules
.end(), module
);
41 if (modIterator
== mModules
.end()) {
42 mModules
.push_back(module
);
43 modIndex
= mModules
.size() - 1;
45 modIndex
= modIterator
- mModules
.begin();
48 mStacks
[aStackIndex
].push_back(
49 ProcessedStack::Frame
{aFrame
.mOffset
, modIndex
});
52 size_t CombinedStacks::AddStack(const Telemetry::ProcessedStack
& aStack
) {
53 size_t index
= mNextIndex
;
54 // Advance the indices of the circular queue holding the stacks.
55 mNextIndex
= (mNextIndex
+ 1) % mMaxStacksCount
;
56 // Grow the vector up to the maximum size, if needed.
57 if (mStacks
.size() < mMaxStacksCount
) {
58 mStacks
.resize(mStacks
.size() + 1);
61 // Clear the old stack before set.
62 mStacks
[index
].clear();
64 size_t stackSize
= aStack
.GetStackSize();
65 for (size_t i
= 0; i
< stackSize
; ++i
) {
66 // Need to specify a return type in the following lambda,
67 // otherwise it's incorrectly deduced to be a non-reference type.
68 AddFrame(index
, aStack
.GetFrame(i
),
69 [&aStack
](int aIdx
) -> const ProcessedStack::Module
& {
70 return aStack
.GetModule(aIdx
);
76 void CombinedStacks::AddStacks(const CombinedStacks
& aStacks
) {
78 std::min(mStacks
.size() + aStacks
.GetStackCount(), mMaxStacksCount
));
80 for (const auto& stack
: aStacks
.mStacks
) {
81 size_t index
= mNextIndex
;
82 // Advance the indices of the circular queue holding the stacks.
83 mNextIndex
= (mNextIndex
+ 1) % mMaxStacksCount
;
85 // Clear the old stack before set.
86 mStacks
[index
].clear();
88 for (const auto& frame
: stack
) {
89 // Need to specify a return type in the following lambda,
90 // otherwise it's incorrectly deduced to be a non-reference type.
91 AddFrame(index
, frame
,
92 [&aStacks
](int aIdx
) -> const ProcessedStack::Module
& {
93 return aStacks
.mModules
[aIdx
];
99 const CombinedStacks::Stack
& CombinedStacks::GetStack(unsigned aIndex
) const {
100 return mStacks
[aIndex
];
103 size_t CombinedStacks::GetStackCount() const { return mStacks
.size(); }
105 size_t CombinedStacks::SizeOfExcludingThis() const {
106 // This is a crude approximation. We would like to do something like
107 // aMallocSizeOf(&mModules[0]), but on linux aMallocSizeOf will call
108 // malloc_usable_size which is only safe on the pointers returned by malloc.
109 // While it works on current libstdc++, it is better to be safe and not assume
110 // that &vec[0] points to one. We could use a custom allocator, but
111 // it doesn't seem worth it.
113 n
+= mModules
.capacity() * sizeof(Telemetry::ProcessedStack::Module
);
114 n
+= mStacks
.capacity() * sizeof(Stack
);
115 for (const auto& s
: mStacks
) {
116 n
+= s
.capacity() * sizeof(Telemetry::ProcessedStack::Frame
);
121 void CombinedStacks::RemoveStack(unsigned aIndex
) {
122 MOZ_ASSERT(aIndex
< mStacks
.size());
124 mStacks
.erase(mStacks
.begin() + aIndex
);
126 if (aIndex
< mNextIndex
) {
127 if (mNextIndex
== 0) {
128 mNextIndex
= mStacks
.size();
134 if (mNextIndex
> mStacks
.size()) {
135 mNextIndex
= mStacks
.size();
139 void CombinedStacks::Swap(CombinedStacks
& aOther
) {
140 mModules
.swap(aOther
.mModules
);
141 mStacks
.swap(aOther
.mStacks
);
143 size_t nextIndex
= aOther
.mNextIndex
;
144 aOther
.mNextIndex
= mNextIndex
;
145 mNextIndex
= nextIndex
;
147 size_t maxStacksCount
= aOther
.mMaxStacksCount
;
148 aOther
.mMaxStacksCount
= mMaxStacksCount
;
149 mMaxStacksCount
= maxStacksCount
;
152 void CombinedStacks::Clear() {
158 JSObject
* CreateJSStackObject(JSContext
* cx
, const CombinedStacks
& stacks
) {
159 JS::Rooted
<JSObject
*> ret(cx
, JS_NewPlainObject(cx
));
164 JS::Rooted
<JSObject
*> moduleArray(cx
, JS::NewArrayObject(cx
, 0));
169 JS_DefineProperty(cx
, ret
, "memoryMap", moduleArray
, JSPROP_ENUMERATE
);
174 const size_t moduleCount
= stacks
.GetModuleCount();
175 for (size_t moduleIndex
= 0; moduleIndex
< moduleCount
; ++moduleIndex
) {
177 const Telemetry::ProcessedStack::Module
& module
=
178 stacks
.GetModule(moduleIndex
);
180 JS::Rooted
<JSObject
*> moduleInfoArray(cx
, JS::NewArrayObject(cx
, 0));
181 if (!moduleInfoArray
) {
184 if (!JS_DefineElement(cx
, moduleArray
, moduleIndex
, moduleInfoArray
,
192 JS::Rooted
<JSString
*> str(cx
, JS_NewUCStringCopyZ(cx
, module
.mName
.get()));
193 if (!str
|| !JS_DefineElement(cx
, moduleInfoArray
, index
++, str
,
198 // Module breakpad identifier
199 JS::Rooted
<JSString
*> id(cx
,
200 JS_NewStringCopyZ(cx
, module
.mBreakpadId
.get()));
202 !JS_DefineElement(cx
, moduleInfoArray
, index
, id
, JSPROP_ENUMERATE
)) {
207 JS::Rooted
<JSObject
*> reportArray(cx
, JS::NewArrayObject(cx
, 0));
211 ok
= JS_DefineProperty(cx
, ret
, "stacks", reportArray
, JSPROP_ENUMERATE
);
216 const size_t length
= stacks
.GetStackCount();
217 for (size_t i
= 0; i
< length
; ++i
) {
218 // Represent call stack PCs as (module index, offset) pairs.
219 JS::Rooted
<JSObject
*> pcArray(cx
, JS::NewArrayObject(cx
, 0));
224 if (!JS_DefineElement(cx
, reportArray
, i
, pcArray
, JSPROP_ENUMERATE
)) {
228 const CombinedStacks::Stack
& stack
= stacks
.GetStack(i
);
229 const uint32_t pcCount
= stack
.size();
230 for (size_t pcIndex
= 0; pcIndex
< pcCount
; ++pcIndex
) {
231 const Telemetry::ProcessedStack::Frame
& frame
= stack
[pcIndex
];
232 JS::Rooted
<JSObject
*> framePair(cx
, JS::NewArrayObject(cx
, 0));
236 int modIndex
= (std::numeric_limits
<uint16_t>::max() == frame
.mModIndex
)
239 if (!JS_DefineElement(cx
, framePair
, 0, modIndex
, JSPROP_ENUMERATE
)) {
242 if (!JS_DefineElement(cx
, framePair
, 1,
243 static_cast<double>(frame
.mOffset
),
247 if (!JS_DefineElement(cx
, pcArray
, pcIndex
, framePair
,
257 } // namespace mozilla::Telemetry