forked from mirrors/gecko-dev
		
	Done with: ./mach static-analysis check --checks="-*, modernize-concat-nested-namespaces" --fix . and then clang-format on the files Differential Revision: https://phabricator.services.mozilla.com/D58217 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			164 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
	
		
			4.8 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 "jsapi.h"
 | 
						|
#include "js/Array.h"  // JS::NewArrayObject
 | 
						|
#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::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.UserData();
 | 
						|
 | 
						|
    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 mozilla::Telemetry
 |