forked from mirrors/gecko-dev
MozStackWalk() is different on Windows to the other platforms. It has two extra arguments, which can be used to walk the stack of a different thread. This patch makes those differences clearer. Instead of having a single function and forbidding those two arguments on non-Windows, it removes those arguments from MozStackWalk, and splits off MozStackWalkThread() which retains them. This also allows those arguments to have more appropriate types (HANDLE instead of uintptr_t; CONTEXT* instead of than void*) and names (aContext instead of aPlatformData). The patch also removes unnecessary reinterpret_casts for the aClosure argument at a couple of MozStackWalk() callsites. --HG-- extra : rebase_source : 111ab7d6426d7be921facc2264f6db86c501d127
163 lines
4.5 KiB
C++
163 lines
4.5 KiB
C++
/* -*- 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 "nsPrintfCString.h"
|
|
#include "mozilla/StackWalk.h"
|
|
#include "ProcessedStack.h"
|
|
#include "jsapi.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();
|
|
}
|
|
|
|
} // namespace Telemetry
|
|
} // namespace mozilla
|