fune/toolkit/components/telemetry/KeyedStackCapturer.cpp
Nicholas Nethercote 08e54b7c13 Bug 1384819 (part 1) - Split MozStackWalk(). r=glandium.
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
2017-07-27 12:46:47 +10:00

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