Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/telemetry/other/ProcessedStack.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 "ProcessedStack.h"
8
#if defined(MOZ_GECKO_PROFILER)
9
#include "shared-libraries.h"
10
#endif // MOZ_GECKO_PROFILER
11
12
namespace {
13
14
struct StackFrame
15
{
16
  uintptr_t mPC;      // The program counter at this position in the call stack.
17
  uint16_t mIndex;    // The number of this frame in the call stack.
18
  uint16_t mModIndex; // The index of module that has this program counter.
19
};
20
21
#ifdef MOZ_GECKO_PROFILER
22
static bool CompareByPC(const StackFrame &a, const StackFrame &b)
23
0
{
24
0
  return a.mPC < b.mPC;
25
0
}
26
27
static bool CompareByIndex(const StackFrame &a, const StackFrame &b)
28
0
{
29
0
  return a.mIndex < b.mIndex;
30
0
}
31
#endif
32
33
} // namespace
34
35
namespace mozilla {
36
namespace Telemetry {
37
38
const size_t kMaxChromeStackDepth = 50;
39
40
0
ProcessedStack::ProcessedStack() = default;
41
42
size_t ProcessedStack::GetStackSize() const
43
0
{
44
0
  return mStack.size();
45
0
}
46
47
size_t ProcessedStack::GetNumModules() const
48
0
{
49
0
  return mModules.size();
50
0
}
51
52
0
bool ProcessedStack::Module::operator==(const Module& aOther) const {
53
0
  return  mName == aOther.mName &&
54
0
    mBreakpadId == aOther.mBreakpadId;
55
0
}
56
57
const ProcessedStack::Frame &ProcessedStack::GetFrame(unsigned aIndex) const
58
0
{
59
0
  MOZ_ASSERT(aIndex < mStack.size());
60
0
  return mStack[aIndex];
61
0
}
62
63
void ProcessedStack::AddFrame(const Frame &aFrame)
64
0
{
65
0
  mStack.push_back(aFrame);
66
0
}
67
68
const ProcessedStack::Module &ProcessedStack::GetModule(unsigned aIndex) const
69
0
{
70
0
  MOZ_ASSERT(aIndex < mModules.size());
71
0
  return mModules[aIndex];
72
0
}
73
74
void ProcessedStack::AddModule(const Module &aModule)
75
0
{
76
0
  mModules.push_back(aModule);
77
0
}
78
79
0
void ProcessedStack::Clear() {
80
0
  mModules.clear();
81
0
  mStack.clear();
82
0
}
83
84
ProcessedStack
85
GetStackAndModules(const std::vector<uintptr_t>& aPCs)
86
0
{
87
0
  std::vector<StackFrame> rawStack;
88
0
  auto stackEnd = aPCs.begin() + std::min(aPCs.size(), kMaxChromeStackDepth);
89
0
  for (auto i = aPCs.begin(); i != stackEnd; ++i) {
90
0
    uintptr_t aPC = *i;
91
0
    StackFrame Frame = {aPC, static_cast<uint16_t>(rawStack.size()),
92
0
                        std::numeric_limits<uint16_t>::max()};
93
0
    rawStack.push_back(Frame);
94
0
  }
95
0
96
0
#ifdef MOZ_GECKO_PROFILER
97
0
  // Remove all modules not referenced by a PC on the stack
98
0
  std::sort(rawStack.begin(), rawStack.end(), CompareByPC);
99
0
100
0
  size_t moduleIndex = 0;
101
0
  size_t stackIndex = 0;
102
0
  size_t stackSize = rawStack.size();
103
0
104
0
  SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf();
105
0
  rawModules.SortByAddress();
106
0
107
0
  while (moduleIndex < rawModules.GetSize()) {
108
0
    const SharedLibrary& module = rawModules.GetEntry(moduleIndex);
109
0
    uintptr_t moduleStart = module.GetStart();
110
0
    uintptr_t moduleEnd = module.GetEnd() - 1;
111
0
    // the interval is [moduleStart, moduleEnd)
112
0
113
0
    bool moduleReferenced = false;
114
0
    for (;stackIndex < stackSize; ++stackIndex) {
115
0
      uintptr_t pc = rawStack[stackIndex].mPC;
116
0
      if (pc >= moduleEnd)
117
0
        break;
118
0
119
0
      if (pc >= moduleStart) {
120
0
        // If the current PC is within the current module, mark
121
0
        // module as used
122
0
        moduleReferenced = true;
123
0
        rawStack[stackIndex].mPC -= moduleStart;
124
0
        rawStack[stackIndex].mModIndex = moduleIndex;
125
0
      } else {
126
0
        // PC does not belong to any module. It is probably from
127
0
        // the JIT. Use a fixed mPC so that we don't get different
128
0
        // stacks on different runs.
129
0
        rawStack[stackIndex].mPC =
130
0
          std::numeric_limits<uintptr_t>::max();
131
0
      }
132
0
    }
133
0
134
0
    if (moduleReferenced) {
135
0
      ++moduleIndex;
136
0
    } else {
137
0
      // Remove module if no PCs within its address range
138
0
      rawModules.RemoveEntries(moduleIndex, moduleIndex + 1);
139
0
    }
140
0
  }
141
0
142
0
  for (;stackIndex < stackSize; ++stackIndex) {
143
0
    // These PCs are past the last module.
144
0
    rawStack[stackIndex].mPC = std::numeric_limits<uintptr_t>::max();
145
0
  }
146
0
147
0
  std::sort(rawStack.begin(), rawStack.end(), CompareByIndex);
148
0
#endif
149
0
150
0
  // Copy the information to the return value.
151
0
  ProcessedStack Ret;
152
0
  for (auto & rawFrame : rawStack) {
153
0
    mozilla::Telemetry::ProcessedStack::Frame frame = { rawFrame.mPC, rawFrame.mModIndex };
154
0
    Ret.AddFrame(frame);
155
0
  }
156
0
157
0
#ifdef MOZ_GECKO_PROFILER
158
0
  for (unsigned i = 0, n = rawModules.GetSize(); i != n; ++i) {
159
0
    const SharedLibrary &info = rawModules.GetEntry(i);
160
0
    mozilla::Telemetry::ProcessedStack::Module module = {
161
0
      info.GetDebugName(),
162
0
      info.GetBreakpadId()
163
0
    };
164
0
    Ret.AddModule(module);
165
0
  }
166
0
#endif
167
0
168
0
  return Ret;
169
0
}
170
171
} // namespace Telemetry
172
} // namespace mozilla