forked from mirrors/gecko-dev
		
	 9e01732a56
			
		
	
	
		9e01732a56
		
	
	
	
	
		
			
			MozReview-Commit-ID: IKe8btKJ028 --HG-- extra : rebase_source : 24ff10777b624a0ecc0f9ec0212351f992b590b1
		
			
				
	
	
		
			172 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
	
		
			4.6 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 "ProcessedStack.h"
 | |
| #if defined(MOZ_GECKO_PROFILER)
 | |
| #include "shared-libraries.h"
 | |
| #endif // MOZ_GECKO_PROFILER
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| struct StackFrame
 | |
| {
 | |
|   uintptr_t mPC;      // The program counter at this position in the call stack.
 | |
|   uint16_t mIndex;    // The number of this frame in the call stack.
 | |
|   uint16_t mModIndex; // The index of module that has this program counter.
 | |
| };
 | |
| 
 | |
| #ifdef MOZ_GECKO_PROFILER
 | |
| static bool CompareByPC(const StackFrame &a, const StackFrame &b)
 | |
| {
 | |
|   return a.mPC < b.mPC;
 | |
| }
 | |
| 
 | |
| static bool CompareByIndex(const StackFrame &a, const StackFrame &b)
 | |
| {
 | |
|   return a.mIndex < b.mIndex;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace Telemetry {
 | |
| 
 | |
| const size_t kMaxChromeStackDepth = 50;
 | |
| 
 | |
| ProcessedStack::ProcessedStack() = default;
 | |
| 
 | |
| size_t ProcessedStack::GetStackSize() const
 | |
| {
 | |
|   return mStack.size();
 | |
| }
 | |
| 
 | |
| size_t ProcessedStack::GetNumModules() const
 | |
| {
 | |
|   return mModules.size();
 | |
| }
 | |
| 
 | |
| bool ProcessedStack::Module::operator==(const Module& aOther) const {
 | |
|   return  mName == aOther.mName &&
 | |
|     mBreakpadId == aOther.mBreakpadId;
 | |
| }
 | |
| 
 | |
| const ProcessedStack::Frame &ProcessedStack::GetFrame(unsigned aIndex) const
 | |
| {
 | |
|   MOZ_ASSERT(aIndex < mStack.size());
 | |
|   return mStack[aIndex];
 | |
| }
 | |
| 
 | |
| void ProcessedStack::AddFrame(const Frame &aFrame)
 | |
| {
 | |
|   mStack.push_back(aFrame);
 | |
| }
 | |
| 
 | |
| const ProcessedStack::Module &ProcessedStack::GetModule(unsigned aIndex) const
 | |
| {
 | |
|   MOZ_ASSERT(aIndex < mModules.size());
 | |
|   return mModules[aIndex];
 | |
| }
 | |
| 
 | |
| void ProcessedStack::AddModule(const Module &aModule)
 | |
| {
 | |
|   mModules.push_back(aModule);
 | |
| }
 | |
| 
 | |
| void ProcessedStack::Clear() {
 | |
|   mModules.clear();
 | |
|   mStack.clear();
 | |
| }
 | |
| 
 | |
| ProcessedStack
 | |
| GetStackAndModules(const std::vector<uintptr_t>& aPCs)
 | |
| {
 | |
|   std::vector<StackFrame> rawStack;
 | |
|   auto stackEnd = aPCs.begin() + std::min(aPCs.size(), kMaxChromeStackDepth);
 | |
|   for (auto i = aPCs.begin(); i != stackEnd; ++i) {
 | |
|     uintptr_t aPC = *i;
 | |
|     StackFrame Frame = {aPC, static_cast<uint16_t>(rawStack.size()),
 | |
|                         std::numeric_limits<uint16_t>::max()};
 | |
|     rawStack.push_back(Frame);
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_GECKO_PROFILER
 | |
|   // Remove all modules not referenced by a PC on the stack
 | |
|   std::sort(rawStack.begin(), rawStack.end(), CompareByPC);
 | |
| 
 | |
|   size_t moduleIndex = 0;
 | |
|   size_t stackIndex = 0;
 | |
|   size_t stackSize = rawStack.size();
 | |
| 
 | |
|   SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf();
 | |
|   rawModules.SortByAddress();
 | |
| 
 | |
|   while (moduleIndex < rawModules.GetSize()) {
 | |
|     const SharedLibrary& module = rawModules.GetEntry(moduleIndex);
 | |
|     uintptr_t moduleStart = module.GetStart();
 | |
|     uintptr_t moduleEnd = module.GetEnd() - 1;
 | |
|     // the interval is [moduleStart, moduleEnd)
 | |
| 
 | |
|     bool moduleReferenced = false;
 | |
|     for (;stackIndex < stackSize; ++stackIndex) {
 | |
|       uintptr_t pc = rawStack[stackIndex].mPC;
 | |
|       if (pc >= moduleEnd)
 | |
|         break;
 | |
| 
 | |
|       if (pc >= moduleStart) {
 | |
|         // If the current PC is within the current module, mark
 | |
|         // module as used
 | |
|         moduleReferenced = true;
 | |
|         rawStack[stackIndex].mPC -= moduleStart;
 | |
|         rawStack[stackIndex].mModIndex = moduleIndex;
 | |
|       } else {
 | |
|         // PC does not belong to any module. It is probably from
 | |
|         // the JIT. Use a fixed mPC so that we don't get different
 | |
|         // stacks on different runs.
 | |
|         rawStack[stackIndex].mPC =
 | |
|           std::numeric_limits<uintptr_t>::max();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (moduleReferenced) {
 | |
|       ++moduleIndex;
 | |
|     } else {
 | |
|       // Remove module if no PCs within its address range
 | |
|       rawModules.RemoveEntries(moduleIndex, moduleIndex + 1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (;stackIndex < stackSize; ++stackIndex) {
 | |
|     // These PCs are past the last module.
 | |
|     rawStack[stackIndex].mPC = std::numeric_limits<uintptr_t>::max();
 | |
|   }
 | |
| 
 | |
|   std::sort(rawStack.begin(), rawStack.end(), CompareByIndex);
 | |
| #endif
 | |
| 
 | |
|   // Copy the information to the return value.
 | |
|   ProcessedStack Ret;
 | |
|   for (auto & rawFrame : rawStack) {
 | |
|     mozilla::Telemetry::ProcessedStack::Frame frame = { rawFrame.mPC, rawFrame.mModIndex };
 | |
|     Ret.AddFrame(frame);
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_GECKO_PROFILER
 | |
|   for (unsigned i = 0, n = rawModules.GetSize(); i != n; ++i) {
 | |
|     const SharedLibrary &info = rawModules.GetEntry(i);
 | |
|     mozilla::Telemetry::ProcessedStack::Module module = {
 | |
|       info.GetDebugName(),
 | |
|       info.GetBreakpadId()
 | |
|     };
 | |
|     Ret.AddModule(module);
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   return Ret;
 | |
| }
 | |
| 
 | |
| } // namespace Telemetry
 | |
| } // namespace mozilla
 |