Line data Source code
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 0 : static bool CompareByPC(const StackFrame &a, const StackFrame &b)
23 : {
24 0 : return a.mPC < b.mPC;
25 : }
26 :
27 0 : static bool CompareByIndex(const StackFrame &a, const StackFrame &b)
28 : {
29 0 : return a.mIndex < b.mIndex;
30 : }
31 : #endif
32 :
33 : } // namespace
34 :
35 : namespace mozilla {
36 : namespace Telemetry {
37 :
38 : const size_t kMaxChromeStackDepth = 50;
39 :
40 : ProcessedStack::ProcessedStack() = default;
41 :
42 0 : size_t ProcessedStack::GetStackSize() const
43 : {
44 0 : return mStack.size();
45 : }
46 :
47 0 : size_t ProcessedStack::GetNumModules() const
48 : {
49 0 : return mModules.size();
50 : }
51 :
52 0 : bool ProcessedStack::Module::operator==(const Module& aOther) const {
53 0 : return mName == aOther.mName &&
54 0 : mBreakpadId == aOther.mBreakpadId;
55 : }
56 :
57 0 : const ProcessedStack::Frame &ProcessedStack::GetFrame(unsigned aIndex) const
58 : {
59 0 : MOZ_ASSERT(aIndex < mStack.size());
60 0 : return mStack[aIndex];
61 : }
62 :
63 0 : void ProcessedStack::AddFrame(const Frame &aFrame)
64 : {
65 0 : mStack.push_back(aFrame);
66 0 : }
67 :
68 0 : const ProcessedStack::Module &ProcessedStack::GetModule(unsigned aIndex) const
69 : {
70 0 : MOZ_ASSERT(aIndex < mModules.size());
71 0 : return mModules[aIndex];
72 : }
73 :
74 0 : void ProcessedStack::AddModule(const Module &aModule)
75 : {
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 0 : GetStackAndModules(const std::vector<uintptr_t>& aPCs)
86 : {
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 : }
95 :
96 : #ifdef MOZ_GECKO_PROFILER
97 : // Remove all modules not referenced by a PC on the stack
98 0 : std::sort(rawStack.begin(), rawStack.end(), CompareByPC);
99 :
100 0 : size_t moduleIndex = 0;
101 0 : size_t stackIndex = 0;
102 0 : size_t stackSize = rawStack.size();
103 :
104 0 : SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf();
105 0 : rawModules.SortByAddress();
106 :
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 : // the interval is [moduleStart, moduleEnd)
112 :
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 :
119 0 : if (pc >= moduleStart) {
120 : // If the current PC is within the current module, mark
121 : // module as used
122 0 : moduleReferenced = true;
123 0 : rawStack[stackIndex].mPC -= moduleStart;
124 0 : rawStack[stackIndex].mModIndex = moduleIndex;
125 : } else {
126 : // PC does not belong to any module. It is probably from
127 : // the JIT. Use a fixed mPC so that we don't get different
128 : // stacks on different runs.
129 0 : rawStack[stackIndex].mPC =
130 0 : std::numeric_limits<uintptr_t>::max();
131 : }
132 : }
133 :
134 0 : if (moduleReferenced) {
135 0 : ++moduleIndex;
136 : } else {
137 : // Remove module if no PCs within its address range
138 0 : rawModules.RemoveEntries(moduleIndex, moduleIndex + 1);
139 : }
140 : }
141 :
142 0 : for (;stackIndex < stackSize; ++stackIndex) {
143 : // These PCs are past the last module.
144 0 : rawStack[stackIndex].mPC = std::numeric_limits<uintptr_t>::max();
145 : }
146 :
147 0 : std::sort(rawStack.begin(), rawStack.end(), CompareByIndex);
148 : #endif
149 :
150 : // 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 : }
156 :
157 : #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 : mozilla::Telemetry::ProcessedStack::Module module = {
161 0 : info.GetDebugName(),
162 0 : info.GetBreakpadId()
163 0 : };
164 0 : Ret.AddModule(module);
165 : }
166 : #endif
167 :
168 0 : return Ret;
169 : }
170 :
171 : } // namespace Telemetry
172 : } // namespace mozilla
|