Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/telemetry/other/CombinedStacks.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 "CombinedStacks.h"
8
#include "mozilla/HangAnnotations.h"
9
#include "jsapi.h"
10
11
namespace mozilla {
12
namespace Telemetry {
13
14
// The maximum number of chrome hangs stacks that we're keeping.
15
const size_t kMaxChromeStacksKept = 50;
16
17
CombinedStacks::CombinedStacks()
18
  : CombinedStacks(kMaxChromeStacksKept)
19
6
{}
20
21
CombinedStacks::CombinedStacks(size_t aMaxStacksCount)
22
  : mNextIndex(0)
23
  , mMaxStacksCount(aMaxStacksCount)
24
6
{}
25
26
size_t
27
0
CombinedStacks::GetModuleCount() const {
28
0
  return mModules.size();
29
0
}
30
31
const Telemetry::ProcessedStack::Module&
32
0
CombinedStacks::GetModule(unsigned aIndex) const {
33
0
  return mModules[aIndex];
34
0
}
35
36
size_t
37
0
CombinedStacks::AddStack(const Telemetry::ProcessedStack& aStack) {
38
0
  size_t index = mNextIndex;
39
0
  // Advance the indices of the circular queue holding the stacks.
40
0
  mNextIndex = (mNextIndex + 1) % mMaxStacksCount;
41
0
  // Grow the vector up to the maximum size, if needed.
42
0
  if (mStacks.size() < mMaxStacksCount) {
43
0
    mStacks.resize(mStacks.size() + 1);
44
0
  }
45
0
  // Get a reference to the location holding the new stack.
46
0
  CombinedStacks::Stack& adjustedStack = mStacks[index];
47
0
  // If we're using an old stack to hold aStack, clear it.
48
0
  adjustedStack.clear();
49
0
50
0
  size_t stackSize = aStack.GetStackSize();
51
0
  for (size_t i = 0; i < stackSize; ++i) {
52
0
    const Telemetry::ProcessedStack::Frame& frame = aStack.GetFrame(i);
53
0
    uint16_t modIndex;
54
0
    if (frame.mModIndex == std::numeric_limits<uint16_t>::max()) {
55
0
      modIndex = frame.mModIndex;
56
0
    } else {
57
0
      const Telemetry::ProcessedStack::Module& module =
58
0
        aStack.GetModule(frame.mModIndex);
59
0
      std::vector<Telemetry::ProcessedStack::Module>::iterator modIterator =
60
0
        std::find(mModules.begin(), mModules.end(), module);
61
0
      if (modIterator == mModules.end()) {
62
0
        mModules.push_back(module);
63
0
        modIndex = mModules.size() - 1;
64
0
      } else {
65
0
        modIndex = modIterator - mModules.begin();
66
0
      }
67
0
    }
68
0
    Telemetry::ProcessedStack::Frame adjustedFrame = { frame.mOffset, modIndex };
69
0
    adjustedStack.push_back(adjustedFrame);
70
0
  }
71
0
  return index;
72
0
}
73
74
const CombinedStacks::Stack&
75
0
CombinedStacks::GetStack(unsigned aIndex) const {
76
0
  return mStacks[aIndex];
77
0
}
78
79
size_t
80
0
CombinedStacks::GetStackCount() const {
81
0
  return mStacks.size();
82
0
}
83
84
size_t
85
0
CombinedStacks::SizeOfExcludingThis() const {
86
0
  // This is a crude approximation. We would like to do something like
87
0
  // aMallocSizeOf(&mModules[0]), but on linux aMallocSizeOf will call
88
0
  // malloc_usable_size which is only safe on the pointers returned by malloc.
89
0
  // While it works on current libstdc++, it is better to be safe and not assume
90
0
  // that &vec[0] points to one. We could use a custom allocator, but
91
0
  // it doesn't seem worth it.
92
0
  size_t n = 0;
93
0
  n += mModules.capacity() * sizeof(Telemetry::ProcessedStack::Module);
94
0
  n += mStacks.capacity() * sizeof(Stack);
95
0
  for (const auto & s : mStacks) {
96
0
    n += s.capacity() * sizeof(Telemetry::ProcessedStack::Frame);
97
0
  }
98
0
  return n;
99
0
}
100
101
void
102
0
CombinedStacks::RemoveStack(unsigned aIndex) {
103
0
  MOZ_ASSERT(aIndex < mStacks.size());
104
0
105
0
  mStacks.erase(mStacks.begin() + aIndex);
106
0
107
0
  if (aIndex < mNextIndex) {
108
0
    if (mNextIndex == 0) {
109
0
      mNextIndex = mStacks.size();
110
0
    } else {
111
0
      mNextIndex--;
112
0
    }
113
0
  }
114
0
115
0
  if (mNextIndex > mStacks.size()) {
116
0
    mNextIndex = mStacks.size();
117
0
  }
118
0
}
119
120
#if defined(MOZ_GECKO_PROFILER)
121
void
122
0
CombinedStacks::Clear() {
123
0
  mNextIndex = 0;
124
0
  mStacks.clear();
125
0
  mModules.clear();
126
0
}
127
#endif
128
129
JSObject *
130
0
CreateJSStackObject(JSContext *cx, const CombinedStacks &stacks) {
131
0
  JS::Rooted<JSObject*> ret(cx, JS_NewPlainObject(cx));
132
0
  if (!ret) {
133
0
    return nullptr;
134
0
  }
135
0
136
0
  JS::Rooted<JSObject*> moduleArray(cx, JS_NewArrayObject(cx, 0));
137
0
  if (!moduleArray) {
138
0
    return nullptr;
139
0
  }
140
0
  bool ok = JS_DefineProperty(cx, ret, "memoryMap", moduleArray,
141
0
                              JSPROP_ENUMERATE);
142
0
  if (!ok) {
143
0
    return nullptr;
144
0
  }
145
0
146
0
  const size_t moduleCount = stacks.GetModuleCount();
147
0
  for (size_t moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
148
0
    // Current module
149
0
    const Telemetry::ProcessedStack::Module& module =
150
0
      stacks.GetModule(moduleIndex);
151
0
152
0
    JS::Rooted<JSObject*> moduleInfoArray(cx, JS_NewArrayObject(cx, 0));
153
0
    if (!moduleInfoArray) {
154
0
      return nullptr;
155
0
    }
156
0
    if (!JS_DefineElement(cx, moduleArray, moduleIndex, moduleInfoArray,
157
0
                          JSPROP_ENUMERATE)) {
158
0
      return nullptr;
159
0
    }
160
0
161
0
    unsigned index = 0;
162
0
163
0
    // Module name
164
0
    JS::Rooted<JSString*> str(cx, JS_NewUCStringCopyZ(cx, module.mName.get()));
165
0
    if (!str || !JS_DefineElement(cx, moduleInfoArray, index++, str, JSPROP_ENUMERATE)) {
166
0
      return nullptr;
167
0
    }
168
0
169
0
    // Module breakpad identifier
170
0
    JS::Rooted<JSString*> id(cx, JS_NewStringCopyZ(cx, module.mBreakpadId.get()));
171
0
    if (!id || !JS_DefineElement(cx, moduleInfoArray, index, id, JSPROP_ENUMERATE)) {
172
0
      return nullptr;
173
0
    }
174
0
  }
175
0
176
0
  JS::Rooted<JSObject*> reportArray(cx, JS_NewArrayObject(cx, 0));
177
0
  if (!reportArray) {
178
0
    return nullptr;
179
0
  }
180
0
  ok = JS_DefineProperty(cx, ret, "stacks", reportArray, JSPROP_ENUMERATE);
181
0
  if (!ok) {
182
0
    return nullptr;
183
0
  }
184
0
185
0
  const size_t length = stacks.GetStackCount();
186
0
  for (size_t i = 0; i < length; ++i) {
187
0
    // Represent call stack PCs as (module index, offset) pairs.
188
0
    JS::Rooted<JSObject*> pcArray(cx, JS_NewArrayObject(cx, 0));
189
0
    if (!pcArray) {
190
0
      return nullptr;
191
0
    }
192
0
193
0
    if (!JS_DefineElement(cx, reportArray, i, pcArray, JSPROP_ENUMERATE)) {
194
0
      return nullptr;
195
0
    }
196
0
197
0
    const CombinedStacks::Stack& stack = stacks.GetStack(i);
198
0
    const uint32_t pcCount = stack.size();
199
0
    for (size_t pcIndex = 0; pcIndex < pcCount; ++pcIndex) {
200
0
      const Telemetry::ProcessedStack::Frame& frame = stack[pcIndex];
201
0
      JS::Rooted<JSObject*> framePair(cx, JS_NewArrayObject(cx, 0));
202
0
      if (!framePair) {
203
0
        return nullptr;
204
0
      }
205
0
      int modIndex = (std::numeric_limits<uint16_t>::max() == frame.mModIndex) ?
206
0
        -1 : frame.mModIndex;
207
0
      if (!JS_DefineElement(cx, framePair, 0, modIndex, JSPROP_ENUMERATE)) {
208
0
        return nullptr;
209
0
      }
210
0
      if (!JS_DefineElement(cx, framePair, 1, static_cast<double>(frame.mOffset),
211
0
                            JSPROP_ENUMERATE)) {
212
0
        return nullptr;
213
0
      }
214
0
      if (!JS_DefineElement(cx, pcArray, pcIndex, framePair, JSPROP_ENUMERATE)) {
215
0
        return nullptr;
216
0
      }
217
0
    }
218
0
  }
219
0
220
0
  return ret;
221
0
}
222
223
} // namespace Telemetry
224
} // namespace mozilla