Coverage Report

Created: 2018-09-25 14:53

/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