#include "HangStack.h" #ifdef MOZ_GECKO_PROFILER #include "shared-libraries.h" #endif namespace mozilla { HangStack::HangStack(const HangStack& aOther) : mModules(aOther.mModules) { if (NS_WARN_IF(!mBuffer.reserve(aOther.mBuffer.length()) || !mImpl.reserve(aOther.mImpl.length()))) { return; } // XXX: I should be able to just memcpy the other stack's mImpl and mBuffer, // and then just re-offset pointers. for (size_t i = 0; i < aOther.length(); ++i) { const Frame& frame = aOther[i]; // If the source string is a reference to aOther's buffer, we have to append // via buffer, otherwise we can just copy the entry over. if (frame.GetKind() == Frame::Kind::STRING) { const char* s = frame.AsString(); if (aOther.IsInBuffer(s)) { InfallibleAppendViaBuffer(s, strlen(s)); continue; } } infallibleAppend(frame); } MOZ_ASSERT(mImpl.length() == aOther.mImpl.length()); MOZ_ASSERT(mBuffer.length() == aOther.mBuffer.length()); } void HangStack::InfallibleAppendViaBuffer(const char* aText, size_t aLength) { MOZ_ASSERT(this->canAppendWithoutRealloc(1)); // Include null-terminator in length count. MOZ_ASSERT(mBuffer.canAppendWithoutRealloc(aLength + 1)); const char* const entry = mBuffer.end(); mBuffer.infallibleAppend(aText, aLength); mBuffer.infallibleAppend('\0'); // Explicitly append null-terminator this->infallibleAppend(Frame(entry)); } bool HangStack::AppendViaBuffer(const char* aText, size_t aLength) { if (!this->reserve(this->length() + 1)) { return false; } // Keep track of the previous buffer in case we need to adjust pointers later. const char* const prevStart = mBuffer.begin(); const char* const prevEnd = mBuffer.end(); // Include null-terminator in length count. if (!mBuffer.reserve(mBuffer.length() + aLength + 1)) { return false; } if (prevStart != mBuffer.begin()) { // The buffer has moved; we have to adjust pointers in the stack. for (auto & frame : *this) { if (frame.GetKind() == Frame::Kind::STRING) { const char*& entry = frame.AsString(); if (entry >= prevStart && entry < prevEnd) { // Move from old buffer to new buffer. entry += mBuffer.begin() - prevStart; } } } } InfallibleAppendViaBuffer(aText, aLength); return true; } namespace { // Sorting comparator used by ReadModuleInformation. Sorts PC Frames by their // PC. struct PCFrameComparator { bool LessThan(HangStack::Frame* const& a, HangStack::Frame* const& b) const { return a->AsPC() < b->AsPC(); } bool Equals(HangStack::Frame* const& a, HangStack::Frame* const& b) const { return a->AsPC() == b->AsPC(); } }; } // anonymous namespace void HangStack::ReadModuleInformation() { // mModules should be empty when we start filling it. mModules.Clear(); #ifdef MOZ_GECKO_PROFILER // Create a sorted list of the PCs in the current stack. AutoTArray frames; for (auto& frame : *this) { if (frame.GetKind() == Frame::Kind::PC) { frames.AppendElement(&frame); } } PCFrameComparator comparator; frames.Sort(comparator); SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf(); rawModules.SortByAddress(); size_t frameIdx = 0; for (size_t i = 0; i < rawModules.GetSize(); ++i) { const SharedLibrary& info = rawModules.GetEntry(i); uintptr_t moduleStart = info.GetStart(); uintptr_t moduleEnd = info.GetEnd() - 1; // the interval is [moduleStart, moduleEnd) bool moduleReferenced = false; for (; frameIdx < frames.Length(); ++frameIdx) { auto& frame = frames[frameIdx]; // We've moved past this frame, let's go to the next one. if (frame->AsPC() >= moduleEnd) { break; } if (frame->AsPC() >= moduleStart) { uint64_t offset = frame->AsPC() - moduleStart; if (NS_WARN_IF(offset > UINT32_MAX)) { continue; // module/offset can only hold 32-bit offsets into shared libraries. } // If we found the module, rewrite the Frame entry to instead be a // ModOffset one. mModules.Length() will be the index of the module when // we append it below, and we set moduleReferenced to true to ensure // that we do. moduleReferenced = true; uint32_t module = mModules.Length(); ModOffset modOffset = { module, static_cast(offset) }; *frame = Frame(modOffset); } } if (moduleReferenced) { nsDependentCString cstr(info.GetBreakpadId().c_str()); Module module = { info.GetDebugName(), cstr }; mModules.AppendElement(module); } } #endif } } // namespace mozilla namespace IPC { void ParamTraits::Write(Message* aMsg, const mozilla::HangStack::ModOffset& aParam) { WriteParam(aMsg, aParam.mModule); WriteParam(aMsg, aParam.mOffset); } bool ParamTraits::Read(const Message* aMsg, PickleIterator* aIter, mozilla::HangStack::ModOffset* aResult) { if (!ReadParam(aMsg, aIter, &aResult->mModule)) { return false; } if (!ReadParam(aMsg, aIter, &aResult->mOffset)) { return false; } return true; } void ParamTraits::Write(Message* aMsg, const mozilla::HangStack::Module& aParam) { WriteParam(aMsg, aParam.mName); WriteParam(aMsg, aParam.mBreakpadId); } bool ParamTraits::Read(const Message* aMsg, PickleIterator* aIter, mozilla::HangStack::Module* aResult) { if (!ReadParam(aMsg, aIter, &aResult->mName)) { return false; } if (!ReadParam(aMsg, aIter, &aResult->mBreakpadId)) { return false; } return true; } void ParamTraits::Write(Message* aMsg, const mozilla::HangStack& aParam) { typedef mozilla::HangStack::Frame Frame; size_t length = aParam.length(); WriteParam(aMsg, length); for (size_t i = 0; i < length; ++i) { const Frame& frame = aParam[i]; WriteParam(aMsg, frame.GetKind()); switch (frame.GetKind()) { case Frame::Kind::STRING: { nsDependentCString str(frame.AsString()); WriteParam(aMsg, static_cast(str)); break; } case Frame::Kind::MODOFFSET: { WriteParam(aMsg, frame.AsModOffset()); break; } case Frame::Kind::PC: { WriteParam(aMsg, frame.AsPC()); break; } case Frame::Kind::CONTENT: case Frame::Kind::WASM: case Frame::Kind::JIT: case Frame::Kind::SUPPRESSED: { // NOTE: no associated data. break; } default: { MOZ_RELEASE_ASSERT(false, "Invalid kind for HangStack Frame"); break; } } } WriteParam(aMsg, aParam.GetModules()); } bool ParamTraits::Read(const Message* aMsg, PickleIterator* aIter, mozilla::HangStack* aResult) { // Shorten the name of Frame typedef mozilla::HangStack::Frame Frame; size_t length; if (!ReadParam(aMsg, aIter, &length)) { return false; } if (!aResult->reserve(length)) { return false; } for (size_t i = 0; i < length; ++i) { Frame::Kind kind; if (!ReadParam(aMsg, aIter, &kind)) { return false; } switch (kind) { case Frame::Kind::STRING: { nsAutoCString str; if (!ReadParam(aMsg, aIter, static_cast(&str))) { return false; } aResult->AppendViaBuffer(str.get(), str.Length()); break; } case Frame::Kind::MODOFFSET: { mozilla::HangStack::ModOffset modOff; if (!ReadParam(aMsg, aIter, &modOff)) { return false; } aResult->infallibleAppend(Frame(modOff)); break; } case Frame::Kind::PC: { uintptr_t pc; if (!ReadParam(aMsg, aIter, &pc)) { return false; } aResult->infallibleAppend(Frame(pc)); break; } case Frame::Kind::CONTENT: aResult->infallibleAppend(Frame::Content()); break; case Frame::Kind::WASM: aResult->infallibleAppend(Frame::Wasm()); break; case Frame::Kind::JIT: aResult->infallibleAppend(Frame::Jit()); break; case Frame::Kind::SUPPRESSED: aResult->infallibleAppend(Frame::Suppressed()); break; default: // We can't deserialize other kinds! return false; } } if (!ReadParam(aMsg, aIter, &aResult->GetModules())) { return false; } return true; } } // namespace IPC