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