-
Notifications
You must be signed in to change notification settings - Fork 54
/
KeyedStackCapturer.cpp
165 lines (138 loc) · 4.81 KB
/
KeyedStackCapturer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "KeyedStackCapturer.h"
#include "jsapi.h"
#include "mozilla/StackWalk.h"
#include "nsPrintfCString.h"
#include "ProcessedStack.h"
namespace {
/** Defines the size of the keyed stack dictionary. */
const uint8_t kMaxKeyLength = 50;
/** The maximum number of captured stacks that we're keeping. */
const size_t kMaxCapturedStacksKept = 50;
/**
* Checks if a single character of the key string is valid.
*
* @param aChar a character to validate.
* @return True, if the char is valid, False - otherwise.
*/
bool IsKeyCharValid(const char aChar) {
return (aChar >= 'A' && aChar <= 'Z') || (aChar >= 'a' && aChar <= 'z') ||
(aChar >= '0' && aChar <= '9') || aChar == '-';
}
/**
* Checks if a given string is a valid telemetry key.
*
* @param aKey is the key string.
* @return True, if the key is valid, False - otherwise.
*/
bool IsKeyValid(const nsACString& aKey) {
// Check key length.
if (aKey.Length() > kMaxKeyLength) {
return false;
}
// Check key characters.
const char* cur = aKey.BeginReading();
const char* end = aKey.EndReading();
for (; cur < end; ++cur) {
if (!IsKeyCharValid(*cur)) {
return false;
}
}
return true;
}
} // anonymous namespace
namespace mozilla {
namespace Telemetry {
void KeyedStackCapturer::Capture(const nsACString& aKey) {
MutexAutoLock captureStackMutex(mStackCapturerMutex);
// Check if the key is ok.
if (!IsKeyValid(aKey)) {
NS_WARNING(nsPrintfCString(
"Invalid key is used to capture stack in telemetry: '%s'",
PromiseFlatCString(aKey).get())
.get());
return;
}
// Trying to find and update the stack information.
StackFrequencyInfo* info = mStackInfos.Get(aKey);
if (info) {
// We already recorded this stack before, only increase the count.
info->mCount++;
return;
}
// Check if we have room for new captures.
if (mStackInfos.Count() >= kMaxCapturedStacksKept) {
// Addressed by Bug 1316793.
return;
}
// We haven't captured a stack for this key before, do it now.
// Note that this does a stackwalk and is an expensive operation.
std::vector<uintptr_t> rawStack;
auto callback = [](uint32_t, void* aPC, void*, void* aClosure) {
std::vector<uintptr_t>* stack =
static_cast<std::vector<uintptr_t>*>(aClosure);
stack->push_back(reinterpret_cast<uintptr_t>(aPC));
};
MozStackWalk(callback, /* skipFrames */ 0, /* maxFrames */ 0, &rawStack);
ProcessedStack stack = GetStackAndModules(rawStack);
// Store the new stack info.
size_t stackIndex = mStacks.AddStack(stack);
mStackInfos.Put(aKey, new StackFrequencyInfo(1, stackIndex));
}
NS_IMETHODIMP
KeyedStackCapturer::ReflectCapturedStacks(JSContext* cx,
JS::MutableHandle<JS::Value> ret) {
MutexAutoLock capturedStackMutex(mStackCapturerMutex);
// this adds the memoryMap and stacks properties.
JS::RootedObject fullReportObj(cx, CreateJSStackObject(cx, mStacks));
if (!fullReportObj) {
return NS_ERROR_FAILURE;
}
JS::RootedObject keysArray(cx, JS_NewArrayObject(cx, 0));
if (!keysArray) {
return NS_ERROR_FAILURE;
}
bool ok = JS_DefineProperty(cx, fullReportObj, "captures", keysArray,
JSPROP_ENUMERATE);
if (!ok) {
return NS_ERROR_FAILURE;
}
size_t keyIndex = 0;
for (auto iter = mStackInfos.ConstIter(); !iter.Done();
iter.Next(), ++keyIndex) {
const StackFrequencyInfo* info = iter.Data();
JS::RootedObject infoArray(cx, JS_NewArrayObject(cx, 0));
if (!keysArray) {
return NS_ERROR_FAILURE;
}
JS::RootedString str(
cx, JS_NewStringCopyZ(cx, PromiseFlatCString(iter.Key()).get()));
if (!str || !JS_DefineElement(cx, infoArray, 0, str, JSPROP_ENUMERATE) ||
!JS_DefineElement(cx, infoArray, 1, info->mIndex, JSPROP_ENUMERATE) ||
!JS_DefineElement(cx, infoArray, 2, info->mCount, JSPROP_ENUMERATE) ||
!JS_DefineElement(cx, keysArray, keyIndex, infoArray,
JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
}
ret.setObject(*fullReportObj);
return NS_OK;
}
void KeyedStackCapturer::Clear() {
MutexAutoLock captureStackMutex(mStackCapturerMutex);
mStackInfos.Clear();
mStacks.Clear();
}
size_t KeyedStackCapturer::SizeOfExcludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
size_t n = 0;
n += mStackInfos.SizeOfExcludingThis(aMallocSizeOf);
n += mStacks.SizeOfExcludingThis();
return n;
}
} // namespace Telemetry
} // namespace mozilla