summaryrefslogtreecommitdiffstats
path: root/tools/profiler
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@gmail.com>2018-05-24 14:06:04 +0200
committerwolfbeast <mcwerewolf@gmail.com>2018-05-24 14:06:04 +0200
commitac25827a87d86f1cf9e48aab6605f77a2c89041a (patch)
treec3533a008e606f4f6393e838b4305cf6d07f47d2 /tools/profiler
parentc8b38a18031f6ae0fca8b2bef73daa86f6f96ae8 (diff)
downloadUXP-ac25827a87d86f1cf9e48aab6605f77a2c89041a.tar
UXP-ac25827a87d86f1cf9e48aab6605f77a2c89041a.tar.gz
UXP-ac25827a87d86f1cf9e48aab6605f77a2c89041a.tar.lz
UXP-ac25827a87d86f1cf9e48aab6605f77a2c89041a.tar.xz
UXP-ac25827a87d86f1cf9e48aab6605f77a2c89041a.zip
Remove SPS profiler.
- Conditionals and code blocks. (MOZ_ENABLE_PROFILER_SPS) - Stub out several profiler-only functions.
Diffstat (limited to 'tools/profiler')
-rw-r--r--tools/profiler/core/EHABIStackWalk.cpp678
-rw-r--r--tools/profiler/core/EHABIStackWalk.h28
-rw-r--r--tools/profiler/core/GeckoSampler.cpp1306
-rw-r--r--tools/profiler/core/GeckoSampler.h181
-rw-r--r--tools/profiler/core/IntelPowerGadget.cpp310
-rw-r--r--tools/profiler/core/IntelPowerGadget.h150
-rw-r--r--tools/profiler/core/ProfileBuffer.cpp89
-rw-r--r--tools/profiler/core/ProfileBuffer.h61
-rw-r--r--tools/profiler/core/ProfileEntry.cpp881
-rw-r--r--tools/profiler/core/ProfileEntry.h407
-rw-r--r--tools/profiler/core/ProfileJSONWriter.cpp115
-rw-r--r--tools/profiler/core/ProfileJSONWriter.h126
-rw-r--r--tools/profiler/core/ProfilerBacktrace.cpp33
-rw-r--r--tools/profiler/core/ProfilerMarkers.cpp210
-rw-r--r--tools/profiler/core/StackTop.cpp48
-rw-r--r--tools/profiler/core/StackTop.h10
-rw-r--r--tools/profiler/core/SyncProfile.cpp57
-rw-r--r--tools/profiler/core/SyncProfile.h43
-rw-r--r--tools/profiler/core/ThreadInfo.cpp73
-rw-r--r--tools/profiler/core/ThreadInfo.h66
-rw-r--r--tools/profiler/core/ThreadProfile.cpp260
-rw-r--r--tools/profiler/core/ThreadProfile.h107
-rw-r--r--tools/profiler/core/platform-linux.cc715
-rw-r--r--tools/profiler/core/platform-macos.cc469
-rw-r--r--tools/profiler/core/platform-win32.cc431
-rw-r--r--tools/profiler/core/platform.cpp1257
-rw-r--r--tools/profiler/core/platform.h428
-rw-r--r--tools/profiler/core/shared-libraries-linux.cc145
-rw-r--r--tools/profiler/core/shared-libraries-macos.cc132
-rw-r--r--tools/profiler/core/shared-libraries-win32.cc137
-rw-r--r--tools/profiler/gecko/ProfileGatherer.cpp207
-rw-r--r--tools/profiler/gecko/Profiler.jsm16
-rw-r--r--tools/profiler/gecko/ProfilerIOInterposeObserver.cpp30
-rw-r--r--tools/profiler/gecko/ProfilerIOInterposeObserver.h20
-rw-r--r--tools/profiler/gecko/SaveProfileTask.cpp45
-rw-r--r--tools/profiler/gecko/SaveProfileTask.h54
-rw-r--r--tools/profiler/gecko/ThreadResponsiveness.cpp118
-rw-r--r--tools/profiler/gecko/ThreadResponsiveness.h38
-rw-r--r--tools/profiler/gecko/nsIProfileSaveEvent.idl19
-rw-r--r--tools/profiler/gecko/nsIProfiler.idl101
-rw-r--r--tools/profiler/gecko/nsProfiler.cpp308
-rw-r--r--tools/profiler/gecko/nsProfiler.h29
-rw-r--r--tools/profiler/gecko/nsProfilerFactory.cpp31
-rw-r--r--tools/profiler/gecko/nsProfilerStartParams.cpp67
-rw-r--r--tools/profiler/gecko/nsProfilerStartParams.h32
-rw-r--r--tools/profiler/lul/AutoObjectMapper.cpp207
-rw-r--r--tools/profiler/lul/AutoObjectMapper.h115
-rw-r--r--tools/profiler/lul/LulCommon.cpp114
-rw-r--r--tools/profiler/lul/LulDwarf.cpp2180
-rw-r--r--tools/profiler/lul/LulDwarfSummariser.cpp359
-rw-r--r--tools/profiler/lul/LulDwarfSummariser.h65
-rw-r--r--tools/profiler/lul/LulElf.cpp915
-rw-r--r--tools/profiler/lul/LulMain.cpp1963
-rw-r--r--tools/profiler/lul/LulMain.h397
-rw-r--r--tools/profiler/lul/platform-linux-lul.cpp88
-rw-r--r--tools/profiler/lul/platform-linux-lul.h24
-rw-r--r--tools/profiler/moz.build106
-rw-r--r--tools/profiler/public/GeckoProfiler.h8
-rw-r--r--tools/profiler/public/GeckoProfilerFunc.h125
-rw-r--r--tools/profiler/public/GeckoProfilerImpl.h515
-rw-r--r--tools/profiler/public/ProfileGatherer.h42
-rw-r--r--tools/profiler/public/ProfilerBacktrace.h36
-rw-r--r--tools/profiler/public/ProfilerMarkers.h193
-rw-r--r--tools/profiler/public/PseudoStack.h469
-rw-r--r--tools/profiler/public/shared-libraries.h137
-rw-r--r--tools/profiler/tests/gtest/LulTest.cpp51
-rw-r--r--tools/profiler/tests/gtest/LulTestDwarf.cpp2597
-rw-r--r--tools/profiler/tests/gtest/LulTestInfrastructure.cpp491
-rw-r--r--tools/profiler/tests/gtest/LulTestInfrastructure.h666
-rw-r--r--tools/profiler/tests/gtest/ThreadProfileTest.cpp75
-rw-r--r--tools/profiler/tests/gtest/moz.build30
71 files changed, 1 insertions, 22035 deletions
diff --git a/tools/profiler/core/EHABIStackWalk.cpp b/tools/profiler/core/EHABIStackWalk.cpp
deleted file mode 100644
index 76068cdea..000000000
--- a/tools/profiler/core/EHABIStackWalk.cpp
+++ /dev/null
@@ -1,678 +0,0 @@
-/* -*- 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/. */
-
-/*
- * This is an implementation of stack unwinding according to a subset
- * of the ARM Exception Handling ABI, as described in:
- * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
- *
- * This handles only the ARM-defined "personality routines" (chapter
- * 9), and don't track the value of FP registers, because profiling
- * needs only chain of PC/SP values.
- *
- * Because the exception handling info may not be accurate for all
- * possible places where an async signal could occur (e.g., in a
- * prologue or epilogue), this bounds-checks all stack accesses.
- *
- * This file uses "struct" for structures in the exception tables and
- * "class" otherwise. We should avoid violating the C++11
- * standard-layout rules in the former.
- */
-
-#include "EHABIStackWalk.h"
-
-#include "shared-libraries.h"
-#include "platform.h"
-
-#include "mozilla/Atomics.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/DebugOnly.h"
-#include "mozilla/EndianUtils.h"
-
-#include <algorithm>
-#include <elf.h>
-#include <stdint.h>
-#include <vector>
-#include <string>
-
-#ifndef PT_ARM_EXIDX
-#define PT_ARM_EXIDX 0x70000001
-#endif
-
-// Bug 1082817: ICS B2G has a buggy linker that doesn't always ensure
-// that the EXIDX is sorted by address, as the spec requires. So in
-// that case we build and sort an array of pointers into the index,
-// and binary-search that; otherwise, we search the index in place
-// (avoiding the time and space overhead of the indirection).
-#if defined(ANDROID_VERSION) && ANDROID_VERSION < 16
-#define HAVE_UNSORTED_EXIDX
-#endif
-
-namespace mozilla {
-
-struct PRel31 {
- uint32_t mBits;
- bool topBit() const { return mBits & 0x80000000; }
- uint32_t value() const { return mBits & 0x7fffffff; }
- int32_t offset() const { return (static_cast<int32_t>(mBits) << 1) >> 1; }
- const void *compute() const {
- return reinterpret_cast<const char *>(this) + offset();
- }
-private:
- PRel31(const PRel31 &copied) = delete;
- PRel31() = delete;
-};
-
-struct EHEntry {
- PRel31 startPC;
- PRel31 exidx;
-private:
- EHEntry(const EHEntry &copied) = delete;
- EHEntry() = delete;
-};
-
-class EHState {
- // Note that any core register can be used as a "frame pointer" to
- // influence the unwinding process, so this must track all of them.
- uint32_t mRegs[16];
-public:
- bool unwind(const EHEntry *aEntry, const void *stackBase);
- uint32_t &operator[](int i) { return mRegs[i]; }
- const uint32_t &operator[](int i) const { return mRegs[i]; }
- EHState(const mcontext_t &);
-};
-
-enum {
- R_SP = 13,
- R_LR = 14,
- R_PC = 15
-};
-
-#ifdef HAVE_UNSORTED_EXIDX
-class EHEntryHandle {
- const EHEntry *mValue;
-public:
- EHEntryHandle(const EHEntry *aEntry) : mValue(aEntry) { }
- const EHEntry *value() const { return mValue; }
-};
-
-bool operator<(const EHEntryHandle &lhs, const EHEntryHandle &rhs) {
- return lhs.value()->startPC.compute() < rhs.value()->startPC.compute();
-}
-#endif
-
-class EHTable {
- uint32_t mStartPC;
- uint32_t mEndPC;
- uint32_t mLoadOffset;
-#ifdef HAVE_UNSORTED_EXIDX
- // In principle we should be able to binary-search the index section in
- // place, but the ICS toolchain's linker is noncompliant and produces
- // indices that aren't entirely sorted (e.g., libc). So we have this:
- std::vector<EHEntryHandle> mEntries;
- typedef std::vector<EHEntryHandle>::const_iterator EntryIterator;
- EntryIterator entriesBegin() const { return mEntries.begin(); }
- EntryIterator entriesEnd() const { return mEntries.end(); }
- static const EHEntry* entryGet(EntryIterator aEntry) {
- return aEntry->value();
- }
-#else
- typedef const EHEntry *EntryIterator;
- EntryIterator mEntriesBegin, mEntriesEnd;
- EntryIterator entriesBegin() const { return mEntriesBegin; }
- EntryIterator entriesEnd() const { return mEntriesEnd; }
- static const EHEntry* entryGet(EntryIterator aEntry) { return aEntry; }
-#endif
- std::string mName;
-public:
- EHTable(const void *aELF, size_t aSize, const std::string &aName);
- const EHEntry *lookup(uint32_t aPC) const;
- bool isValid() const { return entriesEnd() != entriesBegin(); }
- const std::string &name() const { return mName; }
- uint32_t startPC() const { return mStartPC; }
- uint32_t endPC() const { return mEndPC; }
- uint32_t loadOffset() const { return mLoadOffset; }
-};
-
-class EHAddrSpace {
- std::vector<uint32_t> mStarts;
- std::vector<EHTable> mTables;
- static mozilla::Atomic<const EHAddrSpace*> sCurrent;
-public:
- explicit EHAddrSpace(const std::vector<EHTable>& aTables);
- const EHTable *lookup(uint32_t aPC) const;
- static void Update();
- static const EHAddrSpace *Get();
-};
-
-
-void EHABIStackWalkInit()
-{
- EHAddrSpace::Update();
-}
-
-size_t EHABIStackWalk(const mcontext_t &aContext, void *stackBase,
- void **aSPs, void **aPCs, const size_t aNumFrames)
-{
- const EHAddrSpace *space = EHAddrSpace::Get();
- EHState state(aContext);
- size_t count = 0;
-
- while (count < aNumFrames) {
- uint32_t pc = state[R_PC], sp = state[R_SP];
- aPCs[count] = reinterpret_cast<void *>(pc);
- aSPs[count] = reinterpret_cast<void *>(sp);
- count++;
-
- if (!space)
- break;
- // TODO: cache these lookups. Binary-searching libxul is
- // expensive (possibly more expensive than doing the actual
- // unwind), and even a small cache should help.
- const EHTable *table = space->lookup(pc);
- if (!table)
- break;
- const EHEntry *entry = table->lookup(pc);
- if (!entry)
- break;
- if (!state.unwind(entry, stackBase))
- break;
- }
-
- return count;
-}
-
-
-class EHInterp {
-public:
- // Note that stackLimit is exclusive and stackBase is inclusive
- // (i.e, stackLimit < SP <= stackBase), following the convention
- // set by the AAPCS spec.
- EHInterp(EHState &aState, const EHEntry *aEntry,
- uint32_t aStackLimit, uint32_t aStackBase)
- : mState(aState),
- mStackLimit(aStackLimit),
- mStackBase(aStackBase),
- mNextWord(0),
- mWordsLeft(0),
- mFailed(false)
- {
- const PRel31 &exidx = aEntry->exidx;
- uint32_t firstWord;
-
- if (exidx.mBits == 1) { // EXIDX_CANTUNWIND
- mFailed = true;
- return;
- }
- if (exidx.topBit()) {
- firstWord = exidx.mBits;
- } else {
- mNextWord = reinterpret_cast<const uint32_t *>(exidx.compute());
- firstWord = *mNextWord++;
- }
-
- switch (firstWord >> 24) {
- case 0x80: // short
- mWord = firstWord << 8;
- mBytesLeft = 3;
- break;
- case 0x81: case 0x82: // long; catch descriptor size ignored
- mWord = firstWord << 16;
- mBytesLeft = 2;
- mWordsLeft = (firstWord >> 16) & 0xff;
- break;
- default:
- // unknown personality
- mFailed = true;
- }
- }
-
- bool unwind();
-
-private:
- // TODO: GCC has been observed not CSEing repeated reads of
- // mState[R_SP] with writes to mFailed between them, suggesting that
- // it hasn't determined that they can't alias and is thus missing
- // optimization opportunities. So, we may want to flatten EHState
- // into this class; this may also make the code simpler.
- EHState &mState;
- uint32_t mStackLimit;
- uint32_t mStackBase;
- const uint32_t *mNextWord;
- uint32_t mWord;
- uint8_t mWordsLeft;
- uint8_t mBytesLeft;
- bool mFailed;
-
- enum {
- I_ADDSP = 0x00, // 0sxxxxxx (subtract if s)
- M_ADDSP = 0x80,
- I_POPMASK = 0x80, // 1000iiii iiiiiiii (if any i set)
- M_POPMASK = 0xf0,
- I_MOVSP = 0x90, // 1001nnnn
- M_MOVSP = 0xf0,
- I_POPN = 0xa0, // 1010lnnn
- M_POPN = 0xf0,
- I_FINISH = 0xb0, // 10110000
- I_POPLO = 0xb1, // 10110001 0000iiii (if any i set)
- I_ADDSPBIG = 0xb2, // 10110010 uleb128
- I_POPFDX = 0xb3, // 10110011 sssscccc
- I_POPFDX8 = 0xb8, // 10111nnn
- M_POPFDX8 = 0xf8,
- // "Intel Wireless MMX" extensions omitted.
- I_POPFDD = 0xc8, // 1100100h sssscccc
- M_POPFDD = 0xfe,
- I_POPFDD8 = 0xd0, // 11010nnn
- M_POPFDD8 = 0xf8
- };
-
- uint8_t next() {
- if (mBytesLeft == 0) {
- if (mWordsLeft == 0) {
- return I_FINISH;
- }
- mWordsLeft--;
- mWord = *mNextWord++;
- mBytesLeft = 4;
- }
- mBytesLeft--;
- mWord = (mWord << 8) | (mWord >> 24); // rotate
- return mWord;
- }
-
- uint32_t &vSP() { return mState[R_SP]; }
- uint32_t *ptrSP() { return reinterpret_cast<uint32_t *>(vSP()); }
-
- void checkStackBase() { if (vSP() > mStackBase) mFailed = true; }
- void checkStackLimit() { if (vSP() <= mStackLimit) mFailed = true; }
- void checkStackAlign() { if ((vSP() & 3) != 0) mFailed = true; }
- void checkStack() {
- checkStackBase();
- checkStackLimit();
- checkStackAlign();
- }
-
- void popRange(uint8_t first, uint8_t last, uint16_t mask) {
- bool hasSP = false;
- uint32_t tmpSP;
- if (mask == 0)
- mFailed = true;
- for (uint8_t r = first; r <= last; ++r) {
- if (mask & 1) {
- if (r == R_SP) {
- hasSP = true;
- tmpSP = *ptrSP();
- } else
- mState[r] = *ptrSP();
- vSP() += 4;
- checkStackBase();
- if (mFailed)
- return;
- }
- mask >>= 1;
- }
- if (hasSP) {
- vSP() = tmpSP;
- checkStack();
- }
- }
-};
-
-
-bool EHState::unwind(const EHEntry *aEntry, const void *stackBasePtr) {
- // The unwinding program cannot set SP to less than the initial value.
- uint32_t stackLimit = mRegs[R_SP] - 4;
- uint32_t stackBase = reinterpret_cast<uint32_t>(stackBasePtr);
- EHInterp interp(*this, aEntry, stackLimit, stackBase);
- return interp.unwind();
-}
-
-bool EHInterp::unwind() {
- mState[R_PC] = 0;
- checkStack();
- while (!mFailed) {
- uint8_t insn = next();
-#if DEBUG_EHABI_UNWIND
- LOGF("unwind insn = %02x", (unsigned)insn);
-#endif
- // Try to put the common cases first.
-
- // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4
- // 01xxxxxx: vsp = vsp - (xxxxxx << 2) - 4
- if ((insn & M_ADDSP) == I_ADDSP) {
- uint32_t offset = ((insn & 0x3f) << 2) + 4;
- if (insn & 0x40) {
- vSP() -= offset;
- checkStackLimit();
- } else {
- vSP() += offset;
- checkStackBase();
- }
- continue;
- }
-
- // 10100nnn: Pop r4-r[4+nnn]
- // 10101nnn: Pop r4-r[4+nnn], r14
- if ((insn & M_POPN) == I_POPN) {
- uint8_t n = (insn & 0x07) + 1;
- bool lr = insn & 0x08;
- uint32_t *ptr = ptrSP();
- vSP() += (n + (lr ? 1 : 0)) * 4;
- checkStackBase();
- for (uint8_t r = 4; r < 4 + n; ++r)
- mState[r] = *ptr++;
- if (lr)
- mState[R_LR] = *ptr++;
- continue;
- }
-
- // 1011000: Finish
- if (insn == I_FINISH) {
- if (mState[R_PC] == 0) {
- mState[R_PC] = mState[R_LR];
- // Non-standard change (bug 916106): Prevent the caller from
- // re-using LR. Since the caller is by definition not a leaf
- // routine, it will have to restore LR from somewhere to
- // return to its own caller, so we can safely zero it here.
- // This makes a difference only if an error in unwinding
- // (e.g., caused by starting from within a prologue/epilogue)
- // causes us to load a pointer to a leaf routine as LR; if we
- // don't do something, we'll go into an infinite loop of
- // "returning" to that same function.
- mState[R_LR] = 0;
- }
- return true;
- }
-
- // 1001nnnn: Set vsp = r[nnnn]
- if ((insn & M_MOVSP) == I_MOVSP) {
- vSP() = mState[insn & 0x0f];
- checkStack();
- continue;
- }
-
- // 11001000 sssscccc: Pop VFP regs D[16+ssss]-D[16+ssss+cccc] (as FLDMFDD)
- // 11001001 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDD)
- if ((insn & M_POPFDD) == I_POPFDD) {
- uint8_t n = (next() & 0x0f) + 1;
- // Note: if the 16+ssss+cccc > 31, the encoding is reserved.
- // As the space is currently unused, we don't try to check.
- vSP() += 8 * n;
- checkStackBase();
- continue;
- }
-
- // 11010nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDD)
- if ((insn & M_POPFDD8) == I_POPFDD8) {
- uint8_t n = (insn & 0x07) + 1;
- vSP() += 8 * n;
- checkStackBase();
- continue;
- }
-
- // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
- if (insn == I_ADDSPBIG) {
- uint32_t acc = 0;
- uint8_t shift = 0;
- uint8_t byte;
- do {
- if (shift >= 32)
- return false;
- byte = next();
- acc |= (byte & 0x7f) << shift;
- shift += 7;
- } while (byte & 0x80);
- uint32_t offset = 0x204 + (acc << 2);
- // The calculations above could have overflowed.
- // But the one we care about is this:
- if (vSP() + offset < vSP())
- mFailed = true;
- vSP() += offset;
- // ...so that this is the only other check needed:
- checkStackBase();
- continue;
- }
-
- // 1000iiii iiiiiiii (i not all 0): Pop under masks {r15-r12}, {r11-r4}
- if ((insn & M_POPMASK) == I_POPMASK) {
- popRange(4, 15, ((insn & 0x0f) << 8) | next());
- continue;
- }
-
- // 1011001 0000iiii (i not all 0): Pop under mask {r3-r0}
- if (insn == I_POPLO) {
- popRange(0, 3, next() & 0x0f);
- continue;
- }
-
- // 10110011 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDX)
- if (insn == I_POPFDX) {
- uint8_t n = (next() & 0x0f) + 1;
- vSP() += 8 * n + 4;
- checkStackBase();
- continue;
- }
-
- // 10111nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDX)
- if ((insn & M_POPFDX8) == I_POPFDX8) {
- uint8_t n = (insn & 0x07) + 1;
- vSP() += 8 * n + 4;
- checkStackBase();
- continue;
- }
-
- // unhandled instruction
-#ifdef DEBUG_EHABI_UNWIND
- LOGF("Unhandled EHABI instruction 0x%02x", insn);
-#endif
- mFailed = true;
- }
- return false;
-}
-
-
-bool operator<(const EHTable &lhs, const EHTable &rhs) {
- return lhs.startPC() < rhs.startPC();
-}
-
-// Async signal unsafe.
-EHAddrSpace::EHAddrSpace(const std::vector<EHTable>& aTables)
- : mTables(aTables)
-{
- std::sort(mTables.begin(), mTables.end());
- DebugOnly<uint32_t> lastEnd = 0;
- for (std::vector<EHTable>::iterator i = mTables.begin();
- i != mTables.end(); ++i) {
- MOZ_ASSERT(i->startPC() >= lastEnd);
- mStarts.push_back(i->startPC());
- lastEnd = i->endPC();
- }
-}
-
-const EHTable *EHAddrSpace::lookup(uint32_t aPC) const {
- ptrdiff_t i = (std::upper_bound(mStarts.begin(), mStarts.end(), aPC)
- - mStarts.begin()) - 1;
-
- if (i < 0 || aPC >= mTables[i].endPC())
- return 0;
- return &mTables[i];
-}
-
-
-const EHEntry *EHTable::lookup(uint32_t aPC) const {
- MOZ_ASSERT(aPC >= mStartPC);
- if (aPC >= mEndPC)
- return nullptr;
-
- EntryIterator begin = entriesBegin();
- EntryIterator end = entriesEnd();
- MOZ_ASSERT(begin < end);
- if (aPC < reinterpret_cast<uint32_t>(entryGet(begin)->startPC.compute()))
- return nullptr;
-
- while (end - begin > 1) {
-#ifdef EHABI_UNWIND_MORE_ASSERTS
- if (entryGet(end - 1)->startPC.compute()
- < entryGet(begin)->startPC.compute()) {
- MOZ_CRASH("unsorted exidx");
- }
-#endif
- EntryIterator mid = begin + (end - begin) / 2;
- if (aPC < reinterpret_cast<uint32_t>(entryGet(mid)->startPC.compute()))
- end = mid;
- else
- begin = mid;
- }
- return entryGet(begin);
-}
-
-
-#if MOZ_LITTLE_ENDIAN
-static const unsigned char hostEndian = ELFDATA2LSB;
-#elif MOZ_BIG_ENDIAN
-static const unsigned char hostEndian = ELFDATA2MSB;
-#else
-#error "No endian?"
-#endif
-
-// Async signal unsafe: std::vector::reserve, std::string copy ctor.
-EHTable::EHTable(const void *aELF, size_t aSize, const std::string &aName)
- : mStartPC(~0), // largest uint32_t
- mEndPC(0),
-#ifndef HAVE_UNSORTED_EXIDX
- mEntriesBegin(nullptr),
- mEntriesEnd(nullptr),
-#endif
- mName(aName)
-{
- const uint32_t base = reinterpret_cast<uint32_t>(aELF);
-
- if (aSize < sizeof(Elf32_Ehdr))
- return;
-
- const Elf32_Ehdr &file = *(reinterpret_cast<Elf32_Ehdr *>(base));
- if (memcmp(&file.e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 ||
- file.e_ident[EI_CLASS] != ELFCLASS32 ||
- file.e_ident[EI_DATA] != hostEndian ||
- file.e_ident[EI_VERSION] != EV_CURRENT ||
- file.e_ident[EI_OSABI] != ELFOSABI_SYSV ||
-#ifdef EI_ABIVERSION
- file.e_ident[EI_ABIVERSION] != 0 ||
-#endif
- file.e_machine != EM_ARM ||
- file.e_version != EV_CURRENT)
- // e_flags?
- return;
-
- MOZ_ASSERT(file.e_phoff + file.e_phnum * file.e_phentsize <= aSize);
- const Elf32_Phdr *exidxHdr = 0, *zeroHdr = 0;
- for (unsigned i = 0; i < file.e_phnum; ++i) {
- const Elf32_Phdr &phdr =
- *(reinterpret_cast<Elf32_Phdr *>(base + file.e_phoff
- + i * file.e_phentsize));
- if (phdr.p_type == PT_ARM_EXIDX) {
- exidxHdr = &phdr;
- } else if (phdr.p_type == PT_LOAD) {
- if (phdr.p_offset == 0) {
- zeroHdr = &phdr;
- }
- if (phdr.p_flags & PF_X) {
- mStartPC = std::min(mStartPC, phdr.p_vaddr);
- mEndPC = std::max(mEndPC, phdr.p_vaddr + phdr.p_memsz);
- }
- }
- }
- if (!exidxHdr)
- return;
- if (!zeroHdr)
- return;
- mLoadOffset = base - zeroHdr->p_vaddr;
- mStartPC += mLoadOffset;
- mEndPC += mLoadOffset;
-
- // Create a sorted index of the index to work around linker bugs.
- const EHEntry *startTable =
- reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr);
- const EHEntry *endTable =
- reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr
- + exidxHdr->p_memsz);
-#ifdef HAVE_UNSORTED_EXIDX
- mEntries.reserve(endTable - startTable);
- for (const EHEntry *i = startTable; i < endTable; ++i)
- mEntries.push_back(i);
- std::sort(mEntries.begin(), mEntries.end());
-#else
- mEntriesBegin = startTable;
- mEntriesEnd = endTable;
-#endif
-}
-
-
-mozilla::Atomic<const EHAddrSpace*> EHAddrSpace::sCurrent(nullptr);
-
-// Async signal safe; can fail if Update() hasn't returned yet.
-const EHAddrSpace *EHAddrSpace::Get() {
- return sCurrent;
-}
-
-// Collect unwinding information from loaded objects. Calls after the
-// first have no effect. Async signal unsafe.
-void EHAddrSpace::Update() {
- const EHAddrSpace *space = sCurrent;
- if (space)
- return;
-
- SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
- std::vector<EHTable> tables;
-
- for (size_t i = 0; i < info.GetSize(); ++i) {
- const SharedLibrary &lib = info.GetEntry(i);
- if (lib.GetOffset() != 0)
- // TODO: if it has a name, and we haven't seen a mapping of
- // offset 0 for that file, try opening it and reading the
- // headers instead. The only thing I've seen so far that's
- // linked so as to need that treatment is the dynamic linker
- // itself.
- continue;
- EHTable tab(reinterpret_cast<const void *>(lib.GetStart()),
- lib.GetEnd() - lib.GetStart(), lib.GetName());
- if (tab.isValid())
- tables.push_back(tab);
- }
- space = new EHAddrSpace(tables);
-
- if (!sCurrent.compareExchange(nullptr, space)) {
- delete space;
- space = sCurrent;
- }
-}
-
-
-EHState::EHState(const mcontext_t &context) {
-#ifdef linux
- mRegs[0] = context.arm_r0;
- mRegs[1] = context.arm_r1;
- mRegs[2] = context.arm_r2;
- mRegs[3] = context.arm_r3;
- mRegs[4] = context.arm_r4;
- mRegs[5] = context.arm_r5;
- mRegs[6] = context.arm_r6;
- mRegs[7] = context.arm_r7;
- mRegs[8] = context.arm_r8;
- mRegs[9] = context.arm_r9;
- mRegs[10] = context.arm_r10;
- mRegs[11] = context.arm_fp;
- mRegs[12] = context.arm_ip;
- mRegs[13] = context.arm_sp;
- mRegs[14] = context.arm_lr;
- mRegs[15] = context.arm_pc;
-#else
-# error "Unhandled OS for ARM EHABI unwinding"
-#endif
-}
-
-} // namespace mozilla
-
diff --git a/tools/profiler/core/EHABIStackWalk.h b/tools/profiler/core/EHABIStackWalk.h
deleted file mode 100644
index 5529d9511..000000000
--- a/tools/profiler/core/EHABIStackWalk.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- 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/. */
-
-/*
- * This is an implementation of stack unwinding according to a subset
- * of the ARM Exception Handling ABI; see the comment at the top of
- * the .cpp file for details.
- */
-
-#ifndef mozilla_EHABIStackWalk_h__
-#define mozilla_EHABIStackWalk_h__
-
-#include <stddef.h>
-#include <ucontext.h>
-
-namespace mozilla {
-
-void EHABIStackWalkInit();
-
-size_t EHABIStackWalk(const mcontext_t &aContext, void *stackBase,
- void **aSPs, void **aPCs, size_t aNumFrames);
-
-}
-
-#endif
diff --git a/tools/profiler/core/GeckoSampler.cpp b/tools/profiler/core/GeckoSampler.cpp
deleted file mode 100644
index 4283542f6..000000000
--- a/tools/profiler/core/GeckoSampler.cpp
+++ /dev/null
@@ -1,1306 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 <algorithm>
-#include <string>
-#include <stdio.h>
-#include <fstream>
-#include <sstream>
-#include "GeckoProfiler.h"
-#ifndef SPS_STANDALONE
-#include "SaveProfileTask.h"
-#include "nsThreadUtils.h"
-#include "prenv.h"
-#include "prtime.h"
-#include "nsXULAppAPI.h"
-#endif
-#include "ProfileEntry.h"
-#include "SyncProfile.h"
-#include "platform.h"
-#include "shared-libraries.h"
-#include "mozilla/StackWalk.h"
-#include "GeckoSampler.h"
-
-// JSON
-#include "ProfileJSONWriter.h"
-
-#ifndef SPS_STANDALONE
-// Meta
-#include "nsXPCOM.h"
-#include "nsXPCOMCID.h"
-#include "nsIHttpProtocolHandler.h"
-#include "nsServiceManagerUtils.h"
-#include "nsIXULRuntime.h"
-#include "nsIXULAppInfo.h"
-#include "nsDirectoryServiceUtils.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsIObserverService.h"
-#include "mozilla/Services.h"
-#include "PlatformMacros.h"
-#include "nsTArray.h"
-
-#include "mozilla/ProfileGatherer.h"
-#endif
-
-#if defined(SPS_OS_android)
- #include "FennecJNIWrappers.h"
-#endif
-
-#ifndef SPS_STANDALONE
-// JS
-#include "jsfriendapi.h"
-#include "js/ProfilingFrameIterator.h"
-#endif
-
-#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
- #define USE_NS_STACKWALK
-#endif
-
-#if defined(XP_WIN)
-typedef CONTEXT tickcontext_t;
-#elif defined(LINUX)
-#include <ucontext.h>
-typedef ucontext_t tickcontext_t;
-#endif
-
-#if defined(LINUX) || defined(XP_MACOSX)
-#include <sys/types.h>
-pid_t gettid();
-#endif
-
-#if defined(__arm__) && defined(ANDROID)
- // Should also work on ARM Linux, but not tested there yet.
- #define USE_EHABI_STACKWALK
-#endif
-#ifdef USE_EHABI_STACKWALK
- #include "EHABIStackWalk.h"
-#endif
-
-#ifndef SPS_STANDALONE
-#if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
-# define USE_LUL_STACKWALK
-# include "lul/LulMain.h"
-# include "lul/platform-linux-lul.h"
-#endif
-#endif
-
-using std::string;
-using namespace mozilla;
-
-#ifndef MAXPATHLEN
- #ifdef PATH_MAX
- #define MAXPATHLEN PATH_MAX
- #elif defined(MAX_PATH)
- #define MAXPATHLEN MAX_PATH
- #elif defined(_MAX_PATH)
- #define MAXPATHLEN _MAX_PATH
- #elif defined(CCHMAXPATH)
- #define MAXPATHLEN CCHMAXPATH
- #else
- #define MAXPATHLEN 1024
- #endif
-#endif
-
-#ifdef MOZ_VALGRIND
-# include <valgrind/memcheck.h>
-#else
-# define VALGRIND_MAKE_MEM_DEFINED(_addr,_len) ((void)0)
-#endif
-
-
-///////////////////////////////////////////////////////////////////////
-// BEGIN SaveProfileTask et al
-
-static void
-AddSharedLibraryInfoToStream(std::ostream& aStream, const SharedLibrary& aLib)
-{
- aStream << "{";
- aStream << "\"start\":" << aLib.GetStart();
- aStream << ",\"end\":" << aLib.GetEnd();
- aStream << ",\"offset\":" << aLib.GetOffset();
- aStream << ",\"name\":\"" << aLib.GetName() << "\"";
- const std::string &breakpadId = aLib.GetBreakpadId();
- aStream << ",\"breakpadId\":\"" << breakpadId << "\"";
-#ifdef XP_WIN
- // FIXME: remove this XP_WIN code when the profiler plugin has switched to
- // using breakpadId.
- std::string pdbSignature = breakpadId.substr(0, 32);
- std::string pdbAgeStr = breakpadId.substr(32, breakpadId.size() - 1);
-
- std::stringstream stream;
- stream << pdbAgeStr;
-
- unsigned pdbAge;
- stream << std::hex;
- stream >> pdbAge;
-
-#ifdef DEBUG
- std::ostringstream oStream;
- oStream << pdbSignature << std::hex << std::uppercase << pdbAge;
- MOZ_ASSERT(breakpadId == oStream.str());
-#endif
-
- aStream << ",\"pdbSignature\":\"" << pdbSignature << "\"";
- aStream << ",\"pdbAge\":" << pdbAge;
- aStream << ",\"pdbName\":\"" << aLib.GetName() << "\"";
-#endif
- aStream << "}";
-}
-
-std::string
-GetSharedLibraryInfoStringInternal()
-{
- SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
- if (info.GetSize() == 0)
- return "[]";
-
- std::ostringstream os;
- os << "[";
- AddSharedLibraryInfoToStream(os, info.GetEntry(0));
-
- for (size_t i = 1; i < info.GetSize(); i++) {
- os << ",";
- AddSharedLibraryInfoToStream(os, info.GetEntry(i));
- }
-
- os << "]";
- return os.str();
-}
-
-static bool
-hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) {
- for(size_t i = 0; i < aFeatureCount; i++) {
- if (strcmp(aFeatures[i], aFeature) == 0)
- return true;
- }
- return false;
-}
-
-GeckoSampler::GeckoSampler(double aInterval, int aEntrySize,
- const char** aFeatures, uint32_t aFeatureCount,
- const char** aThreadNameFilters, uint32_t aFilterCount)
- : Sampler(aInterval, true, aEntrySize)
- , mPrimaryThreadProfile(nullptr)
- , mBuffer(new ProfileBuffer(aEntrySize))
- , mSaveRequested(false)
-#if defined(XP_WIN)
- , mIntelPowerGadget(nullptr)
-#endif
-{
- mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
-
- mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
- mProfileGPU = hasFeature(aFeatures, aFeatureCount, "gpu");
- mProfilePower = hasFeature(aFeatures, aFeatureCount, "power");
- // Users sometimes ask to filter by a list of threads but forget to request
- // profiling non main threads. Let's make it implificit if we have a filter
- mProfileThreads = hasFeature(aFeatures, aFeatureCount, "threads") || aFilterCount > 0;
- mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
- mPrivacyMode = hasFeature(aFeatures, aFeatureCount, "privacy");
- mAddMainThreadIO = hasFeature(aFeatures, aFeatureCount, "mainthreadio");
- mProfileMemory = hasFeature(aFeatures, aFeatureCount, "memory");
- mTaskTracer = hasFeature(aFeatures, aFeatureCount, "tasktracer");
- mLayersDump = hasFeature(aFeatures, aFeatureCount, "layersdump");
- mDisplayListDump = hasFeature(aFeatures, aFeatureCount, "displaylistdump");
- mProfileRestyle = hasFeature(aFeatures, aFeatureCount, "restyle");
-
-#if defined(XP_WIN)
- if (mProfilePower) {
- mIntelPowerGadget = new IntelPowerGadget();
- mProfilePower = mIntelPowerGadget->Init();
- }
-#endif
-
-#if defined(SPS_OS_android)
- mProfileJava = mozilla::jni::IsFennec() &&
- hasFeature(aFeatures, aFeatureCount, "java");
-#else
- mProfileJava = false;
-#endif
-
- // Deep copy aThreadNameFilters
- MOZ_ALWAYS_TRUE(mThreadNameFilters.resize(aFilterCount));
- for (uint32_t i = 0; i < aFilterCount; ++i) {
- mThreadNameFilters[i] = aThreadNameFilters[i];
- }
-
- // Deep copy aFeatures
- MOZ_ALWAYS_TRUE(mFeatures.resize(aFeatureCount));
- for (uint32_t i = 0; i < aFeatureCount; ++i) {
- mFeatures[i] = aFeatures[i];
- }
-
- bool ignore;
- sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
-
- {
- ::MutexAutoLock lock(*sRegisteredThreadsMutex);
-
- // Create ThreadProfile for each registered thread
- for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
- ThreadInfo* info = sRegisteredThreads->at(i);
-
- RegisterThread(info);
- }
-
- SetActiveSampler(this);
- }
-
-#ifdef MOZ_TASK_TRACER
- if (mTaskTracer) {
- mozilla::tasktracer::StartLogging();
- }
-#endif
-
- mGatherer = new mozilla::ProfileGatherer(this);
-}
-
-GeckoSampler::~GeckoSampler()
-{
- if (IsActive())
- Stop();
-
- SetActiveSampler(nullptr);
-
- // Destroy ThreadProfile for all threads
- {
- ::MutexAutoLock lock(*sRegisteredThreadsMutex);
-
- for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
- ThreadInfo* info = sRegisteredThreads->at(i);
- ThreadProfile* profile = info->Profile();
- if (profile) {
- delete profile;
- info->SetProfile(nullptr);
- }
- // We've stopped profiling. We no longer need to retain
- // information for an old thread.
- if (info->IsPendingDelete()) {
- delete info;
- sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
- i--;
- }
- }
- }
-#if defined(XP_WIN)
- delete mIntelPowerGadget;
-#endif
-
- // Cancel any in-flight async profile gatherering
- // requests
- mGatherer->Cancel();
-}
-
-void GeckoSampler::HandleSaveRequest()
-{
- if (!mSaveRequested)
- return;
- mSaveRequested = false;
-
-#ifndef SPS_STANDALONE
- // TODO: Use use the ipc/chromium Tasks here to support processes
- // without XPCOM.
- nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
- NS_DispatchToMainThread(runnable);
-#endif
-}
-
-void GeckoSampler::DeleteExpiredMarkers()
-{
- mBuffer->deleteExpiredStoredMarkers();
-}
-
-void GeckoSampler::StreamTaskTracer(SpliceableJSONWriter& aWriter)
-{
-#ifdef MOZ_TASK_TRACER
- aWriter.StartArrayProperty("data");
- UniquePtr<nsTArray<nsCString>> data = mozilla::tasktracer::GetLoggedData(sStartTime);
- for (uint32_t i = 0; i < data->Length(); ++i) {
- aWriter.StringElement((data->ElementAt(i)).get());
- }
- aWriter.EndArray();
-
- aWriter.StartArrayProperty("threads");
- ::MutexAutoLock lock(*sRegisteredThreadsMutex);
- for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
- // Thread meta data
- ThreadInfo* info = sRegisteredThreads->at(i);
- aWriter.StartObjectElement();
- if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
- // TODO Add the proper plugin name
- aWriter.StringProperty("name", "Plugin");
- } else {
- aWriter.StringProperty("name", info->Name());
- }
- aWriter.IntProperty("tid", static_cast<int>(info->ThreadId()));
- aWriter.EndObject();
- }
- aWriter.EndArray();
-
- aWriter.DoubleProperty("start", static_cast<double>(mozilla::tasktracer::GetStartTime()));
-#endif
-}
-
-
-void GeckoSampler::StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter)
-{
- aWriter.IntProperty("version", 3);
- aWriter.DoubleProperty("interval", interval());
- aWriter.IntProperty("stackwalk", mUseStackWalk);
-
-#ifndef SPS_STANDALONE
- mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - sStartTime;
- aWriter.DoubleProperty("startTime", static_cast<double>(PR_Now()/1000.0 - delta.ToMilliseconds()));
-
- aWriter.IntProperty("processType", XRE_GetProcessType());
-
- nsresult res;
- nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
- if (!NS_FAILED(res)) {
- nsAutoCString string;
-
- res = http->GetPlatform(string);
- if (!NS_FAILED(res))
- aWriter.StringProperty("platform", string.Data());
-
- res = http->GetOscpu(string);
- if (!NS_FAILED(res))
- aWriter.StringProperty("oscpu", string.Data());
-
- res = http->GetMisc(string);
- if (!NS_FAILED(res))
- aWriter.StringProperty("misc", string.Data());
- }
-
- nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
- if (runtime) {
- nsAutoCString string;
-
- res = runtime->GetXPCOMABI(string);
- if (!NS_FAILED(res))
- aWriter.StringProperty("abi", string.Data());
-
- res = runtime->GetWidgetToolkit(string);
- if (!NS_FAILED(res))
- aWriter.StringProperty("toolkit", string.Data());
- }
-
- nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
- if (appInfo) {
- nsAutoCString string;
-
- res = appInfo->GetName(string);
- if (!NS_FAILED(res))
- aWriter.StringProperty("product", string.Data());
- }
-#endif
-}
-
-void GeckoSampler::ToStreamAsJSON(std::ostream& stream, double aSinceTime)
-{
- SpliceableJSONWriter b(mozilla::MakeUnique<OStreamJSONWriteFunc>(stream));
- StreamJSON(b, aSinceTime);
-}
-
-#ifndef SPS_STANDALONE
-JSObject* GeckoSampler::ToJSObject(JSContext *aCx, double aSinceTime)
-{
- JS::RootedValue val(aCx);
- {
- UniquePtr<char[]> buf = ToJSON(aSinceTime);
- NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
- MOZ_ALWAYS_TRUE(JS_ParseJSON(aCx, static_cast<const char16_t*>(js_string.get()),
- js_string.Length(), &val));
- }
- return &val.toObject();
-}
-
-void GeckoSampler::GetGatherer(nsISupports** aRetVal)
-{
- if (!aRetVal || NS_WARN_IF(!mGatherer)) {
- return;
- }
- NS_ADDREF(*aRetVal = mGatherer);
-}
-#endif
-
-UniquePtr<char[]> GeckoSampler::ToJSON(double aSinceTime)
-{
- SpliceableChunkedJSONWriter b;
- StreamJSON(b, aSinceTime);
- return b.WriteFunc()->CopyData();
-}
-
-void GeckoSampler::ToJSObjectAsync(double aSinceTime,
- mozilla::dom::Promise* aPromise)
-{
- if (NS_WARN_IF(!mGatherer)) {
- return;
- }
-
- mGatherer->Start(aSinceTime, aPromise);
-}
-
-struct SubprocessClosure {
- explicit SubprocessClosure(SpliceableJSONWriter* aWriter)
- : mWriter(aWriter)
- {}
-
- SpliceableJSONWriter* mWriter;
-};
-
-void SubProcessCallback(const char* aProfile, void* aClosure)
-{
- // Called by the observer to get their profile data included
- // as a sub profile
- SubprocessClosure* closure = (SubprocessClosure*)aClosure;
-
- // Add the string profile into the profile
- closure->mWriter->StringElement(aProfile);
-}
-
-
-#if defined(SPS_OS_android)
-static
-void BuildJavaThreadJSObject(SpliceableJSONWriter& aWriter)
-{
- aWriter.StringProperty("name", "Java Main Thread");
-
- aWriter.StartArrayProperty("samples");
-
- // for each sample
- for (int sampleId = 0; true; sampleId++) {
- bool firstRun = true;
- // for each frame
- for (int frameId = 0; true; frameId++) {
- jni::String::LocalRef frameName =
- java::GeckoJavaSampler::GetFrameName(0, sampleId, frameId);
- // when we run out of frames, we stop looping
- if (!frameName) {
- // if we found at least one frame, we have objects to close
- if (!firstRun) {
- aWriter.EndArray();
- aWriter.EndObject();
- }
- break;
- }
- // the first time around, open the sample object and frames array
- if (firstRun) {
- firstRun = false;
-
- double sampleTime =
- java::GeckoJavaSampler::GetSampleTime(0, sampleId);
-
- aWriter.StartObjectElement();
- aWriter.DoubleProperty("time", sampleTime);
-
- aWriter.StartArrayProperty("frames");
- }
- // add a frame to the sample
- aWriter.StartObjectElement();
- aWriter.StringProperty("location",
- frameName->ToCString().BeginReading());
- aWriter.EndObject();
- }
- // if we found no frames for this sample, we are done
- if (firstRun) {
- break;
- }
- }
-
- aWriter.EndArray();
-}
-#endif
-
-void GeckoSampler::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
-{
- aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
- {
- // Put shared library info
- aWriter.StringProperty("libs", GetSharedLibraryInfoStringInternal().c_str());
-
- // Put meta data
- aWriter.StartObjectProperty("meta");
- StreamMetaJSCustomObject(aWriter);
- aWriter.EndObject();
-
- // Data of TaskTracer doesn't belong in the circular buffer.
- if (TaskTracer()) {
- aWriter.StartObjectProperty("tasktracer");
- StreamTaskTracer(aWriter);
- aWriter.EndObject();
- }
-
- // Lists the samples for each ThreadProfile
- aWriter.StartArrayProperty("threads");
- {
- SetPaused(true);
-
- {
- ::MutexAutoLock lock(*sRegisteredThreadsMutex);
-
- for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
- // Thread not being profiled, skip it
- if (!sRegisteredThreads->at(i)->Profile())
- continue;
-
- // Note that we intentionally include ThreadProfile which
- // have been marked for pending delete.
-
- ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
-
- sRegisteredThreads->at(i)->Profile()->StreamJSON(aWriter, aSinceTime);
- }
- }
-
-#ifndef SPS_STANDALONE
- if (Sampler::CanNotifyObservers()) {
- // Send a event asking any subprocesses (plugins) to
- // give us their information
- SubprocessClosure closure(&aWriter);
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os) {
- RefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
- os->NotifyObservers(pse, "profiler-subprocess", nullptr);
- }
- }
-
- #if defined(SPS_OS_android)
- if (ProfileJava()) {
- java::GeckoJavaSampler::Pause();
-
- aWriter.Start();
- {
- BuildJavaThreadJSObject(aWriter);
- }
- aWriter.End();
-
- java::GeckoJavaSampler::Unpause();
- }
- #endif
-#endif
-
- SetPaused(false);
- }
- aWriter.EndArray();
- }
- aWriter.End();
-}
-
-void GeckoSampler::FlushOnJSShutdown(JSContext* aContext)
-{
-#ifndef SPS_STANDALONE
- SetPaused(true);
-
- {
- ::MutexAutoLock lock(*sRegisteredThreadsMutex);
-
- for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
- // Thread not being profiled, skip it.
- if (!sRegisteredThreads->at(i)->Profile() ||
- sRegisteredThreads->at(i)->IsPendingDelete()) {
- continue;
- }
-
- // Thread not profiling the context that's going away, skip it.
- if (sRegisteredThreads->at(i)->Profile()->GetPseudoStack()->mContext != aContext) {
- continue;
- }
-
- ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
- sRegisteredThreads->at(i)->Profile()->FlushSamplesAndMarkers();
- }
- }
-
- SetPaused(false);
-#endif
-}
-
-void PseudoStack::flushSamplerOnJSShutdown()
-{
-#ifndef SPS_STANDALONE
- MOZ_ASSERT(mContext);
- GeckoSampler* t = tlsTicker.get();
- if (t) {
- t->FlushOnJSShutdown(mContext);
- }
-#endif
-}
-
-// END SaveProfileTask et al
-////////////////////////////////////////////////////////////////////////
-
-static
-void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
-{
- aProfile.addTag(ProfileEntry(aTagName, ""));
- // Add one to store the null termination
- size_t strLen = strlen(aStr) + 1;
- for (size_t j = 0; j < strLen;) {
- // Store as many characters in the void* as the platform allows
- char text[sizeof(void*)];
- size_t len = sizeof(void*)/sizeof(char);
- if (j+len >= strLen) {
- len = strLen - j;
- }
- memcpy(text, &aStr[j], len);
- j += sizeof(void*)/sizeof(char);
- // Cast to *((void**) to pass the text data to a void*
- aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
- }
-}
-
-static
-void addPseudoEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
- PseudoStack *stack, void *lastpc)
-{
- // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations
- // and should not be recorded in the profile.
- if (entry.hasFlag(StackEntry::BEGIN_PSEUDO_JS))
- return;
-
- int lineno = -1;
-
- // First entry has tagName 's' (start)
- // Check for magic pointer bit 1 to indicate copy
- const char* sampleLabel = entry.label();
- if (entry.isCopyLabel()) {
- // Store the string using 1 or more 'd' (dynamic) tags
- // that will happen to the preceding tag
-
- addDynamicTag(aProfile, 'c', sampleLabel);
-#ifndef SPS_STANDALONE
- if (entry.isJs()) {
- JSScript* script = entry.script();
- if (script) {
- if (!entry.pc()) {
- // The JIT only allows the top-most entry to have a nullptr pc
- MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
- // If stack-walking was disabled, then that's just unfortunate
- if (lastpc) {
- jsbytecode *jspc = js::ProfilingGetPC(stack->mContext, script,
- lastpc);
- if (jspc) {
- lineno = JS_PCToLineNumber(script, jspc);
- }
- }
- } else {
- lineno = JS_PCToLineNumber(script, entry.pc());
- }
- }
- } else {
- lineno = entry.line();
- }
-#endif
- } else {
- aProfile.addTag(ProfileEntry('c', sampleLabel));
-
- // XXX: Bug 1010578. Don't assume a CPP entry and try to get the
- // line for js entries as well.
- if (entry.isCpp()) {
- lineno = entry.line();
- }
- }
-
- if (lineno != -1) {
- aProfile.addTag(ProfileEntry('n', lineno));
- }
-
- uint32_t category = entry.category();
- MOZ_ASSERT(!(category & StackEntry::IS_CPP_ENTRY));
- MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY));
-
- if (category) {
- aProfile.addTag(ProfileEntry('y', (int)category));
- }
-}
-
-struct NativeStack
-{
- void** pc_array;
- void** sp_array;
- size_t size;
- size_t count;
-};
-
-mozilla::Atomic<bool> WALKING_JS_STACK(false);
-
-struct AutoWalkJSStack {
- bool walkAllowed;
-
- AutoWalkJSStack() : walkAllowed(false) {
- walkAllowed = WALKING_JS_STACK.compareExchange(false, true);
- }
-
- ~AutoWalkJSStack() {
- if (walkAllowed)
- WALKING_JS_STACK = false;
- }
-};
-
-static
-void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, NativeStack& aNativeStack)
-{
- PseudoStack* pseudoStack = aProfile.GetPseudoStack();
- volatile StackEntry *pseudoFrames = pseudoStack->mStack;
- uint32_t pseudoCount = pseudoStack->stackSize();
-
- // Make a copy of the JS stack into a JSFrame array. This is necessary since,
- // like the native stack, the JS stack is iterated youngest-to-oldest and we
- // need to iterate oldest-to-youngest when adding entries to aProfile.
-
- // Synchronous sampling reports an invalid buffer generation to
- // ProfilingFrameIterator to avoid incorrectly resetting the generation of
- // sampled JIT entries inside the JS engine. See note below concerning 'J'
- // entries.
- uint32_t startBufferGen;
- if (aSample->isSamplingCurrentThread) {
- startBufferGen = UINT32_MAX;
- } else {
- startBufferGen = aProfile.bufferGeneration();
- }
- uint32_t jsCount = 0;
-#ifndef SPS_STANDALONE
- JS::ProfilingFrameIterator::Frame jsFrames[1000];
- // Only walk jit stack if profiling frame iterator is turned on.
- if (pseudoStack->mContext && JS::IsProfilingEnabledForContext(pseudoStack->mContext)) {
- AutoWalkJSStack autoWalkJSStack;
- const uint32_t maxFrames = mozilla::ArrayLength(jsFrames);
-
- if (aSample && autoWalkJSStack.walkAllowed) {
- JS::ProfilingFrameIterator::RegisterState registerState;
- registerState.pc = aSample->pc;
- registerState.sp = aSample->sp;
-#ifdef ENABLE_ARM_LR_SAVING
- registerState.lr = aSample->lr;
-#endif
-
- JS::ProfilingFrameIterator jsIter(pseudoStack->mContext,
- registerState,
- startBufferGen);
- for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) {
- // See note below regarding 'J' entries.
- if (aSample->isSamplingCurrentThread || jsIter.isWasm()) {
- uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames);
- jsCount += extracted;
- if (jsCount == maxFrames)
- break;
- } else {
- mozilla::Maybe<JS::ProfilingFrameIterator::Frame> frame =
- jsIter.getPhysicalFrameWithoutLabel();
- if (frame.isSome())
- jsFrames[jsCount++] = mozilla::Move(frame.ref());
- }
- }
- }
- }
-#endif
-
- // Start the sample with a root entry.
- aProfile.addTag(ProfileEntry('s', "(root)"));
-
- // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
- // native arrays are ordered youngest-to-oldest. We must add frames to
- // aProfile oldest-to-youngest. Thus, iterate over the pseudo-stack forwards
- // and JS and native arrays backwards. Note: this means the terminating
- // condition jsIndex and nativeIndex is being < 0.
- uint32_t pseudoIndex = 0;
- int32_t jsIndex = jsCount - 1;
- int32_t nativeIndex = aNativeStack.count - 1;
-
- uint8_t *lastPseudoCppStackAddr = nullptr;
-
- // Iterate as long as there is at least one frame remaining.
- while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
- // There are 1 to 3 frames available. Find and add the oldest.
-
- uint8_t *pseudoStackAddr = nullptr;
- uint8_t *jsStackAddr = nullptr;
- uint8_t *nativeStackAddr = nullptr;
-
- if (pseudoIndex != pseudoCount) {
- volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
-
- if (pseudoFrame.isCpp())
- lastPseudoCppStackAddr = (uint8_t *) pseudoFrame.stackAddress();
-
-#ifndef SPS_STANDALONE
- // Skip any pseudo-stack JS frames which are marked isOSR
- // Pseudostack frames are marked isOSR when the JS interpreter
- // enters a jit frame on a loop edge (via on-stack-replacement,
- // or OSR). To avoid both the pseudoframe and jit frame being
- // recorded (and showing up twice), the interpreter marks the
- // interpreter pseudostack entry with the OSR flag to ensure that
- // it doesn't get counted.
- if (pseudoFrame.isJs() && pseudoFrame.isOSR()) {
- pseudoIndex++;
- continue;
- }
-#endif
-
- MOZ_ASSERT(lastPseudoCppStackAddr);
- pseudoStackAddr = lastPseudoCppStackAddr;
- }
-
-#ifndef SPS_STANDALONE
- if (jsIndex >= 0)
- jsStackAddr = (uint8_t *) jsFrames[jsIndex].stackAddress;
-#endif
-
- if (nativeIndex >= 0)
- nativeStackAddr = (uint8_t *) aNativeStack.sp_array[nativeIndex];
-
- // If there's a native stack entry which has the same SP as a
- // pseudo stack entry, pretend we didn't see the native stack
- // entry. Ditto for a native stack entry which has the same SP as
- // a JS stack entry. In effect this means pseudo or JS entries
- // trump conflicting native entries.
- if (nativeStackAddr && (pseudoStackAddr == nativeStackAddr || jsStackAddr == nativeStackAddr)) {
- nativeStackAddr = nullptr;
- nativeIndex--;
- MOZ_ASSERT(pseudoStackAddr || jsStackAddr);
- }
-
- // Sanity checks.
- MOZ_ASSERT_IF(pseudoStackAddr, pseudoStackAddr != jsStackAddr &&
- pseudoStackAddr != nativeStackAddr);
- MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr &&
- jsStackAddr != nativeStackAddr);
- MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
- nativeStackAddr != jsStackAddr);
-
- // Check to see if pseudoStack frame is top-most.
- if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
- MOZ_ASSERT(pseudoIndex < pseudoCount);
- volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
- addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr);
- pseudoIndex++;
- continue;
- }
-
-#ifndef SPS_STANDALONE
- // Check to see if JS jit stack frame is top-most
- if (jsStackAddr > nativeStackAddr) {
- MOZ_ASSERT(jsIndex >= 0);
- const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
-
- // Stringifying non-wasm JIT frames is delayed until streaming
- // time. To re-lookup the entry in the JitcodeGlobalTable, we need to
- // store the JIT code address ('J') in the circular buffer.
- //
- // Note that we cannot do this when we are sychronously sampling the
- // current thread; that is, when called from profiler_get_backtrace. The
- // captured backtrace is usually externally stored for an indeterminate
- // amount of time, such as in nsRefreshDriver. Problematically, the
- // stored backtrace may be alive across a GC during which the profiler
- // itself is disabled. In that case, the JS engine is free to discard
- // its JIT code. This means that if we inserted such 'J' entries into
- // the buffer, nsRefreshDriver would now be holding on to a backtrace
- // with stale JIT code return addresses.
- if (aSample->isSamplingCurrentThread ||
- jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
- addDynamicTag(aProfile, 'c', jsFrame.label.get());
- } else {
- MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
- jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
- aProfile.addTag(ProfileEntry('J', jsFrames[jsIndex].returnAddress));
- }
-
- jsIndex--;
- continue;
- }
-#endif
-
- // If we reach here, there must be a native stack entry and it must be the
- // greatest entry.
- if (nativeStackAddr) {
- MOZ_ASSERT(nativeIndex >= 0);
- aProfile
- .addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
- }
- if (nativeIndex >= 0) {
- nativeIndex--;
- }
- }
-
-#ifndef SPS_STANDALONE
- // Update the JS context with the current profile sample buffer generation.
- //
- // Do not do this for synchronous sampling, which create their own
- // ProfileBuffers.
- if (!aSample->isSamplingCurrentThread && pseudoStack->mContext) {
- MOZ_ASSERT(aProfile.bufferGeneration() >= startBufferGen);
- uint32_t lapCount = aProfile.bufferGeneration() - startBufferGen;
- JS::UpdateJSContextProfilerSampleBufferGen(pseudoStack->mContext,
- aProfile.bufferGeneration(),
- lapCount);
- }
-#endif
-}
-
-#ifdef USE_NS_STACKWALK
-static
-void StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP,
- void* aClosure)
-{
- NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
- MOZ_ASSERT(nativeStack->count < nativeStack->size);
- nativeStack->sp_array[nativeStack->count] = aSP;
- nativeStack->pc_array[nativeStack->count] = aPC;
- nativeStack->count++;
-}
-
-void GeckoSampler::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
-{
- void* pc_array[1000];
- void* sp_array[1000];
- NativeStack nativeStack = {
- pc_array,
- sp_array,
- mozilla::ArrayLength(pc_array),
- 0
- };
-
- // Start with the current function. We use 0 as the frame number here because
- // the FramePointerStackWalk() and MozStackWalk() calls below will use 1..N.
- // This is a bit weird but it doesn't matter because StackWalkCallback()
- // doesn't use the frame number argument.
- StackWalkCallback(/* frameNumber */ 0, aSample->pc, aSample->sp, &nativeStack);
-
- uint32_t maxFrames = uint32_t(nativeStack.size - nativeStack.count);
- // win X64 doesn't support disabling frame pointers emission so we need
- // to fallback to using StackWalk64 which is slower.
-#if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(V8_HOST_ARCH_X64))
- void *stackEnd = aSample->threadProfile->GetStackTop();
- bool rv = true;
- if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd)
- rv = FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0,
- maxFrames, &nativeStack,
- reinterpret_cast<void**>(aSample->fp), stackEnd);
-#else
- void *platformData = nullptr;
-
- uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData());
- MOZ_ASSERT(thread);
- bool rv = MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
- &nativeStack, thread, platformData);
-#endif
- if (rv)
- mergeStacksIntoProfile(aProfile, aSample, nativeStack);
-}
-#endif
-
-
-#ifdef USE_EHABI_STACKWALK
-void GeckoSampler::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
-{
- void *pc_array[1000];
- void *sp_array[1000];
- NativeStack nativeStack = {
- pc_array,
- sp_array,
- mozilla::ArrayLength(pc_array),
- 0
- };
-
- const mcontext_t *mcontext = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
- mcontext_t savedContext;
- PseudoStack *pseudoStack = aProfile.GetPseudoStack();
-
- nativeStack.count = 0;
- // The pseudostack contains an "EnterJIT" frame whenever we enter
- // JIT code with profiling enabled; the stack pointer value points
- // the saved registers. We use this to unwind resume unwinding
- // after encounting JIT code.
- for (uint32_t i = pseudoStack->stackSize(); i > 0; --i) {
- // The pseudostack grows towards higher indices, so we iterate
- // backwards (from callee to caller).
- volatile StackEntry &entry = pseudoStack->mStack[i - 1];
- if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) {
- // Found JIT entry frame. Unwind up to that point (i.e., force
- // the stack walk to stop before the block of saved registers;
- // note that it yields nondecreasing stack pointers), then restore
- // the saved state.
- uint32_t *vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
-
- nativeStack.count += EHABIStackWalk(*mcontext,
- /* stackBase = */ vSP,
- sp_array + nativeStack.count,
- pc_array + nativeStack.count,
- nativeStack.size - nativeStack.count);
-
- memset(&savedContext, 0, sizeof(savedContext));
- // See also: struct EnterJITStack in js/src/jit/arm/Trampoline-arm.cpp
- savedContext.arm_r4 = *vSP++;
- savedContext.arm_r5 = *vSP++;
- savedContext.arm_r6 = *vSP++;
- savedContext.arm_r7 = *vSP++;
- savedContext.arm_r8 = *vSP++;
- savedContext.arm_r9 = *vSP++;
- savedContext.arm_r10 = *vSP++;
- savedContext.arm_fp = *vSP++;
- savedContext.arm_lr = *vSP++;
- savedContext.arm_sp = reinterpret_cast<uint32_t>(vSP);
- savedContext.arm_pc = savedContext.arm_lr;
- mcontext = &savedContext;
- }
- }
-
- // Now unwind whatever's left (starting from either the last EnterJIT
- // frame or, if no EnterJIT was found, the original registers).
- nativeStack.count += EHABIStackWalk(*mcontext,
- aProfile.GetStackTop(),
- sp_array + nativeStack.count,
- pc_array + nativeStack.count,
- nativeStack.size - nativeStack.count);
-
- mergeStacksIntoProfile(aProfile, aSample, nativeStack);
-}
-#endif
-
-
-#ifdef USE_LUL_STACKWALK
-void GeckoSampler::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
-{
- const mcontext_t* mc
- = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
-
- lul::UnwindRegs startRegs;
- memset(&startRegs, 0, sizeof(startRegs));
-
-# if defined(SPS_PLAT_amd64_linux)
- startRegs.xip = lul::TaggedUWord(mc->gregs[REG_RIP]);
- startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_RSP]);
- startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_RBP]);
-# elif defined(SPS_PLAT_arm_android)
- startRegs.r15 = lul::TaggedUWord(mc->arm_pc);
- startRegs.r14 = lul::TaggedUWord(mc->arm_lr);
- startRegs.r13 = lul::TaggedUWord(mc->arm_sp);
- startRegs.r12 = lul::TaggedUWord(mc->arm_ip);
- startRegs.r11 = lul::TaggedUWord(mc->arm_fp);
- startRegs.r7 = lul::TaggedUWord(mc->arm_r7);
-# elif defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
- startRegs.xip = lul::TaggedUWord(mc->gregs[REG_EIP]);
- startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_ESP]);
- startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_EBP]);
-# else
-# error "Unknown plat"
-# endif
-
- /* Copy up to N_STACK_BYTES from rsp-REDZONE upwards, but not
- going past the stack's registered top point. Do some basic
- sanity checks too. This assumes that the TaggedUWord holding
- the stack pointer value is valid, but it should be, since it
- was constructed that way in the code just above. */
-
- lul::StackImage stackImg;
-
- {
-# if defined(SPS_PLAT_amd64_linux)
- uintptr_t rEDZONE_SIZE = 128;
- uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
-# elif defined(SPS_PLAT_arm_android)
- uintptr_t rEDZONE_SIZE = 0;
- uintptr_t start = startRegs.r13.Value() - rEDZONE_SIZE;
-# elif defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
- uintptr_t rEDZONE_SIZE = 0;
- uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
-# else
-# error "Unknown plat"
-# endif
- uintptr_t end = reinterpret_cast<uintptr_t>(aProfile.GetStackTop());
- uintptr_t ws = sizeof(void*);
- start &= ~(ws-1);
- end &= ~(ws-1);
- uintptr_t nToCopy = 0;
- if (start < end) {
- nToCopy = end - start;
- if (nToCopy > lul::N_STACK_BYTES)
- nToCopy = lul::N_STACK_BYTES;
- }
- MOZ_ASSERT(nToCopy <= lul::N_STACK_BYTES);
- stackImg.mLen = nToCopy;
- stackImg.mStartAvma = start;
- if (nToCopy > 0) {
- memcpy(&stackImg.mContents[0], (void*)start, nToCopy);
- (void)VALGRIND_MAKE_MEM_DEFINED(&stackImg.mContents[0], nToCopy);
- }
- }
-
- // The maximum number of frames that LUL will produce. Setting it
- // too high gives a risk of it wasting a lot of time looping on
- // corrupted stacks.
- const int MAX_NATIVE_FRAMES = 256;
-
- size_t scannedFramesAllowed = 0;
-
- uintptr_t framePCs[MAX_NATIVE_FRAMES];
- uintptr_t frameSPs[MAX_NATIVE_FRAMES];
- size_t framesAvail = mozilla::ArrayLength(framePCs);
- size_t framesUsed = 0;
- size_t scannedFramesAcquired = 0;
- sLUL->Unwind( &framePCs[0], &frameSPs[0],
- &framesUsed, &scannedFramesAcquired,
- framesAvail, scannedFramesAllowed,
- &startRegs, &stackImg );
-
- NativeStack nativeStack = {
- reinterpret_cast<void**>(framePCs),
- reinterpret_cast<void**>(frameSPs),
- mozilla::ArrayLength(framePCs),
- 0
- };
-
- nativeStack.count = framesUsed;
-
- mergeStacksIntoProfile(aProfile, aSample, nativeStack);
-
- // Update stats in the LUL stats object. Unfortunately this requires
- // three global memory operations.
- sLUL->mStats.mContext += 1;
- sLUL->mStats.mCFI += framesUsed - 1 - scannedFramesAcquired;
- sLUL->mStats.mScanned += scannedFramesAcquired;
-}
-#endif
-
-
-static
-void doSampleStackTrace(ThreadProfile &aProfile, TickSample *aSample, bool aAddLeafAddresses)
-{
- NativeStack nativeStack = { nullptr, nullptr, 0, 0 };
- mergeStacksIntoProfile(aProfile, aSample, nativeStack);
-
-#ifdef ENABLE_SPS_LEAF_DATA
- if (aSample && aAddLeafAddresses) {
- aProfile.addTag(ProfileEntry('l', (void*)aSample->pc));
-#ifdef ENABLE_ARM_LR_SAVING
- aProfile.addTag(ProfileEntry('L', (void*)aSample->lr));
-#endif
- }
-#endif
-}
-
-void GeckoSampler::Tick(TickSample* sample)
-{
- // Don't allow for ticks to happen within other ticks.
- InplaceTick(sample);
-}
-
-void GeckoSampler::InplaceTick(TickSample* sample)
-{
- ThreadProfile& currThreadProfile = *sample->threadProfile;
-
- currThreadProfile.addTag(ProfileEntry('T', currThreadProfile.ThreadId()));
-
- if (sample) {
- mozilla::TimeDuration delta = sample->timestamp - sStartTime;
- currThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds()));
- }
-
- PseudoStack* stack = currThreadProfile.GetPseudoStack();
-
-#if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) || \
- defined(USE_LUL_STACKWALK)
- if (mUseStackWalk) {
- doNativeBacktrace(currThreadProfile, sample);
- } else {
- doSampleStackTrace(currThreadProfile, sample, mAddLeafAddresses);
- }
-#else
- doSampleStackTrace(currThreadProfile, sample, mAddLeafAddresses);
-#endif
-
- // Don't process the PeudoStack's markers if we're
- // synchronously sampling the current thread.
- if (!sample->isSamplingCurrentThread) {
- ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
- while (pendingMarkersList && pendingMarkersList->peek()) {
- ProfilerMarker* marker = pendingMarkersList->popHead();
- currThreadProfile.addStoredMarker(marker);
- currThreadProfile.addTag(ProfileEntry('m', marker));
- }
- }
-
-#ifndef SPS_STANDALONE
- if (sample && currThreadProfile.GetThreadResponsiveness()->HasData()) {
- mozilla::TimeDuration delta = currThreadProfile.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp);
- currThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
- }
-#endif
-
- // rssMemory is equal to 0 when we are not recording.
- if (sample && sample->rssMemory != 0) {
- currThreadProfile.addTag(ProfileEntry('R', static_cast<double>(sample->rssMemory)));
- }
-
- // ussMemory is equal to 0 when we are not recording.
- if (sample && sample->ussMemory != 0) {
- currThreadProfile.addTag(ProfileEntry('U', static_cast<double>(sample->ussMemory)));
- }
-
-#if defined(XP_WIN)
- if (mProfilePower) {
- mIntelPowerGadget->TakeSample();
- currThreadProfile.addTag(ProfileEntry('p', static_cast<double>(mIntelPowerGadget->GetTotalPackagePowerInWatts())));
- }
-#endif
-
- if (sLastFrameNumber != sFrameNumber) {
- currThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
- sLastFrameNumber = sFrameNumber;
- }
-}
-
-namespace {
-
-SyncProfile* NewSyncProfile()
-{
- PseudoStack* stack = tlsPseudoStack.get();
- if (!stack) {
- MOZ_ASSERT(stack);
- return nullptr;
- }
- Thread::tid_t tid = Thread::GetCurrentId();
-
- ThreadInfo* info = new ThreadInfo("SyncProfile", tid, false, stack, nullptr);
- SyncProfile* profile = new SyncProfile(info, GET_BACKTRACE_DEFAULT_ENTRY);
- return profile;
-}
-
-} // namespace
-
-SyncProfile* GeckoSampler::GetBacktrace()
-{
- SyncProfile* profile = NewSyncProfile();
-
- TickSample sample;
- sample.threadProfile = profile;
-
-#if defined(HAVE_NATIVE_UNWIND) || defined(USE_LUL_STACKWALK)
-#if defined(XP_WIN) || defined(LINUX)
- tickcontext_t context;
- sample.PopulateContext(&context);
-#elif defined(XP_MACOSX)
- sample.PopulateContext(nullptr);
-#endif
-#endif
-
- sample.isSamplingCurrentThread = true;
- sample.timestamp = mozilla::TimeStamp::Now();
-
- profile->BeginUnwind();
- Tick(&sample);
- profile->EndUnwind();
-
- return profile;
-}
-
-void
-GeckoSampler::GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration)
-{
- *aCurrentPosition = mBuffer->mWritePos;
- *aTotalSize = mBuffer->mEntrySize;
- *aGeneration = mBuffer->mGeneration;
-}
diff --git a/tools/profiler/core/GeckoSampler.h b/tools/profiler/core/GeckoSampler.h
deleted file mode 100644
index da1fdfe43..000000000
--- a/tools/profiler/core/GeckoSampler.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef GeckoSampler_h
-#define GeckoSampler_h
-
-#include "platform.h"
-#include "ProfileEntry.h"
-#include "mozilla/Vector.h"
-#include "ThreadProfile.h"
-#include "ThreadInfo.h"
-#ifndef SPS_STANDALONE
-#include "IntelPowerGadget.h"
-#endif
-#ifdef MOZ_TASK_TRACER
-#include "GeckoTaskTracer.h"
-#endif
-
-#include <algorithm>
-
-namespace mozilla {
-class ProfileGatherer;
-} // namespace mozilla
-
-typedef mozilla::Vector<std::string> ThreadNameFilterList;
-typedef mozilla::Vector<std::string> FeatureList;
-
-static bool
-threadSelected(ThreadInfo* aInfo, const ThreadNameFilterList &aThreadNameFilters) {
- if (aThreadNameFilters.empty()) {
- return true;
- }
-
- std::string name = aInfo->Name();
- std::transform(name.begin(), name.end(), name.begin(), ::tolower);
-
- for (uint32_t i = 0; i < aThreadNameFilters.length(); ++i) {
- std::string filter = aThreadNameFilters[i];
- std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower);
-
- // Crude, non UTF-8 compatible, case insensitive substring search
- if (name.find(filter) != std::string::npos) {
- return true;
- }
- }
-
- return false;
-}
-
-extern mozilla::TimeStamp sLastTracerEvent;
-extern int sFrameNumber;
-extern int sLastFrameNumber;
-
-class GeckoSampler: public Sampler {
- public:
- GeckoSampler(double aInterval, int aEntrySize,
- const char** aFeatures, uint32_t aFeatureCount,
- const char** aThreadNameFilters, uint32_t aFilterCount);
- ~GeckoSampler();
-
- void RegisterThread(ThreadInfo* aInfo) {
- if (!aInfo->IsMainThread() && !mProfileThreads) {
- return;
- }
-
- if (!threadSelected(aInfo, mThreadNameFilters)) {
- return;
- }
-
- ThreadProfile* profile = new ThreadProfile(aInfo, mBuffer);
- aInfo->SetProfile(profile);
- }
-
- // Called within a signal. This function must be reentrant
- virtual void Tick(TickSample* sample) override;
-
- // Immediately captures the calling thread's call stack and returns it.
- virtual SyncProfile* GetBacktrace() override;
-
- // Called within a signal. This function must be reentrant
- virtual void RequestSave() override
- {
- mSaveRequested = true;
-#ifdef MOZ_TASK_TRACER
- if (mTaskTracer) {
- mozilla::tasktracer::StopLogging();
- }
-#endif
- }
-
- virtual void HandleSaveRequest() override;
- virtual void DeleteExpiredMarkers() override;
-
- ThreadProfile* GetPrimaryThreadProfile()
- {
- if (!mPrimaryThreadProfile) {
- ::MutexAutoLock lock(*sRegisteredThreadsMutex);
-
- for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
- ThreadInfo* info = sRegisteredThreads->at(i);
- if (info->IsMainThread() && !info->IsPendingDelete()) {
- mPrimaryThreadProfile = info->Profile();
- break;
- }
- }
- }
-
- return mPrimaryThreadProfile;
- }
-
- void ToStreamAsJSON(std::ostream& stream, double aSinceTime = 0);
-#ifndef SPS_STANDALONE
- virtual JSObject *ToJSObject(JSContext *aCx, double aSinceTime = 0);
- void GetGatherer(nsISupports** aRetVal);
-#endif
- mozilla::UniquePtr<char[]> ToJSON(double aSinceTime = 0);
- virtual void ToJSObjectAsync(double aSinceTime = 0, mozilla::dom::Promise* aPromise = 0);
- void StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter);
- void StreamTaskTracer(SpliceableJSONWriter& aWriter);
- void FlushOnJSShutdown(JSContext* aContext);
- bool ProfileJS() const { return mProfileJS; }
- bool ProfileJava() const { return mProfileJava; }
- bool ProfileGPU() const { return mProfileGPU; }
- bool ProfilePower() const { return mProfilePower; }
- bool ProfileThreads() const override { return mProfileThreads; }
- bool InPrivacyMode() const { return mPrivacyMode; }
- bool AddMainThreadIO() const { return mAddMainThreadIO; }
- bool ProfileMemory() const { return mProfileMemory; }
- bool TaskTracer() const { return mTaskTracer; }
- bool LayersDump() const { return mLayersDump; }
- bool DisplayListDump() const { return mDisplayListDump; }
- bool ProfileRestyle() const { return mProfileRestyle; }
- const ThreadNameFilterList& ThreadNameFilters() { return mThreadNameFilters; }
- const FeatureList& Features() { return mFeatures; }
-
- void GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration);
-
-protected:
- // Called within a signal. This function must be reentrant
- virtual void InplaceTick(TickSample* sample);
-
- // Not implemented on platforms which do not support backtracing
- void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
-
- void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime);
-
- // This represent the application's main thread (SAMPLER_INIT)
- ThreadProfile* mPrimaryThreadProfile;
- RefPtr<ProfileBuffer> mBuffer;
- bool mSaveRequested;
- bool mAddLeafAddresses;
- bool mUseStackWalk;
- bool mProfileJS;
- bool mProfileGPU;
- bool mProfileThreads;
- bool mProfileJava;
- bool mProfilePower;
- bool mLayersDump;
- bool mDisplayListDump;
- bool mProfileRestyle;
-
- // Keep the thread filter to check against new thread that
- // are started while profiling
- ThreadNameFilterList mThreadNameFilters;
- FeatureList mFeatures;
- bool mPrivacyMode;
- bool mAddMainThreadIO;
- bool mProfileMemory;
- bool mTaskTracer;
-#if defined(XP_WIN)
- IntelPowerGadget* mIntelPowerGadget;
-#endif
-
-private:
- RefPtr<mozilla::ProfileGatherer> mGatherer;
-};
-
-#endif
-
diff --git a/tools/profiler/core/IntelPowerGadget.cpp b/tools/profiler/core/IntelPowerGadget.cpp
deleted file mode 100644
index fe267b80f..000000000
--- a/tools/profiler/core/IntelPowerGadget.cpp
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright 2013, Intel Corporation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Author: Joe Olivas <joseph.k.olivas@intel.com>
- */
-
-#include "nsDebug.h"
-#include "nsString.h"
-#include "IntelPowerGadget.h"
-#include "prenv.h"
-
-IntelPowerGadget::IntelPowerGadget() :
- libpowergadget(nullptr),
- Initialize(nullptr),
- GetNumNodes(nullptr),
- GetMsrName(nullptr),
- GetMsrFunc(nullptr),
- ReadMSR(nullptr),
- WriteMSR(nullptr),
- GetIAFrequency(nullptr),
- GetTDP(nullptr),
- GetMaxTemperature(nullptr),
- GetThresholds(nullptr),
- GetTemperature(nullptr),
- ReadSample(nullptr),
- GetSysTime(nullptr),
- GetRDTSC(nullptr),
- GetTimeInterval(nullptr),
- GetBaseFrequency(nullptr),
- GetPowerData(nullptr),
- StartLog(nullptr),
- StopLog(nullptr),
- GetNumMsrs(nullptr),
- packageMSR(-1),
- cpuMSR(-1),
- freqMSR(-1),
- tempMSR(-1)
-{
-}
-
-bool
-IntelPowerGadget::Init()
-{
- bool success = false;
- const char *path = PR_GetEnv("IPG_Dir");
- nsCString ipg_library;
- if (path && *path) {
- ipg_library.Append(path);
- ipg_library.Append('/');
- ipg_library.AppendLiteral(PG_LIBRARY_NAME);
- libpowergadget = PR_LoadLibrary(ipg_library.get());
- }
-
- if(libpowergadget) {
- Initialize = (IPGInitialize) PR_FindFunctionSymbol(libpowergadget, "IntelEnergyLibInitialize");
- GetNumNodes = (IPGGetNumNodes) PR_FindFunctionSymbol(libpowergadget, "GetNumNodes");
- GetMsrName = (IPGGetMsrName) PR_FindFunctionSymbol(libpowergadget, "GetMsrName");
- GetMsrFunc = (IPGGetMsrFunc) PR_FindFunctionSymbol(libpowergadget, "GetMsrFunc");
- ReadMSR = (IPGReadMSR) PR_FindFunctionSymbol(libpowergadget, "ReadMSR");
- WriteMSR = (IPGWriteMSR) PR_FindFunctionSymbol(libpowergadget, "WriteMSR");
- GetIAFrequency = (IPGGetIAFrequency) PR_FindFunctionSymbol(libpowergadget, "GetIAFrequency");
- GetTDP = (IPGGetTDP) PR_FindFunctionSymbol(libpowergadget, "GetTDP");
- GetMaxTemperature = (IPGGetMaxTemperature) PR_FindFunctionSymbol(libpowergadget, "GetMaxTemperature");
- GetThresholds = (IPGGetThresholds) PR_FindFunctionSymbol(libpowergadget, "GetThresholds");
- GetTemperature = (IPGGetTemperature) PR_FindFunctionSymbol(libpowergadget, "GetTemperature");
- ReadSample = (IPGReadSample) PR_FindFunctionSymbol(libpowergadget, "ReadSample");
- GetSysTime = (IPGGetSysTime) PR_FindFunctionSymbol(libpowergadget, "GetSysTime");
- GetRDTSC = (IPGGetRDTSC) PR_FindFunctionSymbol(libpowergadget, "GetRDTSC");
- GetTimeInterval = (IPGGetTimeInterval) PR_FindFunctionSymbol(libpowergadget, "GetTimeInterval");
- GetBaseFrequency = (IPGGetBaseFrequency) PR_FindFunctionSymbol(libpowergadget, "GetBaseFrequency");
- GetPowerData = (IPGGetPowerData) PR_FindFunctionSymbol(libpowergadget, "GetPowerData");
- StartLog = (IPGStartLog) PR_FindFunctionSymbol(libpowergadget, "StartLog");
- StopLog = (IPGStopLog) PR_FindFunctionSymbol(libpowergadget, "StopLog");
- GetNumMsrs = (IPGGetNumMsrs) PR_FindFunctionSymbol(libpowergadget, "GetNumMsrs");
- }
-
- if(Initialize) {
- Initialize();
- int msrCount = GetNumberMsrs();
- wchar_t name[1024] = {0};
- for(int i = 0; i < msrCount; ++i) {
- GetMsrName(i, name);
- int func = 0;
- GetMsrFunc(i, &func);
- // MSR for frequency
- if(wcscmp(name, L"CPU Frequency") == 0 && (func == 0)) {
- this->freqMSR = i;
- }
- // MSR for Package
- else if(wcscmp(name, L"Processor") == 0 && (func == 1)) {
- this->packageMSR = i;
- }
- // MSR for CPU
- else if(wcscmp(name, L"IA") == 0 && (func == 1)) {
- this->cpuMSR = i;
- }
- // MSR for Temperature
- else if(wcscmp(name, L"Package") == 0 && (func == 2)) {
- this->tempMSR = i;
- }
- }
- // Grab one sample at startup for a diff
- TakeSample();
- success = true;
- }
- return success;
-}
-
-IntelPowerGadget::~IntelPowerGadget()
-{
- if(libpowergadget) {
- NS_WARNING("Unloading PowerGadget library!\n");
- PR_UnloadLibrary(libpowergadget);
- libpowergadget = nullptr;
- Initialize = nullptr;
- GetNumNodes = nullptr;
- GetMsrName = nullptr;
- GetMsrFunc = nullptr;
- ReadMSR = nullptr;
- WriteMSR = nullptr;
- GetIAFrequency = nullptr;
- GetTDP = nullptr;
- GetMaxTemperature = nullptr;
- GetThresholds = nullptr;
- GetTemperature = nullptr;
- ReadSample = nullptr;
- GetSysTime = nullptr;
- GetRDTSC = nullptr;
- GetTimeInterval = nullptr;
- GetBaseFrequency = nullptr;
- GetPowerData = nullptr;
- StartLog = nullptr;
- StopLog = nullptr;
- GetNumMsrs = nullptr;
- }
-}
-
-int
-IntelPowerGadget::GetNumberNodes()
-{
- int nodes = 0;
- if(GetNumNodes) {
- int ok = GetNumNodes(&nodes);
- }
- return nodes;
-}
-
-int
-IntelPowerGadget::GetNumberMsrs()
-{
- int msrs = 0;
- if(GetNumMsrs) {
- int ok = GetNumMsrs(&msrs);
- }
- return msrs;
-}
-
-int
-IntelPowerGadget::GetCPUFrequency(int node)
-{
- int frequency = 0;
- if(GetIAFrequency) {
- int ok = GetIAFrequency(node, &frequency);
- }
- return frequency;
-}
-
-double
-IntelPowerGadget::GetTdp(int node)
-{
- double tdp = 0.0;
- if(GetTDP) {
- int ok = GetTDP(node, &tdp);
- }
- return tdp;
-}
-
-int
-IntelPowerGadget::GetMaxTemp(int node)
-{
- int maxTemperatureC = 0;
- if(GetMaxTemperature) {
- int ok = GetMaxTemperature(node, &maxTemperatureC);
- }
- return maxTemperatureC;
-}
-
-int
-IntelPowerGadget::GetTemp(int node)
-{
- int temperatureC = 0;
- if(GetTemperature) {
- int ok = GetTemperature(node, &temperatureC);
- }
- return temperatureC;
-}
-
-int
-IntelPowerGadget::TakeSample()
-{
- int ok = 0;
- if(ReadSample) {
- ok = ReadSample();
- }
- return ok;
-}
-
-uint64_t
-IntelPowerGadget::GetRdtsc()
-{
- uint64_t rdtsc = 0;
- if(GetRDTSC) {
- int ok = GetRDTSC(&rdtsc);
- }
- return rdtsc;
-}
-
-double
-IntelPowerGadget::GetInterval()
-{
- double interval = 0.0;
- if(GetTimeInterval) {
- int ok = GetTimeInterval(&interval);
- }
- return interval;
-}
-
-double
-IntelPowerGadget::GetCPUBaseFrequency(int node)
-{
- double freq = 0.0;
- if(GetBaseFrequency) {
- int ok = GetBaseFrequency(node, &freq);
- }
- return freq;
-}
-
-double
-IntelPowerGadget::GetTotalPackagePowerInWatts()
-{
- int nodes = GetNumberNodes();
- double totalPower = 0.0;
- for(int i = 0; i < nodes; ++i) {
- totalPower += GetPackagePowerInWatts(i);
- }
- return totalPower;
-}
-
-double
-IntelPowerGadget::GetPackagePowerInWatts(int node)
-{
- int numResult = 0;
- double result[] = {0.0, 0.0, 0.0};
- if(GetPowerData && packageMSR != -1) {
- int ok = GetPowerData(node, packageMSR, result, &numResult);
- }
- return result[0];
-}
-
-double
-IntelPowerGadget::GetTotalCPUPowerInWatts()
-{
- int nodes = GetNumberNodes();
- double totalPower = 0.0;
- for(int i = 0; i < nodes; ++i) {
- totalPower += GetCPUPowerInWatts(i);
- }
- return totalPower;
-}
-
-double
-IntelPowerGadget::GetCPUPowerInWatts(int node)
-{
- int numResult = 0;
- double result[] = {0.0, 0.0, 0.0};
- if(GetPowerData && cpuMSR != -1) {
- int ok = GetPowerData(node, cpuMSR, result, &numResult);
- }
- return result[0];
-}
-
-double
-IntelPowerGadget::GetTotalGPUPowerInWatts()
-{
- int nodes = GetNumberNodes();
- double totalPower = 0.0;
- for(int i = 0; i < nodes; ++i) {
- totalPower += GetGPUPowerInWatts(i);
- }
- return totalPower;
-}
-
-double
-IntelPowerGadget::GetGPUPowerInWatts(int node)
-{
- return 0.0;
-}
-
diff --git a/tools/profiler/core/IntelPowerGadget.h b/tools/profiler/core/IntelPowerGadget.h
deleted file mode 100644
index 4a24215b6..000000000
--- a/tools/profiler/core/IntelPowerGadget.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright 2013, Intel Corporation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Author: Joe Olivas <joseph.k.olivas@intel.com>
- */
-
-#ifndef profiler_IntelPowerGadget_h
-#define profiler_IntelPowerGadget_h
-
-#ifdef _MSC_VER
-typedef __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-#else
-#include <stdint.h>
-#endif
-#include "prlink.h"
-
-typedef int (*IPGInitialize) ();
-typedef int (*IPGGetNumNodes) (int *nNodes);
-typedef int (*IPGGetNumMsrs) (int *nMsr);
-typedef int (*IPGGetMsrName) (int iMsr, wchar_t *szName);
-typedef int (*IPGGetMsrFunc) (int iMsr, int *pFuncID);
-typedef int (*IPGReadMSR) (int iNode, unsigned int address, uint64_t *value);
-typedef int (*IPGWriteMSR) (int iNode, unsigned int address, uint64_t value);
-typedef int (*IPGGetIAFrequency) (int iNode, int *freqInMHz);
-typedef int (*IPGGetTDP) (int iNode, double *TDP);
-typedef int (*IPGGetMaxTemperature) (int iNode, int *degreeC);
-typedef int (*IPGGetThresholds) (int iNode, int *degree1C, int *degree2C);
-typedef int (*IPGGetTemperature) (int iNode, int *degreeC);
-typedef int (*IPGReadSample) ();
-typedef int (*IPGGetSysTime) (void *pSysTime);
-typedef int (*IPGGetRDTSC) (uint64_t *pTSC);
-typedef int (*IPGGetTimeInterval) (double *pOffset);
-typedef int (*IPGGetBaseFrequency) (int iNode, double *pBaseFrequency);
-typedef int (*IPGGetPowerData) (int iNode, int iMSR, double *pResult, int *nResult);
-typedef int (*IPGStartLog) (wchar_t *szFileName);
-typedef int (*IPGStopLog) ();
-
-#if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64)
-#define PG_LIBRARY_NAME "EnergyLib64"
-#else
-#define PG_LIBRARY_NAME "EnergyLib32"
-#endif
-
-
-class IntelPowerGadget
-{
-public:
-
- IntelPowerGadget();
- ~IntelPowerGadget();
-
- // Fails if initialization is incomplete
- bool Init();
-
- // Returns the number of packages on the system
- int GetNumberNodes();
-
- // Returns the number of MSRs being tracked
- int GetNumberMsrs();
-
- // Given a node, returns the temperature
- int GetCPUFrequency(int);
-
- // Returns the TDP of the given node
- double GetTdp(int);
-
- // Returns the maximum temperature for the given node
- int GetMaxTemp(int);
-
- // Returns the current temperature in degrees C
- // of the given node
- int GetTemp(int);
-
- // Takes a sample of data. Must be called before
- // any current data is retrieved.
- int TakeSample();
-
- // Gets the timestamp of the most recent sample
- uint64_t GetRdtsc();
-
- // returns number of seconds between the last
- // two samples
- double GetInterval();
-
- // Returns the base frequency for the given node
- double GetCPUBaseFrequency(int node);
-
- // Returns the combined package power for all
- // packages on the system for the last sample.
- double GetTotalPackagePowerInWatts();
- double GetPackagePowerInWatts(int node);
-
- // Returns the combined CPU power for all
- // packages on the system for the last sample.
- // If the reading is not available, returns 0.0
- double GetTotalCPUPowerInWatts();
- double GetCPUPowerInWatts(int node);
-
- // Returns the combined GPU power for all
- // packages on the system for the last sample.
- // If the reading is not available, returns 0.0
- double GetTotalGPUPowerInWatts();
- double GetGPUPowerInWatts(int node);
-
-private:
-
- PRLibrary *libpowergadget;
- IPGInitialize Initialize;
- IPGGetNumNodes GetNumNodes;
- IPGGetNumMsrs GetNumMsrs;
- IPGGetMsrName GetMsrName;
- IPGGetMsrFunc GetMsrFunc;
- IPGReadMSR ReadMSR;
- IPGWriteMSR WriteMSR;
- IPGGetIAFrequency GetIAFrequency;
- IPGGetTDP GetTDP;
- IPGGetMaxTemperature GetMaxTemperature;
- IPGGetThresholds GetThresholds;
- IPGGetTemperature GetTemperature;
- IPGReadSample ReadSample;
- IPGGetSysTime GetSysTime;
- IPGGetRDTSC GetRDTSC;
- IPGGetTimeInterval GetTimeInterval;
- IPGGetBaseFrequency GetBaseFrequency;
- IPGGetPowerData GetPowerData;
- IPGStartLog StartLog;
- IPGStopLog StopLog;
-
- int packageMSR;
- int cpuMSR;
- int freqMSR;
- int tempMSR;
-};
-
-#endif // profiler_IntelPowerGadget_h
diff --git a/tools/profiler/core/ProfileBuffer.cpp b/tools/profiler/core/ProfileBuffer.cpp
deleted file mode 100644
index a4b91d8fc..000000000
--- a/tools/profiler/core/ProfileBuffer.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "ProfileBuffer.h"
-
-ProfileBuffer::ProfileBuffer(int aEntrySize)
- : mEntries(MakeUnique<ProfileEntry[]>(aEntrySize))
- , mWritePos(0)
- , mReadPos(0)
- , mEntrySize(aEntrySize)
- , mGeneration(0)
-{
-}
-
-ProfileBuffer::~ProfileBuffer()
-{
- while (mStoredMarkers.peek()) {
- delete mStoredMarkers.popHead();
- }
-}
-
-// Called from signal, call only reentrant functions
-void ProfileBuffer::addTag(const ProfileEntry& aTag)
-{
- mEntries[mWritePos++] = aTag;
- if (mWritePos == mEntrySize) {
- // Wrapping around may result in things referenced in the buffer (e.g.,
- // JIT code addresses and markers) being incorrectly collected.
- MOZ_ASSERT(mGeneration != UINT32_MAX);
- mGeneration++;
- mWritePos = 0;
- }
- if (mWritePos == mReadPos) {
- // Keep one slot open.
- mEntries[mReadPos] = ProfileEntry();
- mReadPos = (mReadPos + 1) % mEntrySize;
- }
-}
-
-void ProfileBuffer::addStoredMarker(ProfilerMarker *aStoredMarker) {
- aStoredMarker->SetGeneration(mGeneration);
- mStoredMarkers.insert(aStoredMarker);
-}
-
-void ProfileBuffer::deleteExpiredStoredMarkers() {
- // Delete markers of samples that have been overwritten due to circular
- // buffer wraparound.
- uint32_t generation = mGeneration;
- while (mStoredMarkers.peek() &&
- mStoredMarkers.peek()->HasExpired(generation)) {
- delete mStoredMarkers.popHead();
- }
-}
-
-void ProfileBuffer::reset() {
- mGeneration += 2;
- mReadPos = mWritePos = 0;
-}
-
-#define DYNAMIC_MAX_STRING 8192
-
-char* ProfileBuffer::processDynamicTag(int readPos,
- int* tagsConsumed, char* tagBuff)
-{
- int readAheadPos = (readPos + 1) % mEntrySize;
- int tagBuffPos = 0;
-
- // Read the string stored in mTagData until the null character is seen
- bool seenNullByte = false;
- while (readAheadPos != mWritePos && !seenNullByte) {
- (*tagsConsumed)++;
- ProfileEntry readAheadEntry = mEntries[readAheadPos];
- for (size_t pos = 0; pos < sizeof(void*); pos++) {
- tagBuff[tagBuffPos] = readAheadEntry.mTagChars[pos];
- if (tagBuff[tagBuffPos] == '\0' || tagBuffPos == DYNAMIC_MAX_STRING-2) {
- seenNullByte = true;
- break;
- }
- tagBuffPos++;
- }
- if (!seenNullByte)
- readAheadPos = (readAheadPos + 1) % mEntrySize;
- }
- return tagBuff;
-}
-
-
diff --git a/tools/profiler/core/ProfileBuffer.h b/tools/profiler/core/ProfileBuffer.h
deleted file mode 100644
index 7d90fe385..000000000
--- a/tools/profiler/core/ProfileBuffer.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef MOZ_PROFILE_BUFFER_H
-#define MOZ_PROFILE_BUFFER_H
-
-#include "ProfileEntry.h"
-#include "platform.h"
-#include "ProfileJSONWriter.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/RefCounted.h"
-
-class ProfileBuffer : public mozilla::RefCounted<ProfileBuffer> {
-public:
- MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ProfileBuffer)
-
- explicit ProfileBuffer(int aEntrySize);
-
- virtual ~ProfileBuffer();
-
- void addTag(const ProfileEntry& aTag);
- void StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
- JSContext* cx, UniqueStacks& aUniqueStacks);
- void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
- UniqueStacks& aUniqueStacks);
- void DuplicateLastSample(int aThreadId);
-
- void addStoredMarker(ProfilerMarker* aStoredMarker);
-
- // The following two methods are not signal safe! They delete markers.
- void deleteExpiredStoredMarkers();
- void reset();
-
-protected:
- char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff);
- int FindLastSampleOfThread(int aThreadId);
-
-public:
- // Circular buffer 'Keep One Slot Open' implementation for simplicity
- mozilla::UniquePtr<ProfileEntry[]> mEntries;
-
- // Points to the next entry we will write to, which is also the one at which
- // we need to stop reading.
- int mWritePos;
-
- // Points to the entry at which we can start reading.
- int mReadPos;
-
- // The number of entries in our buffer.
- int mEntrySize;
-
- // How many times mWritePos has wrapped around.
- uint32_t mGeneration;
-
- // Markers that marker entries in the buffer might refer to.
- ProfilerMarkerLinkedList mStoredMarkers;
-};
-
-#endif
diff --git a/tools/profiler/core/ProfileEntry.cpp b/tools/profiler/core/ProfileEntry.cpp
deleted file mode 100644
index 22d53a6f3..000000000
--- a/tools/profiler/core/ProfileEntry.cpp
+++ /dev/null
@@ -1,881 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 <ostream>
-#include "platform.h"
-#include "mozilla/HashFunctions.h"
-
-#ifndef SPS_STANDALONE
-#include "nsThreadUtils.h"
-#include "nsXULAppAPI.h"
-
-// JS
-#include "jsapi.h"
-#include "jsfriendapi.h"
-#include "js/TrackedOptimizationInfo.h"
-#endif
-
-// Self
-#include "ProfileEntry.h"
-
-using mozilla::MakeUnique;
-using mozilla::UniquePtr;
-using mozilla::Maybe;
-using mozilla::Some;
-using mozilla::Nothing;
-using mozilla::JSONWriter;
-
-
-////////////////////////////////////////////////////////////////////////
-// BEGIN ProfileEntry
-
-ProfileEntry::ProfileEntry()
- : mTagData(nullptr)
- , mTagName(0)
-{ }
-
-// aTagData must not need release (i.e. be a string from the text segment)
-ProfileEntry::ProfileEntry(char aTagName, const char *aTagData)
- : mTagData(aTagData)
- , mTagName(aTagName)
-{ }
-
-ProfileEntry::ProfileEntry(char aTagName, ProfilerMarker *aTagMarker)
- : mTagMarker(aTagMarker)
- , mTagName(aTagName)
-{ }
-
-ProfileEntry::ProfileEntry(char aTagName, void *aTagPtr)
- : mTagPtr(aTagPtr)
- , mTagName(aTagName)
-{ }
-
-ProfileEntry::ProfileEntry(char aTagName, double aTagDouble)
- : mTagDouble(aTagDouble)
- , mTagName(aTagName)
-{ }
-
-ProfileEntry::ProfileEntry(char aTagName, uintptr_t aTagOffset)
- : mTagOffset(aTagOffset)
- , mTagName(aTagName)
-{ }
-
-ProfileEntry::ProfileEntry(char aTagName, Address aTagAddress)
- : mTagAddress(aTagAddress)
- , mTagName(aTagName)
-{ }
-
-ProfileEntry::ProfileEntry(char aTagName, int aTagInt)
- : mTagInt(aTagInt)
- , mTagName(aTagName)
-{ }
-
-ProfileEntry::ProfileEntry(char aTagName, char aTagChar)
- : mTagChar(aTagChar)
- , mTagName(aTagName)
-{ }
-
-bool ProfileEntry::is_ent_hint(char hintChar) {
- return mTagName == 'h' && mTagChar == hintChar;
-}
-
-bool ProfileEntry::is_ent_hint() {
- return mTagName == 'h';
-}
-
-bool ProfileEntry::is_ent(char tagChar) {
- return mTagName == tagChar;
-}
-
-void* ProfileEntry::get_tagPtr() {
- // No consistency checking. Oh well.
- return mTagPtr;
-}
-
-// END ProfileEntry
-////////////////////////////////////////////////////////////////////////
-
-class JSONSchemaWriter
-{
- JSONWriter& mWriter;
- uint32_t mIndex;
-
-public:
- explicit JSONSchemaWriter(JSONWriter& aWriter)
- : mWriter(aWriter)
- , mIndex(0)
- {
- aWriter.StartObjectProperty("schema");
- }
-
- void WriteField(const char* aName) {
- mWriter.IntProperty(aName, mIndex++);
- }
-
- ~JSONSchemaWriter() {
- mWriter.EndObject();
- }
-};
-
-#ifndef SPS_STANDALONE
-class StreamOptimizationTypeInfoOp : public JS::ForEachTrackedOptimizationTypeInfoOp
-{
- JSONWriter& mWriter;
- UniqueJSONStrings& mUniqueStrings;
- bool mStartedTypeList;
-
-public:
- StreamOptimizationTypeInfoOp(JSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings)
- : mWriter(aWriter)
- , mUniqueStrings(aUniqueStrings)
- , mStartedTypeList(false)
- { }
-
- void readType(const char* keyedBy, const char* name,
- const char* location, Maybe<unsigned> lineno) override {
- if (!mStartedTypeList) {
- mStartedTypeList = true;
- mWriter.StartObjectElement();
- mWriter.StartArrayProperty("typeset");
- }
-
- mWriter.StartObjectElement();
- {
- mUniqueStrings.WriteProperty(mWriter, "keyedBy", keyedBy);
- if (name) {
- mUniqueStrings.WriteProperty(mWriter, "name", name);
- }
- if (location) {
- mUniqueStrings.WriteProperty(mWriter, "location", location);
- }
- if (lineno.isSome()) {
- mWriter.IntProperty("line", *lineno);
- }
- }
- mWriter.EndObject();
- }
-
- void operator()(JS::TrackedTypeSite site, const char* mirType) override {
- if (mStartedTypeList) {
- mWriter.EndArray();
- mStartedTypeList = false;
- } else {
- mWriter.StartObjectElement();
- }
-
- {
- mUniqueStrings.WriteProperty(mWriter, "site", JS::TrackedTypeSiteString(site));
- mUniqueStrings.WriteProperty(mWriter, "mirType", mirType);
- }
- mWriter.EndObject();
- }
-};
-
-// As mentioned in ProfileEntry.h, the JSON format contains many arrays whose
-// elements are laid out according to various schemas to help
-// de-duplication. This RAII class helps write these arrays by keeping track of
-// the last non-null element written and adding the appropriate number of null
-// elements when writing new non-null elements. It also automatically opens and
-// closes an array element on the given JSON writer.
-//
-// Example usage:
-//
-// // Define the schema of elements in this type of array: [FOO, BAR, BAZ]
-// enum Schema : uint32_t {
-// FOO = 0,
-// BAR = 1,
-// BAZ = 2
-// };
-//
-// AutoArraySchemaWriter writer(someJsonWriter, someUniqueStrings);
-// if (shouldWriteFoo) {
-// writer.IntElement(FOO, getFoo());
-// }
-// ... etc ...
-class MOZ_RAII AutoArraySchemaWriter
-{
- friend class AutoObjectWriter;
-
- SpliceableJSONWriter& mJSONWriter;
- UniqueJSONStrings* mStrings;
- uint32_t mNextFreeIndex;
-
-public:
- AutoArraySchemaWriter(SpliceableJSONWriter& aWriter, UniqueJSONStrings& aStrings)
- : mJSONWriter(aWriter)
- , mStrings(&aStrings)
- , mNextFreeIndex(0)
- {
- mJSONWriter.StartArrayElement();
- }
-
- // If you don't have access to a UniqueStrings, you had better not try and
- // write a string element down the line!
- explicit AutoArraySchemaWriter(SpliceableJSONWriter& aWriter)
- : mJSONWriter(aWriter)
- , mStrings(nullptr)
- , mNextFreeIndex(0)
- {
- mJSONWriter.StartArrayElement();
- }
-
- ~AutoArraySchemaWriter() {
- mJSONWriter.EndArray();
- }
-
- void FillUpTo(uint32_t aIndex) {
- MOZ_ASSERT(aIndex >= mNextFreeIndex);
- mJSONWriter.NullElements(aIndex - mNextFreeIndex);
- mNextFreeIndex = aIndex + 1;
- }
-
- void IntElement(uint32_t aIndex, uint32_t aValue) {
- FillUpTo(aIndex);
- mJSONWriter.IntElement(aValue);
- }
-
- void DoubleElement(uint32_t aIndex, double aValue) {
- FillUpTo(aIndex);
- mJSONWriter.DoubleElement(aValue);
- }
-
- void StringElement(uint32_t aIndex, const char* aValue) {
- MOZ_RELEASE_ASSERT(mStrings);
- FillUpTo(aIndex);
- mStrings->WriteElement(mJSONWriter, aValue);
- }
-};
-
-class StreamOptimizationAttemptsOp : public JS::ForEachTrackedOptimizationAttemptOp
-{
- SpliceableJSONWriter& mWriter;
- UniqueJSONStrings& mUniqueStrings;
-
-public:
- StreamOptimizationAttemptsOp(SpliceableJSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings)
- : mWriter(aWriter),
- mUniqueStrings(aUniqueStrings)
- { }
-
- void operator()(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome) override {
- enum Schema : uint32_t {
- STRATEGY = 0,
- OUTCOME = 1
- };
-
- AutoArraySchemaWriter writer(mWriter, mUniqueStrings);
- writer.StringElement(STRATEGY, JS::TrackedStrategyString(strategy));
- writer.StringElement(OUTCOME, JS::TrackedOutcomeString(outcome));
- }
-};
-
-class StreamJSFramesOp : public JS::ForEachProfiledFrameOp
-{
- void* mReturnAddress;
- UniqueStacks::Stack& mStack;
- unsigned mDepth;
-
-public:
- StreamJSFramesOp(void* aReturnAddr, UniqueStacks::Stack& aStack)
- : mReturnAddress(aReturnAddr)
- , mStack(aStack)
- , mDepth(0)
- { }
-
- unsigned depth() const {
- MOZ_ASSERT(mDepth > 0);
- return mDepth;
- }
-
- void operator()(const JS::ForEachProfiledFrameOp::FrameHandle& aFrameHandle) override {
- UniqueStacks::OnStackFrameKey frameKey(mReturnAddress, mDepth, aFrameHandle);
- mStack.AppendFrame(frameKey);
- mDepth++;
- }
-};
-#endif
-
-uint32_t UniqueJSONStrings::GetOrAddIndex(const char* aStr)
-{
- uint32_t index;
- StringKey key(aStr);
-
- auto it = mStringToIndexMap.find(key);
-
- if (it != mStringToIndexMap.end()) {
- return it->second;
- }
- index = mStringToIndexMap.size();
- mStringToIndexMap[key] = index;
- mStringTableWriter.StringElement(aStr);
- return index;
-}
-
-bool UniqueStacks::FrameKey::operator==(const FrameKey& aOther) const
-{
- return mLocation == aOther.mLocation &&
- mLine == aOther.mLine &&
- mCategory == aOther.mCategory &&
- mJITAddress == aOther.mJITAddress &&
- mJITDepth == aOther.mJITDepth;
-}
-
-bool UniqueStacks::StackKey::operator==(const StackKey& aOther) const
-{
- MOZ_ASSERT_IF(mPrefix == aOther.mPrefix, mPrefixHash == aOther.mPrefixHash);
- return mPrefix == aOther.mPrefix && mFrame == aOther.mFrame;
-}
-
-UniqueStacks::Stack::Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot)
- : mUniqueStacks(aUniqueStacks)
- , mStack(aUniqueStacks.GetOrAddFrameIndex(aRoot))
-{
-}
-
-void UniqueStacks::Stack::AppendFrame(const OnStackFrameKey& aFrame)
-{
- // Compute the prefix hash and index before mutating mStack.
- uint32_t prefixHash = mStack.Hash();
- uint32_t prefix = mUniqueStacks.GetOrAddStackIndex(mStack);
- mStack.UpdateHash(prefixHash, prefix, mUniqueStacks.GetOrAddFrameIndex(aFrame));
-}
-
-uint32_t UniqueStacks::Stack::GetOrAddIndex() const
-{
- return mUniqueStacks.GetOrAddStackIndex(mStack);
-}
-
-uint32_t UniqueStacks::FrameKey::Hash() const
-{
- uint32_t hash = 0;
- if (!mLocation.IsEmpty()) {
-#ifdef SPS_STANDALONE
- hash = mozilla::HashString(mLocation.c_str());
-#else
- hash = mozilla::HashString(mLocation.get());
-#endif
- }
- if (mLine.isSome()) {
- hash = mozilla::AddToHash(hash, *mLine);
- }
- if (mCategory.isSome()) {
- hash = mozilla::AddToHash(hash, *mCategory);
- }
- if (mJITAddress.isSome()) {
- hash = mozilla::AddToHash(hash, *mJITAddress);
- if (mJITDepth.isSome()) {
- hash = mozilla::AddToHash(hash, *mJITDepth);
- }
- }
- return hash;
-}
-
-uint32_t UniqueStacks::StackKey::Hash() const
-{
- if (mPrefix.isNothing()) {
- return mozilla::HashGeneric(mFrame);
- }
- return mozilla::AddToHash(*mPrefixHash, mFrame);
-}
-
-UniqueStacks::Stack UniqueStacks::BeginStack(const OnStackFrameKey& aRoot)
-{
- return Stack(*this, aRoot);
-}
-
-UniqueStacks::UniqueStacks(JSContext* aContext)
- : mContext(aContext)
- , mFrameCount(0)
-{
- mFrameTableWriter.StartBareList();
- mStackTableWriter.StartBareList();
-}
-
-#ifdef SPS_STANDALONE
-uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
-{
- uint32_t index;
- auto it = mStackToIndexMap.find(aStack);
-
- if (it != mStackToIndexMap.end()) {
- return it->second;
- }
-
- index = mStackToIndexMap.size();
- mStackToIndexMap[aStack] = index;
- StreamStack(aStack);
- return index;
-}
-#else
-uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
-{
- uint32_t index;
- if (mStackToIndexMap.Get(aStack, &index)) {
- MOZ_ASSERT(index < mStackToIndexMap.Count());
- return index;
- }
-
- index = mStackToIndexMap.Count();
- mStackToIndexMap.Put(aStack, index);
- StreamStack(aStack);
- return index;
-}
-#endif
-
-#ifdef SPS_STANDALONE
-uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame)
-{
- uint32_t index;
- auto it = mFrameToIndexMap.find(aFrame);
- if (it != mFrameToIndexMap.end()) {
- MOZ_ASSERT(it->second < mFrameCount);
- return it->second;
- }
-
- // A manual count is used instead of mFrameToIndexMap.Count() due to
- // forwarding of canonical JIT frames above.
- index = mFrameCount++;
- mFrameToIndexMap[aFrame] = index;
- StreamFrame(aFrame);
- return index;
-}
-#else
-uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame)
-{
- uint32_t index;
- if (mFrameToIndexMap.Get(aFrame, &index)) {
- MOZ_ASSERT(index < mFrameCount);
- return index;
- }
-
- // If aFrame isn't canonical, forward it to the canonical frame's index.
- if (aFrame.mJITFrameHandle) {
- void* canonicalAddr = aFrame.mJITFrameHandle->canonicalAddress();
- if (canonicalAddr != *aFrame.mJITAddress) {
- OnStackFrameKey canonicalKey(canonicalAddr, *aFrame.mJITDepth, *aFrame.mJITFrameHandle);
- uint32_t canonicalIndex = GetOrAddFrameIndex(canonicalKey);
- mFrameToIndexMap.Put(aFrame, canonicalIndex);
- return canonicalIndex;
- }
- }
-
- // A manual count is used instead of mFrameToIndexMap.Count() due to
- // forwarding of canonical JIT frames above.
- index = mFrameCount++;
- mFrameToIndexMap.Put(aFrame, index);
- StreamFrame(aFrame);
- return index;
-}
-#endif
-
-uint32_t UniqueStacks::LookupJITFrameDepth(void* aAddr)
-{
- uint32_t depth;
-
- auto it = mJITFrameDepthMap.find(aAddr);
- if (it != mJITFrameDepthMap.end()) {
- depth = it->second;
- MOZ_ASSERT(depth > 0);
- return depth;
- }
- return 0;
-}
-
-void UniqueStacks::AddJITFrameDepth(void* aAddr, unsigned depth)
-{
- mJITFrameDepthMap[aAddr] = depth;
-}
-
-void UniqueStacks::SpliceFrameTableElements(SpliceableJSONWriter& aWriter)
-{
- mFrameTableWriter.EndBareList();
- aWriter.TakeAndSplice(mFrameTableWriter.WriteFunc());
-}
-
-void UniqueStacks::SpliceStackTableElements(SpliceableJSONWriter& aWriter)
-{
- mStackTableWriter.EndBareList();
- aWriter.TakeAndSplice(mStackTableWriter.WriteFunc());
-}
-
-void UniqueStacks::StreamStack(const StackKey& aStack)
-{
- enum Schema : uint32_t {
- PREFIX = 0,
- FRAME = 1
- };
-
- AutoArraySchemaWriter writer(mStackTableWriter, mUniqueStrings);
- if (aStack.mPrefix.isSome()) {
- writer.IntElement(PREFIX, *aStack.mPrefix);
- }
- writer.IntElement(FRAME, aStack.mFrame);
-}
-
-void UniqueStacks::StreamFrame(const OnStackFrameKey& aFrame)
-{
- enum Schema : uint32_t {
- LOCATION = 0,
- IMPLEMENTATION = 1,
- OPTIMIZATIONS = 2,
- LINE = 3,
- CATEGORY = 4
- };
-
- AutoArraySchemaWriter writer(mFrameTableWriter, mUniqueStrings);
-
-#ifndef SPS_STANDALONE
- if (!aFrame.mJITFrameHandle) {
-#else
- {
-#endif
-#ifdef SPS_STANDALONE
- writer.StringElement(LOCATION, aFrame.mLocation.c_str());
-#else
- writer.StringElement(LOCATION, aFrame.mLocation.get());
-#endif
- if (aFrame.mLine.isSome()) {
- writer.IntElement(LINE, *aFrame.mLine);
- }
- if (aFrame.mCategory.isSome()) {
- writer.IntElement(CATEGORY, *aFrame.mCategory);
- }
- }
-#ifndef SPS_STANDALONE
- else {
- const JS::ForEachProfiledFrameOp::FrameHandle& jitFrame = *aFrame.mJITFrameHandle;
-
- writer.StringElement(LOCATION, jitFrame.label());
-
- JS::ProfilingFrameIterator::FrameKind frameKind = jitFrame.frameKind();
- MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion ||
- frameKind == JS::ProfilingFrameIterator::Frame_Baseline);
- writer.StringElement(IMPLEMENTATION,
- frameKind == JS::ProfilingFrameIterator::Frame_Ion
- ? "ion"
- : "baseline");
-
- if (jitFrame.hasTrackedOptimizations()) {
- writer.FillUpTo(OPTIMIZATIONS);
- mFrameTableWriter.StartObjectElement();
- {
- mFrameTableWriter.StartArrayProperty("types");
- {
- StreamOptimizationTypeInfoOp typeInfoOp(mFrameTableWriter, mUniqueStrings);
- jitFrame.forEachOptimizationTypeInfo(typeInfoOp);
- }
- mFrameTableWriter.EndArray();
-
- JS::Rooted<JSScript*> script(mContext);
- jsbytecode* pc;
- mFrameTableWriter.StartObjectProperty("attempts");
- {
- {
- JSONSchemaWriter schema(mFrameTableWriter);
- schema.WriteField("strategy");
- schema.WriteField("outcome");
- }
-
- mFrameTableWriter.StartArrayProperty("data");
- {
- StreamOptimizationAttemptsOp attemptOp(mFrameTableWriter, mUniqueStrings);
- jitFrame.forEachOptimizationAttempt(attemptOp, script.address(), &pc);
- }
- mFrameTableWriter.EndArray();
- }
- mFrameTableWriter.EndObject();
-
- if (JSAtom* name = js::GetPropertyNameFromPC(script, pc)) {
- char buf[512];
- JS_PutEscapedFlatString(buf, mozilla::ArrayLength(buf), js::AtomToFlatString(name), 0);
- mUniqueStrings.WriteProperty(mFrameTableWriter, "propertyName", buf);
- }
-
- unsigned line, column;
- line = JS_PCToLineNumber(script, pc, &column);
- mFrameTableWriter.IntProperty("line", line);
- mFrameTableWriter.IntProperty("column", column);
- }
- mFrameTableWriter.EndObject();
- }
- }
-#endif
-}
-
-struct ProfileSample
-{
- uint32_t mStack;
- Maybe<double> mTime;
- Maybe<double> mResponsiveness;
- Maybe<double> mRSS;
- Maybe<double> mUSS;
- Maybe<int> mFrameNumber;
- Maybe<double> mPower;
-};
-
-static void WriteSample(SpliceableJSONWriter& aWriter, ProfileSample& aSample)
-{
- enum Schema : uint32_t {
- STACK = 0,
- TIME = 1,
- RESPONSIVENESS = 2,
- RSS = 3,
- USS = 4,
- FRAME_NUMBER = 5,
- POWER = 6
- };
-
- AutoArraySchemaWriter writer(aWriter);
-
- writer.IntElement(STACK, aSample.mStack);
-
- if (aSample.mTime.isSome()) {
- writer.DoubleElement(TIME, *aSample.mTime);
- }
-
- if (aSample.mResponsiveness.isSome()) {
- writer.DoubleElement(RESPONSIVENESS, *aSample.mResponsiveness);
- }
-
- if (aSample.mRSS.isSome()) {
- writer.DoubleElement(RSS, *aSample.mRSS);
- }
-
- if (aSample.mUSS.isSome()) {
- writer.DoubleElement(USS, *aSample.mUSS);
- }
-
- if (aSample.mFrameNumber.isSome()) {
- writer.IntElement(FRAME_NUMBER, *aSample.mFrameNumber);
- }
-
- if (aSample.mPower.isSome()) {
- writer.DoubleElement(POWER, *aSample.mPower);
- }
-}
-
-void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
- double aSinceTime, JSContext* aContext,
- UniqueStacks& aUniqueStacks)
-{
- Maybe<ProfileSample> sample;
- int readPos = mReadPos;
- int currentThreadID = -1;
- Maybe<double> currentTime;
- UniquePtr<char[]> tagBuff = MakeUnique<char[]>(DYNAMIC_MAX_STRING);
-
- while (readPos != mWritePos) {
- ProfileEntry entry = mEntries[readPos];
- if (entry.mTagName == 'T') {
- currentThreadID = entry.mTagInt;
- currentTime.reset();
- int readAheadPos = (readPos + 1) % mEntrySize;
- if (readAheadPos != mWritePos) {
- ProfileEntry readAheadEntry = mEntries[readAheadPos];
- if (readAheadEntry.mTagName == 't') {
- currentTime = Some(readAheadEntry.mTagDouble);
- }
- }
- }
- if (currentThreadID == aThreadId && (currentTime.isNothing() || *currentTime >= aSinceTime)) {
- switch (entry.mTagName) {
- case 'r':
- if (sample.isSome()) {
- sample->mResponsiveness = Some(entry.mTagDouble);
- }
- break;
- case 'p':
- if (sample.isSome()) {
- sample->mPower = Some(entry.mTagDouble);
- }
- break;
- case 'R':
- if (sample.isSome()) {
- sample->mRSS = Some(entry.mTagDouble);
- }
- break;
- case 'U':
- if (sample.isSome()) {
- sample->mUSS = Some(entry.mTagDouble);
- }
- break;
- case 'f':
- if (sample.isSome()) {
- sample->mFrameNumber = Some(entry.mTagInt);
- }
- break;
- case 's':
- {
- // end the previous sample if there was one
- if (sample.isSome()) {
- WriteSample(aWriter, *sample);
- sample.reset();
- }
- // begin the next sample
- sample.emplace();
- sample->mTime = currentTime;
-
- // Seek forward through the entire sample, looking for frames
- // this is an easier approach to reason about than adding more
- // control variables and cases to the loop that goes through the buffer once
-
- UniqueStacks::Stack stack =
- aUniqueStacks.BeginStack(UniqueStacks::OnStackFrameKey("(root)"));
-
- int framePos = (readPos + 1) % mEntrySize;
- ProfileEntry frame = mEntries[framePos];
- while (framePos != mWritePos && frame.mTagName != 's' && frame.mTagName != 'T') {
- int incBy = 1;
- frame = mEntries[framePos];
-
- // Read ahead to the next tag, if it's a 'd' tag process it now
- const char* tagStringData = frame.mTagData;
- int readAheadPos = (framePos + 1) % mEntrySize;
- // Make sure the string is always null terminated if it fills up
- // DYNAMIC_MAX_STRING-2
- tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
-
- if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'd') {
- tagStringData = processDynamicTag(framePos, &incBy, tagBuff.get());
- }
-
- // Write one frame. It can have either
- // 1. only location - 'l' containing a memory address
- // 2. location and line number - 'c' followed by 'd's,
- // an optional 'n' and an optional 'y'
- // 3. a JIT return address - 'j' containing native code address
- if (frame.mTagName == 'l') {
- // Bug 753041
- // We need a double cast here to tell GCC that we don't want to sign
- // extend 32-bit addresses starting with 0xFXXXXXX.
- unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr;
- snprintf(tagBuff.get(), DYNAMIC_MAX_STRING, "%#llx", pc);
- stack.AppendFrame(UniqueStacks::OnStackFrameKey(tagBuff.get()));
- } else if (frame.mTagName == 'c') {
- UniqueStacks::OnStackFrameKey frameKey(tagStringData);
- readAheadPos = (framePos + incBy) % mEntrySize;
- if (readAheadPos != mWritePos &&
- mEntries[readAheadPos].mTagName == 'n') {
- frameKey.mLine = Some((unsigned) mEntries[readAheadPos].mTagInt);
- incBy++;
- }
- readAheadPos = (framePos + incBy) % mEntrySize;
- if (readAheadPos != mWritePos &&
- mEntries[readAheadPos].mTagName == 'y') {
- frameKey.mCategory = Some((unsigned) mEntries[readAheadPos].mTagInt);
- incBy++;
- }
- stack.AppendFrame(frameKey);
-#ifndef SPS_STANDALONE
- } else if (frame.mTagName == 'J') {
- // A JIT frame may expand to multiple frames due to inlining.
- void* pc = frame.mTagPtr;
- unsigned depth = aUniqueStacks.LookupJITFrameDepth(pc);
- if (depth == 0) {
- StreamJSFramesOp framesOp(pc, stack);
- JS::ForEachProfiledFrame(aContext, pc, framesOp);
- aUniqueStacks.AddJITFrameDepth(pc, framesOp.depth());
- } else {
- for (unsigned i = 0; i < depth; i++) {
- UniqueStacks::OnStackFrameKey inlineFrameKey(pc, i);
- stack.AppendFrame(inlineFrameKey);
- }
- }
-#endif
- }
- framePos = (framePos + incBy) % mEntrySize;
- }
-
- sample->mStack = stack.GetOrAddIndex();
- break;
- }
- }
- }
- readPos = (readPos + 1) % mEntrySize;
- }
- if (sample.isSome()) {
- WriteSample(aWriter, *sample);
- }
-}
-
-void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
- double aSinceTime, UniqueStacks& aUniqueStacks)
-{
- int readPos = mReadPos;
- int currentThreadID = -1;
- while (readPos != mWritePos) {
- ProfileEntry entry = mEntries[readPos];
- if (entry.mTagName == 'T') {
- currentThreadID = entry.mTagInt;
- } else if (currentThreadID == aThreadId && entry.mTagName == 'm') {
- const ProfilerMarker* marker = entry.getMarker();
- if (marker->GetTime() >= aSinceTime) {
- entry.getMarker()->StreamJSON(aWriter, aUniqueStacks);
- }
- }
- readPos = (readPos + 1) % mEntrySize;
- }
-}
-
-int ProfileBuffer::FindLastSampleOfThread(int aThreadId)
-{
- // We search backwards from mWritePos-1 to mReadPos.
- // Adding mEntrySize makes the result of the modulus positive.
- for (int readPos = (mWritePos + mEntrySize - 1) % mEntrySize;
- readPos != (mReadPos + mEntrySize - 1) % mEntrySize;
- readPos = (readPos + mEntrySize - 1) % mEntrySize) {
- ProfileEntry entry = mEntries[readPos];
- if (entry.mTagName == 'T' && entry.mTagInt == aThreadId) {
- return readPos;
- }
- }
-
- return -1;
-}
-
-void ProfileBuffer::DuplicateLastSample(int aThreadId)
-{
- int lastSampleStartPos = FindLastSampleOfThread(aThreadId);
- if (lastSampleStartPos == -1) {
- return;
- }
-
- MOZ_ASSERT(mEntries[lastSampleStartPos].mTagName == 'T');
-
- addTag(mEntries[lastSampleStartPos]);
-
- // Go through the whole entry and duplicate it, until we find the next one.
- for (int readPos = (lastSampleStartPos + 1) % mEntrySize;
- readPos != mWritePos;
- readPos = (readPos + 1) % mEntrySize) {
- switch (mEntries[readPos].mTagName) {
- case 'T':
- // We're done.
- return;
- case 't':
- // Copy with new time
- addTag(ProfileEntry('t', (mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds()));
- break;
- case 'm':
- // Don't copy markers
- break;
- // Copy anything else we don't know about
- // L, B, S, c, s, d, l, f, h, r, t, p
- default:
- addTag(mEntries[readPos]);
- break;
- }
- }
-}
-
-// END ProfileBuffer
-////////////////////////////////////////////////////////////////////////
-
-
-////////////////////////////////////////////////////////////////////////
-// BEGIN ThreadProfile
-
-// END ThreadProfile
-////////////////////////////////////////////////////////////////////////
diff --git a/tools/profiler/core/ProfileEntry.h b/tools/profiler/core/ProfileEntry.h
deleted file mode 100644
index b82a2f271..000000000
--- a/tools/profiler/core/ProfileEntry.h
+++ /dev/null
@@ -1,407 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-#ifndef MOZ_PROFILE_ENTRY_H
-#define MOZ_PROFILE_ENTRY_H
-
-#include <ostream>
-#include "GeckoProfiler.h"
-#include "platform.h"
-#include "ProfileJSONWriter.h"
-#include "ProfilerBacktrace.h"
-#include "mozilla/RefPtr.h"
-#include <string>
-#include <map>
-#ifndef SPS_STANDALONE
-#include "js/ProfilingFrameIterator.h"
-#include "js/TrackedOptimizationInfo.h"
-#include "nsHashKeys.h"
-#include "nsDataHashtable.h"
-#endif
-#include "mozilla/Maybe.h"
-#include "mozilla/Vector.h"
-#ifndef SPS_STANDALONE
-#include "gtest/MozGtestFriend.h"
-#else
-#define FRIEND_TEST(a, b) // TODO Support standalone gtest
-#endif
-#include "mozilla/HashFunctions.h"
-#include "mozilla/UniquePtr.h"
-
-class ThreadProfile;
-
-// NB: Packing this structure has been shown to cause SIGBUS issues on ARM.
-#ifndef __arm__
-#pragma pack(push, 1)
-#endif
-
-class ProfileEntry
-{
-public:
- ProfileEntry();
-
- // aTagData must not need release (i.e. be a string from the text segment)
- ProfileEntry(char aTagName, const char *aTagData);
- ProfileEntry(char aTagName, void *aTagPtr);
- ProfileEntry(char aTagName, ProfilerMarker *aTagMarker);
- ProfileEntry(char aTagName, double aTagDouble);
- ProfileEntry(char aTagName, uintptr_t aTagOffset);
- ProfileEntry(char aTagName, Address aTagAddress);
- ProfileEntry(char aTagName, int aTagLine);
- ProfileEntry(char aTagName, char aTagChar);
- bool is_ent_hint(char hintChar);
- bool is_ent_hint();
- bool is_ent(char tagName);
- void* get_tagPtr();
- const ProfilerMarker* getMarker() {
- MOZ_ASSERT(mTagName == 'm');
- return mTagMarker;
- }
-
- char getTagName() const { return mTagName; }
-
-private:
- FRIEND_TEST(ThreadProfile, InsertOneTag);
- FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
- FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
- FRIEND_TEST(ThreadProfile, InsertTagsWrap);
- FRIEND_TEST(ThreadProfile, MemoryMeasure);
- friend class ProfileBuffer;
- union {
- const char* mTagData;
- char mTagChars[sizeof(void*)];
- void* mTagPtr;
- ProfilerMarker* mTagMarker;
- double mTagDouble;
- Address mTagAddress;
- uintptr_t mTagOffset;
- int mTagInt;
- char mTagChar;
- };
- char mTagName;
-};
-
-#ifndef __arm__
-#pragma pack(pop)
-#endif
-
-class UniqueJSONStrings
-{
-public:
- UniqueJSONStrings() {
- mStringTableWriter.StartBareList();
- }
-
- void SpliceStringTableElements(SpliceableJSONWriter& aWriter) {
- aWriter.TakeAndSplice(mStringTableWriter.WriteFunc());
- }
-
- void WriteProperty(mozilla::JSONWriter& aWriter, const char* aName, const char* aStr) {
- aWriter.IntProperty(aName, GetOrAddIndex(aStr));
- }
-
- void WriteElement(mozilla::JSONWriter& aWriter, const char* aStr) {
- aWriter.IntElement(GetOrAddIndex(aStr));
- }
-
- uint32_t GetOrAddIndex(const char* aStr);
-
- struct StringKey {
-
- explicit StringKey(const char* aStr)
- : mStr(strdup(aStr))
- {
- mHash = mozilla::HashString(mStr);
- }
-
- StringKey(const StringKey& aOther)
- : mStr(strdup(aOther.mStr))
- {
- mHash = aOther.mHash;
- }
-
- ~StringKey() {
- free(mStr);
- }
-
- uint32_t Hash() const;
- bool operator==(const StringKey& aOther) const {
- return strcmp(mStr, aOther.mStr) == 0;
- }
- bool operator<(const StringKey& aOther) const {
- return mHash < aOther.mHash;
- }
-
- private:
- uint32_t mHash;
- char* mStr;
- };
-private:
- SpliceableChunkedJSONWriter mStringTableWriter;
- std::map<StringKey, uint32_t> mStringToIndexMap;
-};
-
-class UniqueStacks
-{
-public:
- struct FrameKey {
-#ifdef SPS_STANDALONE
- std::string mLocation;
-#else
- // This cannot be a std::string, as it is not memmove compatible, which
- // is used by nsHashTable
- nsCString mLocation;
-#endif
- mozilla::Maybe<unsigned> mLine;
- mozilla::Maybe<unsigned> mCategory;
- mozilla::Maybe<void*> mJITAddress;
- mozilla::Maybe<uint32_t> mJITDepth;
-
- explicit FrameKey(const char* aLocation)
- : mLocation(aLocation)
- {
- mHash = Hash();
- }
-
- FrameKey(const FrameKey& aToCopy)
- : mLocation(aToCopy.mLocation)
- , mLine(aToCopy.mLine)
- , mCategory(aToCopy.mCategory)
- , mJITAddress(aToCopy.mJITAddress)
- , mJITDepth(aToCopy.mJITDepth)
- {
- mHash = Hash();
- }
-
- FrameKey(void* aJITAddress, uint32_t aJITDepth)
- : mJITAddress(mozilla::Some(aJITAddress))
- , mJITDepth(mozilla::Some(aJITDepth))
- {
- mHash = Hash();
- }
-
- uint32_t Hash() const;
- bool operator==(const FrameKey& aOther) const;
- bool operator<(const FrameKey& aOther) const {
- return mHash < aOther.mHash;
- }
-
- private:
- uint32_t mHash;
- };
-
- // A FrameKey that holds a scoped reference to a JIT FrameHandle.
- struct MOZ_STACK_CLASS OnStackFrameKey : public FrameKey {
- explicit OnStackFrameKey(const char* aLocation)
- : FrameKey(aLocation)
-#ifndef SPS_STANDALONE
- , mJITFrameHandle(nullptr)
-#endif
- { }
-
- OnStackFrameKey(const OnStackFrameKey& aToCopy)
- : FrameKey(aToCopy)
-#ifndef SPS_STANDALONE
- , mJITFrameHandle(aToCopy.mJITFrameHandle)
-#endif
- { }
-
-#ifndef SPS_STANDALONE
- const JS::ForEachProfiledFrameOp::FrameHandle* mJITFrameHandle;
-
- OnStackFrameKey(void* aJITAddress, unsigned aJITDepth)
- : FrameKey(aJITAddress, aJITDepth)
- , mJITFrameHandle(nullptr)
- { }
-
- OnStackFrameKey(void* aJITAddress, unsigned aJITDepth,
- const JS::ForEachProfiledFrameOp::FrameHandle& aJITFrameHandle)
- : FrameKey(aJITAddress, aJITDepth)
- , mJITFrameHandle(&aJITFrameHandle)
- { }
-#endif
- };
-
- struct StackKey {
- mozilla::Maybe<uint32_t> mPrefixHash;
- mozilla::Maybe<uint32_t> mPrefix;
- uint32_t mFrame;
-
- explicit StackKey(uint32_t aFrame)
- : mFrame(aFrame)
- {
- mHash = Hash();
- }
-
- uint32_t Hash() const;
- bool operator==(const StackKey& aOther) const;
- bool operator<(const StackKey& aOther) const {
- return mHash < aOther.mHash;
- }
-
- void UpdateHash(uint32_t aPrefixHash, uint32_t aPrefix, uint32_t aFrame) {
- mPrefixHash = mozilla::Some(aPrefixHash);
- mPrefix = mozilla::Some(aPrefix);
- mFrame = aFrame;
- mHash = Hash();
- }
-
- private:
- uint32_t mHash;
- };
-
- class Stack {
- public:
- Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot);
-
- void AppendFrame(const OnStackFrameKey& aFrame);
- uint32_t GetOrAddIndex() const;
-
- private:
- UniqueStacks& mUniqueStacks;
- StackKey mStack;
- };
-
- explicit UniqueStacks(JSContext* aContext);
-
- Stack BeginStack(const OnStackFrameKey& aRoot);
- uint32_t LookupJITFrameDepth(void* aAddr);
- void AddJITFrameDepth(void* aAddr, unsigned depth);
- void SpliceFrameTableElements(SpliceableJSONWriter& aWriter);
- void SpliceStackTableElements(SpliceableJSONWriter& aWriter);
-
-private:
- uint32_t GetOrAddFrameIndex(const OnStackFrameKey& aFrame);
- uint32_t GetOrAddStackIndex(const StackKey& aStack);
- void StreamFrame(const OnStackFrameKey& aFrame);
- void StreamStack(const StackKey& aStack);
-
-public:
- UniqueJSONStrings mUniqueStrings;
-
-private:
- JSContext* mContext;
-
- // To avoid incurring JitcodeGlobalTable lookup costs for every JIT frame,
- // we cache the depth of frames keyed by JIT code address. If an address a
- // maps to a depth d, then frames keyed by a for depths 0 to d are
- // guaranteed to be in mFrameToIndexMap.
- std::map<void*, uint32_t> mJITFrameDepthMap;
-
- uint32_t mFrameCount;
- SpliceableChunkedJSONWriter mFrameTableWriter;
-#ifdef SPS_STANDALNOE
- std::map<FrameKey, uint32_t> mFrameToIndexMap;
-#else
- nsDataHashtable<nsGenericHashKey<FrameKey>, uint32_t> mFrameToIndexMap;
-#endif
-
- SpliceableChunkedJSONWriter mStackTableWriter;
-
- // This sucks but this is really performance critical, nsDataHashtable is way faster
- // than map/unordered_map but nsDataHashtable is tied to xpcom so we ifdef
- // until we can find a better solution.
-#ifdef SPS_STANDALONE
- std::map<StackKey, uint32_t> mStackToIndexMap;
-#else
- nsDataHashtable<nsGenericHashKey<StackKey>, uint32_t> mStackToIndexMap;
-#endif
-};
-
-//
-// ThreadProfile JSON Format
-// -------------------------
-//
-// The profile contains much duplicate information. The output JSON of the
-// profile attempts to deduplicate strings, frames, and stack prefixes, to cut
-// down on size and to increase JSON streaming speed. Deduplicated values are
-// streamed as indices into their respective tables.
-//
-// Further, arrays of objects with the same set of properties (e.g., samples,
-// frames) are output as arrays according to a schema instead of an object
-// with property names. A property that is not present is represented in the
-// array as null or undefined.
-//
-// The format of the thread profile JSON is shown by the following example
-// with 1 sample and 1 marker:
-//
-// {
-// "name": "Foo",
-// "tid": 42,
-// "samples":
-// {
-// "schema":
-// {
-// "stack": 0, /* index into stackTable */
-// "time": 1, /* number */
-// "responsiveness": 2, /* number */
-// "rss": 3, /* number */
-// "uss": 4, /* number */
-// "frameNumber": 5, /* number */
-// "power": 6 /* number */
-// },
-// "data":
-// [
-// [ 1, 0.0, 0.0 ] /* { stack: 1, time: 0.0, responsiveness: 0.0 } */
-// ]
-// },
-//
-// "markers":
-// {
-// "schema":
-// {
-// "name": 0, /* index into stringTable */
-// "time": 1, /* number */
-// "data": 2 /* arbitrary JSON */
-// },
-// "data":
-// [
-// [ 3, 0.1 ] /* { name: 'example marker', time: 0.1 } */
-// ]
-// },
-//
-// "stackTable":
-// {
-// "schema":
-// {
-// "prefix": 0, /* index into stackTable */
-// "frame": 1 /* index into frameTable */
-// },
-// "data":
-// [
-// [ null, 0 ], /* (root) */
-// [ 0, 1 ] /* (root) > foo.js */
-// ]
-// },
-//
-// "frameTable":
-// {
-// "schema":
-// {
-// "location": 0, /* index into stringTable */
-// "implementation": 1, /* index into stringTable */
-// "optimizations": 2, /* arbitrary JSON */
-// "line": 3, /* number */
-// "category": 4 /* number */
-// },
-// "data":
-// [
-// [ 0 ], /* { location: '(root)' } */
-// [ 1, 2 ] /* { location: 'foo.js', implementation: 'baseline' } */
-// ]
-// },
-//
-// "stringTable":
-// [
-// "(root)",
-// "foo.js",
-// "baseline",
-// "example marker"
-// ]
-// }
-//
-
-#endif /* ndef MOZ_PROFILE_ENTRY_H */
diff --git a/tools/profiler/core/ProfileJSONWriter.cpp b/tools/profiler/core/ProfileJSONWriter.cpp
deleted file mode 100644
index 65a9425a3..000000000
--- a/tools/profiler/core/ProfileJSONWriter.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "mozilla/HashFunctions.h"
-
-#include "ProfileJSONWriter.h"
-
-void
-ChunkedJSONWriteFunc::Write(const char* aStr)
-{
- MOZ_ASSERT(mChunkPtr >= mChunkList.back().get() && mChunkPtr <= mChunkEnd);
- MOZ_ASSERT(mChunkEnd >= mChunkList.back().get() + mChunkLengths.back());
- MOZ_ASSERT(*mChunkPtr == '\0');
-
- size_t len = strlen(aStr);
-
- // Most strings to be written are small, but subprocess profiles (e.g.,
- // from the content process in e10s) may be huge. If the string is larger
- // than a chunk, allocate its own chunk.
- char* newPtr;
- if (len >= kChunkSize) {
- AllocChunk(len + 1);
- newPtr = mChunkPtr + len;
- } else {
- newPtr = mChunkPtr + len;
- if (newPtr >= mChunkEnd) {
- AllocChunk(kChunkSize);
- newPtr = mChunkPtr + len;
- }
- }
-
- memcpy(mChunkPtr, aStr, len);
- *newPtr = '\0';
- mChunkPtr = newPtr;
- mChunkLengths.back() += len;
-}
-
-mozilla::UniquePtr<char[]>
-ChunkedJSONWriteFunc::CopyData() const
-{
- MOZ_ASSERT(mChunkLengths.length() == mChunkList.length());
- size_t totalLen = 1;
- for (size_t i = 0; i < mChunkLengths.length(); i++) {
- MOZ_ASSERT(strlen(mChunkList[i].get()) == mChunkLengths[i]);
- totalLen += mChunkLengths[i];
- }
- mozilla::UniquePtr<char[]> c = mozilla::MakeUnique<char[]>(totalLen);
- char* ptr = c.get();
- for (size_t i = 0; i < mChunkList.length(); i++) {
- size_t len = mChunkLengths[i];
- memcpy(ptr, mChunkList[i].get(), len);
- ptr += len;
- }
- *ptr = '\0';
- return c;
-}
-
-void
-ChunkedJSONWriteFunc::Take(ChunkedJSONWriteFunc&& aOther)
-{
- for (size_t i = 0; i < aOther.mChunkList.length(); i++) {
- MOZ_ALWAYS_TRUE(mChunkLengths.append(aOther.mChunkLengths[i]));
- MOZ_ALWAYS_TRUE(mChunkList.append(mozilla::Move(aOther.mChunkList[i])));
- }
- mChunkPtr = mChunkList.back().get() + mChunkLengths.back();
- mChunkEnd = mChunkPtr;
- aOther.mChunkPtr = nullptr;
- aOther.mChunkEnd = nullptr;
- aOther.mChunkList.clear();
- aOther.mChunkLengths.clear();
-}
-
-void
-ChunkedJSONWriteFunc::AllocChunk(size_t aChunkSize)
-{
- MOZ_ASSERT(mChunkLengths.length() == mChunkList.length());
- mozilla::UniquePtr<char[]> newChunk = mozilla::MakeUnique<char[]>(aChunkSize);
- mChunkPtr = newChunk.get();
- mChunkEnd = mChunkPtr + aChunkSize;
- *mChunkPtr = '\0';
- MOZ_ALWAYS_TRUE(mChunkLengths.append(0));
- MOZ_ALWAYS_TRUE(mChunkList.append(mozilla::Move(newChunk)));
-}
-
-void
-SpliceableJSONWriter::TakeAndSplice(ChunkedJSONWriteFunc* aFunc)
-{
- Separator();
- for (size_t i = 0; i < aFunc->mChunkList.length(); i++) {
- WriteFunc()->Write(aFunc->mChunkList[i].get());
- }
- aFunc->mChunkPtr = nullptr;
- aFunc->mChunkEnd = nullptr;
- aFunc->mChunkList.clear();
- aFunc->mChunkLengths.clear();
- mNeedComma[mDepth] = true;
-}
-
-void
-SpliceableJSONWriter::Splice(const char* aStr)
-{
- Separator();
- WriteFunc()->Write(aStr);
- mNeedComma[mDepth] = true;
-}
-
-void
-SpliceableChunkedJSONWriter::TakeAndSplice(ChunkedJSONWriteFunc* aFunc)
-{
- Separator();
- WriteFunc()->Take(mozilla::Move(*aFunc));
- mNeedComma[mDepth] = true;
-}
diff --git a/tools/profiler/core/ProfileJSONWriter.h b/tools/profiler/core/ProfileJSONWriter.h
deleted file mode 100644
index d9e2115f9..000000000
--- a/tools/profiler/core/ProfileJSONWriter.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef PROFILEJSONWRITER_H
-#define PROFILEJSONWRITER_H
-
-#include <ostream>
-#include <string>
-#include <string.h>
-
-#include "mozilla/JSONWriter.h"
-#include "mozilla/UniquePtr.h"
-
-class SpliceableChunkedJSONWriter;
-
-// On average, profile JSONs are large enough such that we want to avoid
-// reallocating its buffer when expanding. Additionally, the contents of the
-// profile are not accessed until the profile is entirely written. For these
-// reasons we use a chunked writer that keeps an array of chunks, which is
-// concatenated together after writing is finished.
-class ChunkedJSONWriteFunc : public mozilla::JSONWriteFunc
-{
-public:
- friend class SpliceableJSONWriter;
-
- ChunkedJSONWriteFunc() {
- AllocChunk(kChunkSize);
- }
-
- bool IsEmpty() const {
- MOZ_ASSERT_IF(!mChunkPtr, !mChunkEnd &&
- mChunkList.length() == 0 &&
- mChunkLengths.length() == 0);
- return !mChunkPtr;
- }
-
- void Write(const char* aStr) override;
- mozilla::UniquePtr<char[]> CopyData() const;
- void Take(ChunkedJSONWriteFunc&& aOther);
-
-private:
- void AllocChunk(size_t aChunkSize);
-
- static const size_t kChunkSize = 4096 * 512;
-
- // Pointer for writing inside the current chunk.
- //
- // The current chunk is always at the back of mChunkList, i.e.,
- // mChunkList.back() <= mChunkPtr <= mChunkEnd.
- char* mChunkPtr;
-
- // Pointer to the end of the current chunk.
- //
- // The current chunk is always at the back of mChunkList, i.e.,
- // mChunkEnd >= mChunkList.back() + mChunkLengths.back().
- char* mChunkEnd;
-
- // List of chunks and their lengths.
- //
- // For all i, the length of the string in mChunkList[i] is
- // mChunkLengths[i].
- mozilla::Vector<mozilla::UniquePtr<char[]>> mChunkList;
- mozilla::Vector<size_t> mChunkLengths;
-};
-
-struct OStreamJSONWriteFunc : public mozilla::JSONWriteFunc
-{
- explicit OStreamJSONWriteFunc(std::ostream& aStream)
- : mStream(aStream)
- { }
-
- void Write(const char* aStr) override {
- mStream << aStr;
- }
-
- std::ostream& mStream;
-};
-
-class SpliceableJSONWriter : public mozilla::JSONWriter
-{
-public:
- explicit SpliceableJSONWriter(mozilla::UniquePtr<mozilla::JSONWriteFunc> aWriter)
- : JSONWriter(mozilla::Move(aWriter))
- { }
-
- void StartBareList(CollectionStyle aStyle = SingleLineStyle) {
- StartCollection(nullptr, "", aStyle);
- }
-
- void EndBareList() {
- EndCollection("");
- }
-
- void NullElements(uint32_t aCount) {
- for (uint32_t i = 0; i < aCount; i++) {
- NullElement();
- }
- }
-
- void Splice(const ChunkedJSONWriteFunc* aFunc);
- void Splice(const char* aStr);
-
- // Takes the chunks from aFunc and write them. If move is not possible
- // (e.g., using OStreamJSONWriteFunc), aFunc's chunks are copied and its
- // storage cleared.
- virtual void TakeAndSplice(ChunkedJSONWriteFunc* aFunc);
-};
-
-class SpliceableChunkedJSONWriter : public SpliceableJSONWriter
-{
-public:
- explicit SpliceableChunkedJSONWriter()
- : SpliceableJSONWriter(mozilla::MakeUnique<ChunkedJSONWriteFunc>())
- { }
-
- ChunkedJSONWriteFunc* WriteFunc() const {
- return static_cast<ChunkedJSONWriteFunc*>(JSONWriter::WriteFunc());
- }
-
- // Adopts the chunks from aFunc without copying.
- virtual void TakeAndSplice(ChunkedJSONWriteFunc* aFunc) override;
-};
-
-#endif // PROFILEJSONWRITER_H
diff --git a/tools/profiler/core/ProfilerBacktrace.cpp b/tools/profiler/core/ProfilerBacktrace.cpp
deleted file mode 100644
index 7302dd64c..000000000
--- a/tools/profiler/core/ProfilerBacktrace.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/* -*- 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 "ProfilerBacktrace.h"
-
-#include "ProfileJSONWriter.h"
-#include "SyncProfile.h"
-
-ProfilerBacktrace::ProfilerBacktrace(SyncProfile* aProfile)
- : mProfile(aProfile)
-{
- MOZ_COUNT_CTOR(ProfilerBacktrace);
- MOZ_ASSERT(aProfile);
-}
-
-ProfilerBacktrace::~ProfilerBacktrace()
-{
- MOZ_COUNT_DTOR(ProfilerBacktrace);
- if (mProfile->ShouldDestroy()) {
- delete mProfile;
- }
-}
-
-void
-ProfilerBacktrace::StreamJSON(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks)
-{
- ::MutexAutoLock lock(mProfile->GetMutex());
- mProfile->StreamJSON(aWriter, aUniqueStacks);
-}
diff --git a/tools/profiler/core/ProfilerMarkers.cpp b/tools/profiler/core/ProfilerMarkers.cpp
deleted file mode 100644
index 3cb47de48..000000000
--- a/tools/profiler/core/ProfilerMarkers.cpp
+++ /dev/null
@@ -1,210 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "GeckoProfiler.h"
-#include "ProfilerBacktrace.h"
-#include "ProfilerMarkers.h"
-#include "SyncProfile.h"
-#ifndef SPS_STANDALONE
-#include "gfxASurface.h"
-#include "Layers.h"
-#include "mozilla/Sprintf.h"
-#endif
-
-ProfilerMarkerPayload::ProfilerMarkerPayload(ProfilerBacktrace* aStack)
- : mStack(aStack)
-{}
-
-ProfilerMarkerPayload::ProfilerMarkerPayload(const mozilla::TimeStamp& aStartTime,
- const mozilla::TimeStamp& aEndTime,
- ProfilerBacktrace* aStack)
- : mStartTime(aStartTime)
- , mEndTime(aEndTime)
- , mStack(aStack)
-{}
-
-ProfilerMarkerPayload::~ProfilerMarkerPayload()
-{
- profiler_free_backtrace(mStack);
-}
-
-void
-ProfilerMarkerPayload::streamCommonProps(const char* aMarkerType,
- SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks)
-{
- MOZ_ASSERT(aMarkerType);
- aWriter.StringProperty("type", aMarkerType);
- if (!mStartTime.IsNull()) {
- aWriter.DoubleProperty("startTime", profiler_time(mStartTime));
- }
- if (!mEndTime.IsNull()) {
- aWriter.DoubleProperty("endTime", profiler_time(mEndTime));
- }
- if (mStack) {
- aWriter.StartObjectProperty("stack");
- {
- mStack->StreamJSON(aWriter, aUniqueStacks);
- }
- aWriter.EndObject();
- }
-}
-
-ProfilerMarkerTracing::ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData)
- : mCategory(aCategory)
- , mMetaData(aMetaData)
-{
- if (aMetaData == TRACING_EVENT_BACKTRACE) {
- SetStack(profiler_get_backtrace());
- }
-}
-
-ProfilerMarkerTracing::ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData,
- ProfilerBacktrace* aCause)
- : mCategory(aCategory)
- , mMetaData(aMetaData)
-{
- if (aCause) {
- SetStack(aCause);
- }
-}
-
-void
-ProfilerMarkerTracing::StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks)
-{
- streamCommonProps("tracing", aWriter, aUniqueStacks);
-
- if (GetCategory()) {
- aWriter.StringProperty("category", GetCategory());
- }
- if (GetMetaData() != TRACING_DEFAULT) {
- if (GetMetaData() == TRACING_INTERVAL_START) {
- aWriter.StringProperty("interval", "start");
- } else if (GetMetaData() == TRACING_INTERVAL_END) {
- aWriter.StringProperty("interval", "end");
- }
- }
-}
-
-#ifndef SPS_STANDALONE
-GPUMarkerPayload::GPUMarkerPayload(
- const mozilla::TimeStamp& aCpuTimeStart,
- const mozilla::TimeStamp& aCpuTimeEnd,
- uint64_t aGpuTimeStart,
- uint64_t aGpuTimeEnd)
-
- : ProfilerMarkerPayload(aCpuTimeStart, aCpuTimeEnd)
- , mCpuTimeStart(aCpuTimeStart)
- , mCpuTimeEnd(aCpuTimeEnd)
- , mGpuTimeStart(aGpuTimeStart)
- , mGpuTimeEnd(aGpuTimeEnd)
-{ }
-
-void
-GPUMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks)
-{
- streamCommonProps("gpu_timer_query", aWriter, aUniqueStacks);
-
- aWriter.DoubleProperty("cpustart", profiler_time(mCpuTimeStart));
- aWriter.DoubleProperty("cpuend", profiler_time(mCpuTimeEnd));
- aWriter.IntProperty("gpustart", (int)mGpuTimeStart);
- aWriter.IntProperty("gpuend", (int)mGpuTimeEnd);
-}
-
-ProfilerMarkerImagePayload::ProfilerMarkerImagePayload(gfxASurface *aImg)
- : mImg(aImg)
-{ }
-
-void
-ProfilerMarkerImagePayload::StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks)
-{
- streamCommonProps("innerHTML", aWriter, aUniqueStacks);
- // TODO: Finish me
- //aWriter.NameValue("innerHTML", "<img src=''/>");
-}
-
-IOMarkerPayload::IOMarkerPayload(const char* aSource,
- const char* aFilename,
- const mozilla::TimeStamp& aStartTime,
- const mozilla::TimeStamp& aEndTime,
- ProfilerBacktrace* aStack)
- : ProfilerMarkerPayload(aStartTime, aEndTime, aStack),
- mSource(aSource)
-{
- mFilename = aFilename ? strdup(aFilename) : nullptr;
- MOZ_ASSERT(aSource);
-}
-
-IOMarkerPayload::~IOMarkerPayload(){
- free(mFilename);
-}
-
-void
-IOMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
-{
- streamCommonProps("io", aWriter, aUniqueStacks);
- aWriter.StringProperty("source", mSource);
- if (mFilename != nullptr) {
- aWriter.StringProperty("filename", mFilename);
- }
-}
-
-void
-ProfilerJSEventMarker(const char *event)
-{
- PROFILER_MARKER(event);
-}
-
-LayerTranslationPayload::LayerTranslationPayload(mozilla::layers::Layer* aLayer,
- mozilla::gfx::Point aPoint)
- : ProfilerMarkerPayload(mozilla::TimeStamp::Now(), mozilla::TimeStamp::Now(), nullptr)
- , mLayer(aLayer)
- , mPoint(aPoint)
-{
-}
-
-void
-LayerTranslationPayload::StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks)
-{
- const size_t bufferSize = 32;
- char buffer[bufferSize];
- SprintfLiteral(buffer, "%p", mLayer);
-
- aWriter.StringProperty("layer", buffer);
- aWriter.IntProperty("x", mPoint.x);
- aWriter.IntProperty("y", mPoint.y);
- aWriter.StringProperty("category", "LayerTranslation");
-}
-
-TouchDataPayload::TouchDataPayload(const mozilla::ScreenIntPoint& aPoint)
- : ProfilerMarkerPayload(mozilla::TimeStamp::Now(), mozilla::TimeStamp::Now(), nullptr)
-{
- mPoint = aPoint;
-}
-
-void
-TouchDataPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
-{
- aWriter.IntProperty("x", mPoint.x);
- aWriter.IntProperty("y", mPoint.y);
-}
-
-VsyncPayload::VsyncPayload(mozilla::TimeStamp aVsyncTimestamp)
- : ProfilerMarkerPayload(aVsyncTimestamp, aVsyncTimestamp, nullptr)
- , mVsyncTimestamp(aVsyncTimestamp)
-{
-}
-
-void
-VsyncPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
-{
- aWriter.DoubleProperty("vsync", profiler_time(mVsyncTimestamp));
- aWriter.StringProperty("category", "VsyncTimestamp");
-}
-#endif
diff --git a/tools/profiler/core/StackTop.cpp b/tools/profiler/core/StackTop.cpp
deleted file mode 100644
index 1f7944e5e..000000000
--- a/tools/profiler/core/StackTop.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-#ifdef XP_MACOSX
-#include <mach/task.h>
-#include <mach/thread_act.h>
-#include <pthread.h>
-#elif XP_WIN
-#include <windows.h>
-#endif
-
-#include "StackTop.h"
-
-void *GetStackTop(void *guess) {
-#if defined(XP_MACOSX)
- pthread_t thread = pthread_self();
- return pthread_get_stackaddr_np(thread);
-#elif defined(XP_WIN)
-#if defined(_MSC_VER) && defined(_M_IX86)
- // offset 0x18 from the FS segment register gives a pointer to
- // the thread information block for the current thread
- NT_TIB* pTib;
- __asm {
- MOV EAX, FS:[18h]
- MOV pTib, EAX
- }
- return static_cast<void*>(pTib->StackBase);
-#elif defined(__GNUC__) && defined(i386)
- // offset 0x18 from the FS segment register gives a pointer to
- // the thread information block for the current thread
- NT_TIB* pTib;
- asm ( "movl %%fs:0x18, %0\n"
- : "=r" (pTib)
- );
- return static_cast<void*>(pTib->StackBase);
-#elif defined(_M_X64) || defined(__x86_64)
- PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
- return reinterpret_cast<void*>(pTib->StackBase);
-#else
-#error Need a way to get the stack bounds on this platform (Windows)
-#endif
-#else
- return guess;
-#endif
-}
diff --git a/tools/profiler/core/StackTop.h b/tools/profiler/core/StackTop.h
deleted file mode 100644
index a933d10b4..000000000
--- a/tools/profiler/core/StackTop.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-#ifndef MOZ_STACK_TOP_H
-#define MOZ_STACK_TOP_H
-void *GetStackTop(void *guess);
-#endif
diff --git a/tools/profiler/core/SyncProfile.cpp b/tools/profiler/core/SyncProfile.cpp
deleted file mode 100644
index 4c4742f34..000000000
--- a/tools/profiler/core/SyncProfile.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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 "SyncProfile.h"
-
-SyncProfile::SyncProfile(ThreadInfo* aInfo, int aEntrySize)
- : ThreadProfile(aInfo, new ProfileBuffer(aEntrySize))
- , mOwnerState(REFERENCED)
-{
- MOZ_COUNT_CTOR(SyncProfile);
-}
-
-SyncProfile::~SyncProfile()
-{
- MOZ_COUNT_DTOR(SyncProfile);
-
- // SyncProfile owns the ThreadInfo; see NewSyncProfile.
- ThreadInfo* info = GetThreadInfo();
- delete info;
-}
-
-bool
-SyncProfile::ShouldDestroy()
-{
- ::MutexAutoLock lock(GetMutex());
- if (mOwnerState == OWNED) {
- mOwnerState = OWNER_DESTROYING;
- return true;
- }
- mOwnerState = ORPHANED;
- return false;
-}
-
-void
-SyncProfile::EndUnwind()
-{
- if (mOwnerState != ORPHANED) {
- mOwnerState = OWNED;
- }
- // Save mOwnerState before we release the mutex
- OwnerState ownerState = mOwnerState;
- ThreadProfile::EndUnwind();
- if (ownerState == ORPHANED) {
- delete this;
- }
-}
-
-// SyncProfiles' stacks are deduplicated in the context of the containing
-// profile in which the backtrace is as a marker payload.
-void
-SyncProfile::StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
-{
- ThreadProfile::StreamSamplesAndMarkers(aWriter, /* aSinceTime = */ 0, aUniqueStacks);
-}
diff --git a/tools/profiler/core/SyncProfile.h b/tools/profiler/core/SyncProfile.h
deleted file mode 100644
index 58f6b0d81..000000000
--- a/tools/profiler/core/SyncProfile.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-#ifndef __SYNCPROFILE_H
-#define __SYNCPROFILE_H
-
-#include "ProfileEntry.h"
-#include "ThreadProfile.h"
-
-class SyncProfile : public ThreadProfile
-{
-public:
- SyncProfile(ThreadInfo* aInfo, int aEntrySize);
- ~SyncProfile();
-
- // SyncProfiles' stacks are deduplicated in the context of the containing
- // profile in which the backtrace is as a marker payload.
- void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks);
-
- virtual void EndUnwind();
- virtual SyncProfile* AsSyncProfile() { return this; }
-
-private:
- friend class ProfilerBacktrace;
-
- enum OwnerState
- {
- REFERENCED, // ProfilerBacktrace has a pointer to this but doesn't own
- OWNED, // ProfilerBacktrace is responsible for destroying this
- OWNER_DESTROYING, // ProfilerBacktrace owns this and is destroying
- ORPHANED // No owner, we must destroy ourselves
- };
-
- bool ShouldDestroy();
-
- OwnerState mOwnerState;
-};
-
-#endif // __SYNCPROFILE_H
-
diff --git a/tools/profiler/core/ThreadInfo.cpp b/tools/profiler/core/ThreadInfo.cpp
deleted file mode 100644
index 0e25d2330..000000000
--- a/tools/profiler/core/ThreadInfo.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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 "ThreadInfo.h"
-#include "ThreadProfile.h"
-
-#include "mozilla/DebugOnly.h"
-
-ThreadInfo::ThreadInfo(const char* aName, int aThreadId,
- bool aIsMainThread, PseudoStack* aPseudoStack,
- void* aStackTop)
- : mName(strdup(aName))
- , mThreadId(aThreadId)
- , mIsMainThread(aIsMainThread)
- , mPseudoStack(aPseudoStack)
- , mPlatformData(Sampler::AllocPlatformData(aThreadId))
- , mProfile(nullptr)
- , mStackTop(aStackTop)
- , mPendingDelete(false)
-{
- MOZ_COUNT_CTOR(ThreadInfo);
-#ifndef SPS_STANDALONE
- mThread = NS_GetCurrentThread();
-#endif
-
- // We don't have to guess on mac
-#ifdef XP_MACOSX
- pthread_t self = pthread_self();
- mStackTop = pthread_get_stackaddr_np(self);
-#endif
-}
-
-ThreadInfo::~ThreadInfo() {
- MOZ_COUNT_DTOR(ThreadInfo);
- free(mName);
-
- if (mProfile)
- delete mProfile;
-
- Sampler::FreePlatformData(mPlatformData);
-}
-
-void
-ThreadInfo::SetPendingDelete()
-{
- mPendingDelete = true;
- // We don't own the pseudostack so disconnect it.
- mPseudoStack = nullptr;
- if (mProfile) {
- mProfile->SetPendingDelete();
- }
-}
-
-bool
-ThreadInfo::CanInvokeJS() const
-{
-#ifdef SPS_STANDALONE
- return false;
-#else
- nsIThread* thread = GetThread();
- if (!thread) {
- MOZ_ASSERT(IsMainThread());
- return true;
- }
- bool result;
- mozilla::DebugOnly<nsresult> rv = thread->GetCanInvokeJS(&result);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- return result;
-#endif
-}
diff --git a/tools/profiler/core/ThreadInfo.h b/tools/profiler/core/ThreadInfo.h
deleted file mode 100644
index 1cb4e5dc8..000000000
--- a/tools/profiler/core/ThreadInfo.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-#ifndef MOZ_THREAD_INFO_H
-#define MOZ_THREAD_INFO_H
-
-#include "platform.h"
-
-class ThreadInfo {
- public:
- ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop);
-
- virtual ~ThreadInfo();
-
- const char* Name() const { return mName; }
- int ThreadId() const { return mThreadId; }
-
- bool IsMainThread() const { return mIsMainThread; }
- PseudoStack* Stack() const { return mPseudoStack; }
-
- void SetProfile(ThreadProfile* aProfile) { mProfile = aProfile; }
- ThreadProfile* Profile() const { return mProfile; }
-
- PlatformData* GetPlatformData() const { return mPlatformData; }
- void* StackTop() const { return mStackTop; }
-
- virtual void SetPendingDelete();
- bool IsPendingDelete() const { return mPendingDelete; }
-
-#ifndef SPS_STANDALONE
- /**
- * May be null for the main thread if the profiler was started during startup
- */
- nsIThread* GetThread() const { return mThread.get(); }
-
-#endif
-
- bool CanInvokeJS() const;
-
- private:
- char* mName;
- int mThreadId;
- const bool mIsMainThread;
- PseudoStack* mPseudoStack;
- PlatformData* mPlatformData;
- ThreadProfile* mProfile;
- void* mStackTop;
-#ifndef SPS_STANDALONE
- nsCOMPtr<nsIThread> mThread;
-#endif
- bool mPendingDelete;
-};
-
-// Just like ThreadInfo, but owns a reference to the PseudoStack.
-class StackOwningThreadInfo : public ThreadInfo {
- public:
- StackOwningThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop);
- virtual ~StackOwningThreadInfo();
-
- virtual void SetPendingDelete();
-};
-
-#endif
diff --git a/tools/profiler/core/ThreadProfile.cpp b/tools/profiler/core/ThreadProfile.cpp
deleted file mode 100644
index 7452a7ee8..000000000
--- a/tools/profiler/core/ThreadProfile.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-ThreadProfile::ThreadProfile(ThreadInfo* aInfo, ProfileBuffer* aBuffer)
- : mThreadInfo(aInfo)
- , mBuffer(aBuffer)
- , mPseudoStack(aInfo->Stack())
- , mMutex(OS::CreateMutex("ThreadProfile::mMutex"))
- , mThreadId(int(aInfo->ThreadId()))
- , mIsMainThread(aInfo->IsMainThread())
- , mPlatformData(aInfo->GetPlatformData())
- , mStackTop(aInfo->StackTop())
-#ifndef SPS_STANDALONE
- , mRespInfo(this)
-#endif
-#ifdef XP_LINUX
- , mRssMemory(0)
- , mUssMemory(0)
-#endif
-{
- MOZ_COUNT_CTOR(ThreadProfile);
- MOZ_ASSERT(aBuffer);
-
- // I don't know if we can assert this. But we should warn.
- MOZ_ASSERT(aInfo->ThreadId() >= 0, "native thread ID is < 0");
- MOZ_ASSERT(aInfo->ThreadId() <= INT32_MAX, "native thread ID is > INT32_MAX");
-}
-
-ThreadProfile::~ThreadProfile()
-{
- MOZ_COUNT_DTOR(ThreadProfile);
-}
-
-void ThreadProfile::addTag(const ProfileEntry& aTag)
-{
- mBuffer->addTag(aTag);
-}
-
-void ThreadProfile::addStoredMarker(ProfilerMarker *aStoredMarker) {
- mBuffer->addStoredMarker(aStoredMarker);
-}
-
-void ThreadProfile::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
-{
- // mUniqueStacks may already be emplaced from FlushSamplesAndMarkers.
- if (!mUniqueStacks.isSome()) {
-#ifndef SPS_STANDALONE
- mUniqueStacks.emplace(mPseudoStack->mContext);
-#else
- mUniqueStacks.emplace(nullptr);
-#endif
- }
-
- aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
- {
- StreamSamplesAndMarkers(aWriter, aSinceTime, *mUniqueStacks);
-
- aWriter.StartObjectProperty("stackTable");
- {
- {
- JSONSchemaWriter schema(aWriter);
- schema.WriteField("prefix");
- schema.WriteField("frame");
- }
-
- aWriter.StartArrayProperty("data");
- {
- mUniqueStacks->SpliceStackTableElements(aWriter);
- }
- aWriter.EndArray();
- }
- aWriter.EndObject();
-
- aWriter.StartObjectProperty("frameTable");
- {
- {
- JSONSchemaWriter schema(aWriter);
- schema.WriteField("location");
- schema.WriteField("implementation");
- schema.WriteField("optimizations");
- schema.WriteField("line");
- schema.WriteField("category");
- }
-
- aWriter.StartArrayProperty("data");
- {
- mUniqueStacks->SpliceFrameTableElements(aWriter);
- }
- aWriter.EndArray();
- }
- aWriter.EndObject();
-
- aWriter.StartArrayProperty("stringTable");
- {
- mUniqueStacks->mUniqueStrings.SpliceStringTableElements(aWriter);
- }
- aWriter.EndArray();
- }
- aWriter.End();
-
- mUniqueStacks.reset();
-}
-
-void ThreadProfile::StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, double aSinceTime,
- UniqueStacks& aUniqueStacks)
-{
-#ifndef SPS_STANDALONE
- // Thread meta data
- if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
- // TODO Add the proper plugin name
- aWriter.StringProperty("name", "Plugin");
- } else if (XRE_GetProcessType() == GeckoProcessType_Content) {
- // This isn't going to really help once we have multiple content
- // processes, but it'll do for now.
- aWriter.StringProperty("name", "Content");
- } else {
- aWriter.StringProperty("name", Name());
- }
-#else
- aWriter.StringProperty("name", Name());
-#endif
-
- aWriter.IntProperty("tid", static_cast<int>(mThreadId));
-
- aWriter.StartObjectProperty("samples");
- {
- {
- JSONSchemaWriter schema(aWriter);
- schema.WriteField("stack");
- schema.WriteField("time");
- schema.WriteField("responsiveness");
- schema.WriteField("rss");
- schema.WriteField("uss");
- schema.WriteField("frameNumber");
- schema.WriteField("power");
- }
-
- aWriter.StartArrayProperty("data");
- {
- if (mSavedStreamedSamples) {
- // We would only have saved streamed samples during shutdown
- // streaming, which cares about dumping the entire buffer, and thus
- // should have passed in 0 for aSinceTime.
- MOZ_ASSERT(aSinceTime == 0);
- aWriter.Splice(mSavedStreamedSamples.get());
- mSavedStreamedSamples.reset();
- }
- mBuffer->StreamSamplesToJSON(aWriter, mThreadId, aSinceTime,
-#ifndef SPS_STANDALONE
- mPseudoStack->mContext,
-#else
- nullptr,
-#endif
- aUniqueStacks);
- }
- aWriter.EndArray();
- }
- aWriter.EndObject();
-
- aWriter.StartObjectProperty("markers");
- {
- {
- JSONSchemaWriter schema(aWriter);
- schema.WriteField("name");
- schema.WriteField("time");
- schema.WriteField("data");
- }
-
- aWriter.StartArrayProperty("data");
- {
- if (mSavedStreamedMarkers) {
- MOZ_ASSERT(aSinceTime == 0);
- aWriter.Splice(mSavedStreamedMarkers.get());
- mSavedStreamedMarkers.reset();
- }
- mBuffer->StreamMarkersToJSON(aWriter, mThreadId, aSinceTime, aUniqueStacks);
- }
- aWriter.EndArray();
- }
- aWriter.EndObject();
-}
-
-void ThreadProfile::FlushSamplesAndMarkers()
-{
- // This function is used to serialize the current buffer just before
- // JSContext destruction.
- MOZ_ASSERT(mPseudoStack->mContext);
-
- // Unlike StreamJSObject, do not surround the samples in brackets by calling
- // aWriter.{Start,End}BareList. The result string will be a comma-separated
- // list of JSON object literals that will prepended by StreamJSObject into
- // an existing array.
- //
- // Note that the UniqueStacks instance is persisted so that the frame-index
- // mapping is stable across JS shutdown.
-#ifndef SPS_STANDALONE
- mUniqueStacks.emplace(mPseudoStack->mContext);
-#else
- mUniqueStacks.emplace(nullptr);
-#endif
-
- {
- SpliceableChunkedJSONWriter b;
- b.StartBareList();
- {
- mBuffer->StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
-#ifndef SPS_STANDALONE
- mPseudoStack->mContext,
-#else
- nullptr,
-#endif
- *mUniqueStacks);
- }
- b.EndBareList();
- mSavedStreamedSamples = b.WriteFunc()->CopyData();
- }
-
- {
- SpliceableChunkedJSONWriter b;
- b.StartBareList();
- {
- mBuffer->StreamMarkersToJSON(b, mThreadId, /* aSinceTime = */ 0, *mUniqueStacks);
- }
- b.EndBareList();
- mSavedStreamedMarkers = b.WriteFunc()->CopyData();
- }
-
- // Reset the buffer. Attempting to symbolicate JS samples after mContext has
- // gone away will crash.
- mBuffer->reset();
-}
-
-PseudoStack* ThreadProfile::GetPseudoStack()
-{
- return mPseudoStack;
-}
-
-void ThreadProfile::BeginUnwind()
-{
- mMutex->Lock();
-}
-
-void ThreadProfile::EndUnwind()
-{
- mMutex->Unlock();
-}
-
-::Mutex& ThreadProfile::GetMutex()
-{
- return *mMutex.get();
-}
-
-void ThreadProfile::DuplicateLastSample()
-{
- mBuffer->DuplicateLastSample(mThreadId);
-}
-
diff --git a/tools/profiler/core/ThreadProfile.h b/tools/profiler/core/ThreadProfile.h
deleted file mode 100644
index ca2bbfe7a..000000000
--- a/tools/profiler/core/ThreadProfile.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-#ifndef MOZ_THREAD_PROFILE_H
-#define MOZ_THREAD_PROFILE_H
-
-#include "ProfileBuffer.h"
-#include "ThreadInfo.h"
-
-class ThreadProfile
-{
-public:
- ThreadProfile(ThreadInfo* aThreadInfo, ProfileBuffer* aBuffer);
- virtual ~ThreadProfile();
- void addTag(const ProfileEntry& aTag);
-
- /**
- * Track a marker which has been inserted into the ThreadProfile.
- * This marker can safely be deleted once the generation has
- * expired.
- */
- void addStoredMarker(ProfilerMarker *aStoredMarker);
- PseudoStack* GetPseudoStack();
- ::Mutex& GetMutex();
- void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime = 0);
-
- /**
- * Call this method when the JS entries inside the buffer are about to
- * become invalid, i.e., just before JS shutdown.
- */
- void FlushSamplesAndMarkers();
-
- void BeginUnwind();
- virtual void EndUnwind();
- virtual SyncProfile* AsSyncProfile() { return nullptr; }
-
- bool IsMainThread() const { return mIsMainThread; }
- const char* Name() const { return mThreadInfo->Name(); }
- int ThreadId() const { return mThreadId; }
-
- PlatformData* GetPlatformData() const { return mPlatformData; }
- void* GetStackTop() const { return mStackTop; }
- void DuplicateLastSample();
-
- ThreadInfo* GetThreadInfo() const { return mThreadInfo; }
-#ifndef SPS_STANDALONE
- ThreadResponsiveness* GetThreadResponsiveness() { return &mRespInfo; }
-#endif
-
- bool CanInvokeJS() const { return mThreadInfo->CanInvokeJS(); }
-
- void SetPendingDelete()
- {
- mPseudoStack = nullptr;
- mPlatformData = nullptr;
- }
-
- uint32_t bufferGeneration() const {
- return mBuffer->mGeneration;
- }
-
-protected:
- void StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, double aSinceTime,
- UniqueStacks& aUniqueStacks);
-
-private:
- FRIEND_TEST(ThreadProfile, InsertOneTag);
- FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
- FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
- FRIEND_TEST(ThreadProfile, InsertTagsWrap);
- FRIEND_TEST(ThreadProfile, MemoryMeasure);
- ThreadInfo* mThreadInfo;
-
- const RefPtr<ProfileBuffer> mBuffer;
-
- // JS frames in the buffer may require a live JSRuntime to stream (e.g.,
- // stringifying JIT frames). In the case of JSRuntime destruction,
- // FlushSamplesAndMarkers should be called to save them. These are spliced
- // into the final stream.
- mozilla::UniquePtr<char[]> mSavedStreamedSamples;
- mozilla::UniquePtr<char[]> mSavedStreamedMarkers;
- mozilla::Maybe<UniqueStacks> mUniqueStacks;
-
- PseudoStack* mPseudoStack;
- mozilla::UniquePtr<Mutex> mMutex;
- int mThreadId;
- bool mIsMainThread;
- PlatformData* mPlatformData; // Platform specific data.
- void* const mStackTop;
-#ifndef SPS_STANDALONE
- ThreadResponsiveness mRespInfo;
-#endif
-
- // Only Linux is using a signal sender, instead of stopping the thread, so we
- // need some space to store the data which cannot be collected in the signal
- // handler code.
-#ifdef XP_LINUX
-public:
- int64_t mRssMemory;
- int64_t mUssMemory;
-#endif
-};
-
-#endif
diff --git a/tools/profiler/core/platform-linux.cc b/tools/profiler/core/platform-linux.cc
deleted file mode 100644
index 160873c9d..000000000
--- a/tools/profiler/core/platform-linux.cc
+++ /dev/null
@@ -1,715 +0,0 @@
-// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in
-// the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google, Inc. nor the names of its contributors
-// may be used to endorse or promote products derived from this
-// software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-// SUCH DAMAGE.
-
-/*
-# vim: sw=2
-*/
-#include <stdio.h>
-#include <math.h>
-
-#include <pthread.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/prctl.h> // set name
-#include <stdlib.h>
-#include <sched.h>
-#ifdef ANDROID
-#include <android/log.h>
-#else
-#define __android_log_print(a, ...)
-#endif
-#include <ucontext.h>
-// Ubuntu Dapper requires memory pages to be marked as
-// executable. Otherwise, OS raises an exception when executing code
-// in that page.
-#include <sys/types.h> // mmap & munmap
-#include <sys/mman.h> // mmap & munmap
-#include <sys/stat.h> // open
-#include <fcntl.h> // open
-#include <unistd.h> // sysconf
-#include <semaphore.h>
-#ifdef __GLIBC__
-#include <execinfo.h> // backtrace, backtrace_symbols
-#endif // def __GLIBC__
-#include <strings.h> // index
-#include <errno.h>
-#include <stdarg.h>
-#include "prenv.h"
-#include "platform.h"
-#include "GeckoProfiler.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/Atomics.h"
-#include "mozilla/LinuxSignal.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/DebugOnly.h"
-#include "ProfileEntry.h"
-#include "nsThreadUtils.h"
-#include "GeckoSampler.h"
-#include "ThreadResponsiveness.h"
-
-#if defined(__ARM_EABI__) && defined(ANDROID)
- // Should also work on other Android and ARM Linux, but not tested there yet.
-# define USE_EHABI_STACKWALK
-# include "EHABIStackWalk.h"
-#elif defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
-# define USE_LUL_STACKWALK
-# include "lul/LulMain.h"
-# include "lul/platform-linux-lul.h"
-#endif
-
-// Memory profile
-#include "nsMemoryReporterManager.h"
-
-#include <string.h>
-#include <list>
-
-#define SIGNAL_SAVE_PROFILE SIGUSR2
-
-using namespace mozilla;
-
-#if defined(USE_LUL_STACKWALK)
-// A singleton instance of the library. It is initialised at first
-// use. Currently only the main thread can call Sampler::Start, so
-// there is no need for a mechanism to ensure that it is only
-// created once in a multi-thread-use situation.
-lul::LUL* sLUL = nullptr;
-
-// This is the sLUL initialization routine.
-static void sLUL_initialization_routine(void)
-{
- MOZ_ASSERT(!sLUL);
- MOZ_ASSERT(gettid() == getpid()); /* "this is the main thread" */
- sLUL = new lul::LUL(logging_sink_for_LUL);
- // Read all the unwind info currently available.
- read_procmaps(sLUL);
-}
-#endif
-
-/* static */ Thread::tid_t
-Thread::GetCurrentId()
-{
- return gettid();
-}
-
-#if !defined(ANDROID)
-// Keep track of when any of our threads calls fork(), so we can
-// temporarily disable signal delivery during the fork() call. Not
-// doing so appears to cause a kind of race, in which signals keep
-// getting delivered to the thread doing fork(), which keeps causing
-// it to fail and be restarted; hence forward progress is delayed a
-// great deal. A side effect of this is to permanently disable
-// sampling in the child process. See bug 837390.
-
-// Unfortunately this is only doable on non-Android, since Bionic
-// doesn't have pthread_atfork.
-
-// This records the current state at the time we paused it.
-static bool was_paused = false;
-
-// In the parent, just before the fork, record the pausedness state,
-// and then pause.
-static void paf_prepare(void) {
- if (Sampler::GetActiveSampler()) {
- was_paused = Sampler::GetActiveSampler()->IsPaused();
- Sampler::GetActiveSampler()->SetPaused(true);
- } else {
- was_paused = false;
- }
-}
-
-// In the parent, just after the fork, return pausedness to the
-// pre-fork state.
-static void paf_parent(void) {
- if (Sampler::GetActiveSampler())
- Sampler::GetActiveSampler()->SetPaused(was_paused);
-}
-
-// Set up the fork handlers.
-static void* setup_atfork() {
- pthread_atfork(paf_prepare, paf_parent, NULL);
- return NULL;
-}
-#endif /* !defined(ANDROID) */
-
-struct SamplerRegistry {
- static void AddActiveSampler(Sampler *sampler) {
- ASSERT(!SamplerRegistry::sampler);
- SamplerRegistry::sampler = sampler;
- }
- static void RemoveActiveSampler(Sampler *sampler) {
- SamplerRegistry::sampler = NULL;
- }
- static Sampler *sampler;
-};
-
-Sampler *SamplerRegistry::sampler = NULL;
-
-static mozilla::Atomic<ThreadProfile*> sCurrentThreadProfile;
-static sem_t sSignalHandlingDone;
-
-static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) {
- Sampler::GetActiveSampler()->RequestSave();
-}
-
-static void SetSampleContext(TickSample* sample, void* context)
-{
- // Extracting the sample from the context is extremely machine dependent.
- ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
- mcontext_t& mcontext = ucontext->uc_mcontext;
-#if V8_HOST_ARCH_IA32
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
-#elif V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
-#elif V8_HOST_ARCH_ARM
-// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
-#if !defined(ANDROID) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
-#ifdef ENABLE_ARM_LR_SAVING
- sample->lr = reinterpret_cast<Address>(mcontext.gregs[R14]);
-#endif
-#else
- sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
- sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
- sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
-#ifdef ENABLE_ARM_LR_SAVING
- sample->lr = reinterpret_cast<Address>(mcontext.arm_lr);
-#endif
-#endif
-#elif V8_HOST_ARCH_MIPS
- // Implement this on MIPS.
- UNIMPLEMENTED();
-#endif
-}
-
-#ifdef ANDROID
-#define V8_HOST_ARCH_ARM 1
-#define SYS_gettid __NR_gettid
-#define SYS_tgkill __NR_tgkill
-#else
-#define V8_HOST_ARCH_X64 1
-#endif
-
-namespace {
-
-void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
- // Avoid TSan warning about clobbering errno.
- int savedErrno = errno;
-
- if (!Sampler::GetActiveSampler()) {
- sem_post(&sSignalHandlingDone);
- errno = savedErrno;
- return;
- }
-
- TickSample sample_obj;
- TickSample* sample = &sample_obj;
- sample->context = context;
-
- // If profiling, we extract the current pc and sp.
- if (Sampler::GetActiveSampler()->IsProfiling()) {
- SetSampleContext(sample, context);
- }
- sample->threadProfile = sCurrentThreadProfile;
- sample->timestamp = mozilla::TimeStamp::Now();
- sample->rssMemory = sample->threadProfile->mRssMemory;
- sample->ussMemory = sample->threadProfile->mUssMemory;
-
- Sampler::GetActiveSampler()->Tick(sample);
-
- sCurrentThreadProfile = NULL;
- sem_post(&sSignalHandlingDone);
- errno = savedErrno;
-}
-
-} // namespace
-
-static void ProfilerSignalThread(ThreadProfile *profile,
- bool isFirstProfiledThread)
-{
- if (isFirstProfiledThread && Sampler::GetActiveSampler()->ProfileMemory()) {
- profile->mRssMemory = nsMemoryReporterManager::ResidentFast();
- profile->mUssMemory = nsMemoryReporterManager::ResidentUnique();
- } else {
- profile->mRssMemory = 0;
- profile->mUssMemory = 0;
- }
-}
-
-int tgkill(pid_t tgid, pid_t tid, int signalno) {
- return syscall(SYS_tgkill, tgid, tid, signalno);
-}
-
-class PlatformData {
- public:
- PlatformData()
- {
- MOZ_COUNT_CTOR(PlatformData);
- }
-
- ~PlatformData()
- {
- MOZ_COUNT_DTOR(PlatformData);
- }
-};
-
-/* static */ PlatformData*
-Sampler::AllocPlatformData(int aThreadId)
-{
- return new PlatformData;
-}
-
-/* static */ void
-Sampler::FreePlatformData(PlatformData* aData)
-{
- delete aData;
-}
-
-static void* SignalSender(void* arg) {
- // Taken from platform_thread_posix.cc
- prctl(PR_SET_NAME, "SamplerThread", 0, 0, 0);
-
- int vm_tgid_ = getpid();
- DebugOnly<int> my_tid = gettid();
-
- unsigned int nSignalsSent = 0;
-
- TimeDuration lastSleepOverhead = 0;
- TimeStamp sampleStart = TimeStamp::Now();
- while (SamplerRegistry::sampler->IsActive()) {
-
- SamplerRegistry::sampler->HandleSaveRequest();
- SamplerRegistry::sampler->DeleteExpiredMarkers();
-
- if (!SamplerRegistry::sampler->IsPaused()) {
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
- std::vector<ThreadInfo*> threads =
- SamplerRegistry::sampler->GetRegisteredThreads();
-
- bool isFirstProfiledThread = true;
- for (uint32_t i = 0; i < threads.size(); i++) {
- ThreadInfo* info = threads[i];
-
- // This will be null if we're not interested in profiling this thread.
- if (!info->Profile() || info->IsPendingDelete())
- continue;
-
- PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
- if (sleeping == PseudoStack::SLEEPING_AGAIN) {
- info->Profile()->DuplicateLastSample();
- continue;
- }
-
- info->Profile()->GetThreadResponsiveness()->Update();
-
- // We use sCurrentThreadProfile the ThreadProfile for the
- // thread we're profiling to the signal handler
- sCurrentThreadProfile = info->Profile();
-
- int threadId = info->ThreadId();
- MOZ_ASSERT(threadId != my_tid);
-
- // Profile from the signal sender for information which is not signal
- // safe, and will have low variation between the emission of the signal
- // and the signal handler catch.
- ProfilerSignalThread(sCurrentThreadProfile, isFirstProfiledThread);
-
- // Profile from the signal handler for information which is signal safe
- // and needs to be precise too, such as the stack of the interrupted
- // thread.
- if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
- printf_stderr("profiler failed to signal tid=%d\n", threadId);
-#ifdef DEBUG
- abort();
-#else
- continue;
-#endif
- }
-
- // Wait for the signal handler to run before moving on to the next one
- sem_wait(&sSignalHandlingDone);
- isFirstProfiledThread = false;
-
- // The LUL unwind object accumulates frame statistics.
- // Periodically we should poke it to give it a chance to print
- // those statistics. This involves doing I/O (fprintf,
- // __android_log_print, etc) and so can't safely be done from
- // the unwinder threads, which is why it is done here.
- if ((++nSignalsSent & 0xF) == 0) {
-# if defined(USE_LUL_STACKWALK)
- sLUL->MaybeShowStats();
-# endif
- }
- }
- }
-
- TimeStamp targetSleepEndTime = sampleStart + TimeDuration::FromMicroseconds(SamplerRegistry::sampler->interval() * 1000);
- TimeStamp beforeSleep = TimeStamp::Now();
- TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep;
- double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds());
- OS::SleepMicro(sleepTime);
- sampleStart = TimeStamp::Now();
- lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime));
- }
- return 0;
-}
-
-Sampler::Sampler(double interval, bool profiling, int entrySize)
- : interval_(interval),
- profiling_(profiling),
- paused_(false),
- active_(false),
- entrySize_(entrySize) {
- MOZ_COUNT_CTOR(Sampler);
-}
-
-Sampler::~Sampler() {
- MOZ_COUNT_DTOR(Sampler);
- ASSERT(!signal_sender_launched_);
-}
-
-
-void Sampler::Start() {
- LOG("Sampler started");
-
-#if defined(USE_EHABI_STACKWALK)
- mozilla::EHABIStackWalkInit();
-#elif defined(USE_LUL_STACKWALK)
- // NOTE: this isn't thread-safe. But we expect Sampler::Start to be
- // called only from the main thread, so this is OK in general.
- if (!sLUL) {
- sLUL_initialization_routine();
- }
-#endif
-
- SamplerRegistry::AddActiveSampler(this);
-
- // Initialize signal handler communication
- sCurrentThreadProfile = NULL;
- if (sem_init(&sSignalHandlingDone, /* pshared: */ 0, /* value: */ 0) != 0) {
- LOG("Error initializing semaphore");
- return;
- }
-
- // Request profiling signals.
- LOG("Request signal");
- struct sigaction sa;
- sa.sa_sigaction = MOZ_SIGNAL_TRAMPOLINE(ProfilerSignalHandler);
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART | SA_SIGINFO;
- if (sigaction(SIGPROF, &sa, &old_sigprof_signal_handler_) != 0) {
- LOG("Error installing signal");
- return;
- }
-
- // Request save profile signals
- struct sigaction sa2;
- sa2.sa_sigaction = ProfilerSaveSignalHandler;
- sigemptyset(&sa2.sa_mask);
- sa2.sa_flags = SA_RESTART | SA_SIGINFO;
- if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &old_sigsave_signal_handler_) != 0) {
- LOG("Error installing start signal");
- return;
- }
- LOG("Signal installed");
- signal_handler_installed_ = true;
-
-#if defined(USE_LUL_STACKWALK)
- // Switch into unwind mode. After this point, we can't add or
- // remove any unwind info to/from this LUL instance. The only thing
- // we can do with it is Unwind() calls.
- sLUL->EnableUnwinding();
-
- // Has a test been requested?
- if (PR_GetEnv("MOZ_PROFILER_LUL_TEST")) {
- int nTests = 0, nTestsPassed = 0;
- RunLulUnitTests(&nTests, &nTestsPassed, sLUL);
- }
-#endif
-
- // Start a thread that sends SIGPROF signal to VM thread.
- // Sending the signal ourselves instead of relying on itimer provides
- // much better accuracy.
- SetActive(true);
- if (pthread_create(
- &signal_sender_thread_, NULL, SignalSender, NULL) == 0) {
- signal_sender_launched_ = true;
- }
- LOG("Profiler thread started");
-}
-
-
-void Sampler::Stop() {
- SetActive(false);
-
- // Wait for signal sender termination (it will exit after setting
- // active_ to false).
- if (signal_sender_launched_) {
- pthread_join(signal_sender_thread_, NULL);
- signal_sender_launched_ = false;
- }
-
- SamplerRegistry::RemoveActiveSampler(this);
-
- // Restore old signal handler
- if (signal_handler_installed_) {
- sigaction(SIGNAL_SAVE_PROFILE, &old_sigsave_signal_handler_, 0);
- sigaction(SIGPROF, &old_sigprof_signal_handler_, 0);
- signal_handler_installed_ = false;
- }
-}
-
-bool Sampler::RegisterCurrentThread(const char* aName,
- PseudoStack* aPseudoStack,
- bool aIsMainThread, void* stackTop)
-{
- if (!Sampler::sRegisteredThreadsMutex)
- return false;
-
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-
- int id = gettid();
- for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
- ThreadInfo* info = sRegisteredThreads->at(i);
- if (info->ThreadId() == id && !info->IsPendingDelete()) {
- // Thread already registered. This means the first unregister will be
- // too early.
- ASSERT(false);
- return false;
- }
- }
-
- set_tls_stack_top(stackTop);
-
- ThreadInfo* info = new StackOwningThreadInfo(aName, id,
- aIsMainThread, aPseudoStack, stackTop);
-
- if (sActiveSampler) {
- sActiveSampler->RegisterThread(info);
- }
-
- sRegisteredThreads->push_back(info);
-
- return true;
-}
-
-void Sampler::UnregisterCurrentThread()
-{
- if (!Sampler::sRegisteredThreadsMutex)
- return;
-
- tlsStackTop.set(nullptr);
-
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-
- int id = gettid();
-
- for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
- ThreadInfo* info = sRegisteredThreads->at(i);
- if (info->ThreadId() == id && !info->IsPendingDelete()) {
- if (profiler_is_active()) {
- // We still want to show the results of this thread if you
- // save the profile shortly after a thread is terminated.
- // For now we will defer the delete to profile stop.
- info->SetPendingDelete();
- break;
- } else {
- delete info;
- sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
- break;
- }
- }
- }
-}
-
-#ifdef ANDROID
-static struct sigaction old_sigstart_signal_handler;
-const int SIGSTART = SIGUSR2;
-
-static void freeArray(const char** array, int size) {
- for (int i = 0; i < size; i++) {
- free((void*) array[i]);
- }
-}
-
-static uint32_t readCSVArray(char* csvList, const char** buffer) {
- uint32_t count;
- char* savePtr;
- int newlinePos = strlen(csvList) - 1;
- if (csvList[newlinePos] == '\n') {
- csvList[newlinePos] = '\0';
- }
-
- char* item = strtok_r(csvList, ",", &savePtr);
- for (count = 0; item; item = strtok_r(NULL, ",", &savePtr)) {
- int length = strlen(item) + 1; // Include \0
- char* newBuf = (char*) malloc(sizeof(char) * length);
- buffer[count] = newBuf;
- strncpy(newBuf, item, length);
- count++;
- }
-
- return count;
-}
-
-// Currently support only the env variables
-// reported in read_profiler_env
-static void ReadProfilerVars(const char* fileName, const char** features,
- uint32_t* featureCount, const char** threadNames, uint32_t* threadCount) {
- FILE* file = fopen(fileName, "r");
- const int bufferSize = 1024;
- char line[bufferSize];
- char* feature;
- char* value;
- char* savePtr;
-
- if (file) {
- while (fgets(line, bufferSize, file) != NULL) {
- feature = strtok_r(line, "=", &savePtr);
- value = strtok_r(NULL, "", &savePtr);
-
- if (strncmp(feature, PROFILER_INTERVAL, bufferSize) == 0) {
- set_profiler_interval(value);
- } else if (strncmp(feature, PROFILER_ENTRIES, bufferSize) == 0) {
- set_profiler_entries(value);
- } else if (strncmp(feature, PROFILER_STACK, bufferSize) == 0) {
- set_profiler_scan(value);
- } else if (strncmp(feature, PROFILER_FEATURES, bufferSize) == 0) {
- *featureCount = readCSVArray(value, features);
- } else if (strncmp(feature, "threads", bufferSize) == 0) {
- *threadCount = readCSVArray(value, threadNames);
- }
- }
-
- fclose(file);
- }
-}
-
-static void DoStartTask() {
- uint32_t featureCount = 0;
- uint32_t threadCount = 0;
-
- // Just allocate 10 features for now
- // FIXME: these don't really point to const chars*
- // So we free them later, but we don't want to change the const char**
- // declaration in profiler_start. Annoying but ok for now.
- const char* threadNames[10];
- const char* features[10];
- const char* profilerConfigFile = "/data/local/tmp/profiler.options";
-
- ReadProfilerVars(profilerConfigFile, features, &featureCount, threadNames, &threadCount);
- MOZ_ASSERT(featureCount < 10);
- MOZ_ASSERT(threadCount < 10);
-
- profiler_start(PROFILE_DEFAULT_ENTRY, 1,
- features, featureCount,
- threadNames, threadCount);
-
- freeArray(threadNames, threadCount);
- freeArray(features, featureCount);
-}
-
-static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
- class StartTask : public Runnable {
- public:
- NS_IMETHOD Run() override {
- DoStartTask();
- return NS_OK;
- }
- };
- // XXX: technically NS_DispatchToMainThread is NOT async signal safe. We risk
- // nasty things like deadlocks, but the probability is very low and we
- // typically only do this once so it tends to be ok. See bug 909403.
- NS_DispatchToMainThread(new StartTask());
-}
-
-void OS::Startup()
-{
- LOG("Registering start signal");
- struct sigaction sa;
- sa.sa_sigaction = StartSignalHandler;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART | SA_SIGINFO;
- if (sigaction(SIGSTART, &sa, &old_sigstart_signal_handler) != 0) {
- LOG("Error installing signal");
- }
-}
-
-#else
-
-void OS::Startup() {
- // Set up the fork handlers.
- setup_atfork();
-}
-
-#endif
-
-
-
-void TickSample::PopulateContext(void* aContext)
-{
- MOZ_ASSERT(aContext);
- ucontext_t* pContext = reinterpret_cast<ucontext_t*>(aContext);
- if (!getcontext(pContext)) {
- context = pContext;
- SetSampleContext(this, aContext);
- }
-}
-
-void OS::SleepMicro(int microseconds)
-{
- if (MOZ_UNLIKELY(microseconds >= 1000000)) {
- // Use usleep for larger intervals, because the nanosleep
- // code below only supports intervals < 1 second.
- MOZ_ALWAYS_TRUE(!::usleep(microseconds));
- return;
- }
-
- struct timespec ts;
- ts.tv_sec = 0;
- ts.tv_nsec = microseconds * 1000UL;
-
- int rv = ::nanosleep(&ts, &ts);
-
- while (rv != 0 && errno == EINTR) {
- // Keep waiting in case of interrupt.
- // nanosleep puts the remaining time back into ts.
- rv = ::nanosleep(&ts, &ts);
- }
-
- MOZ_ASSERT(!rv, "nanosleep call failed");
-}
diff --git a/tools/profiler/core/platform-macos.cc b/tools/profiler/core/platform-macos.cc
deleted file mode 100644
index 9a98d1a26..000000000
--- a/tools/profiler/core/platform-macos.cc
+++ /dev/null
@@ -1,469 +0,0 @@
-/* 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 <dlfcn.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <mach/mach_init.h>
-#include <mach-o/dyld.h>
-#include <mach-o/getsect.h>
-
-#include <AvailabilityMacros.h>
-
-#include <pthread.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <libkern/OSAtomic.h>
-#include <mach/mach.h>
-#include <mach/semaphore.h>
-#include <mach/task.h>
-#include <mach/vm_statistics.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <math.h>
-
-#ifndef SPS_STANDALONE
-#include "ThreadResponsiveness.h"
-#include "nsThreadUtils.h"
-
-// Memory profile
-#include "nsMemoryReporterManager.h"
-#endif
-
-#include "platform.h"
-#include "GeckoSampler.h"
-#include "mozilla/TimeStamp.h"
-
-using mozilla::TimeStamp;
-using mozilla::TimeDuration;
-
-// this port is based off of v8 svn revision 9837
-
-// XXX: this is a very stubbed out implementation
-// that only supports a single Sampler
-struct SamplerRegistry {
- static void AddActiveSampler(Sampler *sampler) {
- ASSERT(!SamplerRegistry::sampler);
- SamplerRegistry::sampler = sampler;
- }
- static void RemoveActiveSampler(Sampler *sampler) {
- SamplerRegistry::sampler = NULL;
- }
- static Sampler *sampler;
-};
-
-Sampler *SamplerRegistry::sampler = NULL;
-
-#ifdef DEBUG
-// 0 is never a valid thread id on MacOSX since a pthread_t is a pointer.
-static const pthread_t kNoThread = (pthread_t) 0;
-#endif
-
-void OS::Startup() {
-}
-
-void OS::Sleep(int milliseconds) {
- usleep(1000 * milliseconds);
-}
-
-void OS::SleepMicro(int microseconds) {
- usleep(microseconds);
-}
-
-Thread::Thread(const char* name)
- : stack_size_(0) {
- set_name(name);
-}
-
-
-Thread::~Thread() {
-}
-
-
-static void SetThreadName(const char* name) {
- // pthread_setname_np is only available in 10.6 or later, so test
- // for it at runtime.
- int (*dynamic_pthread_setname_np)(const char*);
- *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
- dlsym(RTLD_DEFAULT, "pthread_setname_np");
- if (!dynamic_pthread_setname_np)
- return;
-
- // Mac OS X does not expose the length limit of the name, so hardcode it.
- static const int kMaxNameLength = 63;
- USE(kMaxNameLength);
- ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
- dynamic_pthread_setname_np(name);
-}
-
-
-static void* ThreadEntry(void* arg) {
- Thread* thread = reinterpret_cast<Thread*>(arg);
-
- thread->thread_ = pthread_self();
- SetThreadName(thread->name());
- ASSERT(thread->thread_ != kNoThread);
- thread->Run();
- return NULL;
-}
-
-
-void Thread::set_name(const char* name) {
- strncpy(name_, name, sizeof(name_));
- name_[sizeof(name_) - 1] = '\0';
-}
-
-
-void Thread::Start() {
- pthread_attr_t* attr_ptr = NULL;
- pthread_attr_t attr;
- if (stack_size_ > 0) {
- pthread_attr_init(&attr);
- pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
- attr_ptr = &attr;
- }
- pthread_create(&thread_, attr_ptr, ThreadEntry, this);
- ASSERT(thread_ != kNoThread);
-}
-
-void Thread::Join() {
- pthread_join(thread_, NULL);
-}
-
-class PlatformData {
- public:
- PlatformData() : profiled_thread_(mach_thread_self())
- {
- profiled_pthread_ = pthread_from_mach_thread_np(profiled_thread_);
- }
-
- ~PlatformData() {
- // Deallocate Mach port for thread.
- mach_port_deallocate(mach_task_self(), profiled_thread_);
- }
-
- thread_act_t profiled_thread() { return profiled_thread_; }
- pthread_t profiled_pthread() { return profiled_pthread_; }
-
- private:
- // Note: for profiled_thread_ Mach primitives are used instead of PThread's
- // because the latter doesn't provide thread manipulation primitives required.
- // For details, consult "Mac OS X Internals" book, Section 7.3.
- thread_act_t profiled_thread_;
- // we also store the pthread because Mach threads have no concept of stack
- // and we want to be able to get the stack size when we need to unwind the
- // stack using frame pointers.
- pthread_t profiled_pthread_;
-};
-
-/* static */ PlatformData*
-Sampler::AllocPlatformData(int aThreadId)
-{
- return new PlatformData;
-}
-
-/* static */ void
-Sampler::FreePlatformData(PlatformData* aData)
-{
- delete aData;
-}
-
-class SamplerThread : public Thread {
- public:
- explicit SamplerThread(double interval)
- : Thread("SamplerThread")
- , intervalMicro_(floor(interval * 1000 + 0.5))
- {
- if (intervalMicro_ <= 0) {
- intervalMicro_ = 1;
- }
- }
-
- static void AddActiveSampler(Sampler* sampler) {
- SamplerRegistry::AddActiveSampler(sampler);
- if (instance_ == NULL) {
- instance_ = new SamplerThread(sampler->interval());
- instance_->Start();
- }
- }
-
- static void RemoveActiveSampler(Sampler* sampler) {
- instance_->Join();
- //XXX: unlike v8 we need to remove the active sampler after doing the Join
- // because we drop the sampler immediately
- SamplerRegistry::RemoveActiveSampler(sampler);
- delete instance_;
- instance_ = NULL;
- }
-
- // Implement Thread::Run().
- virtual void Run() {
- TimeDuration lastSleepOverhead = 0;
- TimeStamp sampleStart = TimeStamp::Now();
- while (SamplerRegistry::sampler->IsActive()) {
- SamplerRegistry::sampler->DeleteExpiredMarkers();
- if (!SamplerRegistry::sampler->IsPaused()) {
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
- std::vector<ThreadInfo*> threads =
- SamplerRegistry::sampler->GetRegisteredThreads();
- bool isFirstProfiledThread = true;
- for (uint32_t i = 0; i < threads.size(); i++) {
- ThreadInfo* info = threads[i];
-
- // This will be null if we're not interested in profiling this thread.
- if (!info->Profile() || info->IsPendingDelete())
- continue;
-
- PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
- if (sleeping == PseudoStack::SLEEPING_AGAIN) {
- info->Profile()->DuplicateLastSample();
- continue;
- }
-
-#ifndef SPS_STANDALONE
- info->Profile()->GetThreadResponsiveness()->Update();
-#endif
-
- ThreadProfile* thread_profile = info->Profile();
-
- SampleContext(SamplerRegistry::sampler, thread_profile,
- isFirstProfiledThread);
- isFirstProfiledThread = false;
- }
- }
-
- TimeStamp targetSleepEndTime = sampleStart + TimeDuration::FromMicroseconds(intervalMicro_);
- TimeStamp beforeSleep = TimeStamp::Now();
- TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep;
- double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds());
- OS::SleepMicro(sleepTime);
- sampleStart = TimeStamp::Now();
- lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime));
- }
- }
-
- void SampleContext(Sampler* sampler, ThreadProfile* thread_profile,
- bool isFirstProfiledThread)
- {
- thread_act_t profiled_thread =
- thread_profile->GetPlatformData()->profiled_thread();
-
- TickSample sample_obj;
- TickSample* sample = &sample_obj;
-
- // Unique Set Size is not supported on Mac.
- sample->ussMemory = 0;
- sample->rssMemory = 0;
-
-#ifndef SPS_STANDALONE
- if (isFirstProfiledThread && Sampler::GetActiveSampler()->ProfileMemory()) {
- sample->rssMemory = nsMemoryReporterManager::ResidentFast();
- }
-#endif
-
- // We're using thread_suspend on OS X because pthread_kill (which is what
- // we're using on Linux) has less consistent performance and causes
- // strange crashes, see bug 1166778 and bug 1166808.
-
- if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
-
-#if V8_HOST_ARCH_X64
- thread_state_flavor_t flavor = x86_THREAD_STATE64;
- x86_thread_state64_t state;
- mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
-#if __DARWIN_UNIX03
-#define REGISTER_FIELD(name) __r ## name
-#else
-#define REGISTER_FIELD(name) r ## name
-#endif // __DARWIN_UNIX03
-#elif V8_HOST_ARCH_IA32
- thread_state_flavor_t flavor = i386_THREAD_STATE;
- i386_thread_state_t state;
- mach_msg_type_number_t count = i386_THREAD_STATE_COUNT;
-#if __DARWIN_UNIX03
-#define REGISTER_FIELD(name) __e ## name
-#else
-#define REGISTER_FIELD(name) e ## name
-#endif // __DARWIN_UNIX03
-#else
-#error Unsupported Mac OS X host architecture.
-#endif // V8_HOST_ARCH
-
- if (thread_get_state(profiled_thread,
- flavor,
- reinterpret_cast<natural_t*>(&state),
- &count) == KERN_SUCCESS) {
- sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
- sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
- sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
- sample->timestamp = mozilla::TimeStamp::Now();
- sample->threadProfile = thread_profile;
-
- sampler->Tick(sample);
- }
- thread_resume(profiled_thread);
- }
-
- int intervalMicro_;
- //RuntimeProfilerRateLimiter rate_limiter_;
-
- static SamplerThread* instance_;
-
- DISALLOW_COPY_AND_ASSIGN(SamplerThread);
-};
-
-#undef REGISTER_FIELD
-
-SamplerThread* SamplerThread::instance_ = NULL;
-
-Sampler::Sampler(double interval, bool profiling, int entrySize)
- : // isolate_(isolate),
- interval_(interval),
- profiling_(profiling),
- paused_(false),
- active_(false),
- entrySize_(entrySize) /*,
- samples_taken_(0)*/ {
-}
-
-
-Sampler::~Sampler() {
- ASSERT(!IsActive());
-}
-
-
-void Sampler::Start() {
- ASSERT(!IsActive());
- SetActive(true);
- SamplerThread::AddActiveSampler(this);
-}
-
-
-void Sampler::Stop() {
- ASSERT(IsActive());
- SetActive(false);
- SamplerThread::RemoveActiveSampler(this);
-}
-
-pthread_t
-Sampler::GetProfiledThread(PlatformData* aData)
-{
- return aData->profiled_pthread();
-}
-
-#include <sys/syscall.h>
-pid_t gettid()
-{
- return (pid_t) syscall(SYS_thread_selfid);
-}
-
-/* static */ Thread::tid_t
-Thread::GetCurrentId()
-{
- return gettid();
-}
-
-bool Sampler::RegisterCurrentThread(const char* aName,
- PseudoStack* aPseudoStack,
- bool aIsMainThread, void* stackTop)
-{
- if (!Sampler::sRegisteredThreadsMutex)
- return false;
-
-
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-
- int id = gettid();
- for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
- ThreadInfo* info = sRegisteredThreads->at(i);
- if (info->ThreadId() == id && !info->IsPendingDelete()) {
- // Thread already registered. This means the first unregister will be
- // too early.
- ASSERT(false);
- return false;
- }
- }
-
- set_tls_stack_top(stackTop);
-
- ThreadInfo* info = new StackOwningThreadInfo(aName, id,
- aIsMainThread, aPseudoStack, stackTop);
-
- if (sActiveSampler) {
- sActiveSampler->RegisterThread(info);
- }
-
- sRegisteredThreads->push_back(info);
-
- return true;
-}
-
-void Sampler::UnregisterCurrentThread()
-{
- if (!Sampler::sRegisteredThreadsMutex)
- return;
-
- tlsStackTop.set(nullptr);
-
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-
- int id = gettid();
-
- for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
- ThreadInfo* info = sRegisteredThreads->at(i);
- if (info->ThreadId() == id && !info->IsPendingDelete()) {
- if (profiler_is_active()) {
- // We still want to show the results of this thread if you
- // save the profile shortly after a thread is terminated.
- // For now we will defer the delete to profile stop.
- info->SetPendingDelete();
- break;
- } else {
- delete info;
- sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
- break;
- }
- }
- }
-}
-
-void TickSample::PopulateContext(void* aContext)
-{
- // Note that this asm changes if PopulateContext's parameter list is altered
-#if defined(SPS_PLAT_amd64_darwin)
- asm (
- // Compute caller's %rsp by adding to %rbp:
- // 8 bytes for previous %rbp, 8 bytes for return address
- "leaq 0x10(%%rbp), %0\n\t"
- // Dereference %rbp to get previous %rbp
- "movq (%%rbp), %1\n\t"
- :
- "=r"(sp),
- "=r"(fp)
- );
-#elif defined(SPS_PLAT_x86_darwin)
- asm (
- // Compute caller's %esp by adding to %ebp:
- // 4 bytes for aContext + 4 bytes for return address +
- // 4 bytes for previous %ebp
- "leal 0xc(%%ebp), %0\n\t"
- // Dereference %ebp to get previous %ebp
- "movl (%%ebp), %1\n\t"
- :
- "=r"(sp),
- "=r"(fp)
- );
-#else
-# error "Unsupported architecture"
-#endif
- pc = reinterpret_cast<Address>(__builtin_extract_return_addr(
- __builtin_return_address(0)));
-}
-
diff --git a/tools/profiler/core/platform-win32.cc b/tools/profiler/core/platform-win32.cc
deleted file mode 100644
index 74b311f28..000000000
--- a/tools/profiler/core/platform-win32.cc
+++ /dev/null
@@ -1,431 +0,0 @@
-// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in
-// the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google, Inc. nor the names of its contributors
-// may be used to endorse or promote products derived from this
-// software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-// SUCH DAMAGE.
-
-#include <windows.h>
-#include <mmsystem.h>
-#include <process.h>
-#include "platform.h"
-#include "GeckoSampler.h"
-#include "ThreadResponsiveness.h"
-#include "ProfileEntry.h"
-
-// Memory profile
-#include "nsMemoryReporterManager.h"
-
-#include "mozilla/StackWalk_windows.h"
-
-
-class PlatformData {
- public:
- // Get a handle to the calling thread. This is the thread that we are
- // going to profile. We need to make a copy of the handle because we are
- // going to use it in the sampler thread. Using GetThreadHandle() will
- // not work in this case. We're using OpenThread because DuplicateHandle
- // for some reason doesn't work in Chrome's sandbox.
- PlatformData(int aThreadId) : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
- THREAD_SUSPEND_RESUME |
- THREAD_QUERY_INFORMATION,
- false,
- aThreadId)) {}
-
- ~PlatformData() {
- if (profiled_thread_ != NULL) {
- CloseHandle(profiled_thread_);
- profiled_thread_ = NULL;
- }
- }
-
- HANDLE profiled_thread() { return profiled_thread_; }
-
- private:
- HANDLE profiled_thread_;
-};
-
-/* static */ PlatformData*
-Sampler::AllocPlatformData(int aThreadId)
-{
- return new PlatformData(aThreadId);
-}
-
-/* static */ void
-Sampler::FreePlatformData(PlatformData* aData)
-{
- delete aData;
-}
-
-uintptr_t
-Sampler::GetThreadHandle(PlatformData* aData)
-{
- return (uintptr_t) aData->profiled_thread();
-}
-
-class SamplerThread : public Thread {
- public:
- SamplerThread(double interval, Sampler* sampler)
- : Thread("SamplerThread")
- , sampler_(sampler)
- , interval_(interval)
- {
- interval_ = floor(interval + 0.5);
- if (interval_ <= 0) {
- interval_ = 1;
- }
- }
-
- static void StartSampler(Sampler* sampler) {
- if (instance_ == NULL) {
- instance_ = new SamplerThread(sampler->interval(), sampler);
- instance_->Start();
- } else {
- ASSERT(instance_->interval_ == sampler->interval());
- }
- }
-
- static void StopSampler() {
- instance_->Join();
- delete instance_;
- instance_ = NULL;
- }
-
- // Implement Thread::Run().
- virtual void Run() {
-
- // By default we'll not adjust the timer resolution which tends to be around
- // 16ms. However, if the requested interval is sufficiently low we'll try to
- // adjust the resolution to match.
- if (interval_ < 10)
- ::timeBeginPeriod(interval_);
-
- while (sampler_->IsActive()) {
- sampler_->DeleteExpiredMarkers();
-
- if (!sampler_->IsPaused()) {
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
- std::vector<ThreadInfo*> threads =
- sampler_->GetRegisteredThreads();
- bool isFirstProfiledThread = true;
- for (uint32_t i = 0; i < threads.size(); i++) {
- ThreadInfo* info = threads[i];
-
- // This will be null if we're not interested in profiling this thread.
- if (!info->Profile() || info->IsPendingDelete())
- continue;
-
- PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
- if (sleeping == PseudoStack::SLEEPING_AGAIN) {
- info->Profile()->DuplicateLastSample();
- continue;
- }
-
- info->Profile()->GetThreadResponsiveness()->Update();
-
- ThreadProfile* thread_profile = info->Profile();
-
- SampleContext(sampler_, thread_profile, isFirstProfiledThread);
- isFirstProfiledThread = false;
- }
- }
- OS::Sleep(interval_);
- }
-
- // disable any timer resolution changes we've made
- if (interval_ < 10)
- ::timeEndPeriod(interval_);
- }
-
- void SampleContext(Sampler* sampler, ThreadProfile* thread_profile,
- bool isFirstProfiledThread)
- {
- uintptr_t thread = Sampler::GetThreadHandle(
- thread_profile->GetPlatformData());
- HANDLE profiled_thread = reinterpret_cast<HANDLE>(thread);
- if (profiled_thread == NULL)
- return;
-
- // Context used for sampling the register state of the profiled thread.
- CONTEXT context;
- memset(&context, 0, sizeof(context));
-
- TickSample sample_obj;
- TickSample* sample = &sample_obj;
-
- // Grab the timestamp before pausing the thread, to avoid deadlocks.
- sample->timestamp = mozilla::TimeStamp::Now();
- sample->threadProfile = thread_profile;
-
- if (isFirstProfiledThread && Sampler::GetActiveSampler()->ProfileMemory()) {
- sample->rssMemory = nsMemoryReporterManager::ResidentFast();
- } else {
- sample->rssMemory = 0;
- }
-
- // Unique Set Size is not supported on Windows.
- sample->ussMemory = 0;
-
- static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
- if (SuspendThread(profiled_thread) == kSuspendFailed)
- return;
-
- // SuspendThread is asynchronous, so the thread may still be running.
- // Call GetThreadContext first to ensure the thread is really suspended.
- // See https://blogs.msdn.microsoft.com/oldnewthing/20150205-00/?p=44743.
-
- // Using only CONTEXT_CONTROL is faster but on 64-bit it causes crashes in
- // RtlVirtualUnwind (see bug 1120126) so we set all the flags.
-#if V8_HOST_ARCH_X64
- context.ContextFlags = CONTEXT_FULL;
-#else
- context.ContextFlags = CONTEXT_CONTROL;
-#endif
- if (!GetThreadContext(profiled_thread, &context)) {
- ResumeThread(profiled_thread);
- return;
- }
-
- // Threads that may invoke JS require extra attention. Since, on windows,
- // the jits also need to modify the same dynamic function table that we need
- // to get a stack trace, we have to be wary of that to avoid deadlock.
- //
- // When embedded in Gecko, for threads that aren't the main thread,
- // CanInvokeJS consults an unlocked value in the nsIThread, so we must
- // consult this after suspending the profiled thread to avoid racing
- // against a value change.
- if (thread_profile->CanInvokeJS()) {
- if (!TryAcquireStackWalkWorkaroundLock()) {
- ResumeThread(profiled_thread);
- return;
- }
-
- // It is safe to immediately drop the lock. We only need to contend with
- // the case in which the profiled thread held needed system resources.
- // If the profiled thread had held those resources, the trylock would have
- // failed. Anyone else who grabs those resources will continue to make
- // progress, since those threads are not suspended. Because of this,
- // we cannot deadlock with them, and should let them run as they please.
- ReleaseStackWalkWorkaroundLock();
- }
-
-#if V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(context.Rip);
- sample->sp = reinterpret_cast<Address>(context.Rsp);
- sample->fp = reinterpret_cast<Address>(context.Rbp);
-#else
- sample->pc = reinterpret_cast<Address>(context.Eip);
- sample->sp = reinterpret_cast<Address>(context.Esp);
- sample->fp = reinterpret_cast<Address>(context.Ebp);
-#endif
-
- sample->context = &context;
- sampler->Tick(sample);
-
- ResumeThread(profiled_thread);
- }
-
- Sampler* sampler_;
- int interval_; // units: ms
-
- // Protects the process wide state below.
- static SamplerThread* instance_;
-
- DISALLOW_COPY_AND_ASSIGN(SamplerThread);
-};
-
-SamplerThread* SamplerThread::instance_ = NULL;
-
-
-Sampler::Sampler(double interval, bool profiling, int entrySize)
- : interval_(interval),
- profiling_(profiling),
- paused_(false),
- active_(false),
- entrySize_(entrySize) {
-}
-
-Sampler::~Sampler() {
- ASSERT(!IsActive());
-}
-
-void Sampler::Start() {
- ASSERT(!IsActive());
- SetActive(true);
- SamplerThread::StartSampler(this);
-}
-
-void Sampler::Stop() {
- ASSERT(IsActive());
- SetActive(false);
- SamplerThread::StopSampler();
-}
-
-
-static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
-
-static unsigned int __stdcall ThreadEntry(void* arg) {
- Thread* thread = reinterpret_cast<Thread*>(arg);
- thread->Run();
- return 0;
-}
-
-// Initialize a Win32 thread object. The thread has an invalid thread
-// handle until it is started.
-Thread::Thread(const char* name)
- : stack_size_(0) {
- thread_ = kNoThread;
- set_name(name);
-}
-
-void Thread::set_name(const char* name) {
- strncpy(name_, name, sizeof(name_));
- name_[sizeof(name_) - 1] = '\0';
-}
-
-// Close our own handle for the thread.
-Thread::~Thread() {
- if (thread_ != kNoThread) CloseHandle(thread_);
-}
-
-// Create a new thread. It is important to use _beginthreadex() instead of
-// the Win32 function CreateThread(), because the CreateThread() does not
-// initialize thread specific structures in the C runtime library.
-void Thread::Start() {
- thread_ = reinterpret_cast<HANDLE>(
- _beginthreadex(NULL,
- static_cast<unsigned>(stack_size_),
- ThreadEntry,
- this,
- 0,
- (unsigned int*) &thread_id_));
-}
-
-// Wait for thread to terminate.
-void Thread::Join() {
- if (thread_id_ != GetCurrentId()) {
- WaitForSingleObject(thread_, INFINITE);
- }
-}
-
-/* static */ Thread::tid_t
-Thread::GetCurrentId()
-{
- return GetCurrentThreadId();
-}
-
-void OS::Startup() {
-}
-
-void OS::Sleep(int milliseconds) {
- ::Sleep(milliseconds);
-}
-
-bool Sampler::RegisterCurrentThread(const char* aName,
- PseudoStack* aPseudoStack,
- bool aIsMainThread, void* stackTop)
-{
- if (!Sampler::sRegisteredThreadsMutex)
- return false;
-
-
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-
- int id = GetCurrentThreadId();
-
- for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
- ThreadInfo* info = sRegisteredThreads->at(i);
- if (info->ThreadId() == id && !info->IsPendingDelete()) {
- // Thread already registered. This means the first unregister will be
- // too early.
- ASSERT(false);
- return false;
- }
- }
-
- set_tls_stack_top(stackTop);
-
- ThreadInfo* info = new StackOwningThreadInfo(aName, id,
- aIsMainThread, aPseudoStack, stackTop);
-
- if (sActiveSampler) {
- sActiveSampler->RegisterThread(info);
- }
-
- sRegisteredThreads->push_back(info);
-
- return true;
-}
-
-void Sampler::UnregisterCurrentThread()
-{
- if (!Sampler::sRegisteredThreadsMutex)
- return;
-
- tlsStackTop.set(nullptr);
-
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
-
- int id = GetCurrentThreadId();
-
- for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
- ThreadInfo* info = sRegisteredThreads->at(i);
- if (info->ThreadId() == id && !info->IsPendingDelete()) {
- if (profiler_is_active()) {
- // We still want to show the results of this thread if you
- // save the profile shortly after a thread is terminated.
- // For now we will defer the delete to profile stop.
- info->SetPendingDelete();
- break;
- } else {
- delete info;
- sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
- break;
- }
- }
- }
-}
-
-void TickSample::PopulateContext(void* aContext)
-{
- MOZ_ASSERT(aContext);
- CONTEXT* pContext = reinterpret_cast<CONTEXT*>(aContext);
- context = pContext;
- RtlCaptureContext(pContext);
-
-#if defined(SPS_PLAT_amd64_windows)
-
- pc = reinterpret_cast<Address>(pContext->Rip);
- sp = reinterpret_cast<Address>(pContext->Rsp);
- fp = reinterpret_cast<Address>(pContext->Rbp);
-
-#elif defined(SPS_PLAT_x86_windows)
-
- pc = reinterpret_cast<Address>(pContext->Eip);
- sp = reinterpret_cast<Address>(pContext->Esp);
- fp = reinterpret_cast<Address>(pContext->Ebp);
-
-#endif
-}
-
diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp
deleted file mode 100644
index a8a09d66f..000000000
--- a/tools/profiler/core/platform.cpp
+++ /dev/null
@@ -1,1257 +0,0 @@
-/* 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 <ostream>
-#include <fstream>
-#include <sstream>
-#include <errno.h>
-
-#include "platform.h"
-#include "PlatformMacros.h"
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/UniquePtr.h"
-#include "GeckoProfiler.h"
-#ifndef SPS_STANDALONE
-#include "ProfilerIOInterposeObserver.h"
-#include "mozilla/StaticPtr.h"
-#endif
-#include "mozilla/ThreadLocal.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/Sprintf.h"
-#include "PseudoStack.h"
-#include "GeckoSampler.h"
-#ifndef SPS_STANDALONE
-#include "nsIObserverService.h"
-#include "nsDirectoryServiceUtils.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsXULAppAPI.h"
-#include "nsProfilerStartParams.h"
-#include "mozilla/Services.h"
-#include "nsThreadUtils.h"
-#endif
-#include "ProfilerMarkers.h"
-
-#ifdef MOZ_TASK_TRACER
-#include "GeckoTaskTracer.h"
-#endif
-
-#if defined(SPS_OS_android)
- #include "FennecJNIWrappers.h"
-#endif
-
-#if defined(SPS_OS_android)
-#include "FennecJNINatives.h"
-#endif
-
-#ifndef SPS_STANDALONE
-#if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
-# define USE_LUL_STACKWALK
-# include "lul/LulMain.h"
-# include "lul/platform-linux-lul.h"
-#endif
-#endif
-
-#if defined(SPS_OS_android)
-class GeckoJavaSampler : public java::GeckoJavaSampler::Natives<GeckoJavaSampler>
-{
-private:
- GeckoJavaSampler();
-
-public:
- static double GetProfilerTime() {
- if (!profiler_is_active()) {
- return 0.0;
- }
- return profiler_time();
- };
-};
-#endif
-
-MOZ_THREAD_LOCAL(PseudoStack *) tlsPseudoStack;
-MOZ_THREAD_LOCAL(GeckoSampler *) tlsTicker;
-MOZ_THREAD_LOCAL(void *) tlsStackTop;
-// We need to track whether we've been initialized otherwise
-// we end up using tlsStack without initializing it.
-// Because tlsStack is totally opaque to us we can't reuse
-// it as the flag itself.
-bool stack_key_initialized;
-
-mozilla::TimeStamp sLastTracerEvent; // is raced on
-mozilla::TimeStamp sStartTime;
-int sFrameNumber = 0;
-int sLastFrameNumber = 0;
-int sInitCount = 0; // Each init must have a matched shutdown.
-static bool sIsProfiling = false; // is raced on
-static bool sIsGPUProfiling = false; // is raced on
-static bool sIsLayersDump = false; // is raced on
-static bool sIsDisplayListDump = false; // is raced on
-static bool sIsRestyleProfiling = false; // is raced on
-
-// Environment variables to control the profiler
-const char* PROFILER_HELP = "MOZ_PROFILER_HELP";
-const char* PROFILER_INTERVAL = "MOZ_PROFILER_INTERVAL";
-const char* PROFILER_ENTRIES = "MOZ_PROFILER_ENTRIES";
-const char* PROFILER_STACK = "MOZ_PROFILER_STACK_SCAN";
-const char* PROFILER_FEATURES = "MOZ_PROFILING_FEATURES";
-
-/* we don't need to worry about overflow because we only treat the
- * case of them being the same as special. i.e. we only run into
- * a problem if 2^32 events happen between samples that we need
- * to know are associated with different events */
-
-// Values harvested from env vars, that control the profiler.
-static int sUnwindInterval; /* in milliseconds */
-static int sUnwindStackScan; /* max # of dubious frames allowed */
-static int sProfileEntries; /* how many entries do we store? */
-
-std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = nullptr;
-mozilla::UniquePtr< ::Mutex> Sampler::sRegisteredThreadsMutex;
-
-GeckoSampler* Sampler::sActiveSampler;
-
-#ifndef SPS_STANDALONE
-static mozilla::StaticAutoPtr<mozilla::ProfilerIOInterposeObserver>
- sInterposeObserver;
-#endif
-
-// The name that identifies the gecko thread for calls to
-// profiler_register_thread.
-static const char * gGeckoThreadName = "GeckoMain";
-
-void Sampler::Startup() {
- sRegisteredThreads = new std::vector<ThreadInfo*>();
- sRegisteredThreadsMutex = OS::CreateMutex("sRegisteredThreads mutex");
-
- // We could create the sLUL object and read unwind info into it at
- // this point. That would match the lifetime implied by destruction
- // of it in Sampler::Shutdown just below. However, that gives a big
- // delay on startup, even if no profiling is actually to be done.
- // So, instead, sLUL is created on demand at the first call to
- // Sampler::Start.
-}
-
-void Sampler::Shutdown() {
- while (sRegisteredThreads->size() > 0) {
- delete sRegisteredThreads->back();
- sRegisteredThreads->pop_back();
- }
-
- sRegisteredThreadsMutex = nullptr;
- delete sRegisteredThreads;
-
- // UnregisterThread can be called after shutdown in XPCShell. Thus
- // we need to point to null to ignore such a call after shutdown.
- sRegisteredThreadsMutex = nullptr;
- sRegisteredThreads = nullptr;
-
-#if defined(USE_LUL_STACKWALK)
- // Delete the sLUL object, if it actually got created.
- if (sLUL) {
- delete sLUL;
- sLUL = nullptr;
- }
-#endif
-}
-
-StackOwningThreadInfo::StackOwningThreadInfo(const char* aName, int aThreadId,
- bool aIsMainThread,
- PseudoStack* aPseudoStack,
- void* aStackTop)
- : ThreadInfo(aName, aThreadId, aIsMainThread, aPseudoStack, aStackTop)
-{
- aPseudoStack->ref();
-}
-
-StackOwningThreadInfo::~StackOwningThreadInfo()
-{
- PseudoStack* stack = Stack();
- if (stack) {
- stack->deref();
- }
-}
-
-void
-StackOwningThreadInfo::SetPendingDelete()
-{
- PseudoStack* stack = Stack();
- if (stack) {
- stack->deref();
- }
- ThreadInfo::SetPendingDelete();
-}
-
-ProfilerMarker::ProfilerMarker(const char* aMarkerName,
- ProfilerMarkerPayload* aPayload,
- double aTime)
- : mMarkerName(strdup(aMarkerName))
- , mPayload(aPayload)
- , mTime(aTime)
-{
-}
-
-ProfilerMarker::~ProfilerMarker() {
- free(mMarkerName);
- delete mPayload;
-}
-
-void
-ProfilerMarker::SetGeneration(uint32_t aGenID) {
- mGenID = aGenID;
-}
-
-double
-ProfilerMarker::GetTime() const {
- return mTime;
-}
-
-void ProfilerMarker::StreamJSON(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks) const
-{
- // Schema:
- // [name, time, data]
-
- aWriter.StartArrayElement();
- {
- aUniqueStacks.mUniqueStrings.WriteElement(aWriter, GetMarkerName());
- aWriter.DoubleElement(mTime);
- // TODO: Store the callsite for this marker if available:
- // if have location data
- // b.NameValue(marker, "location", ...);
- if (mPayload) {
- aWriter.StartObjectElement();
- {
- mPayload->StreamPayload(aWriter, aUniqueStacks);
- }
- aWriter.EndObject();
- }
- }
- aWriter.EndArray();
-}
-
-/* Has MOZ_PROFILER_VERBOSE been set? */
-
-// Verbosity control for the profiler. The aim is to check env var
-// MOZ_PROFILER_VERBOSE only once. However, we may need to temporarily
-// override that so as to print the profiler's help message. That's
-// what moz_profiler_set_verbosity is for.
-
-enum class ProfilerVerbosity : int8_t { UNCHECKED, NOTVERBOSE, VERBOSE };
-
-// Raced on, potentially
-static ProfilerVerbosity profiler_verbosity = ProfilerVerbosity::UNCHECKED;
-
-bool moz_profiler_verbose()
-{
- if (profiler_verbosity == ProfilerVerbosity::UNCHECKED) {
- if (getenv("MOZ_PROFILER_VERBOSE") != nullptr)
- profiler_verbosity = ProfilerVerbosity::VERBOSE;
- else
- profiler_verbosity = ProfilerVerbosity::NOTVERBOSE;
- }
-
- return profiler_verbosity == ProfilerVerbosity::VERBOSE;
-}
-
-void moz_profiler_set_verbosity(ProfilerVerbosity pv)
-{
- MOZ_ASSERT(pv == ProfilerVerbosity::UNCHECKED ||
- pv == ProfilerVerbosity::VERBOSE);
- profiler_verbosity = pv;
-}
-
-
-bool set_profiler_interval(const char* interval) {
- if (interval) {
- errno = 0;
- long int n = strtol(interval, (char**)nullptr, 10);
- if (errno == 0 && n >= 1 && n <= 1000) {
- sUnwindInterval = n;
- return true;
- }
- return false;
- }
-
- return true;
-}
-
-bool set_profiler_entries(const char* entries) {
- if (entries) {
- errno = 0;
- long int n = strtol(entries, (char**)nullptr, 10);
- if (errno == 0 && n > 0) {
- sProfileEntries = n;
- return true;
- }
- return false;
- }
-
- return true;
-}
-
-bool set_profiler_scan(const char* scanCount) {
- if (scanCount) {
- errno = 0;
- long int n = strtol(scanCount, (char**)nullptr, 10);
- if (errno == 0 && n >= 0 && n <= 100) {
- sUnwindStackScan = n;
- return true;
- }
- return false;
- }
-
- return true;
-}
-
-bool is_native_unwinding_avail() {
-# if defined(HAVE_NATIVE_UNWIND)
- return true;
-#else
- return false;
-#endif
-}
-
-// Read env vars at startup, so as to set:
-// sUnwindInterval, sProfileEntries, sUnwindStackScan.
-void read_profiler_env_vars()
-{
- /* Set defaults */
- sUnwindInterval = 0; /* We'll have to look elsewhere */
- sProfileEntries = 0;
-
- const char* interval = getenv(PROFILER_INTERVAL);
- const char* entries = getenv(PROFILER_ENTRIES);
- const char* scanCount = getenv(PROFILER_STACK);
-
- if (getenv(PROFILER_HELP)) {
- // Enable verbose output
- moz_profiler_set_verbosity(ProfilerVerbosity::VERBOSE);
- profiler_usage();
- // Now force the next enquiry of moz_profiler_verbose to re-query
- // env var MOZ_PROFILER_VERBOSE.
- moz_profiler_set_verbosity(ProfilerVerbosity::UNCHECKED);
- }
-
- if (!set_profiler_interval(interval) ||
- !set_profiler_entries(entries) ||
- !set_profiler_scan(scanCount)) {
- profiler_usage();
- } else {
- LOG( "SPS:");
- LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
- (int)sUnwindInterval);
- LOGF("SPS: Entry store size = %d (zero means \"platform default\")",
- (int)sProfileEntries);
- LOGF("SPS: UnwindStackScan = %d (max dubious frames per unwind).",
- (int)sUnwindStackScan);
- LOG( "SPS:");
- }
-}
-
-void profiler_usage() {
- LOG( "SPS: ");
- LOG( "SPS: Environment variable usage:");
- LOG( "SPS: ");
- LOG( "SPS: MOZ_PROFILER_HELP");
- LOG( "SPS: If set to any value, prints this message.");
- LOG( "SPS: ");
- LOG( "SPS: MOZ_PROFILER_INTERVAL=<number> (milliseconds, 1 to 1000)");
- LOG( "SPS: If unset, platform default is used.");
- LOG( "SPS: ");
- LOG( "SPS: MOZ_PROFILER_ENTRIES=<number> (count, minimum of 1)");
- LOG( "SPS: If unset, platform default is used.");
- LOG( "SPS: ");
- LOG( "SPS: MOZ_PROFILER_VERBOSE");
- LOG( "SPS: If set to any value, increases verbosity (recommended).");
- LOG( "SPS: ");
- LOG( "SPS: MOZ_PROFILER_STACK_SCAN=<number> (default is zero)");
- LOG( "SPS: The number of dubious (stack-scanned) frames allowed");
- LOG( "SPS: ");
- LOG( "SPS: MOZ_PROFILER_LUL_TEST");
- LOG( "SPS: If set to any value, runs LUL unit tests at startup of");
- LOG( "SPS: the unwinder thread, and prints a short summary of results.");
- LOG( "SPS: ");
- LOGF("SPS: This platform %s native unwinding.",
- is_native_unwinding_avail() ? "supports" : "does not support");
- LOG( "SPS: ");
-
- /* Re-set defaults */
- sUnwindInterval = 0; /* We'll have to look elsewhere */
- sProfileEntries = 0;
- sUnwindStackScan = 0;
-
- LOG( "SPS:");
- LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
- (int)sUnwindInterval);
- LOGF("SPS: Entry store size = %d (zero means \"platform default\")",
- (int)sProfileEntries);
- LOGF("SPS: UnwindStackScan = %d (max dubious frames per unwind).",
- (int)sUnwindStackScan);
- LOG( "SPS:");
-
- return;
-}
-
-void set_tls_stack_top(void* stackTop)
-{
- // Round |stackTop| up to the end of the containing page. We may
- // as well do this -- there's no danger of a fault, and we might
- // get a few more base-of-the-stack frames as a result. This
- // assumes that no target has a page size smaller than 4096.
- uintptr_t stackTopR = (uintptr_t)stackTop;
- if (stackTop) {
- stackTopR = (stackTopR & ~(uintptr_t)4095) + (uintptr_t)4095;
- }
- tlsStackTop.set((void*)stackTopR);
-}
-
-bool is_main_thread_name(const char* aName) {
- if (!aName) {
- return false;
- }
- return strcmp(aName, gGeckoThreadName) == 0;
-}
-
-#ifndef SPS_STANDALONE
-#ifdef HAVE_VA_COPY
-#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar)
-#elif defined(HAVE_VA_LIST_AS_ARRAY)
-#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
-#else
-#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
-#endif
-
-void
-mozilla_sampler_log(const char *fmt, va_list args)
-{
- if (profiler_is_active()) {
- // nsAutoCString AppendPrintf would be nicer but
- // this is mozilla external code
- char buf[2048];
- va_list argsCpy;
- VARARGS_ASSIGN(argsCpy, args);
- int required = VsprintfLiteral(buf, fmt, argsCpy);
- va_end(argsCpy);
-
- if (required < 0) {
- return; // silently drop for now
- } else if (required < 2048) {
- profiler_tracing("log", buf, TRACING_EVENT);
- } else {
- char* heapBuf = new char[required+1];
- va_list argsCpy;
- VARARGS_ASSIGN(argsCpy, args);
- vsnprintf(heapBuf, required+1, fmt, argsCpy);
- va_end(argsCpy);
- // EVENT_BACKTRACE could be used to get a source
- // for all log events. This could be a runtime
- // flag later.
- profiler_tracing("log", heapBuf, TRACING_EVENT);
- delete[] heapBuf;
- }
- }
-}
-#endif
-
-////////////////////////////////////////////////////////////////////////
-// BEGIN externally visible functions
-
-void mozilla_sampler_init(void* stackTop)
-{
- sInitCount++;
-
- if (stack_key_initialized)
- return;
-
-#ifdef MOZ_TASK_TRACER
- mozilla::tasktracer::InitTaskTracer();
-#endif
-
-#ifdef SPS_STANDALONE
- mozilla::TimeStamp::Startup();
-#endif
-
- LOG("BEGIN mozilla_sampler_init");
- if (!tlsPseudoStack.init() || !tlsTicker.init() || !tlsStackTop.init()) {
- LOG("Failed to init.");
- return;
- }
- bool ignore;
- sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
-
- stack_key_initialized = true;
-
- Sampler::Startup();
-
- PseudoStack *stack = PseudoStack::create();
- tlsPseudoStack.set(stack);
-
- bool isMainThread = true;
- Sampler::RegisterCurrentThread(isMainThread ?
- gGeckoThreadName : "Application Thread",
- stack, isMainThread, stackTop);
-
- // Read interval settings from MOZ_PROFILER_INTERVAL and stack-scan
- // threshhold from MOZ_PROFILER_STACK_SCAN.
- read_profiler_env_vars();
-
- // platform specific initialization
- OS::Startup();
-
-#ifndef SPS_STANDALONE
- set_stderr_callback(mozilla_sampler_log);
-#endif
-
-#if defined(SPS_OS_android)
- if (mozilla::jni::IsFennec()) {
- GeckoJavaSampler::Init();
- }
-#endif
-
- // We can't open pref so we use an environment variable
- // to know if we should trigger the profiler on startup
- // NOTE: Default
- const char *val = getenv("MOZ_PROFILER_STARTUP");
- if (!val || !*val) {
- return;
- }
-
- const char* features[] = {"js"
- , "leaf"
- , "threads"
-#if defined(XP_WIN) || defined(XP_MACOSX) \
- || (defined(SPS_ARCH_arm) && defined(linux)) \
- || defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
- , "stackwalk"
-#endif
-#if defined(SPS_OS_android)
- , "java"
-#endif
- };
-
- const char* threadFilters[] = { "GeckoMain", "Compositor" };
-
- profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
- features, MOZ_ARRAY_LENGTH(features),
- threadFilters, MOZ_ARRAY_LENGTH(threadFilters));
- LOG("END mozilla_sampler_init");
-}
-
-void mozilla_sampler_shutdown()
-{
- sInitCount--;
-
- if (sInitCount > 0)
- return;
-
- // Save the profile on shutdown if requested.
- GeckoSampler *t = tlsTicker.get();
- if (t) {
- const char *val = getenv("MOZ_PROFILER_SHUTDOWN");
- if (val) {
- std::ofstream stream;
- stream.open(val);
- if (stream.is_open()) {
- t->ToStreamAsJSON(stream);
- stream.close();
- }
- }
- }
-
- profiler_stop();
-
-#ifndef SPS_STANDALONE
- set_stderr_callback(nullptr);
-#endif
-
- Sampler::Shutdown();
-
-#ifdef SPS_STANDALONE
- mozilla::TimeStamp::Shutdown();
-#endif
-
- PseudoStack *stack = tlsPseudoStack.get();
- stack->deref();
- tlsPseudoStack.set(nullptr);
-
-#ifdef MOZ_TASK_TRACER
- mozilla::tasktracer::ShutdownTaskTracer();
-#endif
-}
-
-void mozilla_sampler_save()
-{
- GeckoSampler *t = tlsTicker.get();
- if (!t) {
- return;
- }
-
- t->RequestSave();
- // We're on the main thread already so we don't
- // have to wait to handle the save request.
- t->HandleSaveRequest();
-}
-
-mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime)
-{
- GeckoSampler *t = tlsTicker.get();
- if (!t) {
- return nullptr;
- }
-
- return t->ToJSON(aSinceTime);
-}
-
-#ifndef SPS_STANDALONE
-JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, double aSinceTime)
-{
- GeckoSampler *t = tlsTicker.get();
- if (!t) {
- return nullptr;
- }
-
- return t->ToJSObject(aCx, aSinceTime);
-}
-
-void mozilla_sampler_get_profile_data_async(double aSinceTime,
- mozilla::dom::Promise* aPromise)
-{
- GeckoSampler *t = tlsTicker.get();
- if (NS_WARN_IF(!t)) {
- return;
- }
-
- t->ToJSObjectAsync(aSinceTime, aPromise);
-}
-
-void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
- double* aInterval,
- mozilla::Vector<const char*>* aFilters,
- mozilla::Vector<const char*>* aFeatures)
-{
- if (NS_WARN_IF(!aEntrySize) || NS_WARN_IF(!aInterval) ||
- NS_WARN_IF(!aFilters) || NS_WARN_IF(!aFeatures)) {
- return;
- }
-
- GeckoSampler *t = tlsTicker.get();
- if (NS_WARN_IF(!t)) {
- return;
- }
-
- *aEntrySize = t->EntrySize();
- *aInterval = t->interval();
-
- const ThreadNameFilterList& threadNameFilterList = t->ThreadNameFilters();
- MOZ_ALWAYS_TRUE(aFilters->resize(threadNameFilterList.length()));
- for (uint32_t i = 0; i < threadNameFilterList.length(); ++i) {
- (*aFilters)[i] = threadNameFilterList[i].c_str();
- }
-
- const FeatureList& featureList = t->Features();
- MOZ_ALWAYS_TRUE(aFeatures->resize(featureList.length()));
- for (size_t i = 0; i < featureList.length(); ++i) {
- (*aFeatures)[i] = featureList[i].c_str();
- }
-}
-
-void mozilla_sampler_get_gatherer(nsISupports** aRetVal)
-{
- if (!aRetVal) {
- return;
- }
-
- if (NS_WARN_IF(!profiler_is_active())) {
- *aRetVal = nullptr;
- return;
- }
-
- GeckoSampler *t = tlsTicker.get();
- if (NS_WARN_IF(!t)) {
- *aRetVal = nullptr;
- return;
- }
-
- t->GetGatherer(aRetVal);
-}
-
-#endif
-
-void mozilla_sampler_save_profile_to_file(const char* aFilename)
-{
- GeckoSampler *t = tlsTicker.get();
- if (!t) {
- return;
- }
-
- std::ofstream stream;
- stream.open(aFilename);
- if (stream.is_open()) {
- t->ToStreamAsJSON(stream);
- stream.close();
- LOGF("Saved to %s", aFilename);
- } else {
- LOG("Fail to open profile log file.");
- }
-}
-
-
-const char** mozilla_sampler_get_features()
-{
- static const char* features[] = {
-#if defined(MOZ_PROFILING) && defined(HAVE_NATIVE_UNWIND)
- // Walk the C++ stack.
- "stackwalk",
-#endif
-#if defined(ENABLE_SPS_LEAF_DATA)
- // Include the C++ leaf node if not stackwalking. DevTools
- // profiler doesn't want the native addresses.
- "leaf",
-#endif
-#if !defined(SPS_OS_windows)
- // Use a seperate thread of walking the stack.
- "unwinder",
-#endif
- "java",
- // Only record samples during periods of bad responsiveness
- "jank",
- // Tell the JS engine to emmit pseudostack entries in the
- // pro/epilogue.
- "js",
- // GPU Profiling (may not be supported by the GL)
- "gpu",
- // Profile the registered secondary threads.
- "threads",
- // Do not include user-identifiable information
- "privacy",
- // Dump the layer tree with the textures.
- "layersdump",
- // Dump the display list with the textures.
- "displaylistdump",
- // Add main thread I/O to the profile
- "mainthreadio",
- // Add RSS collection
- "memory",
-#ifdef MOZ_TASK_TRACER
- // Start profiling with feature TaskTracer.
- "tasktracer",
-#endif
-#if defined(XP_WIN)
- // Add power collection
- "power",
-#endif
- nullptr
- };
-
- return features;
-}
-
-void mozilla_sampler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
- uint32_t *aGeneration)
-{
- *aCurrentPosition = 0;
- *aTotalSize = 0;
- *aGeneration = 0;
-
- if (!stack_key_initialized)
- return;
-
- GeckoSampler *t = tlsTicker.get();
- if (!t)
- return;
-
- t->GetBufferInfo(aCurrentPosition, aTotalSize, aGeneration);
-}
-
-// Values are only honored on the first start
-void mozilla_sampler_start(int aProfileEntries, double aInterval,
- const char** aFeatures, uint32_t aFeatureCount,
- const char** aThreadNameFilters, uint32_t aFilterCount)
-
-{
- LOG("BEGIN mozilla_sampler_start");
-
- if (!stack_key_initialized)
- profiler_init(nullptr);
-
- /* If the sampling interval was set using env vars, use that
- in preference to anything else. */
- if (sUnwindInterval > 0)
- aInterval = sUnwindInterval;
-
- /* If the entry count was set using env vars, use that, too: */
- if (sProfileEntries > 0)
- aProfileEntries = sProfileEntries;
-
- // Reset the current state if the profiler is running
- profiler_stop();
-
- GeckoSampler* t;
- t = new GeckoSampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
- aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
- aFeatures, aFeatureCount,
- aThreadNameFilters, aFilterCount);
-
- tlsTicker.set(t);
- t->Start();
- if (t->ProfileJS() || t->InPrivacyMode()) {
- ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
- std::vector<ThreadInfo*> threads = t->GetRegisteredThreads();
-
- for (uint32_t i = 0; i < threads.size(); i++) {
- ThreadInfo* info = threads[i];
- if (info->IsPendingDelete()) {
- continue;
- }
- ThreadProfile* thread_profile = info->Profile();
- if (!thread_profile) {
- continue;
- }
- thread_profile->GetPseudoStack()->reinitializeOnResume();
-#ifndef SPS_STANDALONE
- if (t->ProfileJS()) {
- thread_profile->GetPseudoStack()->enableJSSampling();
- }
- if (t->InPrivacyMode()) {
- thread_profile->GetPseudoStack()->mPrivacyMode = true;
- }
-#endif
- }
- }
-
-#if defined(SPS_OS_android)
- if (t->ProfileJava()) {
- int javaInterval = aInterval;
- // Java sampling doesn't accuratly keep up with 1ms sampling
- if (javaInterval < 10) {
- aInterval = 10;
- }
- java::GeckoJavaSampler::Start(javaInterval, 1000);
- }
-#endif
-
-#ifndef SPS_STANDALONE
- if (t->AddMainThreadIO()) {
- if (!sInterposeObserver) {
- // Lazily create IO interposer observer
- sInterposeObserver = new mozilla::ProfilerIOInterposeObserver();
- }
- mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll,
- sInterposeObserver);
- }
-#endif
-
- sIsProfiling = true;
-#ifndef SPS_STANDALONE
- sIsGPUProfiling = t->ProfileGPU();
- sIsLayersDump = t->LayersDump();
- sIsDisplayListDump = t->DisplayListDump();
- sIsRestyleProfiling = t->ProfileRestyle();
-
- if (Sampler::CanNotifyObservers()) {
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os) {
- nsTArray<nsCString> featuresArray;
- nsTArray<nsCString> threadNameFiltersArray;
-
- for (size_t i = 0; i < aFeatureCount; ++i) {
- featuresArray.AppendElement(aFeatures[i]);
- }
-
- for (size_t i = 0; i < aFilterCount; ++i) {
- threadNameFiltersArray.AppendElement(aThreadNameFilters[i]);
- }
-
- nsCOMPtr<nsIProfilerStartParams> params =
- new nsProfilerStartParams(aProfileEntries, aInterval, featuresArray,
- threadNameFiltersArray);
-
- os->NotifyObservers(params, "profiler-started", nullptr);
- }
- }
-#endif
-
- LOG("END mozilla_sampler_start");
-}
-
-void mozilla_sampler_stop()
-{
- LOG("BEGIN mozilla_sampler_stop");
-
- if (!stack_key_initialized)
- return;
-
- GeckoSampler *t = tlsTicker.get();
- if (!t) {
- LOG("END mozilla_sampler_stop-early");
- return;
- }
-
- bool disableJS = t->ProfileJS();
-
- t->Stop();
- delete t;
- tlsTicker.set(nullptr);
-
-#ifndef SPS_STANDALONE
- if (disableJS) {
- PseudoStack *stack = tlsPseudoStack.get();
- ASSERT(stack != nullptr);
- stack->disableJSSampling();
- }
-
- mozilla::IOInterposer::Unregister(mozilla::IOInterposeObserver::OpAll,
- sInterposeObserver);
- sInterposeObserver = nullptr;
-#endif
-
- sIsProfiling = false;
-#ifndef SPS_STANDALONE
- sIsGPUProfiling = false;
- sIsLayersDump = false;
- sIsDisplayListDump = false;
- sIsRestyleProfiling = false;
-
- if (Sampler::CanNotifyObservers()) {
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os)
- os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
- }
-#endif
-
- LOG("END mozilla_sampler_stop");
-}
-
-bool mozilla_sampler_is_paused() {
- if (Sampler::GetActiveSampler()) {
- return Sampler::GetActiveSampler()->IsPaused();
- } else {
- return false;
- }
-}
-
-void mozilla_sampler_pause() {
- if (Sampler::GetActiveSampler()) {
- Sampler::GetActiveSampler()->SetPaused(true);
-#ifndef SPS_STANDALONE
- if (Sampler::CanNotifyObservers()) {
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os)
- os->NotifyObservers(nullptr, "profiler-paused", nullptr);
- }
-#endif
- }
-}
-
-void mozilla_sampler_resume() {
- if (Sampler::GetActiveSampler()) {
- Sampler::GetActiveSampler()->SetPaused(false);
-#ifndef SPS_STANDALONE
- if (Sampler::CanNotifyObservers()) {
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os)
- os->NotifyObservers(nullptr, "profiler-resumed", nullptr);
- }
-#endif
- }
-}
-
-bool mozilla_sampler_feature_active(const char* aName)
-{
- if (!profiler_is_active()) {
- return false;
- }
-
- if (strcmp(aName, "gpu") == 0) {
- return sIsGPUProfiling;
- }
-
- if (strcmp(aName, "layersdump") == 0) {
- return sIsLayersDump;
- }
-
- if (strcmp(aName, "displaylistdump") == 0) {
- return sIsDisplayListDump;
- }
-
- if (strcmp(aName, "restyle") == 0) {
- return sIsRestyleProfiling;
- }
-
- return false;
-}
-
-bool mozilla_sampler_is_active()
-{
- return sIsProfiling;
-}
-
-void mozilla_sampler_responsiveness(const mozilla::TimeStamp& aTime)
-{
- sLastTracerEvent = aTime;
-}
-
-void mozilla_sampler_frame_number(int frameNumber)
-{
- sFrameNumber = frameNumber;
-}
-
-void mozilla_sampler_lock()
-{
- profiler_stop();
-#ifndef SPS_STANDALONE
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os)
- os->NotifyObservers(nullptr, "profiler-locked", nullptr);
-#endif
-}
-
-void mozilla_sampler_unlock()
-{
-#ifndef SPS_STANDALONE
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os)
- os->NotifyObservers(nullptr, "profiler-unlocked", nullptr);
-#endif
-}
-
-bool mozilla_sampler_register_thread(const char* aName, void* aGuessStackTop)
-{
- if (sInitCount == 0) {
- return false;
- }
-
- MOZ_ASSERT(tlsPseudoStack.get() == nullptr);
- PseudoStack* stack = PseudoStack::create();
- tlsPseudoStack.set(stack);
- bool isMainThread = is_main_thread_name(aName);
- void* stackTop = GetStackTop(aGuessStackTop);
- return Sampler::RegisterCurrentThread(aName, stack, isMainThread, stackTop);
-}
-
-void mozilla_sampler_unregister_thread()
-{
- // Don't check sInitCount count here -- we may be unregistering the
- // thread after the sampler was shut down.
- if (!stack_key_initialized) {
- return;
- }
-
- PseudoStack *stack = tlsPseudoStack.get();
- if (!stack) {
- return;
- }
- stack->deref();
- tlsPseudoStack.set(nullptr);
-
- Sampler::UnregisterCurrentThread();
-}
-
-void mozilla_sampler_sleep_start() {
- if (sInitCount == 0) {
- return;
- }
-
- PseudoStack *stack = tlsPseudoStack.get();
- if (stack == nullptr) {
- return;
- }
- stack->setSleeping(1);
-}
-
-void mozilla_sampler_sleep_end() {
- if (sInitCount == 0) {
- return;
- }
-
- PseudoStack *stack = tlsPseudoStack.get();
- if (stack == nullptr) {
- return;
- }
- stack->setSleeping(0);
-}
-
-bool mozilla_sampler_is_sleeping() {
- if (sInitCount == 0) {
- return false;
- }
- PseudoStack *stack = tlsPseudoStack.get();
- if (stack == nullptr) {
- return false;
- }
- return stack->isSleeping();
-}
-
-double mozilla_sampler_time(const mozilla::TimeStamp& aTime)
-{
- mozilla::TimeDuration delta = aTime - sStartTime;
- return delta.ToMilliseconds();
-}
-
-double mozilla_sampler_time()
-{
- return mozilla_sampler_time(mozilla::TimeStamp::Now());
-}
-
-ProfilerBacktrace* mozilla_sampler_get_backtrace()
-{
- if (!stack_key_initialized)
- return nullptr;
-
- // Don't capture a stack if we're not profiling
- if (!profiler_is_active()) {
- return nullptr;
- }
-
- // Don't capture a stack if we don't want to include personal information
- if (profiler_in_privacy_mode()) {
- return nullptr;
- }
-
- GeckoSampler* t = tlsTicker.get();
- if (!t) {
- return nullptr;
- }
-
- return new ProfilerBacktrace(t->GetBacktrace());
-}
-
-void mozilla_sampler_free_backtrace(ProfilerBacktrace* aBacktrace)
-{
- delete aBacktrace;
-}
-
-// Fill the output buffer with the following pattern:
-// "Lable 1" "\0" "Label 2" "\0" ... "Label N" "\0" "\0"
-// TODO: use the unwinder instead of pseudo stack.
-void mozilla_sampler_get_backtrace_noalloc(char *output, size_t outputSize)
-{
- MOZ_ASSERT(outputSize >= 2);
- char *bound = output + outputSize - 2;
- output[0] = output[1] = '\0';
- PseudoStack *pseudoStack = tlsPseudoStack.get();
- if (!pseudoStack) {
- return;
- }
-
- volatile StackEntry *pseudoFrames = pseudoStack->mStack;
- uint32_t pseudoCount = pseudoStack->stackSize();
-
- for (uint32_t i = 0; i < pseudoCount; i++) {
- size_t len = strlen(pseudoFrames[i].label());
- if (output + len >= bound)
- break;
- strcpy(output, pseudoFrames[i].label());
- output += len;
- *output++ = '\0';
- *output = '\0';
- }
-}
-
-void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
- TracingMetadata aMetaData)
-{
- mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData));
-}
-
-void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
- ProfilerBacktrace* aCause,
- TracingMetadata aMetaData)
-{
- mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData, aCause));
-}
-
-void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload)
-{
- // Note that aPayload may be allocated by the caller, so we need to make sure
- // that we free it at some point.
- mozilla::UniquePtr<ProfilerMarkerPayload> payload(aPayload);
-
- if (!stack_key_initialized)
- return;
-
- // Don't insert a marker if we're not profiling to avoid
- // the heap copy (malloc).
- if (!profiler_is_active()) {
- return;
- }
-
- // Don't add a marker if we don't want to include personal information
- if (profiler_in_privacy_mode()) {
- return;
- }
-
- PseudoStack *stack = tlsPseudoStack.get();
- if (!stack) {
- return;
- }
-
- mozilla::TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull()) ?
- aPayload->GetStartTime() : mozilla::TimeStamp::Now();
- mozilla::TimeDuration delta = origin - sStartTime;
- stack->addMarker(aMarker, payload.release(), delta.ToMilliseconds());
-}
-
-#ifndef SPS_STANDALONE
-#include "mozilla/Mutex.h"
-
-class GeckoMutex : public ::Mutex {
- public:
- explicit GeckoMutex(const char* aDesc) :
- mMutex(aDesc)
- {}
-
- virtual ~GeckoMutex() {}
-
- virtual int Lock() {
- mMutex.Lock();
- return 0;
- }
-
- virtual int Unlock() {
- mMutex.Unlock();
- return 0;
- }
-
- private:
- mozilla::Mutex mMutex;
-};
-
-mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) {
- return mozilla::MakeUnique<GeckoMutex>(aDesc);
-}
-
-#else
-// Otherwise use c++11 Mutex
-#include <mutex>
-
-class OSXMutex : public ::Mutex {
- public:
- OSXMutex(const char* aDesc) :
- mMutex()
- {}
-
- virtual ~OSXMutex() {}
-
- virtual int Lock() {
- mMutex.lock();
- return 0;
- }
-
- virtual int Unlock() {
- mMutex.unlock();
- return 0;
- }
-
- private:
- std::mutex mMutex;
-};
-
-mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) {
- return mozilla::MakeUnique<GeckoMutex>(aDesc);
-}
-
-#endif
-
-// END externally visible functions
-////////////////////////////////////////////////////////////////////////
diff --git a/tools/profiler/core/platform.h b/tools/profiler/core/platform.h
deleted file mode 100644
index 17c8dd25c..000000000
--- a/tools/profiler/core/platform.h
+++ /dev/null
@@ -1,428 +0,0 @@
-// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in
-// the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google, Inc. nor the names of its contributors
-// may be used to endorse or promote products derived from this
-// software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-// SUCH DAMAGE.
-
-#ifndef TOOLS_PLATFORM_H_
-#define TOOLS_PLATFORM_H_
-
-#ifdef SPS_STANDALONE
-#define MOZ_COUNT_CTOR(name)
-#define MOZ_COUNT_DTOR(name)
-#endif
-
-#ifdef ANDROID
-#include <android/log.h>
-#else
-#define __android_log_print(a, ...)
-#endif
-
-#ifdef XP_UNIX
-#include <pthread.h>
-#endif
-
-#include <stdint.h>
-#include <math.h>
-#ifndef SPS_STANDALONE
-#include "MainThreadUtils.h"
-#include "mozilla/Mutex.h"
-#include "ThreadResponsiveness.h"
-#endif
-#include "mozilla/TimeStamp.h"
-#include "mozilla/UniquePtr.h"
-#include "mozilla/Unused.h"
-#include "PlatformMacros.h"
-#include "v8-support.h"
-#include <vector>
-#include "StackTop.h"
-
-// We need a definition of gettid(), but Linux libc implementations don't
-// provide a wrapper for it (except for Bionic)
-#if defined(__linux__)
-#include <unistd.h>
-#if !defined(__BIONIC__)
-#include <sys/syscall.h>
-static inline pid_t gettid()
-{
- return (pid_t) syscall(SYS_gettid);
-}
-#endif
-#endif
-
-#ifdef XP_WIN
-#include <windows.h>
-#endif
-
-#define ASSERT(a) MOZ_ASSERT(a)
-
-bool moz_profiler_verbose();
-
-#ifdef ANDROID
-# if defined(__arm__) || defined(__thumb__)
-# define ENABLE_SPS_LEAF_DATA
-# define ENABLE_ARM_LR_SAVING
-# endif
-# define LOG(text) \
- do { if (moz_profiler_verbose()) \
- __android_log_write(ANDROID_LOG_ERROR, "Profiler", text); \
- } while (0)
-# define LOGF(format, ...) \
- do { if (moz_profiler_verbose()) \
- __android_log_print(ANDROID_LOG_ERROR, "Profiler", format, \
- __VA_ARGS__); \
- } while (0)
-
-#else
-# define LOG(text) \
- do { if (moz_profiler_verbose()) fprintf(stderr, "Profiler: %s\n", text); \
- } while (0)
-# define LOGF(format, ...) \
- do { if (moz_profiler_verbose()) fprintf(stderr, "Profiler: " format \
- "\n", __VA_ARGS__); \
- } while (0)
-
-#endif
-
-#if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_LINUX)
-#define ENABLE_SPS_LEAF_DATA
-#endif
-
-typedef int32_t Atomic32;
-
-extern mozilla::TimeStamp sStartTime;
-
-typedef uint8_t* Address;
-
-// ----------------------------------------------------------------------------
-// Mutex
-//
-// Mutexes are used for serializing access to non-reentrant sections of code.
-// The implementations of mutex should allow for nested/recursive locking.
-
-class Mutex {
- public:
- virtual ~Mutex() {}
-
- // Locks the given mutex. If the mutex is currently unlocked, it becomes
- // locked and owned by the calling thread, and immediately. If the mutex
- // is already locked by another thread, suspends the calling thread until
- // the mutex is unlocked.
- virtual int Lock() = 0;
-
- // Unlocks the given mutex. The mutex is assumed to be locked and owned by
- // the calling thread on entrance.
- virtual int Unlock() = 0;
-};
-
-class MutexAutoLock {
- public:
- explicit MutexAutoLock(::Mutex& aMutex)
- : mMutex(&aMutex)
- {
- mMutex->Lock();
- }
-
- ~MutexAutoLock() {
- mMutex->Unlock();
- }
-
- private:
- Mutex* mMutex;
-};
-
-// ----------------------------------------------------------------------------
-// OS
-//
-// This class has static methods for the different platform specific
-// functions. Add methods here to cope with differences between the
-// supported platforms.
-
-class OS {
- public:
-
- // Sleep for a number of milliseconds.
- static void Sleep(const int milliseconds);
-
- // Sleep for a number of microseconds.
- static void SleepMicro(const int microseconds);
-
- // Called on startup to initialize platform specific things
- static void Startup();
-
- static mozilla::UniquePtr< ::Mutex> CreateMutex(const char* aDesc);
-
- private:
- static const int msPerSecond = 1000;
-
-};
-
-
-
-
-// ----------------------------------------------------------------------------
-// Thread
-//
-// Thread objects are used for creating and running threads. When the start()
-// method is called the new thread starts running the run() method in the new
-// thread. The Thread object should not be deallocated before the thread has
-// terminated.
-
-class Thread {
- public:
- // Create new thread.
- explicit Thread(const char* name);
- virtual ~Thread();
-
- // Start new thread by calling the Run() method in the new thread.
- void Start();
-
- void Join();
-
- inline const char* name() const {
- return name_;
- }
-
- // Abstract method for run handler.
- virtual void Run() = 0;
-
- // The thread name length is limited to 16 based on Linux's implementation of
- // prctl().
- static const int kMaxThreadNameLength = 16;
-
-#ifdef XP_WIN
- HANDLE thread_;
- typedef DWORD tid_t;
- tid_t thread_id_;
-#else
- typedef ::pid_t tid_t;
-#endif
-#if defined(XP_MACOSX)
- pthread_t thread_;
-#endif
-
- static tid_t GetCurrentId();
-
- private:
- void set_name(const char *name);
-
- char name_[kMaxThreadNameLength];
- int stack_size_;
-
- DISALLOW_COPY_AND_ASSIGN(Thread);
-};
-
-// ----------------------------------------------------------------------------
-// HAVE_NATIVE_UNWIND
-//
-// Pseudo backtraces are available on all platforms. Native
-// backtraces are available only on selected platforms. Breakpad is
-// the only supported native unwinder. HAVE_NATIVE_UNWIND is set at
-// build time to indicate whether native unwinding is possible on this
-// platform.
-
-#undef HAVE_NATIVE_UNWIND
-#if defined(MOZ_PROFILING) \
- && (defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
- || (defined(MOZ_WIDGET_ANDROID) && defined(__arm__)) \
- || defined(SPS_PLAT_x86_linux) \
- || defined(SPS_OS_windows) \
- || defined(SPS_OS_darwin))
-# define HAVE_NATIVE_UNWIND
-#endif
-
-/* Some values extracted at startup from environment variables, that
- control the behaviour of the breakpad unwinder. */
-extern const char* PROFILER_INTERVAL;
-extern const char* PROFILER_ENTRIES;
-extern const char* PROFILER_STACK;
-extern const char* PROFILER_FEATURES;
-
-void read_profiler_env_vars();
-void profiler_usage();
-
-// Helper methods to expose modifying profiler behavior
-bool set_profiler_interval(const char*);
-bool set_profiler_entries(const char*);
-bool set_profiler_scan(const char*);
-bool is_native_unwinding_avail();
-
-void set_tls_stack_top(void* stackTop);
-
-// ----------------------------------------------------------------------------
-// Sampler
-//
-// A sampler periodically samples the state of the VM and optionally
-// (if used for profiling) the program counter and stack pointer for
-// the thread that created it.
-
-struct PseudoStack;
-class ThreadProfile;
-
-// TickSample captures the information collected for each sample.
-class TickSample {
- public:
- TickSample()
- : pc(NULL)
- , sp(NULL)
- , fp(NULL)
-#ifdef ENABLE_ARM_LR_SAVING
- , lr(NULL)
-#endif
- , context(NULL)
- , isSamplingCurrentThread(false)
- , threadProfile(nullptr)
- , rssMemory(0)
- , ussMemory(0)
- {}
-
- void PopulateContext(void* aContext);
-
- Address pc; // Instruction pointer.
- Address sp; // Stack pointer.
- Address fp; // Frame pointer.
-#ifdef ENABLE_ARM_LR_SAVING
- Address lr; // ARM link register
-#endif
- void* context; // The context from the signal handler, if available. On
- // Win32 this may contain the windows thread context.
- bool isSamplingCurrentThread;
- ThreadProfile* threadProfile;
- mozilla::TimeStamp timestamp;
- int64_t rssMemory;
- int64_t ussMemory;
-};
-
-class ThreadInfo;
-class PlatformData;
-class GeckoSampler;
-class SyncProfile;
-class Sampler {
- public:
- // Initialize sampler.
- explicit Sampler(double interval, bool profiling, int entrySize);
- virtual ~Sampler();
-
- double interval() const { return interval_; }
-
- // This method is called for each sampling period with the current
- // program counter.
- virtual void Tick(TickSample* sample) = 0;
-
- // Immediately captures the calling thread's call stack and returns it.
- virtual SyncProfile* GetBacktrace() = 0;
-
- // Request a save from a signal handler
- virtual void RequestSave() = 0;
- // Process any outstanding request outside a signal handler.
- virtual void HandleSaveRequest() = 0;
- // Delete markers which are no longer part of the profile due to buffer wraparound.
- virtual void DeleteExpiredMarkers() = 0;
-
- // Start and stop sampler.
- void Start();
- void Stop();
-
- // Is the sampler used for profiling?
- bool IsProfiling() const { return profiling_; }
-
- // Whether the sampler is running (that is, consumes resources).
- bool IsActive() const { return active_; }
-
- // Low overhead way to stop the sampler from ticking
- bool IsPaused() const { return paused_; }
- void SetPaused(bool value) { NoBarrier_Store(&paused_, value); }
-
- virtual bool ProfileThreads() const = 0;
-
- int EntrySize() { return entrySize_; }
-
- // We can't new/delete the type safely without defining it
- // (-Wdelete-incomplete). Use these Alloc/Free functions instead.
- static PlatformData* AllocPlatformData(int aThreadId);
- static void FreePlatformData(PlatformData*);
-
- // If we move the backtracing code into the platform files we won't
- // need to have these hacks
-#ifdef XP_WIN
- // xxxehsan sucky hack :(
- static uintptr_t GetThreadHandle(PlatformData*);
-#endif
-#ifdef XP_MACOSX
- static pthread_t GetProfiledThread(PlatformData*);
-#endif
-
- static std::vector<ThreadInfo*> GetRegisteredThreads() {
- return *sRegisteredThreads;
- }
-
- static bool RegisterCurrentThread(const char* aName,
- PseudoStack* aPseudoStack,
- bool aIsMainThread, void* stackTop);
- static void UnregisterCurrentThread();
-
- static void Startup();
- // Should only be called on shutdown
- static void Shutdown();
-
- static GeckoSampler* GetActiveSampler() { return sActiveSampler; }
- static void SetActiveSampler(GeckoSampler* sampler) { sActiveSampler = sampler; }
-
- static mozilla::UniquePtr<Mutex> sRegisteredThreadsMutex;
-
- static bool CanNotifyObservers() {
-#if defined(SPS_OS_android)
- // Android ANR reporter uses the profiler off the main thread
- return NS_IsMainThread();
-#else
- MOZ_ASSERT(NS_IsMainThread());
- return true;
-#endif
- }
-
- protected:
- static std::vector<ThreadInfo*>* sRegisteredThreads;
- static GeckoSampler* sActiveSampler;
-
- private:
- void SetActive(bool value) { NoBarrier_Store(&active_, value); }
-
- const double interval_;
- const bool profiling_;
- Atomic32 paused_;
- Atomic32 active_;
- const int entrySize_;
-
- // Refactor me!
-#if defined(SPS_OS_linux) || defined(SPS_OS_android)
- bool signal_handler_installed_;
- struct sigaction old_sigprof_signal_handler_;
- struct sigaction old_sigsave_signal_handler_;
- bool signal_sender_launched_;
- pthread_t signal_sender_thread_;
-#endif
-};
-
-#endif /* ndef TOOLS_PLATFORM_H_ */
diff --git a/tools/profiler/core/shared-libraries-linux.cc b/tools/profiler/core/shared-libraries-linux.cc
deleted file mode 100644
index 553039fb7..000000000
--- a/tools/profiler/core/shared-libraries-linux.cc
+++ /dev/null
@@ -1,145 +0,0 @@
-/* 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 "shared-libraries.h"
-
-#define PATH_MAX_TOSTRING(x) #x
-#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x)
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <limits.h>
-#include <unistd.h>
-#include <fstream>
-#include "platform.h"
-#include "shared-libraries.h"
-
-#include "common/linux/file_id.h"
-#include <algorithm>
-
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-
-// Get the breakpad Id for the binary file pointed by bin_name
-static std::string getId(const char *bin_name)
-{
- using namespace google_breakpad;
- using namespace std;
-
- PageAllocator allocator;
- auto_wasteful_vector<uint8_t, sizeof(MDGUID)> identifier(&allocator);
-
- FileID file_id(bin_name);
- if (file_id.ElfFileIdentifier(identifier)) {
- return FileID::ConvertIdentifierToUUIDString(identifier) + "0";
- }
-
- return "";
-}
-
-// TODO fix me with proper include
-#include "nsDebug.h"
-#ifdef ANDROID
-#include "ElfLoader.h" // dl_phdr_info
-#else
-#include <link.h> // dl_phdr_info
-#endif
-#include <features.h>
-#include <dlfcn.h>
-#include <sys/types.h>
-
-#ifdef ANDROID
-extern "C" MOZ_EXPORT __attribute__((weak))
-int dl_iterate_phdr(
- int (*callback) (struct dl_phdr_info *info,
- size_t size, void *data),
- void *data);
-#endif
-
-static int
-dl_iterate_callback(struct dl_phdr_info *dl_info, size_t size, void *data)
-{
- SharedLibraryInfo& info = *reinterpret_cast<SharedLibraryInfo*>(data);
-
- if (dl_info->dlpi_phnum <= 0)
- return 0;
-
- unsigned long libStart = -1;
- unsigned long libEnd = 0;
-
- for (size_t i = 0; i < dl_info->dlpi_phnum; i++) {
- if (dl_info->dlpi_phdr[i].p_type != PT_LOAD) {
- continue;
- }
- unsigned long start = dl_info->dlpi_addr + dl_info->dlpi_phdr[i].p_vaddr;
- unsigned long end = start + dl_info->dlpi_phdr[i].p_memsz;
- if (start < libStart)
- libStart = start;
- if (end > libEnd)
- libEnd = end;
- }
- const char *name = dl_info->dlpi_name;
- SharedLibrary shlib(libStart, libEnd, 0, getId(name), name);
- info.AddSharedLibrary(shlib);
-
- return 0;
-}
-
-SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
-{
- SharedLibraryInfo info;
-
-#ifdef ANDROID
- if (!dl_iterate_phdr) {
- // On ARM Android, dl_iterate_phdr is provided by the custom linker.
- // So if libxul was loaded by the system linker (e.g. as part of
- // xpcshell when running tests), it won't be available and we should
- // not call it.
- return info;
- }
-#endif // ANDROID
-
- dl_iterate_phdr(dl_iterate_callback, &info);
-
-#if defined(ANDROID)
- pid_t pid = getpid();
- char path[PATH_MAX];
- snprintf(path, PATH_MAX, "/proc/%d/maps", pid);
- std::ifstream maps(path);
- std::string line;
- int count = 0;
- while (std::getline(maps, line)) {
- int ret;
- //XXX: needs input sanitizing
- unsigned long start;
- unsigned long end;
- char perm[6] = "";
- unsigned long offset;
- char name[PATH_MAX] = "";
- ret = sscanf(line.c_str(),
- "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n",
- &start, &end, perm, &offset, name);
- if (!strchr(perm, 'x')) {
- // Ignore non executable entries
- continue;
- }
- if (ret != 5 && ret != 4) {
- LOG("Get maps line failed");
- continue;
- }
- // Use proc/pid/maps to get the dalvik-jit section since it has
- // no associated phdrs
- if (strcmp(name, "/dev/ashmem/dalvik-jit-code-cache") != 0)
- continue;
- SharedLibrary shlib(start, end, offset, getId(name), name);
- info.AddSharedLibrary(shlib);
- if (count > 10000) {
- LOG("Get maps failed");
- break;
- }
- count++;
- }
-#endif // ANDROID
-
- return info;
-}
diff --git a/tools/profiler/core/shared-libraries-macos.cc b/tools/profiler/core/shared-libraries-macos.cc
deleted file mode 100644
index e218d2280..000000000
--- a/tools/profiler/core/shared-libraries-macos.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 <AvailabilityMacros.h>
-#include <mach-o/loader.h>
-#include <mach-o/dyld_images.h>
-#include <mach/task_info.h>
-#include <mach/task.h>
-#include <mach/mach_init.h>
-#include <mach/mach_traps.h>
-#include <string.h>
-#include <stdlib.h>
-#include <vector>
-#include <sstream>
-
-#include "shared-libraries.h"
-
-#ifndef MAC_OS_X_VERSION_10_6
-#define MAC_OS_X_VERSION_10_6 1060
-#endif
-
-#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
-// borrowed from Breakpad
-// Fallback declarations for TASK_DYLD_INFO and friends, introduced in
-// <mach/task_info.h> in the Mac OS X 10.6 SDK.
-#define TASK_DYLD_INFO 17
-struct task_dyld_info {
- mach_vm_address_t all_image_info_addr;
- mach_vm_size_t all_image_info_size;
- };
-typedef struct task_dyld_info task_dyld_info_data_t;
-typedef struct task_dyld_info *task_dyld_info_t;
-#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t))
-
-#endif
-
-// Architecture specific abstraction.
-#ifdef __i386__
-typedef mach_header platform_mach_header;
-typedef segment_command mach_segment_command_type;
-#define MACHO_MAGIC_NUMBER MH_MAGIC
-#define CMD_SEGMENT LC_SEGMENT
-#define seg_size uint32_t
-#else
-typedef mach_header_64 platform_mach_header;
-typedef segment_command_64 mach_segment_command_type;
-#define MACHO_MAGIC_NUMBER MH_MAGIC_64
-#define CMD_SEGMENT LC_SEGMENT_64
-#define seg_size uint64_t
-#endif
-
-static
-void addSharedLibrary(const platform_mach_header* header, char *name, SharedLibraryInfo &info) {
- const struct load_command *cmd =
- reinterpret_cast<const struct load_command *>(header + 1);
-
- seg_size size = 0;
- unsigned long long start = reinterpret_cast<unsigned long long>(header);
- // Find the cmd segment in the macho image. It will contain the offset we care about.
- const uint8_t *uuid_bytes = nullptr;
- for (unsigned int i = 0;
- cmd && (i < header->ncmds) && (uuid_bytes == nullptr || size == 0);
- ++i) {
- if (cmd->cmd == CMD_SEGMENT) {
- const mach_segment_command_type *seg =
- reinterpret_cast<const mach_segment_command_type *>(cmd);
-
- if (!strcmp(seg->segname, "__TEXT")) {
- size = seg->vmsize;
- }
- } else if (cmd->cmd == LC_UUID) {
- const uuid_command *ucmd = reinterpret_cast<const uuid_command *>(cmd);
- uuid_bytes = ucmd->uuid;
- }
-
- cmd = reinterpret_cast<const struct load_command *>
- (reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
- }
-
- std::stringstream uuid;
- uuid << std::hex << std::uppercase;
- if (uuid_bytes != nullptr) {
- for (int i = 0; i < 16; ++i) {
- uuid << ((uuid_bytes[i] & 0xf0) >> 4);
- uuid << (uuid_bytes[i] & 0xf);
- }
- uuid << '0';
- }
-
- info.AddSharedLibrary(SharedLibrary(start, start + size, 0, uuid.str(),
- name));
-}
-
-// Use dyld to inspect the macho image information. We can build the SharedLibraryEntry structure
-// giving us roughtly the same info as /proc/PID/maps in Linux.
-SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
-{
- SharedLibraryInfo sharedLibraryInfo;
-
- task_dyld_info_data_t task_dyld_info;
- mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
- if (task_info(mach_task_self (), TASK_DYLD_INFO, (task_info_t)&task_dyld_info,
- &count) != KERN_SUCCESS) {
- return sharedLibraryInfo;
- }
-
- struct dyld_all_image_infos* aii = (struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr;
- size_t infoCount = aii->infoArrayCount;
-
- // Iterate through all dyld images (loaded libraries) to get their names
- // and offests.
- for (size_t i = 0; i < infoCount; ++i) {
- const dyld_image_info *info = &aii->infoArray[i];
-
- // If the magic number doesn't match then go no further
- // since we're not pointing to where we think we are.
- if (info->imageLoadAddress->magic != MACHO_MAGIC_NUMBER) {
- continue;
- }
-
- const platform_mach_header* header =
- reinterpret_cast<const platform_mach_header*>(info->imageLoadAddress);
-
- // Add the entry for this image.
- addSharedLibrary(header, (char*)info->imageFilePath, sharedLibraryInfo);
-
- }
- return sharedLibraryInfo;
-}
-
diff --git a/tools/profiler/core/shared-libraries-win32.cc b/tools/profiler/core/shared-libraries-win32.cc
deleted file mode 100644
index e2db2579b..000000000
--- a/tools/profiler/core/shared-libraries-win32.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 <windows.h>
-#include <tlhelp32.h>
-#include <dbghelp.h>
-#include <sstream>
-
-#include "shared-libraries.h"
-#include "nsWindowsHelpers.h"
-
-#define CV_SIGNATURE 0x53445352 // 'SDSR'
-
-struct CodeViewRecord70
-{
- uint32_t signature;
- GUID pdbSignature;
- uint32_t pdbAge;
- char pdbFileName[1];
-};
-
-static bool GetPdbInfo(uintptr_t aStart, nsID& aSignature, uint32_t& aAge, char** aPdbName)
-{
- if (!aStart) {
- return false;
- }
-
- PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(aStart);
- if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
- return false;
- }
-
- PIMAGE_NT_HEADERS ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(
- aStart + dosHeader->e_lfanew);
- if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
- return false;
- }
-
- uint32_t relativeVirtualAddress =
- ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
- if (!relativeVirtualAddress) {
- return false;
- }
-
- PIMAGE_DEBUG_DIRECTORY debugDirectory =
- reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(aStart + relativeVirtualAddress);
- if (!debugDirectory || debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
- return false;
- }
-
- CodeViewRecord70 *debugInfo = reinterpret_cast<CodeViewRecord70 *>(
- aStart + debugDirectory->AddressOfRawData);
- if (!debugInfo || debugInfo->signature != CV_SIGNATURE) {
- return false;
- }
-
- aAge = debugInfo->pdbAge;
- GUID& pdbSignature = debugInfo->pdbSignature;
- aSignature.m0 = pdbSignature.Data1;
- aSignature.m1 = pdbSignature.Data2;
- aSignature.m2 = pdbSignature.Data3;
- memcpy(aSignature.m3, pdbSignature.Data4, sizeof(pdbSignature.Data4));
-
- // The PDB file name could be different from module filename, so report both
- // e.g. The PDB for C:\Windows\SysWOW64\ntdll.dll is wntdll.pdb
- char * leafName = strrchr(debugInfo->pdbFileName, '\\');
- if (leafName) {
- // Only report the file portion of the path
- *aPdbName = leafName + 1;
- } else {
- *aPdbName = debugInfo->pdbFileName;
- }
-
- return true;
-}
-
-static bool IsDashOrBraces(char c)
-{
- return c == '-' || c == '{' || c == '}';
-}
-
-SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
-{
- SharedLibraryInfo sharedLibraryInfo;
-
- nsAutoHandle snap(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()));
-
- MODULEENTRY32 module = {0};
- module.dwSize = sizeof(MODULEENTRY32);
- if (Module32First(snap, &module)) {
- do {
- nsID pdbSig;
- uint32_t pdbAge;
- char *pdbName = NULL;
-
- // Load the module again to make sure that its handle will remain remain
- // valid as we attempt to read the PDB information from it. We load the
- // DLL as a datafile so that if the module actually gets unloaded between
- // the call to Module32Next and the following LoadLibraryEx, we don't end
- // up running the now newly loaded module's DllMain function. If the
- // module is already loaded, LoadLibraryEx just increments its refcount.
- //
- // Note that because of the race condition above, merely loading the DLL
- // again is not safe enough, therefore we also need to make sure that we
- // can read the memory mapped at the base address before we can safely
- // proceed to actually access those pages.
- HMODULE handleLock = LoadLibraryEx(module.szExePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
- MEMORY_BASIC_INFORMATION vmemInfo = {0};
- if (handleLock &&
- sizeof(vmemInfo) == VirtualQuery(module.modBaseAddr, &vmemInfo, sizeof(vmemInfo)) &&
- vmemInfo.State == MEM_COMMIT &&
- GetPdbInfo((uintptr_t)module.modBaseAddr, pdbSig, pdbAge, &pdbName)) {
- std::ostringstream stream;
- stream << pdbSig.ToString() << std::hex << pdbAge;
- std::string breakpadId = stream.str();
- std::string::iterator end =
- std::remove_if(breakpadId.begin(), breakpadId.end(), IsDashOrBraces);
- breakpadId.erase(end, breakpadId.end());
- std::transform(breakpadId.begin(), breakpadId.end(),
- breakpadId.begin(), toupper);
-
- SharedLibrary shlib((uintptr_t)module.modBaseAddr,
- (uintptr_t)module.modBaseAddr+module.modBaseSize,
- 0, // DLLs are always mapped at offset 0 on Windows
- breakpadId,
- pdbName);
- sharedLibraryInfo.AddSharedLibrary(shlib);
- }
- FreeLibrary(handleLock); // ok to free null handles
- } while (Module32Next(snap, &module));
- }
-
- return sharedLibraryInfo;
-}
-
diff --git a/tools/profiler/gecko/ProfileGatherer.cpp b/tools/profiler/gecko/ProfileGatherer.cpp
deleted file mode 100644
index 5cd45bee3..000000000
--- a/tools/profiler/gecko/ProfileGatherer.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-/* 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 "mozilla/ProfileGatherer.h"
-#include "mozilla/Services.h"
-#include "nsIObserverService.h"
-#include "GeckoSampler.h"
-
-using mozilla::dom::AutoJSAPI;
-using mozilla::dom::Promise;
-
-namespace mozilla {
-
-/**
- * When a subprocess exits before we've gathered profiles, we'll
- * store profiles for those processes until gathering starts. We'll
- * only store up to MAX_SUBPROCESS_EXIT_PROFILES. The buffer is
- * circular, so as soon as we receive another exit profile, we'll
- * bump the oldest one out of the buffer.
- */
-static const uint32_t MAX_SUBPROCESS_EXIT_PROFILES = 5;
-
-NS_IMPL_ISUPPORTS(ProfileGatherer, nsIObserver)
-
-ProfileGatherer::ProfileGatherer(GeckoSampler* aTicker)
- : mTicker(aTicker)
- , mSinceTime(0)
- , mPendingProfiles(0)
- , mGathering(false)
-{
-}
-
-void
-ProfileGatherer::GatheredOOPProfile()
-{
- MOZ_ASSERT(NS_IsMainThread());
- if (!mGathering) {
- // If we're not actively gathering, then we don't actually
- // care that we gathered a profile here. This can happen for
- // processes that exit while profiling.
- return;
- }
-
- if (NS_WARN_IF(!mPromise)) {
- // If we're not holding on to a Promise, then someone is
- // calling us erroneously.
- return;
- }
-
- mPendingProfiles--;
-
- if (mPendingProfiles == 0) {
- // We've got all of the async profiles now. Let's
- // finish off the profile and resolve the Promise.
- Finish();
- }
-}
-
-void
-ProfileGatherer::WillGatherOOPProfile()
-{
- mPendingProfiles++;
-}
-
-void
-ProfileGatherer::Start(double aSinceTime,
- Promise* aPromise)
-{
- MOZ_ASSERT(NS_IsMainThread());
- if (mGathering) {
- // If we're already gathering, reject the promise - this isn't going
- // to end well.
- if (aPromise) {
- aPromise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
- }
- return;
- }
-
- mSinceTime = aSinceTime;
- mPromise = aPromise;
- mGathering = true;
- mPendingProfiles = 0;
-
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os) {
- DebugOnly<nsresult> rv =
- os->AddObserver(this, "profiler-subprocess", false);
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "AddObserver failed");
- rv = os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyObservers failed");
- }
-
- if (!mPendingProfiles) {
- Finish();
- }
-}
-
-void
-ProfileGatherer::Finish()
-{
- MOZ_ASSERT(NS_IsMainThread());
-
- if (!mTicker) {
- // We somehow got called after we were cancelled! This shouldn't
- // be possible, but doing a belt-and-suspenders check to be sure.
- return;
- }
-
- UniquePtr<char[]> buf = mTicker->ToJSON(mSinceTime);
-
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os) {
- DebugOnly<nsresult> rv = os->RemoveObserver(this, "profiler-subprocess");
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RemoveObserver failed");
- }
-
- AutoJSAPI jsapi;
- if (NS_WARN_IF(!jsapi.Init(mPromise->GlobalJSObject()))) {
- // We're really hosed if we can't get a JS context for some reason.
- Reset();
- return;
- }
-
- JSContext* cx = jsapi.cx();
-
- // Now parse the JSON so that we resolve with a JS Object.
- JS::RootedValue val(cx);
- {
- NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
- if (!JS_ParseJSON(cx, static_cast<const char16_t*>(js_string.get()),
- js_string.Length(), &val)) {
- if (!jsapi.HasException()) {
- mPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
- } else {
- JS::RootedValue exn(cx);
- DebugOnly<bool> gotException = jsapi.StealException(&exn);
- MOZ_ASSERT(gotException);
-
- jsapi.ClearException();
- mPromise->MaybeReject(cx, exn);
- }
- } else {
- mPromise->MaybeResolve(val);
- }
- }
-
- Reset();
-}
-
-void
-ProfileGatherer::Reset()
-{
- mSinceTime = 0;
- mPromise = nullptr;
- mPendingProfiles = 0;
- mGathering = false;
-}
-
-void
-ProfileGatherer::Cancel()
-{
- // The GeckoSampler is going away. If we have a Promise in flight, we
- // should reject it.
- if (mPromise) {
- mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
- }
-
- // Clear out the GeckoSampler reference, since it's being destroyed.
- mTicker = nullptr;
-}
-
-void
-ProfileGatherer::OOPExitProfile(const nsCString& aProfile)
-{
- if (mExitProfiles.Length() >= MAX_SUBPROCESS_EXIT_PROFILES) {
- mExitProfiles.RemoveElementAt(0);
- }
- mExitProfiles.AppendElement(aProfile);
-
- // If a process exited while gathering, we need to make
- // sure we decrement the counter.
- if (mGathering) {
- GatheredOOPProfile();
- }
-}
-
-NS_IMETHODIMP
-ProfileGatherer::Observe(nsISupports* aSubject,
- const char* aTopic,
- const char16_t *someData)
-{
- if (!strcmp(aTopic, "profiler-subprocess")) {
- nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
- if (pse) {
- for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
- if (!mExitProfiles[i].IsEmpty()) {
- pse->AddSubProfile(mExitProfiles[i].get());
- }
- }
- mExitProfiles.Clear();
- }
- }
- return NS_OK;
-}
-
-} // namespace mozilla
diff --git a/tools/profiler/gecko/Profiler.jsm b/tools/profiler/gecko/Profiler.jsm
deleted file mode 100644
index c61218875..000000000
--- a/tools/profiler/gecko/Profiler.jsm
+++ /dev/null
@@ -1,16 +0,0 @@
-/* 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/. */
-
-"use strict";
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cr = Components.results;
-
-this.EXPORTED_SYMBOLS = ["Profiler"];
-
-this.Profiler = {
-
-};
-
diff --git a/tools/profiler/gecko/ProfilerIOInterposeObserver.cpp b/tools/profiler/gecko/ProfilerIOInterposeObserver.cpp
deleted file mode 100644
index 07801535d..000000000
--- a/tools/profiler/gecko/ProfilerIOInterposeObserver.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/* 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 "GeckoProfiler.h"
-#include "ProfilerIOInterposeObserver.h"
-#include "ProfilerMarkers.h"
-
-using namespace mozilla;
-
-void ProfilerIOInterposeObserver::Observe(Observation& aObservation)
-{
- if (!IsMainThread()) {
- return;
- }
-
- ProfilerBacktrace* stack = profiler_get_backtrace();
-
- nsCString filename;
- if (aObservation.Filename()) {
- filename = NS_ConvertUTF16toUTF8(aObservation.Filename());
- }
-
- IOMarkerPayload* markerPayload = new IOMarkerPayload(aObservation.Reference(),
- filename.get(),
- aObservation.Start(),
- aObservation.End(),
- stack);
- PROFILER_MARKER_PAYLOAD(aObservation.ObservedOperationString(), markerPayload);
-}
diff --git a/tools/profiler/gecko/ProfilerIOInterposeObserver.h b/tools/profiler/gecko/ProfilerIOInterposeObserver.h
index 8661b197e..fd47e48bd 100644
--- a/tools/profiler/gecko/ProfilerIOInterposeObserver.h
+++ b/tools/profiler/gecko/ProfilerIOInterposeObserver.h
@@ -5,24 +5,6 @@
#ifndef PROFILERIOINTERPOSEOBSERVER_H
#define PROFILERIOINTERPOSEOBSERVER_H
-#ifdef MOZ_ENABLE_PROFILER_SPS
-
-#include "mozilla/IOInterposer.h"
-
-namespace mozilla {
-
-/**
- * This class is the observer that calls into the profiler whenever
- * main thread I/O occurs.
- */
-class ProfilerIOInterposeObserver final : public IOInterposeObserver
-{
-public:
- virtual void Observe(Observation& aObservation);
-};
-
-} // namespace mozilla
-
-#endif // MOZ_ENABLE_PROFILER_SPS
+ /*** STUB ***/
#endif // PROFILERIOINTERPOSEOBSERVER_H
diff --git a/tools/profiler/gecko/SaveProfileTask.cpp b/tools/profiler/gecko/SaveProfileTask.cpp
deleted file mode 100644
index dbbbe4836..000000000
--- a/tools/profiler/gecko/SaveProfileTask.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "SaveProfileTask.h"
-#include "GeckoProfiler.h"
-
-nsresult
-SaveProfileTask::Run() {
- // Get file path
-#if defined(SPS_PLAT_arm_android)
- nsCString tmpPath;
- tmpPath.AppendPrintf("/sdcard/profile_%i_%i.txt", XRE_GetProcessType(), getpid());
-#else
- nsCOMPtr<nsIFile> tmpFile;
- nsAutoCString tmpPath;
- if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) {
- LOG("Failed to find temporary directory.");
- return NS_ERROR_FAILURE;
- }
- tmpPath.AppendPrintf("profile_%i_%i.txt", XRE_GetProcessType(), getpid());
-
- nsresult rv = tmpFile->AppendNative(tmpPath);
- if (NS_FAILED(rv))
- return rv;
-
- rv = tmpFile->GetNativePath(tmpPath);
- if (NS_FAILED(rv))
- return rv;
-#endif
-
- profiler_save_profile_to_file(tmpPath.get());
-
- return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(ProfileSaveEvent, nsIProfileSaveEvent)
-
-nsresult
-ProfileSaveEvent::AddSubProfile(const char* aProfile) {
- mFunc(aProfile, mClosure);
- return NS_OK;
-}
-
diff --git a/tools/profiler/gecko/SaveProfileTask.h b/tools/profiler/gecko/SaveProfileTask.h
deleted file mode 100644
index 4a215bba0..000000000
--- a/tools/profiler/gecko/SaveProfileTask.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef PROFILER_SAVETASK_H_
-#define PROFILER_SAVETASK_H_
-
-#include "platform.h"
-#include "nsThreadUtils.h"
-#include "nsIXULRuntime.h"
-#include "nsDirectoryServiceUtils.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsXULAppAPI.h"
-#include "nsIProfileSaveEvent.h"
-
-#ifdef XP_WIN
- #include <windows.h>
- #define getpid GetCurrentProcessId
-#else
- #include <unistd.h>
-#endif
-
-/**
- * This is an event used to save the profile on the main thread
- * to be sure that it is not being modified while saving.
- */
-class SaveProfileTask : public mozilla::Runnable {
-public:
- SaveProfileTask() {}
-
- NS_IMETHOD Run();
-};
-
-class ProfileSaveEvent final : public nsIProfileSaveEvent {
-public:
- typedef void (*AddSubProfileFunc)(const char* aProfile, void* aClosure);
- NS_DECL_ISUPPORTS
-
- ProfileSaveEvent(AddSubProfileFunc aFunc, void* aClosure)
- : mFunc(aFunc)
- , mClosure(aClosure)
- {}
-
- NS_IMETHOD AddSubProfile(const char* aProfile) override;
-private:
- ~ProfileSaveEvent() {}
-
- AddSubProfileFunc mFunc;
- void* mClosure;
-};
-
-#endif
-
diff --git a/tools/profiler/gecko/ThreadResponsiveness.cpp b/tools/profiler/gecko/ThreadResponsiveness.cpp
deleted file mode 100644
index 0057251e2..000000000
--- a/tools/profiler/gecko/ThreadResponsiveness.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "ThreadResponsiveness.h"
-#include "platform.h"
-#include "nsComponentManagerUtils.h"
-#include "nsThreadUtils.h"
-#include "nsITimer.h"
-#include "mozilla/Monitor.h"
-#include "ProfileEntry.h"
-#include "ThreadProfile.h"
-
-using mozilla::Monitor;
-using mozilla::MonitorAutoLock;
-using mozilla::TimeStamp;
-
-class CheckResponsivenessTask : public mozilla::Runnable,
- public nsITimerCallback {
-public:
- CheckResponsivenessTask()
- : mLastTracerTime(TimeStamp::Now())
- , mMonitor("CheckResponsivenessTask")
- , mTimer(nullptr)
- , mStop(false)
- {
- MOZ_COUNT_CTOR(CheckResponsivenessTask);
- }
-
-protected:
- ~CheckResponsivenessTask()
- {
- MOZ_COUNT_DTOR(CheckResponsivenessTask);
- }
-
-public:
- NS_IMETHOD Run() override
- {
- MonitorAutoLock mon(mMonitor);
- if (mStop)
- return NS_OK;
-
- // This is raced on because we might pause the thread here
- // for profiling so if we tried to use a monitor to protect
- // mLastTracerTime we could deadlock. We're risking seeing
- // a partial write which will show up as an outlier in our
- // performance data.
- mLastTracerTime = TimeStamp::Now();
- if (!mTimer) {
- mTimer = do_CreateInstance("@mozilla.org/timer;1");
- }
- mTimer->InitWithCallback(this, 16, nsITimer::TYPE_ONE_SHOT);
-
- return NS_OK;
- }
-
- NS_IMETHOD Notify(nsITimer* aTimer) final
- {
- NS_DispatchToMainThread(this);
- return NS_OK;
- }
-
- void Terminate() {
- MonitorAutoLock mon(mMonitor);
- mStop = true;
- }
-
- const TimeStamp& GetLastTracerTime() const {
- return mLastTracerTime;
- }
-
- NS_DECL_ISUPPORTS_INHERITED
-
-private:
- TimeStamp mLastTracerTime;
- Monitor mMonitor;
- nsCOMPtr<nsITimer> mTimer;
- bool mStop;
-};
-
-NS_IMPL_ISUPPORTS_INHERITED(CheckResponsivenessTask, mozilla::Runnable,
- nsITimerCallback)
-
-ThreadResponsiveness::ThreadResponsiveness(ThreadProfile *aThreadProfile)
- : mThreadProfile(aThreadProfile)
- , mActiveTracerEvent(nullptr)
-{
- MOZ_COUNT_CTOR(ThreadResponsiveness);
-}
-
-ThreadResponsiveness::~ThreadResponsiveness()
-{
- MOZ_COUNT_DTOR(ThreadResponsiveness);
- if (mActiveTracerEvent) {
- mActiveTracerEvent->Terminate();
- }
-}
-
-void
-ThreadResponsiveness::Update()
-{
- if (!mActiveTracerEvent) {
- if (mThreadProfile->GetThreadInfo()->IsMainThread()) {
- mActiveTracerEvent = new CheckResponsivenessTask();
- NS_DispatchToMainThread(mActiveTracerEvent);
- } else if (mThreadProfile->GetThreadInfo()->GetThread()) {
- mActiveTracerEvent = new CheckResponsivenessTask();
- mThreadProfile->GetThreadInfo()->
- GetThread()->Dispatch(mActiveTracerEvent, NS_DISPATCH_NORMAL);
- }
- }
-
- if (mActiveTracerEvent) {
- mLastTracerTime = mActiveTracerEvent->GetLastTracerTime();
- }
-}
-
diff --git a/tools/profiler/gecko/ThreadResponsiveness.h b/tools/profiler/gecko/ThreadResponsiveness.h
deleted file mode 100644
index 5454c3c05..000000000
--- a/tools/profiler/gecko/ThreadResponsiveness.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef ThreadResponsiveness_h
-#define ThreadResponsiveness_h
-
-#include "nsISupports.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/TimeStamp.h"
-
-class ThreadProfile;
-class CheckResponsivenessTask;
-
-class ThreadResponsiveness {
-public:
- explicit ThreadResponsiveness(ThreadProfile *aThreadProfile);
-
- ~ThreadResponsiveness();
-
- void Update();
-
- mozilla::TimeDuration GetUnresponsiveDuration(const mozilla::TimeStamp& now) const {
- return now - mLastTracerTime;
- }
-
- bool HasData() const {
- return !mLastTracerTime.IsNull();
- }
-private:
- ThreadProfile* mThreadProfile;
- RefPtr<CheckResponsivenessTask> mActiveTracerEvent;
- mozilla::TimeStamp mLastTracerTime;
-};
-
-#endif
-
diff --git a/tools/profiler/gecko/nsIProfileSaveEvent.idl b/tools/profiler/gecko/nsIProfileSaveEvent.idl
deleted file mode 100644
index c2c4bed02..000000000
--- a/tools/profiler/gecko/nsIProfileSaveEvent.idl
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "nsISupports.idl"
-
-[uuid(f5ad0830-e178-41f9-b253-db9b4fae4cb3)]
-interface nsIProfileSaveEvent : nsISupports
-{
- /**
- * Call this method when observing this event to include
- * a sub profile origining from an external source such
- * as a non native thread or another process.
- */
- void AddSubProfile(in string aMarker);
-};
-
-
diff --git a/tools/profiler/gecko/nsIProfiler.idl b/tools/profiler/gecko/nsIProfiler.idl
deleted file mode 100644
index f9b118650..000000000
--- a/tools/profiler/gecko/nsIProfiler.idl
+++ /dev/null
@@ -1,101 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "nsISupports.idl"
-
-%{C++
-#include "nsTArrayForwardDeclare.h"
-class nsCString;
-%}
-
-[ref] native StringArrayRef(const nsTArray<nsCString>);
-
-/**
- * Start-up parameters for subprocesses are passed through nsIObserverService,
- * which, unfortunately, means we need to implement nsISupports in order to
- * go through it.
- */
-[uuid(0a175ba7-8fcf-4ce9-9c4b-ccc6272f4425)]
-interface nsIProfilerStartParams : nsISupports
-{
- attribute uint32_t entries;
- attribute double interval;
-
- [noscript, notxpcom, nostdcall] StringArrayRef getFeatures();
- [noscript, notxpcom, nostdcall] StringArrayRef getThreadFilterNames();
-};
-
-[scriptable, uuid(ead3f75c-0e0e-4fbb-901c-1e5392ef5b2a)]
-interface nsIProfiler : nsISupports
-{
- boolean CanProfile();
- void StartProfiler(in uint32_t aEntries, in double aInterval,
- [array, size_is(aFeatureCount)] in string aFeatures,
- in uint32_t aFeatureCount,
- [array, size_is(aFilterCount), optional] in string aThreadNameFilters,
- [optional] in uint32_t aFilterCount);
- void StopProfiler();
- boolean IsPaused();
- void PauseSampling();
- void ResumeSampling();
- void AddMarker(in string aMarker);
- /*
- * Returns the JSON string of the profile. If aSinceTime is passed, only
- * report samples taken at >= aSinceTime.
- */
- string GetProfile([optional] in double aSinceTime);
-
- /*
- * Returns a JS object of the profile. If aSinceTime is passed, only report
- * samples taken at >= aSinceTime.
- */
- [implicit_jscontext]
- jsval getProfileData([optional] in double aSinceTime);
-
- [implicit_jscontext]
- nsISupports getProfileDataAsync([optional] in double aSinceTime);
-
- boolean IsActive();
- void GetFeatures(out uint32_t aCount, [retval, array, size_is(aCount)] out string aFeatures);
-
- /**
- * The starting parameters that were sent to the profiler for sampling.
- * If the profiler is not currently sampling, this will return null.
- */
- readonly attribute nsIProfilerStartParams startParams;
-
- /**
- * The profileGatherer will be null if the profiler is not currently
- * active.
- */
- readonly attribute nsISupports profileGatherer;
-
- void GetBufferInfo(out uint32_t aCurrentPosition, out uint32_t aTotalSize,
- out uint32_t aGeneration);
-
- /**
- * Returns the elapsed time, in milliseconds, since the profiler's epoch.
- * The epoch is guaranteed to be constant for the duration of the
- * process, but is otherwise arbitrary.
- */
- double getElapsedTime();
-
- /**
- * Returns a JSON string of an array of shared library objects.
- * Every object has three properties: start, end, and name.
- * start and end are integers describing the address range that the library
- * occupies in memory. name is the path of the library as a string.
- *
- * On Windows profiling builds, the shared library objects will have
- * additional pdbSignature and pdbAge properties for uniquely identifying
- * shared library versions for stack symbolication.
- */
- AString getSharedLibraryInformation();
-
- /**
- * Dump the collected profile to a file.
- */
- void dumpProfileToFile(in string aFilename);
-};
diff --git a/tools/profiler/gecko/nsProfiler.cpp b/tools/profiler/gecko/nsProfiler.cpp
deleted file mode 100644
index c38447381..000000000
--- a/tools/profiler/gecko/nsProfiler.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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 <string>
-#include <sstream>
-#include "GeckoProfiler.h"
-#include "nsProfiler.h"
-#include "nsProfilerStartParams.h"
-#include "nsMemory.h"
-#include "nsString.h"
-#include "mozilla/Services.h"
-#include "nsIObserverService.h"
-#include "nsIInterfaceRequestor.h"
-#include "nsILoadContext.h"
-#include "nsIWebNavigation.h"
-#include "nsIInterfaceRequestorUtils.h"
-#include "shared-libraries.h"
-#include "js/Value.h"
-#include "mozilla/ErrorResult.h"
-#include "mozilla/dom/Promise.h"
-
-using mozilla::ErrorResult;
-using mozilla::dom::Promise;
-using std::string;
-
-NS_IMPL_ISUPPORTS(nsProfiler, nsIProfiler)
-
-nsProfiler::nsProfiler()
- : mLockedForPrivateBrowsing(false)
-{
-}
-
-nsProfiler::~nsProfiler()
-{
- nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
- if (observerService) {
- observerService->RemoveObserver(this, "chrome-document-global-created");
- observerService->RemoveObserver(this, "last-pb-context-exited");
- }
-}
-
-nsresult
-nsProfiler::Init() {
- nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
- if (observerService) {
- observerService->AddObserver(this, "chrome-document-global-created", false);
- observerService->AddObserver(this, "last-pb-context-exited", false);
- }
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::Observe(nsISupports *aSubject,
- const char *aTopic,
- const char16_t *aData)
-{
- if (strcmp(aTopic, "chrome-document-global-created") == 0) {
- nsCOMPtr<nsIInterfaceRequestor> requestor = do_QueryInterface(aSubject);
- nsCOMPtr<nsIWebNavigation> parentWebNav = do_GetInterface(requestor);
- nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(parentWebNav);
- if (loadContext && loadContext->UsePrivateBrowsing() && !mLockedForPrivateBrowsing) {
- mLockedForPrivateBrowsing = true;
- profiler_lock();
- }
- } else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
- mLockedForPrivateBrowsing = false;
- profiler_unlock();
- }
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::CanProfile(bool *aCanProfile)
-{
- *aCanProfile = !mLockedForPrivateBrowsing;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::StartProfiler(uint32_t aEntries, double aInterval,
- const char** aFeatures, uint32_t aFeatureCount,
- const char** aThreadNameFilters, uint32_t aFilterCount)
-{
- if (mLockedForPrivateBrowsing) {
- return NS_ERROR_NOT_AVAILABLE;
- }
-
- profiler_start(aEntries, aInterval,
- aFeatures, aFeatureCount,
- aThreadNameFilters, aFilterCount);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::StopProfiler()
-{
- profiler_stop();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::IsPaused(bool *aIsPaused)
-{
- *aIsPaused = profiler_is_paused();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::PauseSampling()
-{
- profiler_pause();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::ResumeSampling()
-{
- profiler_resume();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::AddMarker(const char *aMarker)
-{
- PROFILER_MARKER(aMarker);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::GetProfile(double aSinceTime, char** aProfile)
-{
- mozilla::UniquePtr<char[]> profile = profiler_get_profile(aSinceTime);
- if (profile) {
- size_t len = strlen(profile.get());
- char *profileStr = static_cast<char *>
- (nsMemory::Clone(profile.get(), (len + 1) * sizeof(char)));
- profileStr[len] = '\0';
- *aProfile = profileStr;
- }
- return NS_OK;
-}
-
-std::string GetSharedLibraryInfoStringInternal();
-
-std::string
-GetSharedLibraryInfoString()
-{
- return GetSharedLibraryInfoStringInternal();
-}
-
-NS_IMETHODIMP
-nsProfiler::GetSharedLibraryInformation(nsAString& aOutString)
-{
- aOutString.Assign(NS_ConvertUTF8toUTF16(GetSharedLibraryInfoString().c_str()));
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::DumpProfileToFile(const char* aFilename)
-{
- profiler_save_profile_to_file(aFilename);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::GetProfileData(double aSinceTime, JSContext* aCx,
- JS::MutableHandle<JS::Value> aResult)
-{
- JS::RootedObject obj(aCx, profiler_get_profile_jsobject(aCx, aSinceTime));
- if (!obj) {
- return NS_ERROR_FAILURE;
- }
- aResult.setObject(*obj);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::GetProfileDataAsync(double aSinceTime, JSContext* aCx,
- nsISupports** aPromise)
-{
- MOZ_ASSERT(NS_IsMainThread());
-
- if (NS_WARN_IF(!aCx)) {
- return NS_ERROR_FAILURE;
- }
-
- nsIGlobalObject* go = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
-
- if (NS_WARN_IF(!go)) {
- return NS_ERROR_FAILURE;
- }
-
- ErrorResult result;
- RefPtr<Promise> promise = Promise::Create(go, result);
- if (NS_WARN_IF(result.Failed())) {
- return result.StealNSResult();
- }
-
- profiler_get_profile_jsobject_async(aSinceTime, promise);
-
- promise.forget(aPromise);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::GetElapsedTime(double* aElapsedTime)
-{
- *aElapsedTime = profiler_time();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::IsActive(bool *aIsActive)
-{
- *aIsActive = profiler_is_active();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::GetFeatures(uint32_t *aCount, char ***aFeatures)
-{
- uint32_t len = 0;
-
- const char **features = profiler_get_features();
- if (!features) {
- *aCount = 0;
- *aFeatures = nullptr;
- return NS_OK;
- }
-
- while (features[len]) {
- len++;
- }
-
- char **featureList = static_cast<char **>
- (moz_xmalloc(len * sizeof(char*)));
-
- for (size_t i = 0; i < len; i++) {
- size_t strLen = strlen(features[i]);
- featureList[i] = static_cast<char *>
- (nsMemory::Clone(features[i], (strLen + 1) * sizeof(char)));
- }
-
- *aFeatures = featureList;
- *aCount = len;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::GetStartParams(nsIProfilerStartParams** aRetVal)
-{
- if (!profiler_is_active()) {
- *aRetVal = nullptr;
- } else {
- int entrySize = 0;
- double interval = 0;
- mozilla::Vector<const char*> filters;
- mozilla::Vector<const char*> features;
- profiler_get_start_params(&entrySize, &interval, &filters, &features);
-
- nsTArray<nsCString> filtersArray;
- for (uint32_t i = 0; i < filters.length(); ++i) {
- filtersArray.AppendElement(filters[i]);
- }
-
- nsTArray<nsCString> featuresArray;
- for (size_t i = 0; i < features.length(); ++i) {
- featuresArray.AppendElement(features[i]);
- }
-
- nsCOMPtr<nsIProfilerStartParams> startParams =
- new nsProfilerStartParams(entrySize, interval, featuresArray,
- filtersArray);
-
- startParams.forget(aRetVal);
- }
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration)
-{
- MOZ_ASSERT(aCurrentPosition);
- MOZ_ASSERT(aTotalSize);
- MOZ_ASSERT(aGeneration);
- profiler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfiler::GetProfileGatherer(nsISupports** aRetVal)
-{
- if (!aRetVal) {
- return NS_ERROR_INVALID_POINTER;
- }
-
- // If we're not profiling, there will be no gatherer.
- if (!profiler_is_active()) {
- *aRetVal = nullptr;
- } else {
- nsCOMPtr<nsISupports> gatherer;
- profiler_get_gatherer(getter_AddRefs(gatherer));
- gatherer.forget(aRetVal);
- }
- return NS_OK;
-} \ No newline at end of file
diff --git a/tools/profiler/gecko/nsProfiler.h b/tools/profiler/gecko/nsProfiler.h
deleted file mode 100644
index 50dabd278..000000000
--- a/tools/profiler/gecko/nsProfiler.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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/. */
-
-#ifndef _NSPROFILER_H_
-#define _NSPROFILER_H_
-
-#include "nsIProfiler.h"
-#include "nsIObserver.h"
-#include "mozilla/Attributes.h"
-
-class nsProfiler final : public nsIProfiler, public nsIObserver
-{
-public:
- nsProfiler();
-
- NS_DECL_ISUPPORTS
- NS_DECL_NSIOBSERVER
- NS_DECL_NSIPROFILER
-
- nsresult Init();
-private:
- ~nsProfiler();
- bool mLockedForPrivateBrowsing;
-};
-
-#endif /* _NSPROFILER_H_ */
-
diff --git a/tools/profiler/gecko/nsProfilerFactory.cpp b/tools/profiler/gecko/nsProfilerFactory.cpp
deleted file mode 100644
index 0cab23e89..000000000
--- a/tools/profiler/gecko/nsProfilerFactory.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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 "mozilla/ModuleUtils.h"
-#include "nsCOMPtr.h"
-#include "nsProfiler.h"
-#include "nsProfilerCIID.h"
-
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsProfiler, Init)
-
-NS_DEFINE_NAMED_CID(NS_PROFILER_CID);
-
-static const mozilla::Module::CIDEntry kProfilerCIDs[] = {
- { &kNS_PROFILER_CID, false, nullptr, nsProfilerConstructor },
- { nullptr }
-};
-
-static const mozilla::Module::ContractIDEntry kProfilerContracts[] = {
- { "@mozilla.org/tools/profiler;1", &kNS_PROFILER_CID },
- { nullptr }
-};
-
-static const mozilla::Module kProfilerModule = {
- mozilla::Module::kVersion,
- kProfilerCIDs,
- kProfilerContracts
-};
-
-NSMODULE_DEFN(nsProfilerModule) = &kProfilerModule;
diff --git a/tools/profiler/gecko/nsProfilerStartParams.cpp b/tools/profiler/gecko/nsProfilerStartParams.cpp
deleted file mode 100644
index 5335e694e..000000000
--- a/tools/profiler/gecko/nsProfilerStartParams.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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 "nsProfilerStartParams.h"
-
-NS_IMPL_ISUPPORTS(nsProfilerStartParams, nsIProfilerStartParams)
-
-nsProfilerStartParams::nsProfilerStartParams(uint32_t aEntries,
- double aInterval,
- const nsTArray<nsCString>& aFeatures,
- const nsTArray<nsCString>& aThreadFilterNames) :
- mEntries(aEntries),
- mInterval(aInterval),
- mFeatures(aFeatures),
- mThreadFilterNames(aThreadFilterNames)
-{
-}
-
-nsProfilerStartParams::~nsProfilerStartParams()
-{
-}
-
-NS_IMETHODIMP
-nsProfilerStartParams::GetEntries(uint32_t* aEntries)
-{
- NS_ENSURE_ARG_POINTER(aEntries);
- *aEntries = mEntries;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfilerStartParams::SetEntries(uint32_t aEntries)
-{
- NS_ENSURE_ARG(aEntries);
- mEntries = aEntries;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfilerStartParams::GetInterval(double* aInterval)
-{
- NS_ENSURE_ARG_POINTER(aInterval);
- *aInterval = mInterval;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsProfilerStartParams::SetInterval(double aInterval)
-{
- NS_ENSURE_ARG(aInterval);
- mInterval = aInterval;
- return NS_OK;
-}
-
-const nsTArray<nsCString>&
-nsProfilerStartParams::GetFeatures()
-{
- return mFeatures;
-}
-
-const nsTArray<nsCString>&
-nsProfilerStartParams::GetThreadFilterNames()
-{
- return mThreadFilterNames;
-}
diff --git a/tools/profiler/gecko/nsProfilerStartParams.h b/tools/profiler/gecko/nsProfilerStartParams.h
deleted file mode 100644
index 98788077f..000000000
--- a/tools/profiler/gecko/nsProfilerStartParams.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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/. */
-
-#ifndef _NSPROFILERSTARTPARAMS_H_
-#define _NSPROFILERSTARTPARAMS_H_
-
-#include "nsIProfiler.h"
-#include "nsString.h"
-#include "nsTArray.h"
-
-class nsProfilerStartParams : public nsIProfilerStartParams
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSIPROFILERSTARTPARAMS
-
- nsProfilerStartParams(uint32_t aEntries,
- double aInterval,
- const nsTArray<nsCString>& aFeatures,
- const nsTArray<nsCString>& aThreadFilterNames);
-
-private:
- virtual ~nsProfilerStartParams();
- uint32_t mEntries;
- double mInterval;
- nsTArray<nsCString> mFeatures;
- nsTArray<nsCString> mThreadFilterNames;
-};
-
-#endif
diff --git a/tools/profiler/lul/AutoObjectMapper.cpp b/tools/profiler/lul/AutoObjectMapper.cpp
deleted file mode 100644
index 1bc5ce62a..000000000
--- a/tools/profiler/lul/AutoObjectMapper.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-/* -*- 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 <sys/mman.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include "mozilla/Assertions.h"
-#include "mozilla/Sprintf.h"
-
-#include "PlatformMacros.h"
-#include "AutoObjectMapper.h"
-
-#if defined(SPS_OS_android)
-# include <dlfcn.h>
-# include "mozilla/Types.h"
- // FIXME move these out of mozglue/linker/ElfLoader.h into their
- // own header, so as to avoid conflicts arising from two definitions
- // of Array
- extern "C" {
- MFBT_API size_t
- __dl_get_mappable_length(void *handle);
- MFBT_API void *
- __dl_mmap(void *handle, void *addr, size_t length, off_t offset);
- MFBT_API void
- __dl_munmap(void *handle, void *addr, size_t length);
- }
- // The following are for get_installation_lib_dir()
-# include "nsString.h"
-# include "nsDirectoryServiceUtils.h"
-# include "nsDirectoryServiceDefs.h"
-#endif
-
-
-// A helper function for creating failure error messages in
-// AutoObjectMapper*::Map.
-static void
-failedToMessage(void(*aLog)(const char*),
- const char* aHowFailed, std::string aFileName)
-{
- char buf[300];
- SprintfLiteral(buf, "AutoObjectMapper::Map: Failed to %s \'%s\'",
- aHowFailed, aFileName.c_str());
- buf[sizeof(buf)-1] = 0;
- aLog(buf);
-}
-
-
-AutoObjectMapperPOSIX::AutoObjectMapperPOSIX(void(*aLog)(const char*))
- : mImage(nullptr)
- , mSize(0)
- , mLog(aLog)
- , mIsMapped(false)
-{}
-
-AutoObjectMapperPOSIX::~AutoObjectMapperPOSIX() {
- if (!mIsMapped) {
- // There's nothing to do.
- MOZ_ASSERT(!mImage);
- MOZ_ASSERT(mSize == 0);
- return;
- }
- MOZ_ASSERT(mSize > 0);
- // The following assertion doesn't necessarily have to be true,
- // but we assume (reasonably enough) that no mmap facility would
- // be crazy enough to map anything at page zero.
- MOZ_ASSERT(mImage);
- munmap(mImage, mSize);
-}
-
-bool AutoObjectMapperPOSIX::Map(/*OUT*/void** start, /*OUT*/size_t* length,
- std::string fileName)
-{
- MOZ_ASSERT(!mIsMapped);
-
- int fd = open(fileName.c_str(), O_RDONLY);
- if (fd == -1) {
- failedToMessage(mLog, "open", fileName);
- return false;
- }
-
- struct stat st;
- int err = fstat(fd, &st);
- size_t sz = (err == 0) ? st.st_size : 0;
- if (err != 0 || sz == 0) {
- failedToMessage(mLog, "fstat", fileName);
- close(fd);
- return false;
- }
-
- void* image = mmap(nullptr, sz, PROT_READ, MAP_SHARED, fd, 0);
- if (image == MAP_FAILED) {
- failedToMessage(mLog, "mmap", fileName);
- close(fd);
- return false;
- }
-
- close(fd);
- mIsMapped = true;
- mImage = *start = image;
- mSize = *length = sz;
- return true;
-}
-
-
-#if defined(SPS_OS_android)
-// A helper function for AutoObjectMapperFaultyLib::Map. Finds out
-// where the installation's lib directory is, since we'll have to look
-// in there to get hold of libmozglue.so. Returned C string is heap
-// allocated and the caller must deallocate it.
-static char*
-get_installation_lib_dir()
-{
- nsCOMPtr<nsIProperties>
- directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
- if (!directoryService) {
- return nullptr;
- }
- nsCOMPtr<nsIFile> greDir;
- nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
- getter_AddRefs(greDir));
- if (NS_FAILED(rv)) return nullptr;
- nsCString path;
- rv = greDir->GetNativePath(path);
- if (NS_FAILED(rv)) {
- return nullptr;
- }
- return strdup(path.get());
-}
-
-AutoObjectMapperFaultyLib::AutoObjectMapperFaultyLib(void(*aLog)(const char*))
- : AutoObjectMapperPOSIX(aLog)
- , mHdl(nullptr)
-{}
-
-AutoObjectMapperFaultyLib::~AutoObjectMapperFaultyLib() {
- if (mHdl) {
- // We've got an object mapped by faulty.lib. Unmap it via faulty.lib.
- MOZ_ASSERT(mSize > 0);
- // Assert on the basis that no valid mapping would start at page zero.
- MOZ_ASSERT(mImage);
- __dl_munmap(mHdl, mImage, mSize);
- dlclose(mHdl);
- // Stop assertions in ~AutoObjectMapperPOSIX from failing.
- mImage = nullptr;
- mSize = 0;
- }
- // At this point the parent class destructor, ~AutoObjectMapperPOSIX,
- // gets called. If that has something mapped in the normal way, it
- // will unmap it in the normal way. Unfortunately there's no
- // obvious way to enforce the requirement that the object is mapped
- // either by faulty.lib or by the parent class, but not by both.
-}
-
-bool AutoObjectMapperFaultyLib::Map(/*OUT*/void** start, /*OUT*/size_t* length,
- std::string fileName)
-{
- MOZ_ASSERT(!mHdl);
-
- if (fileName == "libmozglue.so") {
-
- // Do (2) in the comment above.
- char* libdir = get_installation_lib_dir();
- if (libdir) {
- fileName = std::string(libdir) + "/lib/" + fileName;
- free(libdir);
- }
- // Hand the problem off to the standard mapper.
- return AutoObjectMapperPOSIX::Map(start, length, fileName);
-
- } else {
-
- // Do cases (1) and (3) in the comment above. We have to
- // grapple with faulty.lib directly.
- void* hdl = dlopen(fileName.c_str(), RTLD_GLOBAL | RTLD_LAZY);
- if (!hdl) {
- failedToMessage(mLog, "get handle for ELF file", fileName);
- return false;
- }
-
- size_t sz = __dl_get_mappable_length(hdl);
- if (sz == 0) {
- dlclose(hdl);
- failedToMessage(mLog, "get size for ELF file", fileName);
- return false;
- }
-
- void* image = __dl_mmap(hdl, nullptr, sz, 0);
- if (image == MAP_FAILED) {
- dlclose(hdl);
- failedToMessage(mLog, "mmap ELF file", fileName);
- return false;
- }
-
- mHdl = hdl;
- mImage = *start = image;
- mSize = *length = sz;
- return true;
- }
-}
-
-#endif // defined(SPS_OS_android)
diff --git a/tools/profiler/lul/AutoObjectMapper.h b/tools/profiler/lul/AutoObjectMapper.h
deleted file mode 100644
index 3f60dc44d..000000000
--- a/tools/profiler/lul/AutoObjectMapper.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/* -*- 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/. */
-
-#ifndef AutoObjectMapper_h
-#define AutoObjectMapper_h
-
-#include <string>
-
-#include "mozilla/Attributes.h"
-#include "PlatformMacros.h"
-
-// A (nearly-) RAII class that maps an object in and then unmaps it on
-// destruction. This base class version uses the "normal" POSIX
-// functions: open, fstat, close, mmap, munmap.
-
-class MOZ_STACK_CLASS AutoObjectMapperPOSIX {
-public:
- // The constructor does not attempt to map the file, because that
- // might fail. Instead, once the object has been constructed,
- // call Map() to attempt the mapping. There is no corresponding
- // Unmap() since the unmapping is done in the destructor. Failure
- // messages are sent to |aLog|.
- explicit AutoObjectMapperPOSIX(void(*aLog)(const char*));
-
- // Unmap the file on destruction of this object.
- ~AutoObjectMapperPOSIX();
-
- // Map |fileName| into the address space and return the mapping
- // extents. If the file is zero sized this will fail. The file is
- // mapped read-only and private. Returns true iff the mapping
- // succeeded, in which case *start and *length hold its extent.
- // Once a call to Map succeeds, all subsequent calls to it will
- // fail.
- bool Map(/*OUT*/void** start, /*OUT*/size_t* length, std::string fileName);
-
-protected:
- // If we are currently holding a mapped object, these record the
- // mapped address range.
- void* mImage;
- size_t mSize;
-
- // A logging sink, for complaining about mapping failures.
- void (*mLog)(const char*);
-
-private:
- // Are we currently holding a mapped object? This is private to
- // the base class. Derived classes need to have their own way to
- // track whether they are holding a mapped object.
- bool mIsMapped;
-
- // Disable copying and assignment.
- AutoObjectMapperPOSIX(const AutoObjectMapperPOSIX&);
- AutoObjectMapperPOSIX& operator=(const AutoObjectMapperPOSIX&);
- // Disable heap allocation of this class.
- void* operator new(size_t);
- void* operator new[](size_t);
- void operator delete(void*);
- void operator delete[](void*);
-};
-
-
-#if defined(SPS_OS_android)
-// This is a variant of AutoObjectMapperPOSIX suitable for use in
-// conjunction with faulty.lib on Android. How it behaves depends on
-// the name of the file to be mapped. There are three possible cases:
-//
-// (1) /foo/bar/xyzzy/blah.apk!/libwurble.so
-// We hand it as-is to faulty.lib and let it fish the relevant
-// bits out of the APK.
-//
-// (2) libmozglue.so
-// This is part of the Fennec installation, but is not in the
-// APK. Instead we have to figure out the installation path
-// and look for it there. Because of faulty.lib limitations,
-// we have to use regular open/mmap instead of faulty.lib.
-//
-// (3) libanythingelse.so
-// faulty.lib assumes this is a system library, and prepends
-// "/system/lib/" to the path. So as in (1), we can give it
-// as-is to faulty.lib.
-//
-// Hence (1) and (3) require special-casing here. Case (2) simply
-// hands the problem to the parent class.
-
-class MOZ_STACK_CLASS AutoObjectMapperFaultyLib : public AutoObjectMapperPOSIX {
-public:
- AutoObjectMapperFaultyLib(void(*aLog)(const char*));
-
- ~AutoObjectMapperFaultyLib();
-
- bool Map(/*OUT*/void** start, /*OUT*/size_t* length, std::string fileName);
-
-private:
- // faulty.lib requires us to maintain an abstract handle that can be
- // used later to unmap the area. If this is non-NULL, it is assumed
- // that unmapping is to be done by faulty.lib. Otherwise it goes
- // via the normal mechanism.
- void* mHdl;
-
- // Disable copying and assignment.
- AutoObjectMapperFaultyLib(const AutoObjectMapperFaultyLib&);
- AutoObjectMapperFaultyLib& operator=(const AutoObjectMapperFaultyLib&);
- // Disable heap allocation of this class.
- void* operator new(size_t);
- void* operator new[](size_t);
- void operator delete(void*);
- void operator delete[](void*);
-};
-
-#endif // defined(SPS_OS_android)
-
-#endif // AutoObjectMapper_h
diff --git a/tools/profiler/lul/LulCommon.cpp b/tools/profiler/lul/LulCommon.cpp
deleted file mode 100644
index 7321251c8..000000000
--- a/tools/profiler/lul/LulCommon.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-
-// Copyright (c) 2011, 2013 Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
-
-
-// This file is derived from the following files in
-// toolkit/crashreporter/google-breakpad:
-// src/common/module.cc
-// src/common/unique_string.cc
-
-// There's no internal-only interface for LulCommon. Hence include
-// the external interface directly.
-#include "LulCommonExt.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-#include <map>
-
-
-namespace lul {
-
-using std::string;
-
-////////////////////////////////////////////////////////////////
-// Module
-//
-Module::Module(const string &name, const string &os,
- const string &architecture, const string &id) :
- name_(name),
- os_(os),
- architecture_(architecture),
- id_(id) { }
-
-Module::~Module() {
-}
-
-
-////////////////////////////////////////////////////////////////
-// UniqueString
-//
-class UniqueString {
- public:
- explicit UniqueString(string str) { str_ = strdup(str.c_str()); }
- ~UniqueString() { free(reinterpret_cast<void*>(const_cast<char*>(str_))); }
- const char* str_;
-};
-
-const char* FromUniqueString(const UniqueString* ustr)
-{
- return ustr->str_;
-}
-
-bool IsEmptyUniqueString(const UniqueString* ustr)
-{
- return (ustr->str_)[0] == '\0';
-}
-
-
-////////////////////////////////////////////////////////////////
-// UniqueStringUniverse
-//
-UniqueStringUniverse::~UniqueStringUniverse()
-{
- for (std::map<string, UniqueString*>::iterator it = map_.begin();
- it != map_.end(); it++) {
- delete it->second;
- }
-}
-
-const UniqueString* UniqueStringUniverse::ToUniqueString(string str)
-{
- std::map<string, UniqueString*>::iterator it = map_.find(str);
- if (it == map_.end()) {
- UniqueString* ustr = new UniqueString(str);
- map_[str] = ustr;
- return ustr;
- } else {
- return it->second;
- }
-}
-
-} // namespace lul
diff --git a/tools/profiler/lul/LulDwarf.cpp b/tools/profiler/lul/LulDwarf.cpp
deleted file mode 100644
index 1bdbdabb6..000000000
--- a/tools/profiler/lul/LulDwarf.cpp
+++ /dev/null
@@ -1,2180 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-
-// Copyright (c) 2010 Google Inc. All Rights Reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
-// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
-
-// Implementation of dwarf2reader::LineInfo, dwarf2reader::CompilationUnit,
-// and dwarf2reader::CallFrameInfo. See dwarf2reader.h for details.
-
-// This file is derived from the following files in
-// toolkit/crashreporter/google-breakpad:
-// src/common/dwarf/bytereader.cc
-// src/common/dwarf/dwarf2reader.cc
-// src/common/dwarf_cfi_to_module.cc
-
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <map>
-#include <stack>
-#include <string>
-
-#include "mozilla/Assertions.h"
-#include "mozilla/Sprintf.h"
-
-#include "LulCommonExt.h"
-#include "LulDwarfInt.h"
-
-
-// Set this to 1 for verbose logging
-#define DEBUG_DWARF 0
-
-
-namespace lul {
-
-using std::string;
-
-ByteReader::ByteReader(enum Endianness endian)
- :offset_reader_(NULL), address_reader_(NULL), endian_(endian),
- address_size_(0), offset_size_(0),
- have_section_base_(), have_text_base_(), have_data_base_(),
- have_function_base_() { }
-
-ByteReader::~ByteReader() { }
-
-void ByteReader::SetOffsetSize(uint8 size) {
- offset_size_ = size;
- MOZ_ASSERT(size == 4 || size == 8);
- if (size == 4) {
- this->offset_reader_ = &ByteReader::ReadFourBytes;
- } else {
- this->offset_reader_ = &ByteReader::ReadEightBytes;
- }
-}
-
-void ByteReader::SetAddressSize(uint8 size) {
- address_size_ = size;
- MOZ_ASSERT(size == 4 || size == 8);
- if (size == 4) {
- this->address_reader_ = &ByteReader::ReadFourBytes;
- } else {
- this->address_reader_ = &ByteReader::ReadEightBytes;
- }
-}
-
-uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) {
- const uint64 initial_length = ReadFourBytes(start);
- start += 4;
-
- // In DWARF2/3, if the initial length is all 1 bits, then the offset
- // size is 8 and we need to read the next 8 bytes for the real length.
- if (initial_length == 0xffffffff) {
- SetOffsetSize(8);
- *len = 12;
- return ReadOffset(start);
- } else {
- SetOffsetSize(4);
- *len = 4;
- }
- return initial_length;
-}
-
-bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const {
- if (encoding == DW_EH_PE_omit) return true;
- if (encoding == DW_EH_PE_aligned) return true;
- if ((encoding & 0x7) > DW_EH_PE_udata8)
- return false;
- if ((encoding & 0x70) > DW_EH_PE_funcrel)
- return false;
- return true;
-}
-
-bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const {
- switch (encoding & 0x70) {
- case DW_EH_PE_absptr: return true;
- case DW_EH_PE_pcrel: return have_section_base_;
- case DW_EH_PE_textrel: return have_text_base_;
- case DW_EH_PE_datarel: return have_data_base_;
- case DW_EH_PE_funcrel: return have_function_base_;
- default: return false;
- }
-}
-
-uint64 ByteReader::ReadEncodedPointer(const char *buffer,
- DwarfPointerEncoding encoding,
- size_t *len) const {
- // UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't
- // see it here.
- MOZ_ASSERT(encoding != DW_EH_PE_omit);
-
- // The Linux Standards Base 4.0 does not make this clear, but the
- // GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c)
- // agree that aligned pointers are always absolute, machine-sized,
- // machine-signed pointers.
- if (encoding == DW_EH_PE_aligned) {
- MOZ_ASSERT(have_section_base_);
-
- // We don't need to align BUFFER in *our* address space. Rather, we
- // need to find the next position in our buffer that would be aligned
- // when the .eh_frame section the buffer contains is loaded into the
- // program's memory. So align assuming that buffer_base_ gets loaded at
- // address section_base_, where section_base_ itself may or may not be
- // aligned.
-
- // First, find the offset to START from the closest prior aligned
- // address.
- uint64 skew = section_base_ & (AddressSize() - 1);
- // Now find the offset from that aligned address to buffer.
- uint64 offset = skew + (buffer - buffer_base_);
- // Round up to the next boundary.
- uint64 aligned = (offset + AddressSize() - 1) & -AddressSize();
- // Convert back to a pointer.
- const char *aligned_buffer = buffer_base_ + (aligned - skew);
- // Finally, store the length and actually fetch the pointer.
- *len = aligned_buffer - buffer + AddressSize();
- return ReadAddress(aligned_buffer);
- }
-
- // Extract the value first, ignoring whether it's a pointer or an
- // offset relative to some base.
- uint64 offset;
- switch (encoding & 0x0f) {
- case DW_EH_PE_absptr:
- // DW_EH_PE_absptr is weird, as it is used as a meaningful value for
- // both the high and low nybble of encoding bytes. When it appears in
- // the high nybble, it means that the pointer is absolute, not an
- // offset from some base address. When it appears in the low nybble,
- // as here, it means that the pointer is stored as a normal
- // machine-sized and machine-signed address. A low nybble of
- // DW_EH_PE_absptr does not imply that the pointer is absolute; it is
- // correct for us to treat the value as an offset from a base address
- // if the upper nybble is not DW_EH_PE_absptr.
- offset = ReadAddress(buffer);
- *len = AddressSize();
- break;
-
- case DW_EH_PE_uleb128:
- offset = ReadUnsignedLEB128(buffer, len);
- break;
-
- case DW_EH_PE_udata2:
- offset = ReadTwoBytes(buffer);
- *len = 2;
- break;
-
- case DW_EH_PE_udata4:
- offset = ReadFourBytes(buffer);
- *len = 4;
- break;
-
- case DW_EH_PE_udata8:
- offset = ReadEightBytes(buffer);
- *len = 8;
- break;
-
- case DW_EH_PE_sleb128:
- offset = ReadSignedLEB128(buffer, len);
- break;
-
- case DW_EH_PE_sdata2:
- offset = ReadTwoBytes(buffer);
- // Sign-extend from 16 bits.
- offset = (offset ^ 0x8000) - 0x8000;
- *len = 2;
- break;
-
- case DW_EH_PE_sdata4:
- offset = ReadFourBytes(buffer);
- // Sign-extend from 32 bits.
- offset = (offset ^ 0x80000000ULL) - 0x80000000ULL;
- *len = 4;
- break;
-
- case DW_EH_PE_sdata8:
- // No need to sign-extend; this is the full width of our type.
- offset = ReadEightBytes(buffer);
- *len = 8;
- break;
-
- default:
- abort();
- }
-
- // Find the appropriate base address.
- uint64 base;
- switch (encoding & 0x70) {
- case DW_EH_PE_absptr:
- base = 0;
- break;
-
- case DW_EH_PE_pcrel:
- MOZ_ASSERT(have_section_base_);
- base = section_base_ + (buffer - buffer_base_);
- break;
-
- case DW_EH_PE_textrel:
- MOZ_ASSERT(have_text_base_);
- base = text_base_;
- break;
-
- case DW_EH_PE_datarel:
- MOZ_ASSERT(have_data_base_);
- base = data_base_;
- break;
-
- case DW_EH_PE_funcrel:
- MOZ_ASSERT(have_function_base_);
- base = function_base_;
- break;
-
- default:
- abort();
- }
-
- uint64 pointer = base + offset;
-
- // Remove inappropriate upper bits.
- if (AddressSize() == 4)
- pointer = pointer & 0xffffffff;
- else
- MOZ_ASSERT(AddressSize() == sizeof(uint64));
-
- return pointer;
-}
-
-
-// A DWARF rule for recovering the address or value of a register, or
-// computing the canonical frame address. There is one subclass of this for
-// each '*Rule' member function in CallFrameInfo::Handler.
-//
-// It's annoying that we have to handle Rules using pointers (because
-// the concrete instances can have an arbitrary size). They're small,
-// so it would be much nicer if we could just handle them by value
-// instead of fretting about ownership and destruction.
-//
-// It seems like all these could simply be instances of std::tr1::bind,
-// except that we need instances to be EqualityComparable, too.
-//
-// This could logically be nested within State, but then the qualified names
-// get horrendous.
-class CallFrameInfo::Rule {
- public:
- virtual ~Rule() { }
-
- // Tell HANDLER that, at ADDRESS in the program, REGISTER can be
- // recovered using this rule. If REGISTER is kCFARegister, then this rule
- // describes how to compute the canonical frame address. Return what the
- // HANDLER member function returned.
- virtual bool Handle(Handler *handler, uint64 address, int register) const = 0;
-
- // Equality on rules. We use these to decide which rules we need
- // to report after a DW_CFA_restore_state instruction.
- virtual bool operator==(const Rule &rhs) const = 0;
-
- bool operator!=(const Rule &rhs) const { return ! (*this == rhs); }
-
- // Return a pointer to a copy of this rule.
- virtual Rule *Copy() const = 0;
-
- // If this is a base+offset rule, change its base register to REG.
- // Otherwise, do nothing. (Ugly, but required for DW_CFA_def_cfa_register.)
- virtual void SetBaseRegister(unsigned reg) { }
-
- // If this is a base+offset rule, change its offset to OFFSET. Otherwise,
- // do nothing. (Ugly, but required for DW_CFA_def_cfa_offset.)
- virtual void SetOffset(long long offset) { }
-
- // A RTTI workaround, to make it possible to implement equality
- // comparisons on classes derived from this one.
- enum CFIRTag {
- CFIR_UNDEFINED_RULE,
- CFIR_SAME_VALUE_RULE,
- CFIR_OFFSET_RULE,
- CFIR_VAL_OFFSET_RULE,
- CFIR_REGISTER_RULE,
- CFIR_EXPRESSION_RULE,
- CFIR_VAL_EXPRESSION_RULE
- };
-
- // Produce the tag that identifies the child class of this object.
- virtual CFIRTag getTag() const = 0;
-};
-
-// Rule: the value the register had in the caller cannot be recovered.
-class CallFrameInfo::UndefinedRule: public CallFrameInfo::Rule {
- public:
- UndefinedRule() { }
- ~UndefinedRule() { }
- CFIRTag getTag() const { return CFIR_UNDEFINED_RULE; }
- bool Handle(Handler *handler, uint64 address, int reg) const {
- return handler->UndefinedRule(address, reg);
- }
- bool operator==(const Rule &rhs) const {
- if (rhs.getTag() != CFIR_UNDEFINED_RULE) return false;
- return true;
- }
- Rule *Copy() const { return new UndefinedRule(*this); }
-};
-
-// Rule: the register's value is the same as that it had in the caller.
-class CallFrameInfo::SameValueRule: public CallFrameInfo::Rule {
- public:
- SameValueRule() { }
- ~SameValueRule() { }
- CFIRTag getTag() const { return CFIR_SAME_VALUE_RULE; }
- bool Handle(Handler *handler, uint64 address, int reg) const {
- return handler->SameValueRule(address, reg);
- }
- bool operator==(const Rule &rhs) const {
- if (rhs.getTag() != CFIR_SAME_VALUE_RULE) return false;
- return true;
- }
- Rule *Copy() const { return new SameValueRule(*this); }
-};
-
-// Rule: the register is saved at OFFSET from BASE_REGISTER. BASE_REGISTER
-// may be CallFrameInfo::Handler::kCFARegister.
-class CallFrameInfo::OffsetRule: public CallFrameInfo::Rule {
- public:
- OffsetRule(int base_register, long offset)
- : base_register_(base_register), offset_(offset) { }
- ~OffsetRule() { }
- CFIRTag getTag() const { return CFIR_OFFSET_RULE; }
- bool Handle(Handler *handler, uint64 address, int reg) const {
- return handler->OffsetRule(address, reg, base_register_, offset_);
- }
- bool operator==(const Rule &rhs) const {
- if (rhs.getTag() != CFIR_OFFSET_RULE) return false;
- const OffsetRule *our_rhs = static_cast<const OffsetRule *>(&rhs);
- return (base_register_ == our_rhs->base_register_ &&
- offset_ == our_rhs->offset_);
- }
- Rule *Copy() const { return new OffsetRule(*this); }
- // We don't actually need SetBaseRegister or SetOffset here, since they
- // are only ever applied to CFA rules, for DW_CFA_def_cfa_offset, and it
- // doesn't make sense to use OffsetRule for computing the CFA: it
- // computes the address at which a register is saved, not a value.
- private:
- int base_register_;
- long offset_;
-};
-
-// Rule: the value the register had in the caller is the value of
-// BASE_REGISTER plus offset. BASE_REGISTER may be
-// CallFrameInfo::Handler::kCFARegister.
-class CallFrameInfo::ValOffsetRule: public CallFrameInfo::Rule {
- public:
- ValOffsetRule(int base_register, long offset)
- : base_register_(base_register), offset_(offset) { }
- ~ValOffsetRule() { }
- CFIRTag getTag() const { return CFIR_VAL_OFFSET_RULE; }
- bool Handle(Handler *handler, uint64 address, int reg) const {
- return handler->ValOffsetRule(address, reg, base_register_, offset_);
- }
- bool operator==(const Rule &rhs) const {
- if (rhs.getTag() != CFIR_VAL_OFFSET_RULE) return false;
- const ValOffsetRule *our_rhs = static_cast<const ValOffsetRule *>(&rhs);
- return (base_register_ == our_rhs->base_register_ &&
- offset_ == our_rhs->offset_);
- }
- Rule *Copy() const { return new ValOffsetRule(*this); }
- void SetBaseRegister(unsigned reg) { base_register_ = reg; }
- void SetOffset(long long offset) { offset_ = offset; }
- private:
- int base_register_;
- long offset_;
-};
-
-// Rule: the register has been saved in another register REGISTER_NUMBER_.
-class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule {
- public:
- explicit RegisterRule(int register_number)
- : register_number_(register_number) { }
- ~RegisterRule() { }
- CFIRTag getTag() const { return CFIR_REGISTER_RULE; }
- bool Handle(Handler *handler, uint64 address, int reg) const {
- return handler->RegisterRule(address, reg, register_number_);
- }
- bool operator==(const Rule &rhs) const {
- if (rhs.getTag() != CFIR_REGISTER_RULE) return false;
- const RegisterRule *our_rhs = static_cast<const RegisterRule *>(&rhs);
- return (register_number_ == our_rhs->register_number_);
- }
- Rule *Copy() const { return new RegisterRule(*this); }
- private:
- int register_number_;
-};
-
-// Rule: EXPRESSION evaluates to the address at which the register is saved.
-class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule {
- public:
- explicit ExpressionRule(const string &expression)
- : expression_(expression) { }
- ~ExpressionRule() { }
- CFIRTag getTag() const { return CFIR_EXPRESSION_RULE; }
- bool Handle(Handler *handler, uint64 address, int reg) const {
- return handler->ExpressionRule(address, reg, expression_);
- }
- bool operator==(const Rule &rhs) const {
- if (rhs.getTag() != CFIR_EXPRESSION_RULE) return false;
- const ExpressionRule *our_rhs = static_cast<const ExpressionRule *>(&rhs);
- return (expression_ == our_rhs->expression_);
- }
- Rule *Copy() const { return new ExpressionRule(*this); }
- private:
- string expression_;
-};
-
-// Rule: EXPRESSION evaluates to the previous value of the register.
-class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule {
- public:
- explicit ValExpressionRule(const string &expression)
- : expression_(expression) { }
- ~ValExpressionRule() { }
- CFIRTag getTag() const { return CFIR_VAL_EXPRESSION_RULE; }
- bool Handle(Handler *handler, uint64 address, int reg) const {
- return handler->ValExpressionRule(address, reg, expression_);
- }
- bool operator==(const Rule &rhs) const {
- if (rhs.getTag() != CFIR_VAL_EXPRESSION_RULE) return false;
- const ValExpressionRule *our_rhs =
- static_cast<const ValExpressionRule *>(&rhs);
- return (expression_ == our_rhs->expression_);
- }
- Rule *Copy() const { return new ValExpressionRule(*this); }
- private:
- string expression_;
-};
-
-// A map from register numbers to rules.
-class CallFrameInfo::RuleMap {
- public:
- RuleMap() : cfa_rule_(NULL) { }
- RuleMap(const RuleMap &rhs) : cfa_rule_(NULL) { *this = rhs; }
- ~RuleMap() { Clear(); }
-
- RuleMap &operator=(const RuleMap &rhs);
-
- // Set the rule for computing the CFA to RULE. Take ownership of RULE.
- void SetCFARule(Rule *rule) { delete cfa_rule_; cfa_rule_ = rule; }
-
- // Return the current CFA rule. Unlike RegisterRule, this RuleMap retains
- // ownership of the rule. We use this for DW_CFA_def_cfa_offset and
- // DW_CFA_def_cfa_register, and for detecting references to the CFA before
- // a rule for it has been established.
- Rule *CFARule() const { return cfa_rule_; }
-
- // Return the rule for REG, or NULL if there is none. The caller takes
- // ownership of the result.
- Rule *RegisterRule(int reg) const;
-
- // Set the rule for computing REG to RULE. Take ownership of RULE.
- void SetRegisterRule(int reg, Rule *rule);
-
- // Make all the appropriate calls to HANDLER as if we were changing from
- // this RuleMap to NEW_RULES at ADDRESS. We use this to implement
- // DW_CFA_restore_state, where lots of rules can change simultaneously.
- // Return true if all handlers returned true; otherwise, return false.
- bool HandleTransitionTo(Handler *handler, uint64 address,
- const RuleMap &new_rules) const;
-
- private:
- // A map from register numbers to Rules.
- typedef std::map<int, Rule *> RuleByNumber;
-
- // Remove all register rules and clear cfa_rule_.
- void Clear();
-
- // The rule for computing the canonical frame address. This RuleMap owns
- // this rule.
- Rule *cfa_rule_;
-
- // A map from register numbers to postfix expressions to recover
- // their values. This RuleMap owns the Rules the map refers to.
- RuleByNumber registers_;
-};
-
-CallFrameInfo::RuleMap &CallFrameInfo::RuleMap::operator=(const RuleMap &rhs) {
- Clear();
- // Since each map owns the rules it refers to, assignment must copy them.
- if (rhs.cfa_rule_) cfa_rule_ = rhs.cfa_rule_->Copy();
- for (RuleByNumber::const_iterator it = rhs.registers_.begin();
- it != rhs.registers_.end(); it++)
- registers_[it->first] = it->second->Copy();
- return *this;
-}
-
-CallFrameInfo::Rule *CallFrameInfo::RuleMap::RegisterRule(int reg) const {
- MOZ_ASSERT(reg != Handler::kCFARegister);
- RuleByNumber::const_iterator it = registers_.find(reg);
- if (it != registers_.end())
- return it->second->Copy();
- else
- return NULL;
-}
-
-void CallFrameInfo::RuleMap::SetRegisterRule(int reg, Rule *rule) {
- MOZ_ASSERT(reg != Handler::kCFARegister);
- MOZ_ASSERT(rule);
- Rule **slot = &registers_[reg];
- delete *slot;
- *slot = rule;
-}
-
-bool CallFrameInfo::RuleMap::HandleTransitionTo(
- Handler *handler,
- uint64 address,
- const RuleMap &new_rules) const {
- // Transition from cfa_rule_ to new_rules.cfa_rule_.
- if (cfa_rule_ && new_rules.cfa_rule_) {
- if (*cfa_rule_ != *new_rules.cfa_rule_ &&
- !new_rules.cfa_rule_->Handle(handler, address, Handler::kCFARegister))
- return false;
- } else if (cfa_rule_) {
- // this RuleMap has a CFA rule but new_rules doesn't.
- // CallFrameInfo::Handler has no way to handle this --- and shouldn't;
- // it's garbage input. The instruction interpreter should have
- // detected this and warned, so take no action here.
- } else if (new_rules.cfa_rule_) {
- // This shouldn't be possible: NEW_RULES is some prior state, and
- // there's no way to remove entries.
- MOZ_ASSERT(0);
- } else {
- // Both CFA rules are empty. No action needed.
- }
-
- // Traverse the two maps in order by register number, and report
- // whatever differences we find.
- RuleByNumber::const_iterator old_it = registers_.begin();
- RuleByNumber::const_iterator new_it = new_rules.registers_.begin();
- while (old_it != registers_.end() && new_it != new_rules.registers_.end()) {
- if (old_it->first < new_it->first) {
- // This RuleMap has an entry for old_it->first, but NEW_RULES
- // doesn't.
- //
- // This isn't really the right thing to do, but since CFI generally
- // only mentions callee-saves registers, and GCC's convention for
- // callee-saves registers is that they are unchanged, it's a good
- // approximation.
- if (!handler->SameValueRule(address, old_it->first))
- return false;
- old_it++;
- } else if (old_it->first > new_it->first) {
- // NEW_RULES has entry for new_it->first, but this RuleMap
- // doesn't. This shouldn't be possible: NEW_RULES is some prior
- // state, and there's no way to remove entries.
- MOZ_ASSERT(0);
- } else {
- // Both maps have an entry for this register. Report the new
- // rule if it is different.
- if (*old_it->second != *new_it->second &&
- !new_it->second->Handle(handler, address, new_it->first))
- return false;
- new_it++, old_it++;
- }
- }
- // Finish off entries from this RuleMap with no counterparts in new_rules.
- while (old_it != registers_.end()) {
- if (!handler->SameValueRule(address, old_it->first))
- return false;
- old_it++;
- }
- // Since we only make transitions from a rule set to some previously
- // saved rule set, and we can only add rules to the map, NEW_RULES
- // must have fewer rules than *this.
- MOZ_ASSERT(new_it == new_rules.registers_.end());
-
- return true;
-}
-
-// Remove all register rules and clear cfa_rule_.
-void CallFrameInfo::RuleMap::Clear() {
- delete cfa_rule_;
- cfa_rule_ = NULL;
- for (RuleByNumber::iterator it = registers_.begin();
- it != registers_.end(); it++)
- delete it->second;
- registers_.clear();
-}
-
-// The state of the call frame information interpreter as it processes
-// instructions from a CIE and FDE.
-class CallFrameInfo::State {
- public:
- // Create a call frame information interpreter state with the given
- // reporter, reader, handler, and initial call frame info address.
- State(ByteReader *reader, Handler *handler, Reporter *reporter,
- uint64 address)
- : reader_(reader), handler_(handler), reporter_(reporter),
- address_(address), entry_(NULL), cursor_(NULL),
- saved_rules_(NULL) { }
-
- ~State() {
- if (saved_rules_)
- delete saved_rules_;
- }
-
- // Interpret instructions from CIE, save the resulting rule set for
- // DW_CFA_restore instructions, and return true. On error, report
- // the problem to reporter_ and return false.
- bool InterpretCIE(const CIE &cie);
-
- // Interpret instructions from FDE, and return true. On error,
- // report the problem to reporter_ and return false.
- bool InterpretFDE(const FDE &fde);
-
- private:
- // The operands of a CFI instruction, for ParseOperands.
- struct Operands {
- unsigned register_number; // A register number.
- uint64 offset; // An offset or address.
- long signed_offset; // A signed offset.
- string expression; // A DWARF expression.
- };
-
- // Parse CFI instruction operands from STATE's instruction stream as
- // described by FORMAT. On success, populate OPERANDS with the
- // results, and return true. On failure, report the problem and
- // return false.
- //
- // Each character of FORMAT should be one of the following:
- //
- // 'r' unsigned LEB128 register number (OPERANDS->register_number)
- // 'o' unsigned LEB128 offset (OPERANDS->offset)
- // 's' signed LEB128 offset (OPERANDS->signed_offset)
- // 'a' machine-size address (OPERANDS->offset)
- // (If the CIE has a 'z' augmentation string, 'a' uses the
- // encoding specified by the 'R' argument.)
- // '1' a one-byte offset (OPERANDS->offset)
- // '2' a two-byte offset (OPERANDS->offset)
- // '4' a four-byte offset (OPERANDS->offset)
- // '8' an eight-byte offset (OPERANDS->offset)
- // 'e' a DW_FORM_block holding a (OPERANDS->expression)
- // DWARF expression
- bool ParseOperands(const char *format, Operands *operands);
-
- // Interpret one CFI instruction from STATE's instruction stream, update
- // STATE, report any rule changes to handler_, and return true. On
- // failure, report the problem and return false.
- bool DoInstruction();
-
- // The following Do* member functions are subroutines of DoInstruction,
- // factoring out the actual work of operations that have several
- // different encodings.
-
- // Set the CFA rule to be the value of BASE_REGISTER plus OFFSET, and
- // return true. On failure, report and return false. (Used for
- // DW_CFA_def_cfa and DW_CFA_def_cfa_sf.)
- bool DoDefCFA(unsigned base_register, long offset);
-
- // Change the offset of the CFA rule to OFFSET, and return true. On
- // failure, report and return false. (Subroutine for
- // DW_CFA_def_cfa_offset and DW_CFA_def_cfa_offset_sf.)
- bool DoDefCFAOffset(long offset);
-
- // Specify that REG can be recovered using RULE, and return true. On
- // failure, report and return false.
- bool DoRule(unsigned reg, Rule *rule);
-
- // Specify that REG can be found at OFFSET from the CFA, and return true.
- // On failure, report and return false. (Subroutine for DW_CFA_offset,
- // DW_CFA_offset_extended, and DW_CFA_offset_extended_sf.)
- bool DoOffset(unsigned reg, long offset);
-
- // Specify that the caller's value for REG is the CFA plus OFFSET,
- // and return true. On failure, report and return false. (Subroutine
- // for DW_CFA_val_offset and DW_CFA_val_offset_sf.)
- bool DoValOffset(unsigned reg, long offset);
-
- // Restore REG to the rule established in the CIE, and return true. On
- // failure, report and return false. (Subroutine for DW_CFA_restore and
- // DW_CFA_restore_extended.)
- bool DoRestore(unsigned reg);
-
- // Return the section offset of the instruction at cursor. For use
- // in error messages.
- uint64 CursorOffset() { return entry_->offset + (cursor_ - entry_->start); }
-
- // Report that entry_ is incomplete, and return false. For brevity.
- bool ReportIncomplete() {
- reporter_->Incomplete(entry_->offset, entry_->kind);
- return false;
- }
-
- // For reading multi-byte values with the appropriate endianness.
- ByteReader *reader_;
-
- // The handler to which we should report the data we find.
- Handler *handler_;
-
- // For reporting problems in the info we're parsing.
- Reporter *reporter_;
-
- // The code address to which the next instruction in the stream applies.
- uint64 address_;
-
- // The entry whose instructions we are currently processing. This is
- // first a CIE, and then an FDE.
- const Entry *entry_;
-
- // The next instruction to process.
- const char *cursor_;
-
- // The current set of rules.
- RuleMap rules_;
-
- // The set of rules established by the CIE, used by DW_CFA_restore
- // and DW_CFA_restore_extended. We set this after interpreting the
- // CIE's instructions.
- RuleMap cie_rules_;
-
- // A stack of saved states, for DW_CFA_remember_state and
- // DW_CFA_restore_state.
- std::stack<RuleMap>* saved_rules_;
-};
-
-bool CallFrameInfo::State::InterpretCIE(const CIE &cie) {
- entry_ = &cie;
- cursor_ = entry_->instructions;
- while (cursor_ < entry_->end)
- if (!DoInstruction())
- return false;
- // Note the rules established by the CIE, for use by DW_CFA_restore
- // and DW_CFA_restore_extended.
- cie_rules_ = rules_;
- return true;
-}
-
-bool CallFrameInfo::State::InterpretFDE(const FDE &fde) {
- entry_ = &fde;
- cursor_ = entry_->instructions;
- while (cursor_ < entry_->end)
- if (!DoInstruction())
- return false;
- return true;
-}
-
-bool CallFrameInfo::State::ParseOperands(const char *format,
- Operands *operands) {
- size_t len;
- const char *operand;
-
- for (operand = format; *operand; operand++) {
- size_t bytes_left = entry_->end - cursor_;
- switch (*operand) {
- case 'r':
- operands->register_number = reader_->ReadUnsignedLEB128(cursor_, &len);
- if (len > bytes_left) return ReportIncomplete();
- cursor_ += len;
- break;
-
- case 'o':
- operands->offset = reader_->ReadUnsignedLEB128(cursor_, &len);
- if (len > bytes_left) return ReportIncomplete();
- cursor_ += len;
- break;
-
- case 's':
- operands->signed_offset = reader_->ReadSignedLEB128(cursor_, &len);
- if (len > bytes_left) return ReportIncomplete();
- cursor_ += len;
- break;
-
- case 'a':
- operands->offset =
- reader_->ReadEncodedPointer(cursor_, entry_->cie->pointer_encoding,
- &len);
- if (len > bytes_left) return ReportIncomplete();
- cursor_ += len;
- break;
-
- case '1':
- if (1 > bytes_left) return ReportIncomplete();
- operands->offset = static_cast<unsigned char>(*cursor_++);
- break;
-
- case '2':
- if (2 > bytes_left) return ReportIncomplete();
- operands->offset = reader_->ReadTwoBytes(cursor_);
- cursor_ += 2;
- break;
-
- case '4':
- if (4 > bytes_left) return ReportIncomplete();
- operands->offset = reader_->ReadFourBytes(cursor_);
- cursor_ += 4;
- break;
-
- case '8':
- if (8 > bytes_left) return ReportIncomplete();
- operands->offset = reader_->ReadEightBytes(cursor_);
- cursor_ += 8;
- break;
-
- case 'e': {
- size_t expression_length = reader_->ReadUnsignedLEB128(cursor_, &len);
- if (len > bytes_left || expression_length > bytes_left - len)
- return ReportIncomplete();
- cursor_ += len;
- operands->expression = string(cursor_, expression_length);
- cursor_ += expression_length;
- break;
- }
-
- default:
- MOZ_ASSERT(0);
- }
- }
-
- return true;
-}
-
-bool CallFrameInfo::State::DoInstruction() {
- CIE *cie = entry_->cie;
- Operands ops;
-
- // Our entry's kind should have been set by now.
- MOZ_ASSERT(entry_->kind != kUnknown);
-
- // We shouldn't have been invoked unless there were more
- // instructions to parse.
- MOZ_ASSERT(cursor_ < entry_->end);
-
- unsigned opcode = *cursor_++;
- if ((opcode & 0xc0) != 0) {
- switch (opcode & 0xc0) {
- // Advance the address.
- case DW_CFA_advance_loc: {
- size_t code_offset = opcode & 0x3f;
- address_ += code_offset * cie->code_alignment_factor;
- break;
- }
-
- // Find a register at an offset from the CFA.
- case DW_CFA_offset:
- if (!ParseOperands("o", &ops) ||
- !DoOffset(opcode & 0x3f, ops.offset * cie->data_alignment_factor))
- return false;
- break;
-
- // Restore the rule established for a register by the CIE.
- case DW_CFA_restore:
- if (!DoRestore(opcode & 0x3f)) return false;
- break;
-
- // The 'if' above should have excluded this possibility.
- default:
- MOZ_ASSERT(0);
- }
-
- // Return here, so the big switch below won't be indented.
- return true;
- }
-
- switch (opcode) {
- // Set the address.
- case DW_CFA_set_loc:
- if (!ParseOperands("a", &ops)) return false;
- address_ = ops.offset;
- break;
-
- // Advance the address.
- case DW_CFA_advance_loc1:
- if (!ParseOperands("1", &ops)) return false;
- address_ += ops.offset * cie->code_alignment_factor;
- break;
-
- // Advance the address.
- case DW_CFA_advance_loc2:
- if (!ParseOperands("2", &ops)) return false;
- address_ += ops.offset * cie->code_alignment_factor;
- break;
-
- // Advance the address.
- case DW_CFA_advance_loc4:
- if (!ParseOperands("4", &ops)) return false;
- address_ += ops.offset * cie->code_alignment_factor;
- break;
-
- // Advance the address.
- case DW_CFA_MIPS_advance_loc8:
- if (!ParseOperands("8", &ops)) return false;
- address_ += ops.offset * cie->code_alignment_factor;
- break;
-
- // Compute the CFA by adding an offset to a register.
- case DW_CFA_def_cfa:
- if (!ParseOperands("ro", &ops) ||
- !DoDefCFA(ops.register_number, ops.offset))
- return false;
- break;
-
- // Compute the CFA by adding an offset to a register.
- case DW_CFA_def_cfa_sf:
- if (!ParseOperands("rs", &ops) ||
- !DoDefCFA(ops.register_number,
- ops.signed_offset * cie->data_alignment_factor))
- return false;
- break;
-
- // Change the base register used to compute the CFA.
- case DW_CFA_def_cfa_register: {
- Rule *cfa_rule = rules_.CFARule();
- if (!cfa_rule) {
- reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
- return false;
- }
- if (!ParseOperands("r", &ops)) return false;
- cfa_rule->SetBaseRegister(ops.register_number);
- if (!cfa_rule->Handle(handler_, address_, Handler::kCFARegister))
- return false;
- break;
- }
-
- // Change the offset used to compute the CFA.
- case DW_CFA_def_cfa_offset:
- if (!ParseOperands("o", &ops) ||
- !DoDefCFAOffset(ops.offset))
- return false;
- break;
-
- // Change the offset used to compute the CFA.
- case DW_CFA_def_cfa_offset_sf:
- if (!ParseOperands("s", &ops) ||
- !DoDefCFAOffset(ops.signed_offset * cie->data_alignment_factor))
- return false;
- break;
-
- // Specify an expression whose value is the CFA.
- case DW_CFA_def_cfa_expression: {
- if (!ParseOperands("e", &ops))
- return false;
- Rule *rule = new ValExpressionRule(ops.expression);
- rules_.SetCFARule(rule);
- if (!rule->Handle(handler_, address_, Handler::kCFARegister))
- return false;
- break;
- }
-
- // The register's value cannot be recovered.
- case DW_CFA_undefined: {
- if (!ParseOperands("r", &ops) ||
- !DoRule(ops.register_number, new UndefinedRule()))
- return false;
- break;
- }
-
- // The register's value is unchanged from its value in the caller.
- case DW_CFA_same_value: {
- if (!ParseOperands("r", &ops) ||
- !DoRule(ops.register_number, new SameValueRule()))
- return false;
- break;
- }
-
- // Find a register at an offset from the CFA.
- case DW_CFA_offset_extended:
- if (!ParseOperands("ro", &ops) ||
- !DoOffset(ops.register_number,
- ops.offset * cie->data_alignment_factor))
- return false;
- break;
-
- // The register is saved at an offset from the CFA.
- case DW_CFA_offset_extended_sf:
- if (!ParseOperands("rs", &ops) ||
- !DoOffset(ops.register_number,
- ops.signed_offset * cie->data_alignment_factor))
- return false;
- break;
-
- // The register is saved at an offset from the CFA.
- case DW_CFA_GNU_negative_offset_extended:
- if (!ParseOperands("ro", &ops) ||
- !DoOffset(ops.register_number,
- -ops.offset * cie->data_alignment_factor))
- return false;
- break;
-
- // The register's value is the sum of the CFA plus an offset.
- case DW_CFA_val_offset:
- if (!ParseOperands("ro", &ops) ||
- !DoValOffset(ops.register_number,
- ops.offset * cie->data_alignment_factor))
- return false;
- break;
-
- // The register's value is the sum of the CFA plus an offset.
- case DW_CFA_val_offset_sf:
- if (!ParseOperands("rs", &ops) ||
- !DoValOffset(ops.register_number,
- ops.signed_offset * cie->data_alignment_factor))
- return false;
- break;
-
- // The register has been saved in another register.
- case DW_CFA_register: {
- if (!ParseOperands("ro", &ops) ||
- !DoRule(ops.register_number, new RegisterRule(ops.offset)))
- return false;
- break;
- }
-
- // An expression yields the address at which the register is saved.
- case DW_CFA_expression: {
- if (!ParseOperands("re", &ops) ||
- !DoRule(ops.register_number, new ExpressionRule(ops.expression)))
- return false;
- break;
- }
-
- // An expression yields the caller's value for the register.
- case DW_CFA_val_expression: {
- if (!ParseOperands("re", &ops) ||
- !DoRule(ops.register_number, new ValExpressionRule(ops.expression)))
- return false;
- break;
- }
-
- // Restore the rule established for a register by the CIE.
- case DW_CFA_restore_extended:
- if (!ParseOperands("r", &ops) ||
- !DoRestore( ops.register_number))
- return false;
- break;
-
- // Save the current set of rules on a stack.
- case DW_CFA_remember_state:
- if (!saved_rules_) {
- saved_rules_ = new std::stack<RuleMap>();
- }
- saved_rules_->push(rules_);
- break;
-
- // Pop the current set of rules off the stack.
- case DW_CFA_restore_state: {
- if (!saved_rules_ || saved_rules_->empty()) {
- reporter_->EmptyStateStack(entry_->offset, entry_->kind,
- CursorOffset());
- return false;
- }
- const RuleMap &new_rules = saved_rules_->top();
- if (rules_.CFARule() && !new_rules.CFARule()) {
- reporter_->ClearingCFARule(entry_->offset, entry_->kind,
- CursorOffset());
- return false;
- }
- rules_.HandleTransitionTo(handler_, address_, new_rules);
- rules_ = new_rules;
- saved_rules_->pop();
- break;
- }
-
- // No operation. (Padding instruction.)
- case DW_CFA_nop:
- break;
-
- // A SPARC register window save: Registers 8 through 15 (%o0-%o7)
- // are saved in registers 24 through 31 (%i0-%i7), and registers
- // 16 through 31 (%l0-%l7 and %i0-%i7) are saved at CFA offsets
- // (0-15 * the register size). The register numbers must be
- // hard-coded. A GNU extension, and not a pretty one.
- case DW_CFA_GNU_window_save: {
- // Save %o0-%o7 in %i0-%i7.
- for (int i = 8; i < 16; i++)
- if (!DoRule(i, new RegisterRule(i + 16)))
- return false;
- // Save %l0-%l7 and %i0-%i7 at the CFA.
- for (int i = 16; i < 32; i++)
- // Assume that the byte reader's address size is the same as
- // the architecture's register size. !@#%*^ hilarious.
- if (!DoRule(i, new OffsetRule(Handler::kCFARegister,
- (i - 16) * reader_->AddressSize())))
- return false;
- break;
- }
-
- // I'm not sure what this is. GDB doesn't use it for unwinding.
- case DW_CFA_GNU_args_size:
- if (!ParseOperands("o", &ops)) return false;
- break;
-
- // An opcode we don't recognize.
- default: {
- reporter_->BadInstruction(entry_->offset, entry_->kind, CursorOffset());
- return false;
- }
- }
-
- return true;
-}
-
-bool CallFrameInfo::State::DoDefCFA(unsigned base_register, long offset) {
- Rule *rule = new ValOffsetRule(base_register, offset);
- rules_.SetCFARule(rule);
- return rule->Handle(handler_, address_, Handler::kCFARegister);
-}
-
-bool CallFrameInfo::State::DoDefCFAOffset(long offset) {
- Rule *cfa_rule = rules_.CFARule();
- if (!cfa_rule) {
- reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
- return false;
- }
- cfa_rule->SetOffset(offset);
- return cfa_rule->Handle(handler_, address_, Handler::kCFARegister);
-}
-
-bool CallFrameInfo::State::DoRule(unsigned reg, Rule *rule) {
- rules_.SetRegisterRule(reg, rule);
- return rule->Handle(handler_, address_, reg);
-}
-
-bool CallFrameInfo::State::DoOffset(unsigned reg, long offset) {
- if (!rules_.CFARule()) {
- reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
- return false;
- }
- return DoRule(reg,
- new OffsetRule(Handler::kCFARegister, offset));
-}
-
-bool CallFrameInfo::State::DoValOffset(unsigned reg, long offset) {
- if (!rules_.CFARule()) {
- reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
- return false;
- }
- return DoRule(reg,
- new ValOffsetRule(Handler::kCFARegister, offset));
-}
-
-bool CallFrameInfo::State::DoRestore(unsigned reg) {
- // DW_CFA_restore and DW_CFA_restore_extended don't make sense in a CIE.
- if (entry_->kind == kCIE) {
- reporter_->RestoreInCIE(entry_->offset, CursorOffset());
- return false;
- }
- Rule *rule = cie_rules_.RegisterRule(reg);
- if (!rule) {
- // This isn't really the right thing to do, but since CFI generally
- // only mentions callee-saves registers, and GCC's convention for
- // callee-saves registers is that they are unchanged, it's a good
- // approximation.
- rule = new SameValueRule();
- }
- return DoRule(reg, rule);
-}
-
-bool CallFrameInfo::ReadEntryPrologue(const char *cursor, Entry *entry) {
- const char *buffer_end = buffer_ + buffer_length_;
-
- // Initialize enough of ENTRY for use in error reporting.
- entry->offset = cursor - buffer_;
- entry->start = cursor;
- entry->kind = kUnknown;
- entry->end = NULL;
-
- // Read the initial length. This sets reader_'s offset size.
- size_t length_size;
- uint64 length = reader_->ReadInitialLength(cursor, &length_size);
- if (length_size > size_t(buffer_end - cursor))
- return ReportIncomplete(entry);
- cursor += length_size;
-
- // In a .eh_frame section, a length of zero marks the end of the series
- // of entries.
- if (length == 0 && eh_frame_) {
- entry->kind = kTerminator;
- entry->end = cursor;
- return true;
- }
-
- // Validate the length.
- if (length > size_t(buffer_end - cursor))
- return ReportIncomplete(entry);
-
- // The length is the number of bytes after the initial length field;
- // we have that position handy at this point, so compute the end
- // now. (If we're parsing 64-bit-offset DWARF on a 32-bit machine,
- // and the length didn't fit in a size_t, we would have rejected it
- // above.)
- entry->end = cursor + length;
-
- // Parse the next field: either the offset of a CIE or a CIE id.
- size_t offset_size = reader_->OffsetSize();
- if (offset_size > size_t(entry->end - cursor)) return ReportIncomplete(entry);
- entry->id = reader_->ReadOffset(cursor);
-
- // Don't advance cursor past id field yet; in .eh_frame data we need
- // the id's position to compute the section offset of an FDE's CIE.
-
- // Now we can decide what kind of entry this is.
- if (eh_frame_) {
- // In .eh_frame data, an ID of zero marks the entry as a CIE, and
- // anything else is an offset from the id field of the FDE to the start
- // of the CIE.
- if (entry->id == 0) {
- entry->kind = kCIE;
- } else {
- entry->kind = kFDE;
- // Turn the offset from the id into an offset from the buffer's start.
- entry->id = (cursor - buffer_) - entry->id;
- }
- } else {
- // In DWARF CFI data, an ID of ~0 (of the appropriate width, given the
- // offset size for the entry) marks the entry as a CIE, and anything
- // else is the offset of the CIE from the beginning of the section.
- if (offset_size == 4)
- entry->kind = (entry->id == 0xffffffff) ? kCIE : kFDE;
- else {
- MOZ_ASSERT(offset_size == 8);
- entry->kind = (entry->id == 0xffffffffffffffffULL) ? kCIE : kFDE;
- }
- }
-
- // Now advance cursor past the id.
- cursor += offset_size;
-
- // The fields specific to this kind of entry start here.
- entry->fields = cursor;
-
- entry->cie = NULL;
-
- return true;
-}
-
-bool CallFrameInfo::ReadCIEFields(CIE *cie) {
- const char *cursor = cie->fields;
- size_t len;
-
- MOZ_ASSERT(cie->kind == kCIE);
-
- // Prepare for early exit.
- cie->version = 0;
- cie->augmentation.clear();
- cie->code_alignment_factor = 0;
- cie->data_alignment_factor = 0;
- cie->return_address_register = 0;
- cie->has_z_augmentation = false;
- cie->pointer_encoding = DW_EH_PE_absptr;
- cie->instructions = 0;
-
- // Parse the version number.
- if (cie->end - cursor < 1)
- return ReportIncomplete(cie);
- cie->version = reader_->ReadOneByte(cursor);
- cursor++;
-
- // If we don't recognize the version, we can't parse any more fields of the
- // CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a
- // version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
- // the difference between those versions seems to be the same as for
- // .debug_frame.
- if (cie->version < 1 || cie->version > 3) {
- reporter_->UnrecognizedVersion(cie->offset, cie->version);
- return false;
- }
-
- const char *augmentation_start = cursor;
- const void *augmentation_end =
- memchr(augmentation_start, '\0', cie->end - augmentation_start);
- if (! augmentation_end) return ReportIncomplete(cie);
- cursor = static_cast<const char *>(augmentation_end);
- cie->augmentation = string(augmentation_start,
- cursor - augmentation_start);
- // Skip the terminating '\0'.
- cursor++;
-
- // Is this CFI augmented?
- if (!cie->augmentation.empty()) {
- // Is it an augmentation we recognize?
- if (cie->augmentation[0] == DW_Z_augmentation_start) {
- // Linux C++ ABI 'z' augmentation, used for exception handling data.
- cie->has_z_augmentation = true;
- } else {
- // Not an augmentation we recognize. Augmentations can have arbitrary
- // effects on the form of rest of the content, so we have to give up.
- reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation);
- return false;
- }
- }
-
- // Parse the code alignment factor.
- cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len);
- if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
- cursor += len;
-
- // Parse the data alignment factor.
- cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len);
- if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
- cursor += len;
-
- // Parse the return address register. This is a ubyte in version 1, and
- // a ULEB128 in version 3.
- if (cie->version == 1) {
- if (cursor >= cie->end) return ReportIncomplete(cie);
- cie->return_address_register = uint8(*cursor++);
- } else {
- cie->return_address_register = reader_->ReadUnsignedLEB128(cursor, &len);
- if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
- cursor += len;
- }
-
- // If we have a 'z' augmentation string, find the augmentation data and
- // use the augmentation string to parse it.
- if (cie->has_z_augmentation) {
- uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &len);
- if (size_t(cie->end - cursor) < len + data_size)
- return ReportIncomplete(cie);
- cursor += len;
- const char *data = cursor;
- cursor += data_size;
- const char *data_end = cursor;
-
- cie->has_z_lsda = false;
- cie->has_z_personality = false;
- cie->has_z_signal_frame = false;
-
- // Walk the augmentation string, and extract values from the
- // augmentation data as the string directs.
- for (size_t i = 1; i < cie->augmentation.size(); i++) {
- switch (cie->augmentation[i]) {
- case DW_Z_has_LSDA:
- // The CIE's augmentation data holds the language-specific data
- // area pointer's encoding, and the FDE's augmentation data holds
- // the pointer itself.
- cie->has_z_lsda = true;
- // Fetch the LSDA encoding from the augmentation data.
- if (data >= data_end) return ReportIncomplete(cie);
- cie->lsda_encoding = DwarfPointerEncoding(*data++);
- if (!reader_->ValidEncoding(cie->lsda_encoding)) {
- reporter_->InvalidPointerEncoding(cie->offset, cie->lsda_encoding);
- return false;
- }
- // Don't check if the encoding is usable here --- we haven't
- // read the FDE's fields yet, so we're not prepared for
- // DW_EH_PE_funcrel, although that's a fine encoding for the
- // LSDA to use, since it appears in the FDE.
- break;
-
- case DW_Z_has_personality_routine:
- // The CIE's augmentation data holds the personality routine
- // pointer's encoding, followed by the pointer itself.
- cie->has_z_personality = true;
- // Fetch the personality routine pointer's encoding from the
- // augmentation data.
- if (data >= data_end) return ReportIncomplete(cie);
- cie->personality_encoding = DwarfPointerEncoding(*data++);
- if (!reader_->ValidEncoding(cie->personality_encoding)) {
- reporter_->InvalidPointerEncoding(cie->offset,
- cie->personality_encoding);
- return false;
- }
- if (!reader_->UsableEncoding(cie->personality_encoding)) {
- reporter_->UnusablePointerEncoding(cie->offset,
- cie->personality_encoding);
- return false;
- }
- // Fetch the personality routine's pointer itself from the data.
- cie->personality_address =
- reader_->ReadEncodedPointer(data, cie->personality_encoding,
- &len);
- if (len > size_t(data_end - data))
- return ReportIncomplete(cie);
- data += len;
- break;
-
- case DW_Z_has_FDE_address_encoding:
- // The CIE's augmentation data holds the pointer encoding to use
- // for addresses in the FDE.
- if (data >= data_end) return ReportIncomplete(cie);
- cie->pointer_encoding = DwarfPointerEncoding(*data++);
- if (!reader_->ValidEncoding(cie->pointer_encoding)) {
- reporter_->InvalidPointerEncoding(cie->offset,
- cie->pointer_encoding);
- return false;
- }
- if (!reader_->UsableEncoding(cie->pointer_encoding)) {
- reporter_->UnusablePointerEncoding(cie->offset,
- cie->pointer_encoding);
- return false;
- }
- break;
-
- case DW_Z_is_signal_trampoline:
- // Frames using this CIE are signal delivery frames.
- cie->has_z_signal_frame = true;
- break;
-
- default:
- // An augmentation we don't recognize.
- reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation);
- return false;
- }
- }
- }
-
- // The CIE's instructions start here.
- cie->instructions = cursor;
-
- return true;
-}
-
-bool CallFrameInfo::ReadFDEFields(FDE *fde) {
- const char *cursor = fde->fields;
- size_t size;
-
- fde->address = reader_->ReadEncodedPointer(cursor, fde->cie->pointer_encoding,
- &size);
- if (size > size_t(fde->end - cursor))
- return ReportIncomplete(fde);
- cursor += size;
- reader_->SetFunctionBase(fde->address);
-
- // For the length, we strip off the upper nybble of the encoding used for
- // the starting address.
- DwarfPointerEncoding length_encoding =
- DwarfPointerEncoding(fde->cie->pointer_encoding & 0x0f);
- fde->size = reader_->ReadEncodedPointer(cursor, length_encoding, &size);
- if (size > size_t(fde->end - cursor))
- return ReportIncomplete(fde);
- cursor += size;
-
- // If the CIE has a 'z' augmentation string, then augmentation data
- // appears here.
- if (fde->cie->has_z_augmentation) {
- uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &size);
- if (size_t(fde->end - cursor) < size + data_size)
- return ReportIncomplete(fde);
- cursor += size;
-
- // In the abstract, we should walk the augmentation string, and extract
- // items from the FDE's augmentation data as we encounter augmentation
- // string characters that specify their presence: the ordering of items
- // in the augmentation string determines the arrangement of values in
- // the augmentation data.
- //
- // In practice, there's only ever one value in FDE augmentation data
- // that we support --- the LSDA pointer --- and we have to bail if we
- // see any unrecognized augmentation string characters. So if there is
- // anything here at all, we know what it is, and where it starts.
- if (fde->cie->has_z_lsda) {
- // Check whether the LSDA's pointer encoding is usable now: only once
- // we've parsed the FDE's starting address do we call reader_->
- // SetFunctionBase, so that the DW_EH_PE_funcrel encoding becomes
- // usable.
- if (!reader_->UsableEncoding(fde->cie->lsda_encoding)) {
- reporter_->UnusablePointerEncoding(fde->cie->offset,
- fde->cie->lsda_encoding);
- return false;
- }
-
- fde->lsda_address =
- reader_->ReadEncodedPointer(cursor, fde->cie->lsda_encoding, &size);
- if (size > data_size)
- return ReportIncomplete(fde);
- // Ideally, we would also complain here if there were unconsumed
- // augmentation data.
- }
-
- cursor += data_size;
- }
-
- // The FDE's instructions start after those.
- fde->instructions = cursor;
-
- return true;
-}
-
-bool CallFrameInfo::Start() {
- const char *buffer_end = buffer_ + buffer_length_;
- const char *cursor;
- bool all_ok = true;
- const char *entry_end;
- bool ok;
-
- // Traverse all the entries in buffer_, skipping CIEs and offering
- // FDEs to the handler.
- for (cursor = buffer_; cursor < buffer_end;
- cursor = entry_end, all_ok = all_ok && ok) {
- FDE fde;
-
- // Make it easy to skip this entry with 'continue': assume that
- // things are not okay until we've checked all the data, and
- // prepare the address of the next entry.
- ok = false;
-
- // Read the entry's prologue.
- if (!ReadEntryPrologue(cursor, &fde)) {
- if (!fde.end) {
- // If we couldn't even figure out this entry's extent, then we
- // must stop processing entries altogether.
- all_ok = false;
- break;
- }
- entry_end = fde.end;
- continue;
- }
-
- // The next iteration picks up after this entry.
- entry_end = fde.end;
-
- // Did we see an .eh_frame terminating mark?
- if (fde.kind == kTerminator) {
- // If there appears to be more data left in the section after the
- // terminating mark, warn the user. But this is just a warning;
- // we leave all_ok true.
- if (fde.end < buffer_end) reporter_->EarlyEHTerminator(fde.offset);
- break;
- }
-
- // In this loop, we skip CIEs. We only parse them fully when we
- // parse an FDE that refers to them. This limits our memory
- // consumption (beyond the buffer itself) to that needed to
- // process the largest single entry.
- if (fde.kind != kFDE) {
- ok = true;
- continue;
- }
-
- // Validate the CIE pointer.
- if (fde.id > buffer_length_) {
- reporter_->CIEPointerOutOfRange(fde.offset, fde.id);
- continue;
- }
-
- CIE cie;
-
- // Parse this FDE's CIE header.
- if (!ReadEntryPrologue(buffer_ + fde.id, &cie))
- continue;
- // This had better be an actual CIE.
- if (cie.kind != kCIE) {
- reporter_->BadCIEId(fde.offset, fde.id);
- continue;
- }
- if (!ReadCIEFields(&cie))
- continue;
-
- // We now have the values that govern both the CIE and the FDE.
- cie.cie = &cie;
- fde.cie = &cie;
-
- // Parse the FDE's header.
- if (!ReadFDEFields(&fde))
- continue;
-
- // Call Entry to ask the consumer if they're interested.
- if (!handler_->Entry(fde.offset, fde.address, fde.size,
- cie.version, cie.augmentation,
- cie.return_address_register)) {
- // The handler isn't interested in this entry. That's not an error.
- ok = true;
- continue;
- }
-
- if (cie.has_z_augmentation) {
- // Report the personality routine address, if we have one.
- if (cie.has_z_personality) {
- if (!handler_
- ->PersonalityRoutine(cie.personality_address,
- IsIndirectEncoding(cie.personality_encoding)))
- continue;
- }
-
- // Report the language-specific data area address, if we have one.
- if (cie.has_z_lsda) {
- if (!handler_
- ->LanguageSpecificDataArea(fde.lsda_address,
- IsIndirectEncoding(cie.lsda_encoding)))
- continue;
- }
-
- // If this is a signal-handling frame, report that.
- if (cie.has_z_signal_frame) {
- if (!handler_->SignalHandler())
- continue;
- }
- }
-
- // Interpret the CIE's instructions, and then the FDE's instructions.
- State state(reader_, handler_, reporter_, fde.address);
- ok = state.InterpretCIE(cie) && state.InterpretFDE(fde);
-
- // Tell the ByteReader that the function start address from the
- // FDE header is no longer valid.
- reader_->ClearFunctionBase();
-
- // Report the end of the entry.
- handler_->End();
- }
-
- return all_ok;
-}
-
-const char *CallFrameInfo::KindName(EntryKind kind) {
- if (kind == CallFrameInfo::kUnknown)
- return "entry";
- else if (kind == CallFrameInfo::kCIE)
- return "common information entry";
- else if (kind == CallFrameInfo::kFDE)
- return "frame description entry";
- else {
- MOZ_ASSERT (kind == CallFrameInfo::kTerminator);
- return ".eh_frame sequence terminator";
- }
-}
-
-bool CallFrameInfo::ReportIncomplete(Entry *entry) {
- reporter_->Incomplete(entry->offset, entry->kind);
- return false;
-}
-
-void CallFrameInfo::Reporter::Incomplete(uint64 offset,
- CallFrameInfo::EntryKind kind) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI %s at offset 0x%llx in '%s': entry ends early\n",
- filename_.c_str(), CallFrameInfo::KindName(kind), offset,
- section_.c_str());
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::EarlyEHTerminator(uint64 offset) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI at offset 0x%llx in '%s': saw end-of-data marker"
- " before end of section contents\n",
- filename_.c_str(), offset, section_.c_str());
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::CIEPointerOutOfRange(uint64 offset,
- uint64 cie_offset) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI frame description entry at offset 0x%llx in '%s':"
- " CIE pointer is out of range: 0x%llx\n",
- filename_.c_str(), offset, section_.c_str(), cie_offset);
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI frame description entry at offset 0x%llx in '%s':"
- " CIE pointer does not point to a CIE: 0x%llx\n",
- filename_.c_str(), offset, section_.c_str(), cie_offset);
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI frame description entry at offset 0x%llx in '%s':"
- " CIE specifies unrecognized version: %d\n",
- filename_.c_str(), offset, section_.c_str(), version);
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset,
- const string &aug) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI frame description entry at offset 0x%llx in '%s':"
- " CIE specifies unrecognized augmentation: '%s'\n",
- filename_.c_str(), offset, section_.c_str(), aug.c_str());
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::InvalidPointerEncoding(uint64 offset,
- uint8 encoding) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI common information entry at offset 0x%llx in '%s':"
- " 'z' augmentation specifies invalid pointer encoding: "
- "0x%02x\n",
- filename_.c_str(), offset, section_.c_str(), encoding);
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::UnusablePointerEncoding(uint64 offset,
- uint8 encoding) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI common information entry at offset 0x%llx in '%s':"
- " 'z' augmentation specifies a pointer encoding for which"
- " we have no base address: 0x%02x\n",
- filename_.c_str(), offset, section_.c_str(), encoding);
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::RestoreInCIE(uint64 offset, uint64 insn_offset) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI common information entry at offset 0x%llx in '%s':"
- " the DW_CFA_restore instruction at offset 0x%llx"
- " cannot be used in a common information entry\n",
- filename_.c_str(), offset, section_.c_str(), insn_offset);
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::BadInstruction(uint64 offset,
- CallFrameInfo::EntryKind kind,
- uint64 insn_offset) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI %s at offset 0x%llx in section '%s':"
- " the instruction at offset 0x%llx is unrecognized\n",
- filename_.c_str(), CallFrameInfo::KindName(kind),
- offset, section_.c_str(), insn_offset);
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::NoCFARule(uint64 offset,
- CallFrameInfo::EntryKind kind,
- uint64 insn_offset) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI %s at offset 0x%llx in section '%s':"
- " the instruction at offset 0x%llx assumes that a CFA rule "
- "has been set, but none has been set\n",
- filename_.c_str(), CallFrameInfo::KindName(kind), offset,
- section_.c_str(), insn_offset);
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::EmptyStateStack(uint64 offset,
- CallFrameInfo::EntryKind kind,
- uint64 insn_offset) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI %s at offset 0x%llx in section '%s':"
- " the DW_CFA_restore_state instruction at offset 0x%llx"
- " should pop a saved state from the stack, but the stack "
- "is empty\n",
- filename_.c_str(), CallFrameInfo::KindName(kind), offset,
- section_.c_str(), insn_offset);
- log_(buf);
-}
-
-void CallFrameInfo::Reporter::ClearingCFARule(uint64 offset,
- CallFrameInfo::EntryKind kind,
- uint64 insn_offset) {
- char buf[300];
- SprintfLiteral(buf,
- "%s: CFI %s at offset 0x%llx in section '%s':"
- " the DW_CFA_restore_state instruction at offset 0x%llx"
- " would clear the CFA rule in effect\n",
- filename_.c_str(), CallFrameInfo::KindName(kind), offset,
- section_.c_str(), insn_offset);
- log_(buf);
-}
-
-
-unsigned int DwarfCFIToModule::RegisterNames::I386() {
- /*
- 8 "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi",
- 3 "$eip", "$eflags", "$unused1",
- 8 "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
- 2 "$unused2", "$unused3",
- 8 "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
- 8 "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
- 3 "$fcw", "$fsw", "$mxcsr",
- 8 "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5",
- 2 "$tr", "$ldtr"
- */
- return 8 + 3 + 8 + 2 + 8 + 8 + 3 + 8 + 2;
-}
-
-unsigned int DwarfCFIToModule::RegisterNames::X86_64() {
- /*
- 8 "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp",
- 8 "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15",
- 1 "$rip",
- 8 "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
- 8 "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15",
- 8 "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
- 8 "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
- 1 "$rflags",
- 8 "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2",
- 4 "$fs.base", "$gs.base", "$unused3", "$unused4",
- 2 "$tr", "$ldtr",
- 3 "$mxcsr", "$fcw", "$fsw"
- */
- return 8 + 8 + 1 + 8 + 8 + 8 + 8 + 1 + 8 + 4 + 2 + 3;
-}
-
-// Per ARM IHI 0040A, section 3.1
-unsigned int DwarfCFIToModule::RegisterNames::ARM() {
- /*
- 8 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
- 8 "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
- 8 "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
- 8 "fps", "cpsr", "", "", "", "", "", "",
- 8 "", "", "", "", "", "", "", "",
- 8 "", "", "", "", "", "", "", "",
- 8 "", "", "", "", "", "", "", "",
- 8 "", "", "", "", "", "", "", "",
- 8 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
- 8 "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
- 8 "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
- 8 "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
- 8 "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"
- */
- return 13 * 8;
-}
-
-// See prototype for comments.
-int32_t parseDwarfExpr(Summariser* summ, const ByteReader* reader,
- string expr, bool debug,
- bool pushCfaAtStart, bool derefAtEnd)
-{
- const char* cursor = expr.c_str();
- const char* end1 = cursor + expr.length();
-
- char buf[100];
- if (debug) {
- SprintfLiteral(buf, "LUL.DW << DwarfExpr, len is %d\n",
- (int)(end1 - cursor));
- summ->Log(buf);
- }
-
- // Add a marker for the start of this expression. In it, indicate
- // whether or not the CFA should be pushed onto the stack prior to
- // evaluation.
- int32_t start_ix
- = summ->AddPfxInstr(PfxInstr(PX_Start, pushCfaAtStart ? 1 : 0));
- MOZ_ASSERT(start_ix >= 0);
-
- while (cursor < end1) {
-
- uint8 opc = reader->ReadOneByte(cursor);
- cursor++;
-
- const char* nm = nullptr;
- PfxExprOp pxop = PX_End;
-
- switch (opc) {
-
- case DW_OP_lit0 ... DW_OP_lit31: {
- int32_t simm32 = (int32_t)(opc - DW_OP_lit0);
- if (debug) {
- SprintfLiteral(buf, "LUL.DW DW_OP_lit%d\n", (int)simm32);
- summ->Log(buf);
- }
- (void) summ->AddPfxInstr(PfxInstr(PX_SImm32, simm32));
- break;
- }
-
- case DW_OP_breg0 ... DW_OP_breg31: {
- size_t len;
- int64_t n = reader->ReadSignedLEB128(cursor, &len);
- cursor += len;
- DW_REG_NUMBER reg = (DW_REG_NUMBER)(opc - DW_OP_breg0);
- if (debug) {
- SprintfLiteral(buf, "LUL.DW DW_OP_breg%d %lld\n",
- (int)reg, (long long int)n);
- summ->Log(buf);
- }
- // PfxInstr only allows a 32 bit signed offset. So we
- // must fail if the immediate is out of range.
- if (n < INT32_MIN || INT32_MAX < n)
- goto fail;
- (void) summ->AddPfxInstr(PfxInstr(PX_DwReg, reg));
- (void) summ->AddPfxInstr(PfxInstr(PX_SImm32, (int32_t)n));
- (void) summ->AddPfxInstr(PfxInstr(PX_Add));
- break;
- }
-
- case DW_OP_const4s: {
- uint64_t u64 = reader->ReadFourBytes(cursor);
- cursor += 4;
- // u64 is guaranteed by |ReadFourBytes| to be in the
- // range 0 .. FFFFFFFF inclusive. But to be safe:
- uint32_t u32 = (uint32_t)(u64 & 0xFFFFFFFF);
- int32_t s32 = (int32_t)u32;
- if (debug) {
- SprintfLiteral(buf, "LUL.DW DW_OP_const4s %d\n", (int)s32);
- summ->Log(buf);
- }
- (void) summ->AddPfxInstr(PfxInstr(PX_SImm32, s32));
- break;
- }
-
- case DW_OP_deref: nm = "deref"; pxop = PX_Deref; goto no_operands;
- case DW_OP_and: nm = "and"; pxop = PX_And; goto no_operands;
- case DW_OP_plus: nm = "plus"; pxop = PX_Add; goto no_operands;
- case DW_OP_minus: nm = "minus"; pxop = PX_Sub; goto no_operands;
- case DW_OP_shl: nm = "shl"; pxop = PX_Shl; goto no_operands;
- case DW_OP_ge: nm = "ge"; pxop = PX_CmpGES; goto no_operands;
- no_operands:
- MOZ_ASSERT(nm && pxop != PX_End);
- if (debug) {
- SprintfLiteral(buf, "LUL.DW DW_OP_%s\n", nm);
- summ->Log(buf);
- }
- (void) summ->AddPfxInstr(PfxInstr(pxop));
- break;
-
- default:
- if (debug) {
- SprintfLiteral(buf, "LUL.DW unknown opc %d\n", (int)opc);
- summ->Log(buf);
- }
- goto fail;
-
- } // switch (opc)
-
- } // while (cursor < end1)
-
- MOZ_ASSERT(cursor >= end1);
-
- if (cursor > end1) {
- // We overran the Dwarf expression. Give up.
- goto fail;
- }
-
- // For DW_CFA_expression, what the expression denotes is the address
- // of where the previous value is located. The caller of this routine
- // may therefore request one last dereference before the end marker is
- // inserted.
- if (derefAtEnd) {
- (void) summ->AddPfxInstr(PfxInstr(PX_Deref));
- }
-
- // Insert an end marker, and declare success.
- (void) summ->AddPfxInstr(PfxInstr(PX_End));
- if (debug) {
- SprintfLiteral(buf, "LUL.DW conversion of dwarf expression succeeded, "
- "ix = %d\n", (int)start_ix);
- summ->Log(buf);
- summ->Log("LUL.DW >>\n");
- }
- return start_ix;
-
- fail:
- if (debug) {
- summ->Log("LUL.DW conversion of dwarf expression failed\n");
- summ->Log("LUL.DW >>\n");
- }
- return -1;
-}
-
-
-bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
- uint8 version, const string &augmentation,
- unsigned return_address) {
- if (DEBUG_DWARF) {
- char buf[100];
- SprintfLiteral(buf, "LUL.DW DwarfCFIToModule::Entry 0x%llx,+%lld\n",
- address, length);
- summ_->Log(buf);
- }
-
- summ_->Entry(address, length);
-
- // If dwarf2reader::CallFrameInfo can handle this version and
- // augmentation, then we should be okay with that, so there's no
- // need to check them here.
-
- // Get ready to collect entries.
- return_address_ = return_address;
-
- // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI
- // may not establish any rule for .ra if the return address column
- // is an ordinary register, and that register holds the return
- // address on entry to the function. So establish an initial .ra
- // rule citing the return address register.
- if (return_address_ < num_dw_regs_) {
- summ_->Rule(address, return_address_, NODEREF, return_address, 0);
- }
-
- return true;
-}
-
-const UniqueString* DwarfCFIToModule::RegisterName(int i) {
- if (i < 0) {
- MOZ_ASSERT(i == kCFARegister);
- return usu_->ToUniqueString(".cfa");
- }
- unsigned reg = i;
- if (reg == return_address_)
- return usu_->ToUniqueString(".ra");
-
- char buf[30];
- SprintfLiteral(buf, "dwarf_reg_%u", reg);
- return usu_->ToUniqueString(buf);
-}
-
-bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) {
- reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg));
- // Treat this as a non-fatal error.
- return true;
-}
-
-bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) {
- if (DEBUG_DWARF) {
- char buf[100];
- SprintfLiteral(buf, "LUL.DW 0x%llx: old r%d = Same\n", address, reg);
- summ_->Log(buf);
- }
- // reg + 0
- summ_->Rule(address, reg, NODEREF, reg, 0);
- return true;
-}
-
-bool DwarfCFIToModule::OffsetRule(uint64 address, int reg,
- int base_register, long offset) {
- if (DEBUG_DWARF) {
- char buf[100];
- SprintfLiteral(buf, "LUL.DW 0x%llx: old r%d = *(r%d + %ld)\n",
- address, reg, base_register, offset);
- summ_->Log(buf);
- }
- // *(base_register + offset)
- summ_->Rule(address, reg, DEREF, base_register, offset);
- return true;
-}
-
-bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg,
- int base_register, long offset) {
- if (DEBUG_DWARF) {
- char buf[100];
- SprintfLiteral(buf, "LUL.DW 0x%llx: old r%d = r%d + %ld\n",
- address, reg, base_register, offset);
- summ_->Log(buf);
- }
- // base_register + offset
- summ_->Rule(address, reg, NODEREF, base_register, offset);
- return true;
-}
-
-bool DwarfCFIToModule::RegisterRule(uint64 address, int reg,
- int base_register) {
- if (DEBUG_DWARF) {
- char buf[100];
- SprintfLiteral(buf, "LUL.DW 0x%llx: old r%d = r%d\n",
- address, reg, base_register);
- summ_->Log(buf);
- }
- // base_register + 0
- summ_->Rule(address, reg, NODEREF, base_register, 0);
- return true;
-}
-
-bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg,
- const string &expression)
-{
- bool debug = !!DEBUG_DWARF;
- int32_t start_ix = parseDwarfExpr(summ_, reader_, expression, debug,
- true/*pushCfaAtStart*/,
- true/*derefAtEnd*/);
- if (start_ix >= 0) {
- summ_->Rule(address, reg, PFXEXPR, 0, start_ix);
- } else {
- // Parsing of the Dwarf expression failed. Treat this as a
- // non-fatal error, hence return |true| even on this path.
- reporter_->ExpressionCouldNotBeSummarised(entry_offset_, RegisterName(reg));
- }
- return true;
-}
-
-bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg,
- const string &expression)
-{
- bool debug = !!DEBUG_DWARF;
- int32_t start_ix = parseDwarfExpr(summ_, reader_, expression, debug,
- true/*pushCfaAtStart*/,
- false/*!derefAtEnd*/);
- if (start_ix >= 0) {
- summ_->Rule(address, reg, PFXEXPR, 0, start_ix);
- } else {
- // Parsing of the Dwarf expression failed. Treat this as a
- // non-fatal error, hence return |true| even on this path.
- reporter_->ExpressionCouldNotBeSummarised(entry_offset_, RegisterName(reg));
- }
- return true;
-}
-
-bool DwarfCFIToModule::End() {
- //module_->AddStackFrameEntry(entry_);
- if (DEBUG_DWARF) {
- summ_->Log("LUL.DW DwarfCFIToModule::End()\n");
- }
- summ_->End();
- return true;
-}
-
-void DwarfCFIToModule::Reporter::UndefinedNotSupported(
- size_t offset,
- const UniqueString* reg) {
- char buf[300];
- SprintfLiteral(buf, "DwarfCFIToModule::Reporter::UndefinedNotSupported()\n");
- log_(buf);
- //BPLOG(INFO) << file_ << ", section '" << section_
- // << "': the call frame entry at offset 0x"
- // << std::setbase(16) << offset << std::setbase(10)
- // << " sets the rule for register '" << FromUniqueString(reg)
- // << "' to 'undefined', but the Breakpad symbol file format cannot "
- // << " express this";
-}
-
-// FIXME: move this somewhere sensible
-static bool is_power_of_2(uint64_t n)
-{
- int i, nSetBits = 0;
- for (i = 0; i < 8*(int)sizeof(n); i++) {
- if ((n & ((uint64_t)1) << i) != 0)
- nSetBits++;
- }
- return nSetBits <= 1;
-}
-
-void DwarfCFIToModule::Reporter::ExpressionCouldNotBeSummarised(
- size_t offset,
- const UniqueString* reg) {
- static uint64_t n_complaints = 0; // This isn't threadsafe
- n_complaints++;
- if (!is_power_of_2(n_complaints))
- return;
- char buf[300];
- SprintfLiteral(buf,
- "DwarfCFIToModule::Reporter::"
- "ExpressionCouldNotBeSummarised(shown %llu times)\n",
- (unsigned long long int)n_complaints);
- log_(buf);
-}
-
-} // namespace lul
diff --git a/tools/profiler/lul/LulDwarfSummariser.cpp b/tools/profiler/lul/LulDwarfSummariser.cpp
deleted file mode 100644
index 74c2565df..000000000
--- a/tools/profiler/lul/LulDwarfSummariser.cpp
+++ /dev/null
@@ -1,359 +0,0 @@
-/* -*- 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 "LulDwarfSummariser.h"
-
-#include "mozilla/Assertions.h"
-
-// Set this to 1 for verbose logging
-#define DEBUG_SUMMARISER 0
-
-namespace lul {
-
-// Do |s64|'s lowest 32 bits sign extend back to |s64| itself?
-static inline bool fitsIn32Bits(int64 s64) {
- return s64 == ((s64 & 0xffffffff) ^ 0x80000000) - 0x80000000;
-}
-
-// Check a LExpr prefix expression, starting at pfxInstrs[start] up to
-// the next PX_End instruction, to ensure that:
-// * It only mentions registers that are tracked on this target
-// * The start point is sane
-// If the expression is ok, return NULL. Else return a pointer
-// a const char* holding a bit of text describing the problem.
-static const char*
-checkPfxExpr(const vector<PfxInstr>* pfxInstrs, int64_t start)
-{
- size_t nInstrs = pfxInstrs->size();
- if (start < 0 || start >= (ssize_t)nInstrs) {
- return "bogus start point";
- }
- size_t i;
- for (i = start; i < nInstrs; i++) {
- PfxInstr pxi = (*pfxInstrs)[i];
- if (pxi.mOpcode == PX_End)
- break;
- if (pxi.mOpcode == PX_DwReg &&
- !registerIsTracked((DW_REG_NUMBER)pxi.mOperand)) {
- return "uses untracked reg";
- }
- }
- return nullptr; // success
-}
-
-
-Summariser::Summariser(SecMap* aSecMap, uintptr_t aTextBias,
- void(*aLog)(const char*))
- : mSecMap(aSecMap)
- , mTextBias(aTextBias)
- , mLog(aLog)
-{
- mCurrAddr = 0;
- mMax1Addr = 0; // Gives an empty range.
-
- // Initialise the running RuleSet to "haven't got a clue" status.
- new (&mCurrRules) RuleSet();
-}
-
-void
-Summariser::Entry(uintptr_t aAddress, uintptr_t aLength)
-{
- aAddress += mTextBias;
- if (DEBUG_SUMMARISER) {
- char buf[100];
- SprintfLiteral(buf,
- "LUL Entry(%llx, %llu)\n",
- (unsigned long long int)aAddress,
- (unsigned long long int)aLength);
- mLog(buf);
- }
- // This throws away any previous summary, that is, assumes
- // that the previous summary, if any, has been properly finished
- // by a call to End().
- mCurrAddr = aAddress;
- mMax1Addr = aAddress + aLength;
- new (&mCurrRules) RuleSet();
-}
-
-void
-Summariser::Rule(uintptr_t aAddress, int aNewReg,
- LExprHow how, int16_t oldReg, int64_t offset)
-{
- aAddress += mTextBias;
- if (DEBUG_SUMMARISER) {
- char buf[100];
- if (how == NODEREF || how == DEREF) {
- bool deref = how == DEREF;
- SprintfLiteral(buf,
- "LUL 0x%llx old-r%d = %sr%d + %lld%s\n",
- (unsigned long long int)aAddress, aNewReg,
- deref ? "*(" : "", (int)oldReg, (long long int)offset,
- deref ? ")" : "");
- } else if (how == PFXEXPR) {
- SprintfLiteral(buf,
- "LUL 0x%llx old-r%d = pfx-expr-at %lld\n",
- (unsigned long long int)aAddress, aNewReg,
- (long long int)offset);
- } else {
- SprintfLiteral(buf,
- "LUL 0x%llx old-r%d = (invalid LExpr!)\n",
- (unsigned long long int)aAddress, aNewReg);
- }
- mLog(buf);
- }
-
- if (mCurrAddr < aAddress) {
- // Flush the existing summary first.
- mCurrRules.mAddr = mCurrAddr;
- mCurrRules.mLen = aAddress - mCurrAddr;
- mSecMap->AddRuleSet(&mCurrRules);
- if (DEBUG_SUMMARISER) {
- mLog("LUL "); mCurrRules.Print(mLog);
- mLog("\n");
- }
- mCurrAddr = aAddress;
- }
-
- // If for some reason summarisation fails, either or both of these
- // become non-null and point at constant text describing the
- // problem. Using two rather than just one avoids complications of
- // having to concatenate two strings to produce a complete error message.
- const char* reason1 = nullptr;
- const char* reason2 = nullptr;
-
- // |offset| needs to be a 32 bit value that sign extends to 64 bits
- // on a 64 bit target. We will need to incorporate |offset| into
- // any LExpr made here. So we may as well check it right now.
- if (!fitsIn32Bits(offset)) {
- reason1 = "offset not in signed 32-bit range";
- goto cant_summarise;
- }
-
- // FIXME: factor out common parts of the arch-dependent summarisers.
-
-#if defined(LUL_ARCH_arm)
-
- // ----------------- arm ----------------- //
-
- // Now, can we add the rule to our summary? This depends on whether
- // the registers and the overall expression are representable. This
- // is the heart of the summarisation process.
- switch (aNewReg) {
-
- case DW_REG_CFA:
- // This is a rule that defines the CFA. The only forms we
- // choose to represent are: r7/11/12/13 + offset. The offset
- // must fit into 32 bits since 'uintptr_t' is 32 bit on ARM,
- // hence there is no need to check it for overflow.
- if (how != NODEREF) {
- reason1 = "rule for DW_REG_CFA: invalid |how|";
- goto cant_summarise;
- }
- switch (oldReg) {
- case DW_REG_ARM_R7: case DW_REG_ARM_R11:
- case DW_REG_ARM_R12: case DW_REG_ARM_R13:
- break;
- default:
- reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
- goto cant_summarise;
- }
- mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
- break;
-
- case DW_REG_ARM_R7: case DW_REG_ARM_R11: case DW_REG_ARM_R12:
- case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: {
- // This is a new rule for R7, R11, R12, R13 (SP), R14 (LR) or
- // R15 (the return address).
- switch (how) {
- case NODEREF: case DEREF:
- // Check the old register is one we're tracking.
- if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
- oldReg != DW_REG_CFA) {
- reason1 = "rule for R7/11/12/13/14/15: uses untracked reg";
- goto cant_summarise;
- }
- break;
- case PFXEXPR: {
- // Check that the prefix expression only mentions tracked registers.
- const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
- reason2 = checkPfxExpr(pfxInstrs, offset);
- if (reason2) {
- reason1 = "rule for R7/11/12/13/14/15: ";
- goto cant_summarise;
- }
- break;
- }
- default:
- goto cant_summarise;
- }
- LExpr expr = LExpr(how, oldReg, offset);
- switch (aNewReg) {
- case DW_REG_ARM_R7: mCurrRules.mR7expr = expr; break;
- case DW_REG_ARM_R11: mCurrRules.mR11expr = expr; break;
- case DW_REG_ARM_R12: mCurrRules.mR12expr = expr; break;
- case DW_REG_ARM_R13: mCurrRules.mR13expr = expr; break;
- case DW_REG_ARM_R14: mCurrRules.mR14expr = expr; break;
- case DW_REG_ARM_R15: mCurrRules.mR15expr = expr; break;
- default: MOZ_ASSERT(0);
- }
- break;
- }
-
- default:
- // Leave |reason1| and |reason2| unset here. This program point
- // is reached so often that it causes a flood of "Can't
- // summarise" messages. In any case, we don't really care about
- // the fact that this summary would produce a new value for a
- // register that we're not tracking. We do on the other hand
- // care if the summary's expression *uses* a register that we're
- // not tracking. But in that case one of the above failures
- // should tell us which.
- goto cant_summarise;
- }
-
- // Mark callee-saved registers (r4 .. r11) as unchanged, if there is
- // no other information about them. FIXME: do this just once, at
- // the point where the ruleset is committed.
- if (mCurrRules.mR7expr.mHow == UNKNOWN) {
- mCurrRules.mR7expr = LExpr(NODEREF, DW_REG_ARM_R7, 0);
- }
- if (mCurrRules.mR11expr.mHow == UNKNOWN) {
- mCurrRules.mR11expr = LExpr(NODEREF, DW_REG_ARM_R11, 0);
- }
- if (mCurrRules.mR12expr.mHow == UNKNOWN) {
- mCurrRules.mR12expr = LExpr(NODEREF, DW_REG_ARM_R12, 0);
- }
-
- // The old r13 (SP) value before the call is always the same as the
- // CFA.
- mCurrRules.mR13expr = LExpr(NODEREF, DW_REG_CFA, 0);
-
- // If there's no information about R15 (the return address), say
- // it's a copy of R14 (the link register).
- if (mCurrRules.mR15expr.mHow == UNKNOWN) {
- mCurrRules.mR15expr = LExpr(NODEREF, DW_REG_ARM_R14, 0);
- }
-
-#elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
-
- // ---------------- x64/x86 ---------------- //
-
- // Now, can we add the rule to our summary? This depends on whether
- // the registers and the overall expression are representable. This
- // is the heart of the summarisation process.
- switch (aNewReg) {
-
- case DW_REG_CFA:
- // This is a rule that defines the CFA. The only forms we can
- // represent are: = SP+offset or = FP+offset.
- if (how != NODEREF) {
- reason1 = "rule for DW_REG_CFA: invalid |how|";
- goto cant_summarise;
- }
- if (oldReg != DW_REG_INTEL_XSP && oldReg != DW_REG_INTEL_XBP) {
- reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
- goto cant_summarise;
- }
- mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
- break;
-
- case DW_REG_INTEL_XSP: case DW_REG_INTEL_XBP: case DW_REG_INTEL_XIP: {
- // This is a new rule for XSP, XBP or XIP (the return address).
- switch (how) {
- case NODEREF: case DEREF:
- // Check the old register is one we're tracking.
- if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
- oldReg != DW_REG_CFA) {
- reason1 = "rule for XSP/XBP/XIP: uses untracked reg";
- goto cant_summarise;
- }
- break;
- case PFXEXPR: {
- // Check that the prefix expression only mentions tracked registers.
- const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
- reason2 = checkPfxExpr(pfxInstrs, offset);
- if (reason2) {
- reason1 = "rule for XSP/XBP/XIP: ";
- goto cant_summarise;
- }
- break;
- }
- default:
- goto cant_summarise;
- }
- LExpr expr = LExpr(how, oldReg, offset);
- switch (aNewReg) {
- case DW_REG_INTEL_XBP: mCurrRules.mXbpExpr = expr; break;
- case DW_REG_INTEL_XSP: mCurrRules.mXspExpr = expr; break;
- case DW_REG_INTEL_XIP: mCurrRules.mXipExpr = expr; break;
- default: MOZ_CRASH("impossible value for aNewReg");
- }
- break;
- }
-
- default:
- // Leave |reason1| and |reason2| unset here, for the reasons
- // explained in the analogous point in the ARM case just above.
- goto cant_summarise;
-
- }
-
- // On Intel, it seems the old SP value before the call is always the
- // same as the CFA. Therefore, in the absence of any other way to
- // recover the SP, specify that the CFA should be copied.
- if (mCurrRules.mXspExpr.mHow == UNKNOWN) {
- mCurrRules.mXspExpr = LExpr(NODEREF, DW_REG_CFA, 0);
- }
-
- // Also, gcc says "Undef" for BP when it is unchanged.
- if (mCurrRules.mXbpExpr.mHow == UNKNOWN) {
- mCurrRules.mXbpExpr = LExpr(NODEREF, DW_REG_INTEL_XBP, 0);
- }
-
-#else
-
-# error "Unsupported arch"
-#endif
-
- return;
-
- cant_summarise:
- if (reason1 || reason2) {
- char buf[200];
- SprintfLiteral(buf, "LUL can't summarise: "
- "SVMA=0x%llx: %s%s, expr=LExpr(%s,%u,%lld)\n",
- (unsigned long long int)(aAddress - mTextBias),
- reason1 ? reason1 : "", reason2 ? reason2 : "",
- NameOf_LExprHow(how),
- (unsigned int)oldReg, (long long int)offset);
- mLog(buf);
- }
-}
-
-uint32_t
-Summariser::AddPfxInstr(PfxInstr pfxi)
-{
- return mSecMap->AddPfxInstr(pfxi);
-}
-
-void
-Summariser::End()
-{
- if (DEBUG_SUMMARISER) {
- mLog("LUL End\n");
- }
- if (mCurrAddr < mMax1Addr) {
- mCurrRules.mAddr = mCurrAddr;
- mCurrRules.mLen = mMax1Addr - mCurrAddr;
- mSecMap->AddRuleSet(&mCurrRules);
- if (DEBUG_SUMMARISER) {
- mLog("LUL "); mCurrRules.Print(mLog);
- mLog("\n");
- }
- }
-}
-
-} // namespace lul
diff --git a/tools/profiler/lul/LulDwarfSummariser.h b/tools/profiler/lul/LulDwarfSummariser.h
deleted file mode 100644
index b41db1ee3..000000000
--- a/tools/profiler/lul/LulDwarfSummariser.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* -*- 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/. */
-
-#ifndef LulDwarfSummariser_h
-#define LulDwarfSummariser_h
-
-#include "LulMainInt.h"
-
-namespace lul {
-
-class Summariser
-{
-public:
- Summariser(SecMap* aSecMap, uintptr_t aTextBias, void(*aLog)(const char*));
-
- virtual void Entry(uintptr_t aAddress, uintptr_t aLength);
- virtual void End();
-
- // Tell the summariser that the value for |aNewReg| at |aAddress| is
- // recovered using the LExpr that can be constructed using the
- // components |how|, |oldReg| and |offset|. The summariser will
- // inspect the components and may reject them for various reasons,
- // but the hope is that it will find them acceptable and record this
- // rule permanently.
- virtual void Rule(uintptr_t aAddress, int aNewReg,
- LExprHow how, int16_t oldReg, int64_t offset);
-
- virtual uint32_t AddPfxInstr(PfxInstr pfxi);
-
- // Send output to the logging sink, for debugging.
- virtual void Log(const char* str) { mLog(str); }
-
-private:
- // The SecMap in which we park the finished summaries (RuleSets) and
- // also any PfxInstrs derived from Dwarf expressions.
- SecMap* mSecMap;
-
- // Running state for the current summary (RuleSet) under construction.
- RuleSet mCurrRules;
-
- // The start of the address range to which the RuleSet under
- // construction applies.
- uintptr_t mCurrAddr;
-
- // The highest address, plus one, for which the RuleSet under
- // construction could possibly apply. If there are no further
- // incoming events then mCurrRules will eventually be emitted
- // as-is, for the range mCurrAddr.. mMax1Addr - 1, if that is
- // nonempty.
- uintptr_t mMax1Addr;
-
- // The bias value (to add to the SVMAs, to get AVMAs) to be used
- // when adding entries into mSecMap.
- uintptr_t mTextBias;
-
- // A logging sink, for debugging.
- void (*mLog)(const char* aFmt);
-};
-
-} // namespace lul
-
-#endif // LulDwarfSummariser_h
diff --git a/tools/profiler/lul/LulElf.cpp b/tools/profiler/lul/LulElf.cpp
deleted file mode 100644
index 6f90d5f13..000000000
--- a/tools/profiler/lul/LulElf.cpp
+++ /dev/null
@@ -1,915 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-
-// Copyright (c) 2006, 2011, 2012 Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
-
-// (derived from)
-// dump_symbols.cc: implement google_breakpad::WriteSymbolFile:
-// Find all the debugging info in a file and dump it as a Breakpad symbol file.
-//
-// dump_symbols.h: Read debugging information from an ELF file, and write
-// it out as a Breakpad symbol file.
-
-// This file is derived from the following files in
-// toolkit/crashreporter/google-breakpad:
-// src/common/linux/dump_symbols.cc
-// src/common/linux/elfutils.cc
-// src/common/linux/file_id.cc
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "mozilla/Assertions.h"
-#include "mozilla/Sprintf.h"
-
-#include "LulPlatformMacros.h"
-#include "LulCommonExt.h"
-#include "LulDwarfExt.h"
-#include "LulElfInt.h"
-#include "LulMainInt.h"
-
-
-#if defined(LUL_PLAT_arm_android) && !defined(SHT_ARM_EXIDX)
-// bionic and older glibsc don't define it
-# define SHT_ARM_EXIDX (SHT_LOPROC + 1)
-#endif
-
-
-// This namespace contains helper functions.
-namespace {
-
-using lul::DwarfCFIToModule;
-using lul::FindElfSectionByName;
-using lul::GetOffset;
-using lul::IsValidElf;
-using lul::Module;
-using lul::UniqueStringUniverse;
-using lul::scoped_ptr;
-using lul::Summariser;
-using std::string;
-using std::vector;
-using std::set;
-
-//
-// FDWrapper
-//
-// Wrapper class to make sure opened file is closed.
-//
-class FDWrapper {
- public:
- explicit FDWrapper(int fd) :
- fd_(fd) {}
- ~FDWrapper() {
- if (fd_ != -1)
- close(fd_);
- }
- int get() {
- return fd_;
- }
- int release() {
- int fd = fd_;
- fd_ = -1;
- return fd;
- }
- private:
- int fd_;
-};
-
-//
-// MmapWrapper
-//
-// Wrapper class to make sure mapped regions are unmapped.
-//
-class MmapWrapper {
- public:
- MmapWrapper() : is_set_(false), base_(NULL), size_(0){}
- ~MmapWrapper() {
- if (is_set_ && base_ != NULL) {
- MOZ_ASSERT(size_ > 0);
- munmap(base_, size_);
- }
- }
- void set(void *mapped_address, size_t mapped_size) {
- is_set_ = true;
- base_ = mapped_address;
- size_ = mapped_size;
- }
- void release() {
- MOZ_ASSERT(is_set_);
- is_set_ = false;
- base_ = NULL;
- size_ = 0;
- }
-
- private:
- bool is_set_;
- void *base_;
- size_t size_;
-};
-
-
-// Set NUM_DW_REGNAMES to be the number of Dwarf register names
-// appropriate to the machine architecture given in HEADER. Return
-// true on success, or false if HEADER's machine architecture is not
-// supported.
-template<typename ElfClass>
-bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header,
- unsigned int* num_dw_regnames) {
- switch (elf_header->e_machine) {
- case EM_386:
- *num_dw_regnames = DwarfCFIToModule::RegisterNames::I386();
- return true;
- case EM_ARM:
- *num_dw_regnames = DwarfCFIToModule::RegisterNames::ARM();
- return true;
- case EM_X86_64:
- *num_dw_regnames = DwarfCFIToModule::RegisterNames::X86_64();
- return true;
- default:
- MOZ_ASSERT(0);
- return false;
- }
-}
-
-template<typename ElfClass>
-bool LoadDwarfCFI(const string& dwarf_filename,
- const typename ElfClass::Ehdr* elf_header,
- const char* section_name,
- const typename ElfClass::Shdr* section,
- const bool eh_frame,
- const typename ElfClass::Shdr* got_section,
- const typename ElfClass::Shdr* text_section,
- const bool big_endian,
- SecMap* smap,
- uintptr_t text_bias,
- UniqueStringUniverse* usu,
- void (*log)(const char*)) {
- // Find the appropriate set of register names for this file's
- // architecture.
- unsigned int num_dw_regs = 0;
- if (!DwarfCFIRegisterNames<ElfClass>(elf_header, &num_dw_regs)) {
- fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
- " cannot convert DWARF call frame information\n",
- dwarf_filename.c_str(), elf_header->e_machine);
- return false;
- }
-
- const lul::Endianness endianness
- = big_endian ? lul::ENDIANNESS_BIG : lul::ENDIANNESS_LITTLE;
-
- // Find the call frame information and its size.
- const char* cfi =
- GetOffset<ElfClass, char>(elf_header, section->sh_offset);
- size_t cfi_size = section->sh_size;
-
- // Plug together the parser, handler, and their entourages.
-
- // Here's a summariser, which will receive the output of the
- // parser, create summaries, and add them to |smap|.
- Summariser summ(smap, text_bias, log);
-
- lul::ByteReader reader(endianness);
- reader.SetAddressSize(ElfClass::kAddrSize);
-
- DwarfCFIToModule::Reporter module_reporter(log, dwarf_filename, section_name);
- DwarfCFIToModule handler(num_dw_regs, &module_reporter, &reader, usu, &summ);
-
- // Provide the base addresses for .eh_frame encoded pointers, if
- // possible.
- reader.SetCFIDataBase(section->sh_addr, cfi);
- if (got_section)
- reader.SetDataBase(got_section->sh_addr);
- if (text_section)
- reader.SetTextBase(text_section->sh_addr);
-
- lul::CallFrameInfo::Reporter dwarf_reporter(log, dwarf_filename,
- section_name);
- lul::CallFrameInfo parser(cfi, cfi_size,
- &reader, &handler, &dwarf_reporter,
- eh_frame);
- parser.Start();
-
- return true;
-}
-
-bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper,
- void** elf_header) {
- int obj_fd = open(obj_file.c_str(), O_RDONLY);
- if (obj_fd < 0) {
- fprintf(stderr, "Failed to open ELF file '%s': %s\n",
- obj_file.c_str(), strerror(errno));
- return false;
- }
- FDWrapper obj_fd_wrapper(obj_fd);
- struct stat st;
- if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) {
- fprintf(stderr, "Unable to fstat ELF file '%s': %s\n",
- obj_file.c_str(), strerror(errno));
- return false;
- }
- // Mapping it read-only is good enough. In any case, mapping it
- // read-write confuses Valgrind's debuginfo acquire/discard
- // heuristics, making it hard to profile the profiler.
- void *obj_base = mmap(nullptr, st.st_size,
- PROT_READ, MAP_PRIVATE, obj_fd, 0);
- if (obj_base == MAP_FAILED) {
- fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
- obj_file.c_str(), strerror(errno));
- return false;
- }
- map_wrapper->set(obj_base, st.st_size);
- *elf_header = obj_base;
- if (!IsValidElf(*elf_header)) {
- fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
- return false;
- }
- return true;
-}
-
-// Get the endianness of ELF_HEADER. If it's invalid, return false.
-template<typename ElfClass>
-bool ElfEndianness(const typename ElfClass::Ehdr* elf_header,
- bool* big_endian) {
- if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) {
- *big_endian = false;
- return true;
- }
- if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) {
- *big_endian = true;
- return true;
- }
-
- fprintf(stderr, "bad data encoding in ELF header: %d\n",
- elf_header->e_ident[EI_DATA]);
- return false;
-}
-
-//
-// LoadSymbolsInfo
-//
-// Holds the state between the two calls to LoadSymbols() in case it's necessary
-// to follow the .gnu_debuglink section and load debug information from a
-// different file.
-//
-template<typename ElfClass>
-class LoadSymbolsInfo {
- public:
- typedef typename ElfClass::Addr Addr;
-
- explicit LoadSymbolsInfo(const vector<string>& dbg_dirs) :
- debug_dirs_(dbg_dirs),
- has_loading_addr_(false) {}
-
- // Keeps track of which sections have been loaded so sections don't
- // accidentally get loaded twice from two different files.
- void LoadedSection(const string &section) {
- if (loaded_sections_.count(section) == 0) {
- loaded_sections_.insert(section);
- } else {
- fprintf(stderr, "Section %s has already been loaded.\n",
- section.c_str());
- }
- }
-
- string debuglink_file() const {
- return debuglink_file_;
- }
-
- private:
- const vector<string>& debug_dirs_; // Directories in which to
- // search for the debug ELF file.
-
- string debuglink_file_; // Full path to the debug ELF file.
-
- bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid.
-
- set<string> loaded_sections_; // Tracks the Loaded ELF sections
- // between calls to LoadSymbols().
-};
-
-// Find the preferred loading address of the binary.
-template<typename ElfClass>
-typename ElfClass::Addr GetLoadingAddress(
- const typename ElfClass::Phdr* program_headers,
- int nheader) {
- typedef typename ElfClass::Phdr Phdr;
-
- // For non-PIC executables (e_type == ET_EXEC), the load address is
- // the start address of the first PT_LOAD segment. (ELF requires
- // the segments to be sorted by load address.) For PIC executables
- // and dynamic libraries (e_type == ET_DYN), this address will
- // normally be zero.
- for (int i = 0; i < nheader; ++i) {
- const Phdr& header = program_headers[i];
- if (header.p_type == PT_LOAD)
- return header.p_vaddr;
- }
- return 0;
-}
-
-template<typename ElfClass>
-bool LoadSymbols(const string& obj_file,
- const bool big_endian,
- const typename ElfClass::Ehdr* elf_header,
- const bool read_gnu_debug_link,
- LoadSymbolsInfo<ElfClass>* info,
- SecMap* smap,
- void* rx_avma, size_t rx_size,
- UniqueStringUniverse* usu,
- void (*log)(const char*)) {
- typedef typename ElfClass::Phdr Phdr;
- typedef typename ElfClass::Shdr Shdr;
-
- char buf[500];
- SprintfLiteral(buf, "LoadSymbols: BEGIN %s\n", obj_file.c_str());
- buf[sizeof(buf)-1] = 0;
- log(buf);
-
- // This is how the text bias is calculated.
- // BEGIN CALCULATE BIAS
- uintptr_t loading_addr = GetLoadingAddress<ElfClass>(
- GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff),
- elf_header->e_phnum);
- uintptr_t text_bias = ((uintptr_t)rx_avma) - loading_addr;
- SprintfLiteral(buf,
- "LoadSymbols: rx_avma=%llx, text_bias=%llx",
- (unsigned long long int)(uintptr_t)rx_avma,
- (unsigned long long int)text_bias);
- buf[sizeof(buf)-1] = 0;
- log(buf);
- // END CALCULATE BIAS
-
- const Shdr* sections =
- GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
- const Shdr* section_names = sections + elf_header->e_shstrndx;
- const char* names =
- GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
- const char *names_end = names + section_names->sh_size;
- bool found_usable_info = false;
-
- // Dwarf Call Frame Information (CFI) is actually independent from
- // the other DWARF debugging information, and can be used alone.
- const Shdr* dwarf_cfi_section =
- FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS,
- sections, names, names_end,
- elf_header->e_shnum);
- if (dwarf_cfi_section) {
- // Ignore the return value of this function; even without call frame
- // information, the other debugging information could be perfectly
- // useful.
- info->LoadedSection(".debug_frame");
- bool result =
- LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".debug_frame",
- dwarf_cfi_section, false, 0, 0, big_endian,
- smap, text_bias, usu, log);
- found_usable_info = found_usable_info || result;
- if (result)
- log("LoadSymbols: read CFI from .debug_frame");
- }
-
- // Linux C++ exception handling information can also provide
- // unwinding data.
- const Shdr* eh_frame_section =
- FindElfSectionByName<ElfClass>(".eh_frame", SHT_PROGBITS,
- sections, names, names_end,
- elf_header->e_shnum);
- if (eh_frame_section) {
- // Pointers in .eh_frame data may be relative to the base addresses of
- // certain sections. Provide those sections if present.
- const Shdr* got_section =
- FindElfSectionByName<ElfClass>(".got", SHT_PROGBITS,
- sections, names, names_end,
- elf_header->e_shnum);
- const Shdr* text_section =
- FindElfSectionByName<ElfClass>(".text", SHT_PROGBITS,
- sections, names, names_end,
- elf_header->e_shnum);
- info->LoadedSection(".eh_frame");
- // As above, ignore the return value of this function.
- bool result =
- LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame",
- eh_frame_section, true,
- got_section, text_section, big_endian,
- smap, text_bias, usu, log);
- found_usable_info = found_usable_info || result;
- if (result)
- log("LoadSymbols: read CFI from .eh_frame");
- }
-
- SprintfLiteral(buf, "LoadSymbols: END %s\n", obj_file.c_str());
- buf[sizeof(buf)-1] = 0;
- log(buf);
-
- return found_usable_info;
-}
-
-// Return the breakpad symbol file identifier for the architecture of
-// ELF_HEADER.
-template<typename ElfClass>
-const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
- typedef typename ElfClass::Half Half;
- Half arch = elf_header->e_machine;
- switch (arch) {
- case EM_386: return "x86";
- case EM_ARM: return "arm";
- case EM_MIPS: return "mips";
- case EM_PPC64: return "ppc64";
- case EM_PPC: return "ppc";
- case EM_S390: return "s390";
- case EM_SPARC: return "sparc";
- case EM_SPARCV9: return "sparcv9";
- case EM_X86_64: return "x86_64";
- default: return NULL;
- }
-}
-
-// Format the Elf file identifier in IDENTIFIER as a UUID with the
-// dashes removed.
-string FormatIdentifier(unsigned char identifier[16]) {
- char identifier_str[40];
- lul::FileID::ConvertIdentifierToString(
- identifier,
- identifier_str,
- sizeof(identifier_str));
- string id_no_dash;
- for (int i = 0; identifier_str[i] != '\0'; ++i)
- if (identifier_str[i] != '-')
- id_no_dash += identifier_str[i];
- // Add an extra "0" by the end. PDB files on Windows have an 'age'
- // number appended to the end of the file identifier; this isn't
- // really used or necessary on other platforms, but be consistent.
- id_no_dash += '0';
- return id_no_dash;
-}
-
-// Return the non-directory portion of FILENAME: the portion after the
-// last slash, or the whole filename if there are no slashes.
-string BaseFileName(const string &filename) {
- // Lots of copies! basename's behavior is less than ideal.
- char *c_filename = strdup(filename.c_str());
- string base = basename(c_filename);
- free(c_filename);
- return base;
-}
-
-template<typename ElfClass>
-bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
- const string& obj_filename,
- const vector<string>& debug_dirs,
- SecMap* smap, void* rx_avma, size_t rx_size,
- UniqueStringUniverse* usu,
- void (*log)(const char*)) {
- typedef typename ElfClass::Ehdr Ehdr;
-
- unsigned char identifier[16];
- if (!lul
- ::FileID::ElfFileIdentifierFromMappedFile(elf_header, identifier)) {
- fprintf(stderr, "%s: unable to generate file identifier\n",
- obj_filename.c_str());
- return false;
- }
-
- const char *architecture = ElfArchitecture<ElfClass>(elf_header);
- if (!architecture) {
- fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
- obj_filename.c_str(), elf_header->e_machine);
- return false;
- }
-
- // Figure out what endianness this file is.
- bool big_endian;
- if (!ElfEndianness<ElfClass>(elf_header, &big_endian))
- return false;
-
- string name = BaseFileName(obj_filename);
- string os = "Linux";
- string id = FormatIdentifier(identifier);
-
- LoadSymbolsInfo<ElfClass> info(debug_dirs);
- if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header,
- !debug_dirs.empty(), &info,
- smap, rx_avma, rx_size, usu, log)) {
- const string debuglink_file = info.debuglink_file();
- if (debuglink_file.empty())
- return false;
-
- // Load debuglink ELF file.
- fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str());
- MmapWrapper debug_map_wrapper;
- Ehdr* debug_elf_header = NULL;
- if (!LoadELF(debuglink_file, &debug_map_wrapper,
- reinterpret_cast<void**>(&debug_elf_header)))
- return false;
- // Sanity checks to make sure everything matches up.
- const char *debug_architecture =
- ElfArchitecture<ElfClass>(debug_elf_header);
- if (!debug_architecture) {
- fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
- debuglink_file.c_str(), debug_elf_header->e_machine);
- return false;
- }
- if (strcmp(architecture, debug_architecture)) {
- fprintf(stderr, "%s with ELF machine architecture %s does not match "
- "%s with ELF architecture %s\n",
- debuglink_file.c_str(), debug_architecture,
- obj_filename.c_str(), architecture);
- return false;
- }
-
- bool debug_big_endian;
- if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian))
- return false;
- if (debug_big_endian != big_endian) {
- fprintf(stderr, "%s and %s does not match in endianness\n",
- obj_filename.c_str(), debuglink_file.c_str());
- return false;
- }
-
- if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian,
- debug_elf_header, false, &info,
- smap, rx_avma, rx_size, usu, log)) {
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace (anon)
-
-
-namespace lul {
-
-bool ReadSymbolDataInternal(const uint8_t* obj_file,
- const string& obj_filename,
- const vector<string>& debug_dirs,
- SecMap* smap, void* rx_avma, size_t rx_size,
- UniqueStringUniverse* usu,
- void (*log)(const char*)) {
-
- if (!IsValidElf(obj_file)) {
- fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str());
- return false;
- }
-
- int elfclass = ElfClass(obj_file);
- if (elfclass == ELFCLASS32) {
- return ReadSymbolDataElfClass<ElfClass32>(
- reinterpret_cast<const Elf32_Ehdr*>(obj_file),
- obj_filename, debug_dirs, smap, rx_avma, rx_size, usu, log);
- }
- if (elfclass == ELFCLASS64) {
- return ReadSymbolDataElfClass<ElfClass64>(
- reinterpret_cast<const Elf64_Ehdr*>(obj_file),
- obj_filename, debug_dirs, smap, rx_avma, rx_size, usu, log);
- }
-
- return false;
-}
-
-bool ReadSymbolData(const string& obj_file,
- const vector<string>& debug_dirs,
- SecMap* smap, void* rx_avma, size_t rx_size,
- UniqueStringUniverse* usu,
- void (*log)(const char*)) {
- MmapWrapper map_wrapper;
- void* elf_header = NULL;
- if (!LoadELF(obj_file, &map_wrapper, &elf_header))
- return false;
-
- return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header),
- obj_file, debug_dirs,
- smap, rx_avma, rx_size, usu, log);
-}
-
-
-namespace {
-
-template<typename ElfClass>
-void FindElfClassSection(const char *elf_base,
- const char *section_name,
- typename ElfClass::Word section_type,
- const void **section_start,
- int *section_size) {
- typedef typename ElfClass::Ehdr Ehdr;
- typedef typename ElfClass::Shdr Shdr;
-
- MOZ_ASSERT(elf_base);
- MOZ_ASSERT(section_start);
- MOZ_ASSERT(section_size);
-
- MOZ_ASSERT(strncmp(elf_base, ELFMAG, SELFMAG) == 0);
-
- const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
- MOZ_ASSERT(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
-
- const Shdr* sections =
- GetOffset<ElfClass,Shdr>(elf_header, elf_header->e_shoff);
- const Shdr* section_names = sections + elf_header->e_shstrndx;
- const char* names =
- GetOffset<ElfClass,char>(elf_header, section_names->sh_offset);
- const char *names_end = names + section_names->sh_size;
-
- const Shdr* section =
- FindElfSectionByName<ElfClass>(section_name, section_type,
- sections, names, names_end,
- elf_header->e_shnum);
-
- if (section != NULL && section->sh_size > 0) {
- *section_start = elf_base + section->sh_offset;
- *section_size = section->sh_size;
- }
-}
-
-template<typename ElfClass>
-void FindElfClassSegment(const char *elf_base,
- typename ElfClass::Word segment_type,
- const void **segment_start,
- int *segment_size) {
- typedef typename ElfClass::Ehdr Ehdr;
- typedef typename ElfClass::Phdr Phdr;
-
- MOZ_ASSERT(elf_base);
- MOZ_ASSERT(segment_start);
- MOZ_ASSERT(segment_size);
-
- MOZ_ASSERT(strncmp(elf_base, ELFMAG, SELFMAG) == 0);
-
- const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
- MOZ_ASSERT(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
-
- const Phdr* phdrs =
- GetOffset<ElfClass,Phdr>(elf_header, elf_header->e_phoff);
-
- for (int i = 0; i < elf_header->e_phnum; ++i) {
- if (phdrs[i].p_type == segment_type) {
- *segment_start = elf_base + phdrs[i].p_offset;
- *segment_size = phdrs[i].p_filesz;
- return;
- }
- }
-}
-
-} // namespace (anon)
-
-bool IsValidElf(const void* elf_base) {
- return strncmp(reinterpret_cast<const char*>(elf_base),
- ELFMAG, SELFMAG) == 0;
-}
-
-int ElfClass(const void* elf_base) {
- const ElfW(Ehdr)* elf_header =
- reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
-
- return elf_header->e_ident[EI_CLASS];
-}
-
-bool FindElfSection(const void *elf_mapped_base,
- const char *section_name,
- uint32_t section_type,
- const void **section_start,
- int *section_size,
- int *elfclass) {
- MOZ_ASSERT(elf_mapped_base);
- MOZ_ASSERT(section_start);
- MOZ_ASSERT(section_size);
-
- *section_start = NULL;
- *section_size = 0;
-
- if (!IsValidElf(elf_mapped_base))
- return false;
-
- int cls = ElfClass(elf_mapped_base);
- if (elfclass) {
- *elfclass = cls;
- }
-
- const char* elf_base =
- static_cast<const char*>(elf_mapped_base);
-
- if (cls == ELFCLASS32) {
- FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
- section_start, section_size);
- return *section_start != NULL;
- } else if (cls == ELFCLASS64) {
- FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
- section_start, section_size);
- return *section_start != NULL;
- }
-
- return false;
-}
-
-bool FindElfSegment(const void *elf_mapped_base,
- uint32_t segment_type,
- const void **segment_start,
- int *segment_size,
- int *elfclass) {
- MOZ_ASSERT(elf_mapped_base);
- MOZ_ASSERT(segment_start);
- MOZ_ASSERT(segment_size);
-
- *segment_start = NULL;
- *segment_size = 0;
-
- if (!IsValidElf(elf_mapped_base))
- return false;
-
- int cls = ElfClass(elf_mapped_base);
- if (elfclass) {
- *elfclass = cls;
- }
-
- const char* elf_base =
- static_cast<const char*>(elf_mapped_base);
-
- if (cls == ELFCLASS32) {
- FindElfClassSegment<ElfClass32>(elf_base, segment_type,
- segment_start, segment_size);
- return *segment_start != NULL;
- } else if (cls == ELFCLASS64) {
- FindElfClassSegment<ElfClass64>(elf_base, segment_type,
- segment_start, segment_size);
- return *segment_start != NULL;
- }
-
- return false;
-}
-
-
-// (derived from)
-// file_id.cc: Return a unique identifier for a file
-//
-// See file_id.h for documentation
-//
-
-// ELF note name and desc are 32-bits word padded.
-#define NOTE_PADDING(a) ((a + 3) & ~3)
-
-// These functions are also used inside the crashed process, so be safe
-// and use the syscall/libc wrappers instead of direct syscalls or libc.
-
-template<typename ElfClass>
-static bool ElfClassBuildIDNoteIdentifier(const void *section, int length,
- uint8_t identifier[kMDGUIDSize]) {
- typedef typename ElfClass::Nhdr Nhdr;
-
- const void* section_end = reinterpret_cast<const char*>(section) + length;
- const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
- while (reinterpret_cast<const void *>(note_header) < section_end) {
- if (note_header->n_type == NT_GNU_BUILD_ID)
- break;
- note_header = reinterpret_cast<const Nhdr*>(
- reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
- NOTE_PADDING(note_header->n_namesz) +
- NOTE_PADDING(note_header->n_descsz));
- }
- if (reinterpret_cast<const void *>(note_header) >= section_end ||
- note_header->n_descsz == 0) {
- return false;
- }
-
- const char* build_id = reinterpret_cast<const char*>(note_header) +
- sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz);
- // Copy as many bits of the build ID as will fit
- // into the GUID space.
- memset(identifier, 0, kMDGUIDSize);
- memcpy(identifier, build_id,
- std::min(kMDGUIDSize, (size_t)note_header->n_descsz));
-
- return true;
-}
-
-// Attempt to locate a .note.gnu.build-id section in an ELF binary
-// and copy as many bytes of it as will fit into |identifier|.
-static bool FindElfBuildIDNote(const void *elf_mapped_base,
- uint8_t identifier[kMDGUIDSize]) {
- void* note_section;
- int note_size, elfclass;
- if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
- (const void**)&note_section, &note_size, &elfclass) ||
- note_size == 0) &&
- (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
- (const void**)&note_section, &note_size, &elfclass) ||
- note_size == 0)) {
- return false;
- }
-
- if (elfclass == ELFCLASS32) {
- return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size,
- identifier);
- } else if (elfclass == ELFCLASS64) {
- return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, note_size,
- identifier);
- }
-
- return false;
-}
-
-// Attempt to locate the .text section of an ELF binary and generate
-// a simple hash by XORing the first page worth of bytes into |identifier|.
-static bool HashElfTextSection(const void *elf_mapped_base,
- uint8_t identifier[kMDGUIDSize]) {
- void* text_section;
- int text_size;
- if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
- (const void**)&text_section, &text_size, NULL) ||
- text_size == 0) {
- return false;
- }
-
- memset(identifier, 0, kMDGUIDSize);
- const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
- const uint8_t* ptr_end = ptr + std::min(text_size, 4096);
- while (ptr < ptr_end) {
- for (unsigned i = 0; i < kMDGUIDSize; i++)
- identifier[i] ^= ptr[i];
- ptr += kMDGUIDSize;
- }
- return true;
-}
-
-// static
-bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
- uint8_t identifier[kMDGUIDSize]) {
- // Look for a build id note first.
- if (FindElfBuildIDNote(base, identifier))
- return true;
-
- // Fall back on hashing the first page of the text section.
- return HashElfTextSection(base, identifier);
-}
-
-// static
-void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
- char* buffer, int buffer_length) {
- uint8_t identifier_swapped[kMDGUIDSize];
-
- // Endian-ness swap to match dump processor expectation.
- memcpy(identifier_swapped, identifier, kMDGUIDSize);
- uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
- *data1 = htonl(*data1);
- uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
- *data2 = htons(*data2);
- uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
- *data3 = htons(*data3);
-
- int buffer_idx = 0;
- for (unsigned int idx = 0;
- (buffer_idx < buffer_length) && (idx < kMDGUIDSize);
- ++idx) {
- int hi = (identifier_swapped[idx] >> 4) & 0x0F;
- int lo = (identifier_swapped[idx]) & 0x0F;
-
- if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
- buffer[buffer_idx++] = '-';
-
- buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
- buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
- }
-
- // NULL terminate
- buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0;
-}
-
-} // namespace lul
diff --git a/tools/profiler/lul/LulMain.cpp b/tools/profiler/lul/LulMain.cpp
deleted file mode 100644
index 2e78f03ec..000000000
--- a/tools/profiler/lul/LulMain.cpp
+++ /dev/null
@@ -1,1963 +0,0 @@
-/* -*- 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 "LulMain.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <algorithm> // std::sort
-#include <string>
-
-#include "mozilla/Assertions.h"
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/CheckedInt.h"
-#include "mozilla/DebugOnly.h"
-#include "mozilla/MemoryChecking.h"
-#include "mozilla/Sprintf.h"
-
-#include "LulCommonExt.h"
-#include "LulElfExt.h"
-
-#include "LulMainInt.h"
-
-#include "platform-linux-lul.h" // for gettid()
-
-// Set this to 1 for verbose logging
-#define DEBUG_MAIN 0
-
-namespace lul {
-
-using std::string;
-using std::vector;
-using std::pair;
-using mozilla::CheckedInt;
-using mozilla::DebugOnly;
-
-
-// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
-//
-// Some functions in this file are marked RUNS IN NO-MALLOC CONTEXT.
-// Any such function -- and, hence, the transitive closure of those
-// reachable from it -- must not do any dynamic memory allocation.
-// Doing so risks deadlock. There is exactly one root function for
-// the transitive closure: Lul::Unwind.
-//
-// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
-
-
-////////////////////////////////////////////////////////////////
-// RuleSet //
-////////////////////////////////////////////////////////////////
-
-static const char*
-NameOf_DW_REG(int16_t aReg)
-{
- switch (aReg) {
- case DW_REG_CFA: return "cfa";
-#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- case DW_REG_INTEL_XBP: return "xbp";
- case DW_REG_INTEL_XSP: return "xsp";
- case DW_REG_INTEL_XIP: return "xip";
-#elif defined(LUL_ARCH_arm)
- case DW_REG_ARM_R7: return "r7";
- case DW_REG_ARM_R11: return "r11";
- case DW_REG_ARM_R12: return "r12";
- case DW_REG_ARM_R13: return "r13";
- case DW_REG_ARM_R14: return "r14";
- case DW_REG_ARM_R15: return "r15";
-#else
-# error "Unsupported arch"
-#endif
- default: return "???";
- }
-}
-
-string
-LExpr::ShowRule(const char* aNewReg) const
-{
- char buf[64];
- string res = string(aNewReg) + "=";
- switch (mHow) {
- case UNKNOWN:
- res += "Unknown";
- break;
- case NODEREF:
- SprintfLiteral(buf, "%s+%d",
- NameOf_DW_REG(mReg), (int)mOffset);
- res += buf;
- break;
- case DEREF:
- SprintfLiteral(buf, "*(%s+%d)",
- NameOf_DW_REG(mReg), (int)mOffset);
- res += buf;
- break;
- case PFXEXPR:
- SprintfLiteral(buf, "PfxExpr-at-%d", (int)mOffset);
- res += buf;
- break;
- default:
- res += "???";
- break;
- }
- return res;
-}
-
-void
-RuleSet::Print(void(*aLog)(const char*)) const
-{
- char buf[96];
- SprintfLiteral(buf, "[%llx .. %llx]: let ",
- (unsigned long long int)mAddr,
- (unsigned long long int)(mAddr + mLen - 1));
- string res = string(buf);
- res += mCfaExpr.ShowRule("cfa");
- res += " in";
- // For each reg we care about, print the recovery expression.
-#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- res += mXipExpr.ShowRule(" RA");
- res += mXspExpr.ShowRule(" SP");
- res += mXbpExpr.ShowRule(" BP");
-#elif defined(LUL_ARCH_arm)
- res += mR15expr.ShowRule(" R15");
- res += mR7expr .ShowRule(" R7" );
- res += mR11expr.ShowRule(" R11");
- res += mR12expr.ShowRule(" R12");
- res += mR13expr.ShowRule(" R13");
- res += mR14expr.ShowRule(" R14");
-#else
-# error "Unsupported arch"
-#endif
- aLog(res.c_str());
-}
-
-LExpr*
-RuleSet::ExprForRegno(DW_REG_NUMBER aRegno) {
- switch (aRegno) {
- case DW_REG_CFA: return &mCfaExpr;
-# if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- case DW_REG_INTEL_XIP: return &mXipExpr;
- case DW_REG_INTEL_XSP: return &mXspExpr;
- case DW_REG_INTEL_XBP: return &mXbpExpr;
-# elif defined(LUL_ARCH_arm)
- case DW_REG_ARM_R15: return &mR15expr;
- case DW_REG_ARM_R14: return &mR14expr;
- case DW_REG_ARM_R13: return &mR13expr;
- case DW_REG_ARM_R12: return &mR12expr;
- case DW_REG_ARM_R11: return &mR11expr;
- case DW_REG_ARM_R7: return &mR7expr;
-# else
-# error "Unknown arch"
-# endif
- default: return nullptr;
- }
-}
-
-RuleSet::RuleSet()
-{
- mAddr = 0;
- mLen = 0;
- // The only other fields are of type LExpr and those are initialised
- // by LExpr::LExpr().
-}
-
-
-////////////////////////////////////////////////////////////////
-// SecMap //
-////////////////////////////////////////////////////////////////
-
-// See header file LulMainInt.h for comments about invariants.
-
-SecMap::SecMap(void(*aLog)(const char*))
- : mSummaryMinAddr(1)
- , mSummaryMaxAddr(0)
- , mUsable(true)
- , mLog(aLog)
-{}
-
-SecMap::~SecMap() {
- mRuleSets.clear();
-}
-
-// RUNS IN NO-MALLOC CONTEXT
-RuleSet*
-SecMap::FindRuleSet(uintptr_t ia) {
- // Binary search mRuleSets to find one that brackets |ia|.
- // lo and hi need to be signed, else the loop termination tests
- // don't work properly. Note that this works correctly even when
- // mRuleSets.size() == 0.
-
- // Can't do this until the array has been sorted and preened.
- MOZ_ASSERT(mUsable);
-
- long int lo = 0;
- long int hi = (long int)mRuleSets.size() - 1;
- while (true) {
- // current unsearched space is from lo to hi, inclusive.
- if (lo > hi) {
- // not found
- return nullptr;
- }
- long int mid = lo + ((hi - lo) / 2);
- RuleSet* mid_ruleSet = &mRuleSets[mid];
- uintptr_t mid_minAddr = mid_ruleSet->mAddr;
- uintptr_t mid_maxAddr = mid_minAddr + mid_ruleSet->mLen - 1;
- if (ia < mid_minAddr) { hi = mid-1; continue; }
- if (ia > mid_maxAddr) { lo = mid+1; continue; }
- MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr);
- return mid_ruleSet;
- }
- // NOTREACHED
-}
-
-// Add a RuleSet to the collection. The rule is copied in. Calling
-// this makes the map non-searchable.
-void
-SecMap::AddRuleSet(const RuleSet* rs) {
- mUsable = false;
- mRuleSets.push_back(*rs);
-}
-
-// Add a PfxInstr to the vector of such instrs, and return the index
-// in the vector. Calling this makes the map non-searchable.
-uint32_t
-SecMap::AddPfxInstr(PfxInstr pfxi) {
- mUsable = false;
- mPfxInstrs.push_back(pfxi);
- return mPfxInstrs.size() - 1;
-}
-
-
-static bool
-CmpRuleSetsByAddrLE(const RuleSet& rs1, const RuleSet& rs2) {
- return rs1.mAddr < rs2.mAddr;
-}
-
-// Prepare the map for searching. Completely remove any which don't
-// fall inside the specified range [start, +len).
-void
-SecMap::PrepareRuleSets(uintptr_t aStart, size_t aLen)
-{
- if (mRuleSets.empty()) {
- return;
- }
-
- MOZ_ASSERT(aLen > 0);
- if (aLen == 0) {
- // This should never happen.
- mRuleSets.clear();
- return;
- }
-
- // Sort by start addresses.
- std::sort(mRuleSets.begin(), mRuleSets.end(), CmpRuleSetsByAddrLE);
-
- // Detect any entry not completely contained within [start, +len).
- // Set its length to zero, so that the next pass will remove it.
- for (size_t i = 0; i < mRuleSets.size(); ++i) {
- RuleSet* rs = &mRuleSets[i];
- if (rs->mLen > 0 &&
- (rs->mAddr < aStart || rs->mAddr + rs->mLen > aStart + aLen)) {
- rs->mLen = 0;
- }
- }
-
- // Iteratively truncate any overlaps and remove any zero length
- // entries that might result, or that may have been present
- // initially. Unless the input is seriously screwy, this is
- // expected to iterate only once.
- while (true) {
- size_t i;
- size_t n = mRuleSets.size();
- size_t nZeroLen = 0;
-
- if (n == 0) {
- break;
- }
-
- for (i = 1; i < n; ++i) {
- RuleSet* prev = &mRuleSets[i-1];
- RuleSet* here = &mRuleSets[i];
- MOZ_ASSERT(prev->mAddr <= here->mAddr);
- if (prev->mAddr + prev->mLen > here->mAddr) {
- prev->mLen = here->mAddr - prev->mAddr;
- }
- if (prev->mLen == 0)
- nZeroLen++;
- }
-
- if (mRuleSets[n-1].mLen == 0) {
- nZeroLen++;
- }
-
- // At this point, the entries are in-order and non-overlapping.
- // If none of them are zero-length, we are done.
- if (nZeroLen == 0) {
- break;
- }
-
- // Slide back the entries to remove the zero length ones.
- size_t j = 0; // The write-point.
- for (i = 0; i < n; ++i) {
- if (mRuleSets[i].mLen == 0) {
- continue;
- }
- if (j != i) mRuleSets[j] = mRuleSets[i];
- ++j;
- }
- MOZ_ASSERT(i == n);
- MOZ_ASSERT(nZeroLen <= n);
- MOZ_ASSERT(j == n - nZeroLen);
- while (nZeroLen > 0) {
- mRuleSets.pop_back();
- nZeroLen--;
- }
-
- MOZ_ASSERT(mRuleSets.size() == j);
- }
-
- size_t n = mRuleSets.size();
-
-#ifdef DEBUG
- // Do a final check on the rules: their address ranges must be
- // ascending, non overlapping, non zero sized.
- if (n > 0) {
- MOZ_ASSERT(mRuleSets[0].mLen > 0);
- for (size_t i = 1; i < n; ++i) {
- RuleSet* prev = &mRuleSets[i-1];
- RuleSet* here = &mRuleSets[i];
- MOZ_ASSERT(prev->mAddr < here->mAddr);
- MOZ_ASSERT(here->mLen > 0);
- MOZ_ASSERT(prev->mAddr + prev->mLen <= here->mAddr);
- }
- }
-#endif
-
- // Set the summary min and max address values.
- if (n == 0) {
- // Use the values defined in comments in the class declaration.
- mSummaryMinAddr = 1;
- mSummaryMaxAddr = 0;
- } else {
- mSummaryMinAddr = mRuleSets[0].mAddr;
- mSummaryMaxAddr = mRuleSets[n-1].mAddr + mRuleSets[n-1].mLen - 1;
- }
- char buf[150];
- SprintfLiteral(buf,
- "PrepareRuleSets: %d entries, smin/smax 0x%llx, 0x%llx\n",
- (int)n, (unsigned long long int)mSummaryMinAddr,
- (unsigned long long int)mSummaryMaxAddr);
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
-
- // Is now usable for binary search.
- mUsable = true;
-
- if (0) {
- mLog("\nRulesets after preening\n");
- for (size_t i = 0; i < mRuleSets.size(); ++i) {
- mRuleSets[i].Print(mLog);
- mLog("\n");
- }
- mLog("\n");
- }
-}
-
-bool SecMap::IsEmpty() {
- return mRuleSets.empty();
-}
-
-
-////////////////////////////////////////////////////////////////
-// SegArray //
-////////////////////////////////////////////////////////////////
-
-// A SegArray holds a set of address ranges that together exactly
-// cover an address range, with no overlaps or holes. Each range has
-// an associated value, which in this case has been specialised to be
-// a simple boolean. The representation is kept to minimal canonical
-// form in which adjacent ranges with the same associated value are
-// merged together. Each range is represented by a |struct Seg|.
-//
-// SegArrays are used to keep track of which parts of the address
-// space are known to contain instructions.
-class SegArray {
-
- public:
- void add(uintptr_t lo, uintptr_t hi, bool val) {
- if (lo > hi) {
- return;
- }
- split_at(lo);
- if (hi < UINTPTR_MAX) {
- split_at(hi+1);
- }
- std::vector<Seg>::size_type iLo, iHi, i;
- iLo = find(lo);
- iHi = find(hi);
- for (i = iLo; i <= iHi; ++i) {
- mSegs[i].val = val;
- }
- preen();
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- bool getBoundingCodeSegment(/*OUT*/uintptr_t* rx_min,
- /*OUT*/uintptr_t* rx_max, uintptr_t addr) {
- std::vector<Seg>::size_type i = find(addr);
- if (!mSegs[i].val) {
- return false;
- }
- *rx_min = mSegs[i].lo;
- *rx_max = mSegs[i].hi;
- return true;
- }
-
- SegArray() {
- Seg s(0, UINTPTR_MAX, false);
- mSegs.push_back(s);
- }
-
- private:
- struct Seg {
- Seg(uintptr_t lo, uintptr_t hi, bool val) : lo(lo), hi(hi), val(val) {}
- uintptr_t lo;
- uintptr_t hi;
- bool val;
- };
-
- void preen() {
- for (std::vector<Seg>::iterator iter = mSegs.begin();
- iter < mSegs.end()-1;
- ++iter) {
- if (iter[0].val != iter[1].val) {
- continue;
- }
- iter[0].hi = iter[1].hi;
- mSegs.erase(iter+1);
- // Back up one, so as not to miss an opportunity to merge
- // with the entry after this one.
- --iter;
- }
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- std::vector<Seg>::size_type find(uintptr_t a) {
- long int lo = 0;
- long int hi = (long int)mSegs.size();
- while (true) {
- // The unsearched space is lo .. hi inclusive.
- if (lo > hi) {
- // Not found. This can't happen.
- return (std::vector<Seg>::size_type)(-1);
- }
- long int mid = lo + ((hi - lo) / 2);
- uintptr_t mid_lo = mSegs[mid].lo;
- uintptr_t mid_hi = mSegs[mid].hi;
- if (a < mid_lo) { hi = mid-1; continue; }
- if (a > mid_hi) { lo = mid+1; continue; }
- return (std::vector<Seg>::size_type)mid;
- }
- }
-
- void split_at(uintptr_t a) {
- std::vector<Seg>::size_type i = find(a);
- if (mSegs[i].lo == a) {
- return;
- }
- mSegs.insert( mSegs.begin()+i+1, mSegs[i] );
- mSegs[i].hi = a-1;
- mSegs[i+1].lo = a;
- }
-
- void show() {
- printf("<< %d entries:\n", (int)mSegs.size());
- for (std::vector<Seg>::iterator iter = mSegs.begin();
- iter < mSegs.end();
- ++iter) {
- printf(" %016llx %016llx %s\n",
- (unsigned long long int)(*iter).lo,
- (unsigned long long int)(*iter).hi,
- (*iter).val ? "true" : "false");
- }
- printf(">>\n");
- }
-
- std::vector<Seg> mSegs;
-};
-
-
-////////////////////////////////////////////////////////////////
-// PriMap //
-////////////////////////////////////////////////////////////////
-
-class PriMap {
- public:
- explicit PriMap(void (*aLog)(const char*))
- : mLog(aLog)
- {}
-
- ~PriMap() {
- for (std::vector<SecMap*>::iterator iter = mSecMaps.begin();
- iter != mSecMaps.end();
- ++iter) {
- delete *iter;
- }
- mSecMaps.clear();
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- pair<const RuleSet*, const vector<PfxInstr>*>
- Lookup(uintptr_t ia)
- {
- SecMap* sm = FindSecMap(ia);
- return pair<const RuleSet*, const vector<PfxInstr>*>
- (sm ? sm->FindRuleSet(ia) : nullptr,
- sm ? sm->GetPfxInstrs() : nullptr);
- }
-
- // Add a secondary map. No overlaps allowed w.r.t. existing
- // secondary maps.
- void AddSecMap(SecMap* aSecMap) {
- // We can't add an empty SecMap to the PriMap. But that's OK
- // since we'd never be able to find anything in it anyway.
- if (aSecMap->IsEmpty()) {
- return;
- }
-
- // Iterate through the SecMaps and find the right place for this
- // one. At the same time, ensure that the in-order
- // non-overlapping invariant is preserved (and, generally, holds).
- // FIXME: this gives a cost that is O(N^2) in the total number of
- // shared objects in the system. ToDo: better.
- MOZ_ASSERT(aSecMap->mSummaryMinAddr <= aSecMap->mSummaryMaxAddr);
-
- size_t num_secMaps = mSecMaps.size();
- uintptr_t i;
- for (i = 0; i < num_secMaps; ++i) {
- SecMap* sm_i = mSecMaps[i];
- MOZ_ASSERT(sm_i->mSummaryMinAddr <= sm_i->mSummaryMaxAddr);
- if (aSecMap->mSummaryMinAddr < sm_i->mSummaryMaxAddr) {
- // |aSecMap| needs to be inserted immediately before mSecMaps[i].
- break;
- }
- }
- MOZ_ASSERT(i <= num_secMaps);
- if (i == num_secMaps) {
- // It goes at the end.
- mSecMaps.push_back(aSecMap);
- } else {
- std::vector<SecMap*>::iterator iter = mSecMaps.begin() + i;
- mSecMaps.insert(iter, aSecMap);
- }
- char buf[100];
- SprintfLiteral(buf, "AddSecMap: now have %d SecMaps\n",
- (int)mSecMaps.size());
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
- }
-
- // Remove and delete any SecMaps in the mapping, that intersect
- // with the specified address range.
- void RemoveSecMapsInRange(uintptr_t avma_min, uintptr_t avma_max) {
- MOZ_ASSERT(avma_min <= avma_max);
- size_t num_secMaps = mSecMaps.size();
- if (num_secMaps > 0) {
- intptr_t i;
- // Iterate from end to start over the vector, so as to ensure
- // that the special case where |avma_min| and |avma_max| denote
- // the entire address space, can be completed in time proportional
- // to the number of elements in the map.
- for (i = (intptr_t)num_secMaps-1; i >= 0; i--) {
- SecMap* sm_i = mSecMaps[i];
- if (sm_i->mSummaryMaxAddr < avma_min ||
- avma_max < sm_i->mSummaryMinAddr) {
- // There's no overlap. Move on.
- continue;
- }
- // We need to remove mSecMaps[i] and slide all those above it
- // downwards to cover the hole.
- mSecMaps.erase(mSecMaps.begin() + i);
- delete sm_i;
- }
- }
- }
-
- // Return the number of currently contained SecMaps.
- size_t CountSecMaps() {
- return mSecMaps.size();
- }
-
- // Assess heuristically whether the given address is an instruction
- // immediately following a call instruction.
- // RUNS IN NO-MALLOC CONTEXT
- bool MaybeIsReturnPoint(TaggedUWord aInstrAddr, SegArray* aSegArray) {
- if (!aInstrAddr.Valid()) {
- return false;
- }
-
- uintptr_t ia = aInstrAddr.Value();
-
- // Assume that nobody would be crazy enough to put code in the
- // first or last page.
- if (ia < 4096 || ((uintptr_t)(-ia)) < 4096) {
- return false;
- }
-
- // See if it falls inside a known r-x mapped area. Poking around
- // outside such places risks segfaulting.
- uintptr_t insns_min, insns_max;
- bool b = aSegArray->getBoundingCodeSegment(&insns_min, &insns_max, ia);
- if (!b) {
- // no code (that we know about) at this address
- return false;
- }
-
- // |ia| falls within an r-x range. So we can
- // safely poke around in [insns_min, insns_max].
-
-#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- // Is the previous instruction recognisably a CALL? This is
- // common for the 32- and 64-bit versions, except for the
- // simm32(%rip) case, which is 64-bit only.
- //
- // For all other cases, the 64 bit versions are either identical
- // to the 32 bit versions, or have an optional extra leading REX.W
- // byte (0x41). Since the extra 0x41 is optional we have to
- // ignore it, with the convenient result that the same matching
- // logic works for both 32- and 64-bit cases.
-
- uint8_t* p = (uint8_t*)ia;
-# if defined(LUL_ARCH_x64)
- // CALL simm32(%rip) == FF15 simm32
- if (ia - 6 >= insns_min && p[-6] == 0xFF && p[-5] == 0x15) {
- return true;
- }
-# endif
- // CALL rel32 == E8 rel32 (both 32- and 64-bit)
- if (ia - 5 >= insns_min && p[-5] == 0xE8) {
- return true;
- }
- // CALL *%eax .. CALL *%edi == FFD0 .. FFD7 (32-bit)
- // CALL *%rax .. CALL *%rdi == FFD0 .. FFD7 (64-bit)
- // CALL *%r8 .. CALL *%r15 == 41FFD0 .. 41FFD7 (64-bit)
- if (ia - 2 >= insns_min &&
- p[-2] == 0xFF && p[-1] >= 0xD0 && p[-1] <= 0xD7) {
- return true;
- }
- // Almost all of the remaining cases that occur in practice are
- // of the form CALL *simm8(reg) or CALL *simm32(reg).
- //
- // 64 bit cases:
- //
- // call *simm8(%rax) FF50 simm8
- // call *simm8(%rcx) FF51 simm8
- // call *simm8(%rdx) FF52 simm8
- // call *simm8(%rbx) FF53 simm8
- // call *simm8(%rsp) FF5424 simm8
- // call *simm8(%rbp) FF55 simm8
- // call *simm8(%rsi) FF56 simm8
- // call *simm8(%rdi) FF57 simm8
- //
- // call *simm8(%r8) 41FF50 simm8
- // call *simm8(%r9) 41FF51 simm8
- // call *simm8(%r10) 41FF52 simm8
- // call *simm8(%r11) 41FF53 simm8
- // call *simm8(%r12) 41FF5424 simm8
- // call *simm8(%r13) 41FF55 simm8
- // call *simm8(%r14) 41FF56 simm8
- // call *simm8(%r15) 41FF57 simm8
- //
- // call *simm32(%rax) FF90 simm32
- // call *simm32(%rcx) FF91 simm32
- // call *simm32(%rdx) FF92 simm32
- // call *simm32(%rbx) FF93 simm32
- // call *simm32(%rsp) FF9424 simm32
- // call *simm32(%rbp) FF95 simm32
- // call *simm32(%rsi) FF96 simm32
- // call *simm32(%rdi) FF97 simm32
- //
- // call *simm32(%r8) 41FF90 simm32
- // call *simm32(%r9) 41FF91 simm32
- // call *simm32(%r10) 41FF92 simm32
- // call *simm32(%r11) 41FF93 simm32
- // call *simm32(%r12) 41FF9424 simm32
- // call *simm32(%r13) 41FF95 simm32
- // call *simm32(%r14) 41FF96 simm32
- // call *simm32(%r15) 41FF97 simm32
- //
- // 32 bit cases:
- //
- // call *simm8(%eax) FF50 simm8
- // call *simm8(%ecx) FF51 simm8
- // call *simm8(%edx) FF52 simm8
- // call *simm8(%ebx) FF53 simm8
- // call *simm8(%esp) FF5424 simm8
- // call *simm8(%ebp) FF55 simm8
- // call *simm8(%esi) FF56 simm8
- // call *simm8(%edi) FF57 simm8
- //
- // call *simm32(%eax) FF90 simm32
- // call *simm32(%ecx) FF91 simm32
- // call *simm32(%edx) FF92 simm32
- // call *simm32(%ebx) FF93 simm32
- // call *simm32(%esp) FF9424 simm32
- // call *simm32(%ebp) FF95 simm32
- // call *simm32(%esi) FF96 simm32
- // call *simm32(%edi) FF97 simm32
- if (ia - 3 >= insns_min &&
- p[-3] == 0xFF &&
- (p[-2] >= 0x50 && p[-2] <= 0x57 && p[-2] != 0x54)) {
- // imm8 case, not including %esp/%rsp
- return true;
- }
- if (ia - 4 >= insns_min &&
- p[-4] == 0xFF && p[-3] == 0x54 && p[-2] == 0x24) {
- // imm8 case for %esp/%rsp
- return true;
- }
- if (ia - 6 >= insns_min &&
- p[-6] == 0xFF &&
- (p[-5] >= 0x90 && p[-5] <= 0x97 && p[-5] != 0x94)) {
- // imm32 case, not including %esp/%rsp
- return true;
- }
- if (ia - 7 >= insns_min &&
- p[-7] == 0xFF && p[-6] == 0x94 && p[-5] == 0x24) {
- // imm32 case for %esp/%rsp
- return true;
- }
-
-#elif defined(LUL_ARCH_arm)
- if (ia & 1) {
- uint16_t w0 = 0, w1 = 0;
- // The return address has its lowest bit set, indicating a return
- // to Thumb code.
- ia &= ~(uintptr_t)1;
- if (ia - 2 >= insns_min && ia - 1 <= insns_max) {
- w1 = *(uint16_t*)(ia - 2);
- }
- if (ia - 4 >= insns_min && ia - 1 <= insns_max) {
- w0 = *(uint16_t*)(ia - 4);
- }
- // Is it a 32-bit Thumb call insn?
- // BL simm26 (Encoding T1)
- if ((w0 & 0xF800) == 0xF000 && (w1 & 0xC000) == 0xC000) {
- return true;
- }
- // BLX simm26 (Encoding T2)
- if ((w0 & 0xF800) == 0xF000 && (w1 & 0xC000) == 0xC000) {
- return true;
- }
- // Other possible cases:
- // (BLX Rm, Encoding T1).
- // BLX Rm (encoding T1, 16 bit, inspect w1 and ignore w0.)
- // 0100 0111 1 Rm 000
- } else {
- // Returning to ARM code.
- uint32_t a0 = 0;
- if ((ia & 3) == 0 && ia - 4 >= insns_min && ia - 1 <= insns_max) {
- a0 = *(uint32_t*)(ia - 4);
- }
- // Leading E forces unconditional only -- fix. It could be
- // anything except F, which is the deprecated NV code.
- // BL simm26 (Encoding A1)
- if ((a0 & 0xFF000000) == 0xEB000000) {
- return true;
- }
- // Other possible cases:
- // BLX simm26 (Encoding A2)
- //if ((a0 & 0xFE000000) == 0xFA000000)
- // return true;
- // BLX (register) (A1): BLX <c> <Rm>
- // cond 0001 0010 1111 1111 1111 0011 Rm
- // again, cond can be anything except NV (0xF)
- }
-
-#else
-# error "Unsupported arch"
-#endif
-
- // Not an insn we recognise.
- return false;
- }
-
- private:
- // RUNS IN NO-MALLOC CONTEXT
- SecMap* FindSecMap(uintptr_t ia) {
- // Binary search mSecMaps to find one that brackets |ia|.
- // lo and hi need to be signed, else the loop termination tests
- // don't work properly.
- long int lo = 0;
- long int hi = (long int)mSecMaps.size() - 1;
- while (true) {
- // current unsearched space is from lo to hi, inclusive.
- if (lo > hi) {
- // not found
- return nullptr;
- }
- long int mid = lo + ((hi - lo) / 2);
- SecMap* mid_secMap = mSecMaps[mid];
- uintptr_t mid_minAddr = mid_secMap->mSummaryMinAddr;
- uintptr_t mid_maxAddr = mid_secMap->mSummaryMaxAddr;
- if (ia < mid_minAddr) { hi = mid-1; continue; }
- if (ia > mid_maxAddr) { lo = mid+1; continue; }
- MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr);
- return mid_secMap;
- }
- // NOTREACHED
- }
-
- private:
- // sorted array of per-object ranges, non overlapping, non empty
- std::vector<SecMap*> mSecMaps;
-
- // a logging sink, for debugging.
- void (*mLog)(const char*);
-};
-
-
-////////////////////////////////////////////////////////////////
-// LUL //
-////////////////////////////////////////////////////////////////
-
-#define LUL_LOG(_str) \
- do { \
- char buf[200]; \
- SprintfLiteral(buf, \
- "LUL: pid %d tid %d lul-obj %p: %s", \
- getpid(), gettid(), this, (_str)); \
- buf[sizeof(buf)-1] = 0; \
- mLog(buf); \
- } while (0)
-
-LUL::LUL(void (*aLog)(const char*))
- : mLog(aLog)
- , mAdminMode(true)
- , mAdminThreadId(gettid())
- , mPriMap(new PriMap(aLog))
- , mSegArray(new SegArray())
- , mUSU(new UniqueStringUniverse())
-{
- LUL_LOG("LUL::LUL: Created object");
-}
-
-
-LUL::~LUL()
-{
- LUL_LOG("LUL::~LUL: Destroyed object");
- delete mPriMap;
- delete mSegArray;
- mLog = nullptr;
- delete mUSU;
-}
-
-
-void
-LUL::MaybeShowStats()
-{
- // This is racey in the sense that it can't guarantee that
- // n_new == n_new_Context + n_new_CFI + n_new_Scanned
- // if it should happen that mStats is updated by some other thread
- // in between computation of n_new and n_new_{Context,CFI,Scanned}.
- // But it's just stats printing, so we don't really care.
- uint32_t n_new = mStats - mStatsPrevious;
- if (n_new >= 5000) {
- uint32_t n_new_Context = mStats.mContext - mStatsPrevious.mContext;
- uint32_t n_new_CFI = mStats.mCFI - mStatsPrevious.mCFI;
- uint32_t n_new_Scanned = mStats.mScanned - mStatsPrevious.mScanned;
- mStatsPrevious = mStats;
- char buf[200];
- SprintfLiteral(buf,
- "LUL frame stats: TOTAL %5u"
- " CTX %4u CFI %4u SCAN %4u",
- n_new, n_new_Context, n_new_CFI, n_new_Scanned);
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
- }
-}
-
-
-void
-LUL::EnableUnwinding()
-{
- LUL_LOG("LUL::EnableUnwinding");
- // Don't assert for Admin mode here. That is, tolerate a call here
- // if we are already in Unwinding mode.
- MOZ_ASSERT(gettid() == mAdminThreadId);
-
- mAdminMode = false;
-}
-
-
-void
-LUL::NotifyAfterMap(uintptr_t aRXavma, size_t aSize,
- const char* aFileName, const void* aMappedImage)
-{
- MOZ_ASSERT(mAdminMode);
- MOZ_ASSERT(gettid() == mAdminThreadId);
-
- mLog(":\n");
- char buf[200];
- SprintfLiteral(buf, "NotifyMap %llx %llu %s\n",
- (unsigned long long int)aRXavma, (unsigned long long int)aSize,
- aFileName);
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
-
- // Ignore obviously-stupid notifications.
- if (aSize > 0) {
-
- // Here's a new mapping, for this object.
- SecMap* smap = new SecMap(mLog);
-
- // Read CFI or EXIDX unwind data into |smap|.
- if (!aMappedImage) {
- (void)lul::ReadSymbolData(
- string(aFileName), std::vector<string>(), smap,
- (void*)aRXavma, aSize, mUSU, mLog);
- } else {
- (void)lul::ReadSymbolDataInternal(
- (const uint8_t*)aMappedImage,
- string(aFileName), std::vector<string>(), smap,
- (void*)aRXavma, aSize, mUSU, mLog);
- }
-
- mLog("NotifyMap .. preparing entries\n");
-
- smap->PrepareRuleSets(aRXavma, aSize);
-
- SprintfLiteral(buf,
- "NotifyMap got %lld entries\n", (long long int)smap->Size());
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
-
- // Add it to the primary map (the top level set of mapped objects).
- mPriMap->AddSecMap(smap);
-
- // Tell the segment array about the mapping, so that the stack
- // scan and __kernel_syscall mechanisms know where valid code is.
- mSegArray->add(aRXavma, aRXavma + aSize - 1, true);
- }
-}
-
-
-void
-LUL::NotifyExecutableArea(uintptr_t aRXavma, size_t aSize)
-{
- MOZ_ASSERT(mAdminMode);
- MOZ_ASSERT(gettid() == mAdminThreadId);
-
- mLog(":\n");
- char buf[200];
- SprintfLiteral(buf, "NotifyExecutableArea %llx %llu\n",
- (unsigned long long int)aRXavma, (unsigned long long int)aSize);
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
-
- // Ignore obviously-stupid notifications.
- if (aSize > 0) {
- // Tell the segment array about the mapping, so that the stack
- // scan and __kernel_syscall mechanisms know where valid code is.
- mSegArray->add(aRXavma, aRXavma + aSize - 1, true);
- }
-}
-
-
-void
-LUL::NotifyBeforeUnmap(uintptr_t aRXavmaMin, uintptr_t aRXavmaMax)
-{
- MOZ_ASSERT(mAdminMode);
- MOZ_ASSERT(gettid() == mAdminThreadId);
-
- mLog(":\n");
- char buf[100];
- SprintfLiteral(buf, "NotifyUnmap %016llx-%016llx\n",
- (unsigned long long int)aRXavmaMin,
- (unsigned long long int)aRXavmaMax);
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
-
- MOZ_ASSERT(aRXavmaMin <= aRXavmaMax);
-
- // Remove from the primary map, any secondary maps that intersect
- // with the address range. Also delete the secondary maps.
- mPriMap->RemoveSecMapsInRange(aRXavmaMin, aRXavmaMax);
-
- // Tell the segment array that the address range no longer
- // contains valid code.
- mSegArray->add(aRXavmaMin, aRXavmaMax, false);
-
- SprintfLiteral(buf, "NotifyUnmap: now have %d SecMaps\n",
- (int)mPriMap->CountSecMaps());
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
-}
-
-
-size_t
-LUL::CountMappings()
-{
- MOZ_ASSERT(mAdminMode);
- MOZ_ASSERT(gettid() == mAdminThreadId);
-
- return mPriMap->CountSecMaps();
-}
-
-
-// RUNS IN NO-MALLOC CONTEXT
-static
-TaggedUWord DerefTUW(TaggedUWord aAddr, const StackImage* aStackImg)
-{
- if (!aAddr.Valid()) {
- return TaggedUWord();
- }
-
- // Lower limit check. |aAddr.Value()| is the lowest requested address
- // and |aStackImg->mStartAvma| is the lowest address we actually have,
- // so the comparison is straightforward.
- if (aAddr.Value() < aStackImg->mStartAvma) {
- return TaggedUWord();
- }
-
- // Upper limit check. We must compute the highest requested address
- // and the highest address we actually have, but being careful to
- // avoid overflow. In particular if |aAddr| is 0xFFF...FFF or the
- // 3/7 values below that, then we will get overflow. See bug #1245477.
- typedef CheckedInt<uintptr_t> CheckedUWord;
- CheckedUWord highest_requested_plus_one
- = CheckedUWord(aAddr.Value()) + CheckedUWord(sizeof(uintptr_t));
- CheckedUWord highest_available_plus_one
- = CheckedUWord(aStackImg->mStartAvma) + CheckedUWord(aStackImg->mLen);
- if (!highest_requested_plus_one.isValid() // overflow?
- || !highest_available_plus_one.isValid() // overflow?
- || (highest_requested_plus_one.value()
- > highest_available_plus_one.value())) { // in range?
- return TaggedUWord();
- }
-
- return TaggedUWord(*(uintptr_t*)(aStackImg->mContents + aAddr.Value()
- - aStackImg->mStartAvma));
-}
-
-// RUNS IN NO-MALLOC CONTEXT
-static
-TaggedUWord EvaluateReg(int16_t aReg, const UnwindRegs* aOldRegs,
- TaggedUWord aCFA)
-{
- switch (aReg) {
- case DW_REG_CFA: return aCFA;
-#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- case DW_REG_INTEL_XBP: return aOldRegs->xbp;
- case DW_REG_INTEL_XSP: return aOldRegs->xsp;
- case DW_REG_INTEL_XIP: return aOldRegs->xip;
-#elif defined(LUL_ARCH_arm)
- case DW_REG_ARM_R7: return aOldRegs->r7;
- case DW_REG_ARM_R11: return aOldRegs->r11;
- case DW_REG_ARM_R12: return aOldRegs->r12;
- case DW_REG_ARM_R13: return aOldRegs->r13;
- case DW_REG_ARM_R14: return aOldRegs->r14;
- case DW_REG_ARM_R15: return aOldRegs->r15;
-#else
-# error "Unsupported arch"
-#endif
- default: MOZ_ASSERT(0); return TaggedUWord();
- }
-}
-
-// RUNS IN NO-MALLOC CONTEXT
-// See prototype for comment.
-TaggedUWord EvaluatePfxExpr(int32_t start,
- const UnwindRegs* aOldRegs,
- TaggedUWord aCFA, const StackImage* aStackImg,
- const vector<PfxInstr>& aPfxInstrs)
-{
- // A small evaluation stack, and a stack pointer, which points to
- // the highest numbered in-use element.
- const int N_STACK = 10;
- TaggedUWord stack[N_STACK];
- int stackPointer = -1;
- for (int i = 0; i < N_STACK; i++)
- stack[i] = TaggedUWord();
-
-# define PUSH(_tuw) \
- do { \
- if (stackPointer >= N_STACK-1) goto fail; /* overflow */ \
- stack[++stackPointer] = (_tuw); \
- } while (0)
-
-# define POP(_lval) \
- do { \
- if (stackPointer < 0) goto fail; /* underflow */ \
- _lval = stack[stackPointer--]; \
- } while (0)
-
- // Cursor in the instruction sequence.
- size_t curr = start + 1;
-
- // Check the start point is sane.
- size_t nInstrs = aPfxInstrs.size();
- if (start < 0 || (size_t)start >= nInstrs)
- goto fail;
-
- {
- // The instruction sequence must start with PX_Start. If not,
- // something is seriously wrong.
- PfxInstr first = aPfxInstrs[start];
- if (first.mOpcode != PX_Start)
- goto fail;
-
- // Push the CFA on the stack to start with (or not), as required by
- // the original DW_OP_*expression* CFI.
- if (first.mOperand != 0)
- PUSH(aCFA);
- }
-
- while (true) {
- if (curr >= nInstrs)
- goto fail; // ran off the end of the sequence
-
- PfxInstr pfxi = aPfxInstrs[curr++];
- if (pfxi.mOpcode == PX_End)
- break; // we're done
-
- switch (pfxi.mOpcode) {
- case PX_Start:
- // This should appear only at the start of the sequence.
- goto fail;
- case PX_End:
- // We just took care of that, so we shouldn't see it again.
- MOZ_ASSERT(0);
- goto fail;
- case PX_SImm32:
- PUSH(TaggedUWord((intptr_t)pfxi.mOperand));
- break;
- case PX_DwReg: {
- DW_REG_NUMBER reg = (DW_REG_NUMBER)pfxi.mOperand;
- MOZ_ASSERT(reg != DW_REG_CFA);
- PUSH(EvaluateReg(reg, aOldRegs, aCFA));
- break;
- }
- case PX_Deref: {
- TaggedUWord addr;
- POP(addr);
- PUSH(DerefTUW(addr, aStackImg));
- break;
- }
- case PX_Add: {
- TaggedUWord x, y;
- POP(x); POP(y); PUSH(y + x);
- break;
- }
- case PX_Sub: {
- TaggedUWord x, y;
- POP(x); POP(y); PUSH(y - x);
- break;
- }
- case PX_And: {
- TaggedUWord x, y;
- POP(x); POP(y); PUSH(y & x);
- break;
- }
- case PX_Or: {
- TaggedUWord x, y;
- POP(x); POP(y); PUSH(y | x);
- break;
- }
- case PX_CmpGES: {
- TaggedUWord x, y;
- POP(x); POP(y); PUSH(y.CmpGEs(x));
- break;
- }
- case PX_Shl: {
- TaggedUWord x, y;
- POP(x); POP(y); PUSH(y << x);
- break;
- }
- default:
- MOZ_ASSERT(0);
- goto fail;
- }
- } // while (true)
-
- // Evaluation finished. The top value on the stack is the result.
- if (stackPointer >= 0) {
- return stack[stackPointer];
- }
- // Else fall through
-
- fail:
- return TaggedUWord();
-
-# undef PUSH
-# undef POP
-}
-
-// RUNS IN NO-MALLOC CONTEXT
-TaggedUWord LExpr::EvaluateExpr(const UnwindRegs* aOldRegs,
- TaggedUWord aCFA, const StackImage* aStackImg,
- const vector<PfxInstr>* aPfxInstrs) const
-{
- switch (mHow) {
- case UNKNOWN:
- return TaggedUWord();
- case NODEREF: {
- TaggedUWord tuw = EvaluateReg(mReg, aOldRegs, aCFA);
- tuw = tuw + TaggedUWord((intptr_t)mOffset);
- return tuw;
- }
- case DEREF: {
- TaggedUWord tuw = EvaluateReg(mReg, aOldRegs, aCFA);
- tuw = tuw + TaggedUWord((intptr_t)mOffset);
- return DerefTUW(tuw, aStackImg);
- }
- case PFXEXPR: {
- MOZ_ASSERT(aPfxInstrs);
- if (!aPfxInstrs) {
- return TaggedUWord();
- }
- return EvaluatePfxExpr(mOffset, aOldRegs, aCFA, aStackImg, *aPfxInstrs);
- }
- default:
- MOZ_ASSERT(0);
- return TaggedUWord();
- }
-}
-
-// RUNS IN NO-MALLOC CONTEXT
-static
-void UseRuleSet(/*MOD*/UnwindRegs* aRegs,
- const StackImage* aStackImg, const RuleSet* aRS,
- const vector<PfxInstr>* aPfxInstrs)
-{
- // Take a copy of regs, since we'll need to refer to the old values
- // whilst computing the new ones.
- UnwindRegs old_regs = *aRegs;
-
- // Mark all the current register values as invalid, so that the
- // caller can see, on our return, which ones have been computed
- // anew. If we don't even manage to compute a new PC value, then
- // the caller will have to abandon the unwind.
- // FIXME: Create and use instead: aRegs->SetAllInvalid();
-#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- aRegs->xbp = TaggedUWord();
- aRegs->xsp = TaggedUWord();
- aRegs->xip = TaggedUWord();
-#elif defined(LUL_ARCH_arm)
- aRegs->r7 = TaggedUWord();
- aRegs->r11 = TaggedUWord();
- aRegs->r12 = TaggedUWord();
- aRegs->r13 = TaggedUWord();
- aRegs->r14 = TaggedUWord();
- aRegs->r15 = TaggedUWord();
-#else
-# error "Unsupported arch"
-#endif
-
- // This is generally useful.
- const TaggedUWord inval = TaggedUWord();
-
- // First, compute the CFA.
- TaggedUWord cfa
- = aRS->mCfaExpr.EvaluateExpr(&old_regs,
- inval/*old cfa*/, aStackImg, aPfxInstrs);
-
- // If we didn't manage to compute the CFA, well .. that's ungood,
- // but keep going anyway. It'll be OK provided none of the register
- // value rules mention the CFA. In any case, compute the new values
- // for each register that we're tracking.
-
-#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- aRegs->xbp
- = aRS->mXbpExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
- aRegs->xsp
- = aRS->mXspExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
- aRegs->xip
- = aRS->mXipExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
-#elif defined(LUL_ARCH_arm)
- aRegs->r7
- = aRS->mR7expr .EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
- aRegs->r11
- = aRS->mR11expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
- aRegs->r12
- = aRS->mR12expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
- aRegs->r13
- = aRS->mR13expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
- aRegs->r14
- = aRS->mR14expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
- aRegs->r15
- = aRS->mR15expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
-#else
-# error "Unsupported arch"
-#endif
-
- // We're done. Any regs for which we didn't manage to compute a
- // new value will now be marked as invalid.
-}
-
-// RUNS IN NO-MALLOC CONTEXT
-void
-LUL::Unwind(/*OUT*/uintptr_t* aFramePCs,
- /*OUT*/uintptr_t* aFrameSPs,
- /*OUT*/size_t* aFramesUsed,
- /*OUT*/size_t* aScannedFramesAcquired,
- size_t aFramesAvail,
- size_t aScannedFramesAllowed,
- UnwindRegs* aStartRegs, StackImage* aStackImg)
-{
- MOZ_ASSERT(!mAdminMode);
-
- /////////////////////////////////////////////////////////
- // BEGIN UNWIND
-
- *aFramesUsed = 0;
-
- UnwindRegs regs = *aStartRegs;
- TaggedUWord last_valid_sp = TaggedUWord();
-
- // Stack-scan control
- unsigned int n_scanned_frames = 0; // # s-s frames recovered so far
- static const int NUM_SCANNED_WORDS = 50; // max allowed scan length
-
- while (true) {
-
- if (DEBUG_MAIN) {
- char buf[300];
- mLog("\n");
-#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- SprintfLiteral(buf,
- "LoopTop: rip %d/%llx rsp %d/%llx rbp %d/%llx\n",
- (int)regs.xip.Valid(), (unsigned long long int)regs.xip.Value(),
- (int)regs.xsp.Valid(), (unsigned long long int)regs.xsp.Value(),
- (int)regs.xbp.Valid(), (unsigned long long int)regs.xbp.Value());
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
-#elif defined(LUL_ARCH_arm)
- SprintfLiteral(buf,
- "LoopTop: r15 %d/%llx r7 %d/%llx r11 %d/%llx"
- " r12 %d/%llx r13 %d/%llx r14 %d/%llx\n",
- (int)regs.r15.Valid(), (unsigned long long int)regs.r15.Value(),
- (int)regs.r7.Valid(), (unsigned long long int)regs.r7.Value(),
- (int)regs.r11.Valid(), (unsigned long long int)regs.r11.Value(),
- (int)regs.r12.Valid(), (unsigned long long int)regs.r12.Value(),
- (int)regs.r13.Valid(), (unsigned long long int)regs.r13.Value(),
- (int)regs.r14.Valid(), (unsigned long long int)regs.r14.Value());
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
-#else
-# error "Unsupported arch"
-#endif
- }
-
-#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- TaggedUWord ia = regs.xip;
- TaggedUWord sp = regs.xsp;
-#elif defined(LUL_ARCH_arm)
- TaggedUWord ia = (*aFramesUsed == 0 ? regs.r15 : regs.r14);
- TaggedUWord sp = regs.r13;
-#else
-# error "Unsupported arch"
-#endif
-
- if (*aFramesUsed >= aFramesAvail) {
- break;
- }
-
- // If we don't have a valid value for the PC, give up.
- if (!ia.Valid()) {
- break;
- }
-
- // If this is the innermost frame, record the SP value, which
- // presumably is valid. If this isn't the innermost frame, and we
- // have a valid SP value, check that its SP value isn't less that
- // the one we've seen so far, so as to catch potential SP value
- // cycles.
- if (*aFramesUsed == 0) {
- last_valid_sp = sp;
- } else {
- MOZ_ASSERT(last_valid_sp.Valid());
- if (sp.Valid()) {
- if (sp.Value() < last_valid_sp.Value()) {
- // Hmm, SP going in the wrong direction. Let's stop.
- break;
- }
- // Remember where we got to.
- last_valid_sp = sp;
- }
- }
-
- // For the innermost frame, the IA value is what we need. For all
- // other frames, it's actually the return address, so back up one
- // byte so as to get it into the calling instruction.
- aFramePCs[*aFramesUsed] = ia.Value() - (*aFramesUsed == 0 ? 0 : 1);
- aFrameSPs[*aFramesUsed] = sp.Valid() ? sp.Value() : 0;
- (*aFramesUsed)++;
-
- // Find the RuleSet for the current IA, if any. This will also
- // query the backing (secondary) maps if it isn't found in the
- // thread-local cache.
-
- // If this isn't the innermost frame, back up into the calling insn.
- if (*aFramesUsed > 1) {
- ia = ia + TaggedUWord((uintptr_t)(-1));
- }
-
- pair<const RuleSet*, const vector<PfxInstr>*> ruleset_and_pfxinstrs
- = mPriMap->Lookup(ia.Value());
- const RuleSet* ruleset = ruleset_and_pfxinstrs.first;
- const vector<PfxInstr>* pfxinstrs = ruleset_and_pfxinstrs.second;
-
- if (DEBUG_MAIN) {
- char buf[100];
- SprintfLiteral(buf, "ruleset for 0x%llx = %p\n",
- (unsigned long long int)ia.Value(), ruleset);
- buf[sizeof(buf)-1] = 0;
- mLog(buf);
- }
-
- /////////////////////////////////////////////
- ////
- // On 32 bit x86-linux, syscalls are often done via the VDSO
- // function __kernel_vsyscall, which doesn't have a corresponding
- // object that we can read debuginfo from. That effectively kills
- // off all stack traces for threads blocked in syscalls. Hence
- // special-case by looking at the code surrounding the program
- // counter.
- //
- // 0xf7757420 <__kernel_vsyscall+0>: push %ecx
- // 0xf7757421 <__kernel_vsyscall+1>: push %edx
- // 0xf7757422 <__kernel_vsyscall+2>: push %ebp
- // 0xf7757423 <__kernel_vsyscall+3>: mov %esp,%ebp
- // 0xf7757425 <__kernel_vsyscall+5>: sysenter
- // 0xf7757427 <__kernel_vsyscall+7>: nop
- // 0xf7757428 <__kernel_vsyscall+8>: nop
- // 0xf7757429 <__kernel_vsyscall+9>: nop
- // 0xf775742a <__kernel_vsyscall+10>: nop
- // 0xf775742b <__kernel_vsyscall+11>: nop
- // 0xf775742c <__kernel_vsyscall+12>: nop
- // 0xf775742d <__kernel_vsyscall+13>: nop
- // 0xf775742e <__kernel_vsyscall+14>: int $0x80
- // 0xf7757430 <__kernel_vsyscall+16>: pop %ebp
- // 0xf7757431 <__kernel_vsyscall+17>: pop %edx
- // 0xf7757432 <__kernel_vsyscall+18>: pop %ecx
- // 0xf7757433 <__kernel_vsyscall+19>: ret
- //
- // In cases where the sampled thread is blocked in a syscall, its
- // program counter will point at "pop %ebp". Hence we look for
- // the sequence "int $0x80; pop %ebp; pop %edx; pop %ecx; ret", and
- // the corresponding register-recovery actions are:
- // new_ebp = *(old_esp + 0)
- // new eip = *(old_esp + 12)
- // new_esp = old_esp + 16
- //
- // It may also be the case that the program counter points two
- // nops before the "int $0x80", viz, is __kernel_vsyscall+12, in
- // the case where the syscall has been restarted but the thread
- // hasn't been rescheduled. The code below doesn't handle that;
- // it could easily be made to.
- //
-#if defined(LUL_PLAT_x86_android) || defined(LUL_PLAT_x86_linux)
- if (!ruleset && *aFramesUsed == 1 && ia.Valid() && sp.Valid()) {
- uintptr_t insns_min, insns_max;
- uintptr_t eip = ia.Value();
- bool b = mSegArray->getBoundingCodeSegment(&insns_min, &insns_max, eip);
- if (b && eip - 2 >= insns_min && eip + 3 <= insns_max) {
- uint8_t* eipC = (uint8_t*)eip;
- if (eipC[-2] == 0xCD && eipC[-1] == 0x80 && eipC[0] == 0x5D &&
- eipC[1] == 0x5A && eipC[2] == 0x59 && eipC[3] == 0xC3) {
- TaggedUWord sp_plus_0 = sp;
- TaggedUWord sp_plus_12 = sp;
- TaggedUWord sp_plus_16 = sp;
- sp_plus_12 = sp_plus_12 + TaggedUWord(12);
- sp_plus_16 = sp_plus_16 + TaggedUWord(16);
- TaggedUWord new_ebp = DerefTUW(sp_plus_0, aStackImg);
- TaggedUWord new_eip = DerefTUW(sp_plus_12, aStackImg);
- TaggedUWord new_esp = sp_plus_16;
- if (new_ebp.Valid() && new_eip.Valid() && new_esp.Valid()) {
- regs.xbp = new_ebp;
- regs.xip = new_eip;
- regs.xsp = new_esp;
- continue;
- }
- }
- }
- }
-#endif
- ////
- /////////////////////////////////////////////
-
- // So, do we have a ruleset for this address? If so, use it now.
- if (ruleset) {
-
- if (DEBUG_MAIN) {
- ruleset->Print(mLog); mLog("\n");
- }
- // Use the RuleSet to compute the registers for the previous
- // frame. |regs| is modified in-place.
- UseRuleSet(&regs, aStackImg, ruleset, pfxinstrs);
-
- } else {
-
- // There's no RuleSet for the specified address, so see if
- // it's possible to get anywhere by stack-scanning.
-
- // Use stack scanning frugally.
- if (n_scanned_frames++ >= aScannedFramesAllowed) {
- break;
- }
-
- // We can't scan the stack without a valid, aligned stack pointer.
- if (!sp.IsAligned()) {
- break;
- }
-
- bool scan_succeeded = false;
- for (int i = 0; i < NUM_SCANNED_WORDS; ++i) {
- TaggedUWord aWord = DerefTUW(sp, aStackImg);
- // aWord is something we fished off the stack. It should be
- // valid, unless we overran the stack bounds.
- if (!aWord.Valid()) {
- break;
- }
-
- // Now, does aWord point inside a text section and immediately
- // after something that looks like a call instruction?
- if (mPriMap->MaybeIsReturnPoint(aWord, mSegArray)) {
- // Yes it does. Update the unwound registers heuristically,
- // using the same schemes as Breakpad does.
- scan_succeeded = true;
- (*aScannedFramesAcquired)++;
-
-#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- // The same logic applies for the 32- and 64-bit cases.
- // Register names of the form xsp etc refer to (eg) esp in
- // the 32-bit case and rsp in the 64-bit case.
-# if defined(LUL_ARCH_x64)
- const int wordSize = 8;
-# else
- const int wordSize = 4;
-# endif
- // The return address -- at XSP -- will have been pushed by
- // the CALL instruction. So the caller's XSP value
- // immediately before and after that CALL instruction is the
- // word above XSP.
- regs.xsp = sp + TaggedUWord(wordSize);
-
- // aWord points at the return point, so back up one byte
- // to put it in the calling instruction.
- regs.xip = aWord + TaggedUWord((uintptr_t)(-1));
-
- // Computing a new value from the frame pointer is more tricky.
- if (regs.xbp.Valid() &&
- sp.Valid() && regs.xbp.Value() == sp.Value() - wordSize) {
- // One possibility is that the callee begins with the standard
- // preamble "push %xbp; mov %xsp, %xbp". In which case, the
- // (1) caller's XBP value will be at the word below XSP, and
- // (2) the current (callee's) XBP will point at that word:
- regs.xbp = DerefTUW(regs.xbp, aStackImg);
- } else if (regs.xbp.Valid() &&
- sp.Valid() && regs.xbp.Value() >= sp.Value() + wordSize) {
- // If that didn't work out, maybe the callee didn't change
- // XBP, so it still holds the caller's value. For that to
- // be plausible, XBP will need to have a value at least
- // higher than XSP since that holds the purported return
- // address. In which case do nothing, since XBP already
- // holds the "right" value.
- } else {
- // Mark XBP as invalid, so that subsequent unwind iterations
- // don't assume it holds valid data.
- regs.xbp = TaggedUWord();
- }
-
- // Move on to the next word up the stack
- sp = sp + TaggedUWord(wordSize);
-
-#elif defined(LUL_ARCH_arm)
- // Set all registers to be undefined, except for SP(R13) and
- // PC(R15).
-
- // aWord points either at the return point, if returning to
- // ARM code, or one insn past the return point if returning
- // to Thumb code. In both cases, aWord-2 is guaranteed to
- // fall within the calling instruction.
- regs.r15 = aWord + TaggedUWord((uintptr_t)(-2));
-
- // Make SP be the word above the location where the return
- // address was found.
- regs.r13 = sp + TaggedUWord(4);
-
- // All other regs are undefined.
- regs.r7 = regs.r11 = regs.r12 = regs.r14 = TaggedUWord();
-
- // Move on to the next word up the stack
- sp = sp + TaggedUWord(4);
-
-#else
-# error "Unknown plat"
-#endif
-
- break;
- }
-
- } // for (int i = 0; i < NUM_SCANNED_WORDS; i++)
-
- // We tried to make progress by scanning the stack, but failed.
- // So give up -- fall out of the top level unwind loop.
- if (!scan_succeeded) {
- break;
- }
- }
-
- } // top level unwind loop
-
- // END UNWIND
- /////////////////////////////////////////////////////////
-}
-
-
-////////////////////////////////////////////////////////////////
-// LUL Unit Testing //
-////////////////////////////////////////////////////////////////
-
-static const int LUL_UNIT_TEST_STACK_SIZE = 16384;
-
-// This function is innermost in the test call sequence. It uses LUL
-// to unwind, and compares the result with the sequence specified in
-// the director string. These need to agree in order for the test to
-// pass. In order not to screw up the results, this function needs
-// to have a not-very big stack frame, since we're only presenting
-// the innermost LUL_UNIT_TEST_STACK_SIZE bytes of stack to LUL, and
-// that chunk unavoidably includes the frame for this function.
-//
-// This function must not be inlined into its callers. Doing so will
-// cause the expected-vs-actual backtrace consistency checking to
-// fail. Prints summary results to |aLUL|'s logging sink and also
-// returns a boolean indicating whether or not the test passed.
-static __attribute__((noinline))
-bool GetAndCheckStackTrace(LUL* aLUL, const char* dstring)
-{
- // Get hold of the current unwind-start registers.
- UnwindRegs startRegs;
- memset(&startRegs, 0, sizeof(startRegs));
-#if defined(LUL_PLAT_x64_linux)
- volatile uintptr_t block[3];
- MOZ_ASSERT(sizeof(block) == 24);
- __asm__ __volatile__(
- "leaq 0(%%rip), %%r15" "\n\t"
- "movq %%r15, 0(%0)" "\n\t"
- "movq %%rsp, 8(%0)" "\n\t"
- "movq %%rbp, 16(%0)" "\n"
- : : "r"(&block[0]) : "memory", "r15"
- );
- startRegs.xip = TaggedUWord(block[0]);
- startRegs.xsp = TaggedUWord(block[1]);
- startRegs.xbp = TaggedUWord(block[2]);
- const uintptr_t REDZONE_SIZE = 128;
- uintptr_t start = block[1] - REDZONE_SIZE;
-#elif defined(LUL_PLAT_x86_linux) || defined(LUL_PLAT_x86_android)
- volatile uintptr_t block[3];
- MOZ_ASSERT(sizeof(block) == 12);
- __asm__ __volatile__(
- ".byte 0xE8,0x00,0x00,0x00,0x00"/*call next insn*/ "\n\t"
- "popl %%edi" "\n\t"
- "movl %%edi, 0(%0)" "\n\t"
- "movl %%esp, 4(%0)" "\n\t"
- "movl %%ebp, 8(%0)" "\n"
- : : "r"(&block[0]) : "memory", "edi"
- );
- startRegs.xip = TaggedUWord(block[0]);
- startRegs.xsp = TaggedUWord(block[1]);
- startRegs.xbp = TaggedUWord(block[2]);
- const uintptr_t REDZONE_SIZE = 0;
- uintptr_t start = block[1] - REDZONE_SIZE;
-#elif defined(LUL_PLAT_arm_android)
- volatile uintptr_t block[6];
- MOZ_ASSERT(sizeof(block) == 24);
- __asm__ __volatile__(
- "mov r0, r15" "\n\t"
- "str r0, [%0, #0]" "\n\t"
- "str r14, [%0, #4]" "\n\t"
- "str r13, [%0, #8]" "\n\t"
- "str r12, [%0, #12]" "\n\t"
- "str r11, [%0, #16]" "\n\t"
- "str r7, [%0, #20]" "\n"
- : : "r"(&block[0]) : "memory", "r0"
- );
- startRegs.r15 = TaggedUWord(block[0]);
- startRegs.r14 = TaggedUWord(block[1]);
- startRegs.r13 = TaggedUWord(block[2]);
- startRegs.r12 = TaggedUWord(block[3]);
- startRegs.r11 = TaggedUWord(block[4]);
- startRegs.r7 = TaggedUWord(block[5]);
- const uintptr_t REDZONE_SIZE = 0;
- uintptr_t start = block[1] - REDZONE_SIZE;
-#else
-# error "Unsupported platform"
-#endif
-
- // Get hold of the innermost LUL_UNIT_TEST_STACK_SIZE bytes of the
- // stack.
- uintptr_t end = start + LUL_UNIT_TEST_STACK_SIZE;
- uintptr_t ws = sizeof(void*);
- start &= ~(ws-1);
- end &= ~(ws-1);
- uintptr_t nToCopy = end - start;
- if (nToCopy > lul::N_STACK_BYTES) {
- nToCopy = lul::N_STACK_BYTES;
- }
- MOZ_ASSERT(nToCopy <= lul::N_STACK_BYTES);
- StackImage* stackImg = new StackImage();
- stackImg->mLen = nToCopy;
- stackImg->mStartAvma = start;
- if (nToCopy > 0) {
- MOZ_MAKE_MEM_DEFINED((void*)start, nToCopy);
- memcpy(&stackImg->mContents[0], (void*)start, nToCopy);
- }
-
- // Unwind it.
- const int MAX_TEST_FRAMES = 64;
- uintptr_t framePCs[MAX_TEST_FRAMES];
- uintptr_t frameSPs[MAX_TEST_FRAMES];
- size_t framesAvail = mozilla::ArrayLength(framePCs);
- size_t framesUsed = 0;
- size_t scannedFramesAllowed = 0;
- size_t scannedFramesAcquired = 0;
- aLUL->Unwind( &framePCs[0], &frameSPs[0],
- &framesUsed, &scannedFramesAcquired,
- framesAvail, scannedFramesAllowed,
- &startRegs, stackImg );
-
- delete stackImg;
-
- //if (0) {
- // // Show what we have.
- // fprintf(stderr, "Got %d frames:\n", (int)framesUsed);
- // for (size_t i = 0; i < framesUsed; i++) {
- // fprintf(stderr, " [%2d] SP %p PC %p\n",
- // (int)i, (void*)frameSPs[i], (void*)framePCs[i]);
- // }
- // fprintf(stderr, "\n");
- //}
-
- // Check to see if there's a consistent binding between digits in
- // the director string ('1' .. '8') and the PC values acquired by
- // the unwind. If there isn't, the unwinding has failed somehow.
- uintptr_t binding[8]; // binding for '1' .. binding for '8'
- memset((void*)binding, 0, sizeof(binding));
-
- // The general plan is to work backwards along the director string
- // and forwards along the framePCs array. Doing so corresponds to
- // working outwards from the innermost frame of the recursive test set.
- const char* cursor = dstring;
-
- // Find the end. This leaves |cursor| two bytes past the first
- // character we want to look at -- see comment below.
- while (*cursor) cursor++;
-
- // Counts the number of consistent frames.
- size_t nConsistent = 0;
-
- // Iterate back to the start of the director string. The starting
- // points are a bit complex. We can't use framePCs[0] because that
- // contains the PC in this frame (above). We can't use framePCs[1]
- // because that will contain the PC at return point in the recursive
- // test group (TestFn[1-8]) for their call "out" to this function,
- // GetAndCheckStackTrace. Although LUL will compute a correct
- // return address, that will not be the same return address as for a
- // recursive call out of the the function to another function in the
- // group. Hence we can only start consistency checking at
- // framePCs[2].
- //
- // To be consistent, then, we must ignore the last element in the
- // director string as that corresponds to framePCs[1]. Hence the
- // start points are: framePCs[2] and the director string 2 bytes
- // before the terminating zero.
- //
- // Also as a result of this, the number of consistent frames counted
- // will always be one less than the length of the director string
- // (not including its terminating zero).
- size_t frameIx;
- for (cursor = cursor-2, frameIx = 2;
- cursor >= dstring && frameIx < framesUsed;
- cursor--, frameIx++) {
- char c = *cursor;
- uintptr_t pc = framePCs[frameIx];
- // If this doesn't hold, the director string is ill-formed.
- MOZ_ASSERT(c >= '1' && c <= '8');
- int n = ((int)c) - ((int)'1');
- if (binding[n] == 0) {
- // There's no binding for |c| yet, so install |pc| and carry on.
- binding[n] = pc;
- nConsistent++;
- continue;
- }
- // There's a pre-existing binding for |c|. Check it's consistent.
- if (binding[n] != pc) {
- // Not consistent. Give up now.
- break;
- }
- // Consistent. Keep going.
- nConsistent++;
- }
-
- // So, did we succeed?
- bool passed = nConsistent+1 == strlen(dstring);
-
- // Show the results.
- char buf[200];
- SprintfLiteral(buf, "LULUnitTest: dstring = %s\n", dstring);
- buf[sizeof(buf)-1] = 0;
- aLUL->mLog(buf);
- SprintfLiteral(buf,
- "LULUnitTest: %d consistent, %d in dstring: %s\n",
- (int)nConsistent, (int)strlen(dstring),
- passed ? "PASS" : "FAIL");
- buf[sizeof(buf)-1] = 0;
- aLUL->mLog(buf);
-
- return passed;
-}
-
-
-// Macro magic to create a set of 8 mutually recursive functions with
-// varying frame sizes. These will recurse amongst themselves as
-// specified by |strP|, the directory string, and call
-// GetAndCheckStackTrace when the string becomes empty, passing it the
-// original value of the string. This checks the result, printing
-// results on |aLUL|'s logging sink, and also returns a boolean
-// indicating whether or not the results are acceptable (correct).
-
-#define DECL_TEST_FN(NAME) \
- bool NAME(LUL* aLUL, const char* strPorig, const char* strP);
-
-#define GEN_TEST_FN(NAME, FRAMESIZE) \
- bool NAME(LUL* aLUL, const char* strPorig, const char* strP) { \
- volatile char space[FRAMESIZE]; \
- memset((char*)&space[0], 0, sizeof(space)); \
- if (*strP == '\0') { \
- /* We've come to the end of the director string. */ \
- /* Take a stack snapshot. */ \
- return GetAndCheckStackTrace(aLUL, strPorig); \
- } else { \
- /* Recurse onwards. This is a bit subtle. The obvious */ \
- /* thing to do here is call onwards directly, from within the */ \
- /* arms of the case statement. That gives a problem in that */ \
- /* there will be multiple return points inside each function when */ \
- /* unwinding, so it will be difficult to check for consistency */ \
- /* against the director string. Instead, we make an indirect */ \
- /* call, so as to guarantee that there is only one call site */ \
- /* within each function. This does assume that the compiler */ \
- /* won't transform it back to the simple direct-call form. */ \
- /* To discourage it from doing so, the call is bracketed with */ \
- /* __asm__ __volatile__ sections so as to make it not-movable. */ \
- bool (*nextFn)(LUL*, const char*, const char*) = NULL; \
- switch (*strP) { \
- case '1': nextFn = TestFn1; break; \
- case '2': nextFn = TestFn2; break; \
- case '3': nextFn = TestFn3; break; \
- case '4': nextFn = TestFn4; break; \
- case '5': nextFn = TestFn5; break; \
- case '6': nextFn = TestFn6; break; \
- case '7': nextFn = TestFn7; break; \
- case '8': nextFn = TestFn8; break; \
- default: nextFn = TestFn8; break; \
- } \
- __asm__ __volatile__("":::"cc","memory"); \
- bool passed = nextFn(aLUL, strPorig, strP+1); \
- __asm__ __volatile__("":::"cc","memory"); \
- return passed; \
- } \
- }
-
-// The test functions are mutually recursive, so it is necessary to
-// declare them before defining them.
-DECL_TEST_FN(TestFn1)
-DECL_TEST_FN(TestFn2)
-DECL_TEST_FN(TestFn3)
-DECL_TEST_FN(TestFn4)
-DECL_TEST_FN(TestFn5)
-DECL_TEST_FN(TestFn6)
-DECL_TEST_FN(TestFn7)
-DECL_TEST_FN(TestFn8)
-
-GEN_TEST_FN(TestFn1, 123)
-GEN_TEST_FN(TestFn2, 456)
-GEN_TEST_FN(TestFn3, 789)
-GEN_TEST_FN(TestFn4, 23)
-GEN_TEST_FN(TestFn5, 47)
-GEN_TEST_FN(TestFn6, 117)
-GEN_TEST_FN(TestFn7, 1)
-GEN_TEST_FN(TestFn8, 99)
-
-
-// This starts the test sequence going. Call here to generate a
-// sequence of calls as directed by the string |dstring|. The call
-// sequence will, from its innermost frame, finish by calling
-// GetAndCheckStackTrace() and passing it |dstring|.
-// GetAndCheckStackTrace() will unwind the stack, check consistency
-// of those results against |dstring|, and print a pass/fail message
-// to aLUL's logging sink. It also updates the counters in *aNTests
-// and aNTestsPassed.
-__attribute__((noinline)) void
-TestUnw(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed,
- LUL* aLUL, const char* dstring)
-{
- // Ensure that the stack has at least this much space on it. This
- // makes it safe to saw off the top LUL_UNIT_TEST_STACK_SIZE bytes
- // and hand it to LUL. Safe in the sense that no segfault can
- // happen because the stack is at least this big. This is all
- // somewhat dubious in the sense that a sufficiently clever compiler
- // (clang, for one) can figure out that space[] is unused and delete
- // it from the frame. Hence the somewhat elaborate hoop jumping to
- // fill it up before the call and to at least appear to use the
- // value afterwards.
- int i;
- volatile char space[LUL_UNIT_TEST_STACK_SIZE];
- for (i = 0; i < LUL_UNIT_TEST_STACK_SIZE; i++) {
- space[i] = (char)(i & 0x7F);
- }
-
- // Really run the test.
- bool passed = TestFn1(aLUL, dstring, dstring);
-
- // Appear to use space[], by visiting the value to compute some kind
- // of checksum, and then (apparently) using the checksum.
- int sum = 0;
- for (i = 0; i < LUL_UNIT_TEST_STACK_SIZE; i++) {
- // If this doesn't fool LLVM, I don't know what will.
- sum += space[i] - 3*i;
- }
- __asm__ __volatile__("" : : "r"(sum));
-
- // Update the counters.
- (*aNTests)++;
- if (passed) {
- (*aNTestsPassed)++;
- }
-}
-
-
-void
-RunLulUnitTests(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed, LUL* aLUL)
-{
- aLUL->mLog(":\n");
- aLUL->mLog("LULUnitTest: BEGIN\n");
- *aNTests = *aNTestsPassed = 0;
- TestUnw(aNTests, aNTestsPassed, aLUL, "11111111");
- TestUnw(aNTests, aNTestsPassed, aLUL, "11222211");
- TestUnw(aNTests, aNTestsPassed, aLUL, "111222333");
- TestUnw(aNTests, aNTestsPassed, aLUL, "1212121231212331212121212121212");
- TestUnw(aNTests, aNTestsPassed, aLUL, "31415827271828325332173258");
- TestUnw(aNTests, aNTestsPassed, aLUL,
- "123456781122334455667788777777777777777777777");
- aLUL->mLog("LULUnitTest: END\n");
- aLUL->mLog(":\n");
-}
-
-
-} // namespace lul
diff --git a/tools/profiler/lul/LulMain.h b/tools/profiler/lul/LulMain.h
deleted file mode 100644
index 0916d1b26..000000000
--- a/tools/profiler/lul/LulMain.h
+++ /dev/null
@@ -1,397 +0,0 @@
-/* -*- 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/. */
-
-#ifndef LulMain_h
-#define LulMain_h
-
-#include "LulPlatformMacros.h"
-#include "mozilla/Atomics.h"
-
-// LUL: A Lightweight Unwind Library.
-// This file provides the end-user (external) interface for LUL.
-
-// Some comments about naming in the implementation. These are safe
-// to ignore if you are merely using LUL, but are important if you
-// hack on its internals.
-//
-// Debuginfo readers in general have tended to use the word "address"
-// to mean several different things. This sometimes makes them
-// difficult to understand and maintain. LUL tries hard to avoid
-// using the word "address" and instead uses the following more
-// precise terms:
-//
-// * SVMA ("Stated Virtual Memory Address"): this is an address of a
-// symbol (etc) as it is stated in the symbol table, or other
-// metadata, of an object. Such values are typically small and
-// start from zero or thereabouts, unless the object has been
-// prelinked.
-//
-// * AVMA ("Actual Virtual Memory Address"): this is the address of a
-// symbol (etc) in a running process, that is, once the associated
-// object has been mapped into a process. Such values are typically
-// much larger than SVMAs, since objects can get mapped arbitrarily
-// far along the address space.
-//
-// * "Bias": the difference between AVMA and SVMA for a given symbol
-// (specifically, AVMA - SVMA). The bias is always an integral
-// number of pages. Once we know the bias for a given object's
-// text section (for example), we can compute the AVMAs of all of
-// its text symbols by adding the bias to their SVMAs.
-//
-// * "Image address": typically, to read debuginfo from an object we
-// will temporarily mmap in the file so as to read symbol tables
-// etc. Addresses in this temporary mapping are called "Image
-// addresses". Note that the temporary mapping is entirely
-// unrelated to the mappings of the file that the dynamic linker
-// must perform merely in order to get the program to run. Hence
-// image addresses are unrelated to either SVMAs or AVMAs.
-
-
-namespace lul {
-
-// A machine word plus validity tag.
-class TaggedUWord {
-public:
- // RUNS IN NO-MALLOC CONTEXT
- // Construct a valid one.
- explicit TaggedUWord(uintptr_t w)
- : mValue(w)
- , mValid(true)
- {}
-
- // RUNS IN NO-MALLOC CONTEXT
- // Construct an invalid one.
- TaggedUWord()
- : mValue(0)
- , mValid(false)
- {}
-
- // RUNS IN NO-MALLOC CONTEXT
- TaggedUWord operator+(TaggedUWord rhs) const {
- return (Valid() && rhs.Valid()) ? TaggedUWord(Value() + rhs.Value())
- : TaggedUWord();
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- TaggedUWord operator-(TaggedUWord rhs) const {
- return (Valid() && rhs.Valid()) ? TaggedUWord(Value() - rhs.Value())
- : TaggedUWord();
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- TaggedUWord operator&(TaggedUWord rhs) const {
- return (Valid() && rhs.Valid()) ? TaggedUWord(Value() & rhs.Value())
- : TaggedUWord();
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- TaggedUWord operator|(TaggedUWord rhs) const {
- return (Valid() && rhs.Valid()) ? TaggedUWord(Value() | rhs.Value())
- : TaggedUWord();
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- TaggedUWord CmpGEs(TaggedUWord rhs) const {
- if (Valid() && rhs.Valid()) {
- intptr_t s1 = (intptr_t)Value();
- intptr_t s2 = (intptr_t)rhs.Value();
- return TaggedUWord(s1 >= s2 ? 1 : 0);
- }
- return TaggedUWord();
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- TaggedUWord operator<<(TaggedUWord rhs) const {
- if (Valid() && rhs.Valid()) {
- uintptr_t shift = rhs.Value();
- if (shift < 8 * sizeof(uintptr_t))
- return TaggedUWord(Value() << shift);
- }
- return TaggedUWord();
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- // Is equal? Note: non-validity on either side gives non-equality.
- bool operator==(TaggedUWord other) const {
- return (mValid && other.Valid()) ? (mValue == other.Value()) : false;
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- // Is it word-aligned?
- bool IsAligned() const {
- return mValid && (mValue & (sizeof(uintptr_t)-1)) == 0;
- }
-
- // RUNS IN NO-MALLOC CONTEXT
- uintptr_t Value() const { return mValue; }
-
- // RUNS IN NO-MALLOC CONTEXT
- bool Valid() const { return mValid; }
-
-private:
- uintptr_t mValue;
- bool mValid;
-};
-
-
-// The registers, with validity tags, that will be unwound.
-
-struct UnwindRegs {
-#if defined(LUL_ARCH_arm)
- TaggedUWord r7;
- TaggedUWord r11;
- TaggedUWord r12;
- TaggedUWord r13;
- TaggedUWord r14;
- TaggedUWord r15;
-#elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
- TaggedUWord xbp;
- TaggedUWord xsp;
- TaggedUWord xip;
-#else
-# error "Unknown plat"
-#endif
-};
-
-
-// The maximum number of bytes in a stack snapshot. This can be
-// increased if necessary, but larger values cost performance, since a
-// stack snapshot needs to be copied between sampling and worker
-// threads for each snapshot. In practice 32k seems to be enough
-// to get good backtraces.
-static const size_t N_STACK_BYTES = 32768;
-
-// The stack chunk image that will be unwound.
-struct StackImage {
- // [start_avma, +len) specify the address range in the buffer.
- // Obviously we require 0 <= len <= N_STACK_BYTES.
- uintptr_t mStartAvma;
- size_t mLen;
- uint8_t mContents[N_STACK_BYTES];
-};
-
-
-// Statistics collection for the unwinder.
-template<typename T>
-class LULStats {
-public:
- LULStats()
- : mContext(0)
- , mCFI(0)
- , mScanned(0)
- {}
-
- template <typename S>
- explicit LULStats(const LULStats<S>& aOther)
- : mContext(aOther.mContext)
- , mCFI(aOther.mCFI)
- , mScanned(aOther.mScanned)
- {}
-
- template <typename S>
- LULStats<T>& operator=(const LULStats<S>& aOther)
- {
- mContext = aOther.mContext;
- mCFI = aOther.mCFI;
- mScanned = aOther.mScanned;
- return *this;
- }
-
- template <typename S>
- uint32_t operator-(const LULStats<S>& aOther) {
- return (mContext - aOther.mContext) +
- (mCFI - aOther.mCFI) + (mScanned - aOther.mScanned);
- }
-
- T mContext; // Number of context frames
- T mCFI; // Number of CFI/EXIDX frames
- T mScanned; // Number of scanned frames
-};
-
-
-// The core unwinder library class. Just one of these is needed, and
-// it can be shared by multiple unwinder threads.
-//
-// The library operates in one of two modes.
-//
-// * Admin mode. The library is this state after creation. In Admin
-// mode, no unwinding may be performed. It is however allowable to
-// perform administrative tasks -- primarily, loading of unwind info
-// -- in this mode. In particular, it is safe for the library to
-// perform dynamic memory allocation in this mode. Safe in the
-// sense that there is no risk of deadlock against unwinding threads
-// that might -- because of where they have been sampled -- hold the
-// system's malloc lock.
-//
-// * Unwind mode. In this mode, calls to ::Unwind may be made, but
-// nothing else. ::Unwind guarantees not to make any dynamic memory
-// requests, so as to guarantee that the calling thread won't
-// deadlock in the case where it already holds the system's malloc lock.
-//
-// The library is created in Admin mode. After debuginfo is loaded,
-// the caller must switch it into Unwind mode by calling
-// ::EnableUnwinding. There is no way to switch it back to Admin mode
-// after that. To safely switch back to Admin mode would require the
-// caller (or other external agent) to guarantee that there are no
-// pending ::Unwind calls.
-
-class PriMap;
-class SegArray;
-class UniqueStringUniverse;
-
-class LUL {
-public:
- // Create; supply a logging sink. Sets the object in Admin mode.
- explicit LUL(void (*aLog)(const char*));
-
- // Destroy. Caller is responsible for ensuring that no other
- // threads are in Unwind calls. All resources are freed and all
- // registered unwinder threads are deregistered. Can be called
- // either in Admin or Unwind mode.
- ~LUL();
-
- // Notify the library that unwinding is now allowed and so
- // admin-mode calls are no longer allowed. The object is initially
- // created in admin mode. The only possible transition is
- // admin->unwinding, therefore.
- void EnableUnwinding();
-
- // Notify of a new r-x mapping, and load the associated unwind info.
- // The filename is strdup'd and used for debug printing. If
- // aMappedImage is NULL, this function will mmap/munmap the file
- // itself, so as to be able to read the unwind info. If
- // aMappedImage is non-NULL then it is assumed to point to a
- // called-supplied and caller-managed mapped image of the file.
- // May only be called in Admin mode.
- void NotifyAfterMap(uintptr_t aRXavma, size_t aSize,
- const char* aFileName, const void* aMappedImage);
-
- // In rare cases we know an executable area exists but don't know
- // what the associated file is. This call notifies LUL of such
- // areas. This is important for correct functioning of stack
- // scanning and of the x86-{linux,android} special-case
- // __kernel_syscall function handling.
- // This must be called only after the code area in
- // question really has been mapped.
- // May only be called in Admin mode.
- void NotifyExecutableArea(uintptr_t aRXavma, size_t aSize);
-
- // Notify that a mapped area has been unmapped; discard any
- // associated unwind info. Acquires mRWlock for writing. Note that
- // to avoid segfaulting the stack-scan unwinder, which inspects code
- // areas, this must be called before the code area in question is
- // really unmapped. Note that, unlike NotifyAfterMap(), this
- // function takes the start and end addresses of the range to be
- // unmapped, rather than a start and a length parameter. This is so
- // as to make it possible to notify an unmap for the entire address
- // space using a single call.
- // May only be called in Admin mode.
- void NotifyBeforeUnmap(uintptr_t aAvmaMin, uintptr_t aAvmaMax);
-
- // Apply NotifyBeforeUnmap to the entire address space. This causes
- // LUL to discard all unwind and executable-area information for the
- // entire address space.
- // May only be called in Admin mode.
- void NotifyBeforeUnmapAll() {
- NotifyBeforeUnmap(0, UINTPTR_MAX);
- }
-
- // Returns the number of mappings currently registered.
- // May only be called in Admin mode.
- size_t CountMappings();
-
- // Unwind |aStackImg| starting with the context in |aStartRegs|.
- // Write the number of frames recovered in *aFramesUsed. Put
- // the PC values in aFramePCs[0 .. *aFramesUsed-1] and
- // the SP values in aFrameSPs[0 .. *aFramesUsed-1].
- // |aFramesAvail| is the size of the two output arrays and hence the
- // largest possible value of *aFramesUsed. PC values are always
- // valid, and the unwind will stop when the PC becomes invalid, but
- // the SP values might be invalid, in which case the value zero will
- // be written in the relevant frameSPs[] slot.
- //
- // Unwinding may optionally use stack scanning. The maximum number
- // of frames that may be recovered by stack scanning is
- // |aScannedFramesAllowed| and the actual number recovered is
- // written into *aScannedFramesAcquired. |aScannedFramesAllowed|
- // must be less than or equal to |aFramesAvail|.
- //
- // This function assumes that the SP values increase as it unwinds
- // away from the innermost frame -- that is, that the stack grows
- // down. It monitors SP values as it unwinds to check they
- // decrease, so as to avoid looping on corrupted stacks.
- //
- // May only be called in Unwind mode. Multiple threads may unwind
- // at once. LUL user is responsible for ensuring that no thread makes
- // any Admin calls whilst in Unwind mode.
- // MOZ_CRASHes if the calling thread is not registered for unwinding.
- //
- // Up to aScannedFramesAllowed stack-scanned frames may be recovered.
- //
- // The calling thread must previously have been registered via a call to
- // RegisterSampledThread.
- void Unwind(/*OUT*/uintptr_t* aFramePCs,
- /*OUT*/uintptr_t* aFrameSPs,
- /*OUT*/size_t* aFramesUsed,
- /*OUT*/size_t* aScannedFramesAcquired,
- size_t aFramesAvail,
- size_t aScannedFramesAllowed,
- UnwindRegs* aStartRegs, StackImage* aStackImg);
-
- // The logging sink. Call to send debug strings to the caller-
- // specified destination. Can only be called by the Admin thread.
- void (*mLog)(const char*);
-
- // Statistics relating to unwinding. These have to be atomic since
- // unwinding can occur on different threads simultaneously.
- LULStats<mozilla::Atomic<uint32_t>> mStats;
-
- // Possibly show the statistics. This may not be called from any
- // registered sampling thread, since it involves I/O.
- void MaybeShowStats();
-
-private:
- // The statistics counters at the point where they were last printed.
- LULStats<uint32_t> mStatsPrevious;
-
- // Are we in admin mode? Initially |true| but changes to |false|
- // once unwinding begins.
- bool mAdminMode;
-
- // The thread ID associated with admin mode. This is the only thread
- // that is allowed do perform non-Unwind calls on this object. Conversely,
- // no registered Unwinding thread may be the admin thread. This is so
- // as to clearly partition the one thread that may do dynamic memory
- // allocation from the threads that are being sampled, since the latter
- // absolutely may not do dynamic memory allocation.
- int mAdminThreadId;
-
- // The top level mapping from code address ranges to postprocessed
- // unwind info. Basically a sorted array of (addr, len, info)
- // records. This field is updated by NotifyAfterMap and NotifyBeforeUnmap.
- PriMap* mPriMap;
-
- // An auxiliary structure that records which address ranges are
- // mapped r-x, for the benefit of the stack scanner.
- SegArray* mSegArray;
-
- // A UniqueStringUniverse that holds all the strdup'd strings created
- // whilst reading unwind information. This is included so as to make
- // it possible to free them in ~LUL.
- UniqueStringUniverse* mUSU;
-};
-
-
-// Run unit tests on an initialised, loaded-up LUL instance, and print
-// summary results on |aLUL|'s logging sink. Also return the number
-// of tests run in *aNTests and the number that passed in
-// *aNTestsPassed.
-void
-RunLulUnitTests(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed, LUL* aLUL);
-
-} // namespace lul
-
-#endif // LulMain_h
diff --git a/tools/profiler/lul/platform-linux-lul.cpp b/tools/profiler/lul/platform-linux-lul.cpp
deleted file mode 100644
index da764f3cf..000000000
--- a/tools/profiler/lul/platform-linux-lul.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 <stdio.h>
-#include <signal.h>
-#include <string.h>
-#include <stdlib.h>
-#include <time.h>
-
-#include "platform.h"
-#include "PlatformMacros.h"
-#include "LulMain.h"
-#include "shared-libraries.h"
-#include "AutoObjectMapper.h"
-
-// Contains miscellaneous helpers that are used to connect SPS and LUL.
-
-
-// Find out, in a platform-dependent way, where the code modules got
-// mapped in the process' virtual address space, and get |aLUL| to
-// load unwind info for them.
-void
-read_procmaps(lul::LUL* aLUL)
-{
- MOZ_ASSERT(aLUL->CountMappings() == 0);
-
-# if defined(SPS_OS_linux) || defined(SPS_OS_android) || defined(SPS_OS_darwin)
- SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
-
- for (size_t i = 0; i < info.GetSize(); i++) {
- const SharedLibrary& lib = info.GetEntry(i);
-
-# if defined(SPS_OS_android)
- // We're using faulty.lib. Use a special-case object mapper.
- AutoObjectMapperFaultyLib mapper(aLUL->mLog);
-# else
- // We can use the standard POSIX-based mapper.
- AutoObjectMapperPOSIX mapper(aLUL->mLog);
-# endif
-
- // Ask |mapper| to map the object. Then hand its mapped address
- // to NotifyAfterMap().
- void* image = nullptr;
- size_t size = 0;
- bool ok = mapper.Map(&image, &size, lib.GetName());
- if (ok && image && size > 0) {
- aLUL->NotifyAfterMap(lib.GetStart(), lib.GetEnd()-lib.GetStart(),
- lib.GetName().c_str(), image);
- } else if (!ok && lib.GetName() == "") {
- // The object has no name and (as a consequence) the mapper
- // failed to map it. This happens on Linux, where
- // GetInfoForSelf() produces two such mappings: one for the
- // executable and one for the VDSO. The executable one isn't a
- // big deal since there's not much interesting code in there,
- // but the VDSO one is a problem on x86-{linux,android} because
- // lack of knowledge about the mapped area inhibits LUL's
- // special __kernel_syscall handling. Hence notify |aLUL| at
- // least of the mapping, even though it can't read any unwind
- // information for the area.
- aLUL->NotifyExecutableArea(lib.GetStart(), lib.GetEnd()-lib.GetStart());
- }
-
- // |mapper| goes out of scope at this point and so its destructor
- // unmaps the object.
- }
-
-# else
-# error "Unknown platform"
-# endif
-}
-
-
-// LUL needs a callback for its logging sink.
-void
-logging_sink_for_LUL(const char* str) {
- // Ignore any trailing \n, since LOG will add one anyway.
- size_t n = strlen(str);
- if (n > 0 && str[n-1] == '\n') {
- char* tmp = strdup(str);
- tmp[n-1] = 0;
- LOG(tmp);
- free(tmp);
- } else {
- LOG(str);
- }
-}
diff --git a/tools/profiler/lul/platform-linux-lul.h b/tools/profiler/lul/platform-linux-lul.h
deleted file mode 100644
index 4698cd388..000000000
--- a/tools/profiler/lul/platform-linux-lul.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef MOZ_PLATFORM_LINUX_LUL_H
-#define MOZ_PLATFORM_LINUX_LUL_H
-
-#include "platform.h"
-
-// Find out, in a platform-dependent way, where the code modules got
-// mapped in the process' virtual address space, and get |aLUL| to
-// load unwind info for them.
-void
-read_procmaps(lul::LUL* aLUL);
-
-// LUL needs a callback for its logging sink.
-void
-logging_sink_for_LUL(const char* str);
-
-// A singleton instance of the library.
-extern lul::LUL* sLUL;
-
-#endif /* ndef MOZ_PLATFORM_LINUX_LUL_H */
diff --git a/tools/profiler/moz.build b/tools/profiler/moz.build
index 57a74cefc..465d01630 100644
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -4,112 +4,6 @@
# 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/.
-if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
- XPIDL_MODULE = 'profiler'
- XPIDL_SOURCES += [
- 'gecko/nsIProfiler.idl',
- 'gecko/nsIProfileSaveEvent.idl',
- ]
- EXPORTS += [
- 'public/GeckoProfilerFunc.h',
- 'public/GeckoProfilerImpl.h',
- 'public/ProfilerBacktrace.h',
- 'public/ProfilerMarkers.h',
- 'public/PseudoStack.h',
- 'public/shared-libraries.h',
- ]
- EXPORTS.mozilla += [
- 'public/ProfileGatherer.h',
- ]
- EXTRA_JS_MODULES += [
- 'gecko/Profiler.jsm',
- ]
- UNIFIED_SOURCES += [
- 'core/GeckoSampler.cpp',
- 'core/platform.cpp',
- 'core/ProfileBuffer.cpp',
- 'core/ProfileEntry.cpp',
- 'core/ProfileJSONWriter.cpp',
- 'core/ProfilerBacktrace.cpp',
- 'core/ProfilerMarkers.cpp',
- 'core/StackTop.cpp',
- 'core/SyncProfile.cpp',
- 'core/ThreadInfo.cpp',
- 'core/ThreadProfile.cpp',
- 'gecko/nsProfiler.cpp',
- 'gecko/nsProfilerFactory.cpp',
- 'gecko/nsProfilerStartParams.cpp',
- 'gecko/ProfileGatherer.cpp',
- 'gecko/ProfilerIOInterposeObserver.cpp',
- 'gecko/SaveProfileTask.cpp',
- 'gecko/ThreadResponsiveness.cpp',
- ]
-
- if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
- UNIFIED_SOURCES += [
- 'lul/AutoObjectMapper.cpp',
- 'lul/LulCommon.cpp',
- 'lul/LulDwarf.cpp',
- 'lul/LulDwarfSummariser.cpp',
- 'lul/LulElf.cpp',
- 'lul/LulMain.cpp',
- 'lul/platform-linux-lul.cpp',
- ]
- # These files cannot be built in unified mode because of name clashes with mozglue headers on Android.
- SOURCES += [
- 'core/platform-linux.cc',
- 'core/shared-libraries-linux.cc',
- ]
- SOURCES += [
- '/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.cc',
- '/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc',
- '/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc',
- '/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc',
- '/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.cc',
- ]
- if CONFIG['CPU_ARCH'] == 'arm':
- SOURCES += [
- 'core/EHABIStackWalk.cpp',
- ]
- elif CONFIG['OS_TARGET'] == 'Darwin':
- UNIFIED_SOURCES += [
- 'core/platform-macos.cc',
- 'core/shared-libraries-macos.cc',
- ]
- elif CONFIG['OS_TARGET'] == 'WINNT':
- SOURCES += [
- 'core/IntelPowerGadget.cpp',
- 'core/platform-win32.cc',
- 'core/shared-libraries-win32.cc',
- ]
-
- LOCAL_INCLUDES += [
- '/docshell/base',
- '/ipc/chromium/src',
- '/mozglue/linker',
- '/toolkit/crashreporter/google-breakpad/src',
- '/tools/profiler/core/',
- '/tools/profiler/gecko/',
- '/xpcom/base',
- ]
-
- if CONFIG['OS_TARGET'] == 'Android':
- LOCAL_INCLUDES += [
- # We need access to Breakpad's getcontext(3) which is suitable for Android
- '/toolkit/crashreporter/google-breakpad/src/common/android/include',
- ]
-
- if CONFIG['OS_TARGET'] == 'Android':
- SOURCES += ['/toolkit/crashreporter/google-breakpad/src/common/android/breakpad_getcontext.S']
-
- if CONFIG['ANDROID_CPU_ARCH'] == 'armeabi':
- DEFINES['ARCH_ARMV6'] = True
-
- if CONFIG['ENABLE_TESTS']:
- DIRS += ['tests/gtest']
-
- FINAL_LIBRARY = 'xul'
-
IPDL_SOURCES += [
'gecko/ProfilerTypes.ipdlh',
]
diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h
index bef017d11..92fc6e052 100644
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -79,8 +79,6 @@ enum TracingMetadata {
TRACING_TIMESTAMP
};
-#if !defined(MOZ_ENABLE_PROFILER_SPS)
-
#include <stdint.h>
#include <stdarg.h>
@@ -248,12 +246,6 @@ static inline bool profiler_in_privacy_mode() { return false; }
static inline void profiler_log(const char *str) {}
static inline void profiler_log(const char *fmt, va_list args) {}
-#else
-
-#include "GeckoProfilerImpl.h"
-
-#endif
-
class MOZ_RAII GeckoProfilerInitRAII {
public:
explicit GeckoProfilerInitRAII(void* stackTop) {
diff --git a/tools/profiler/public/GeckoProfilerFunc.h b/tools/profiler/public/GeckoProfilerFunc.h
deleted file mode 100644
index e0d27f593..000000000
--- a/tools/profiler/public/GeckoProfilerFunc.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef PROFILER_FUNCS_H
-#define PROFILER_FUNCS_H
-
-#ifndef SPS_STANDALONE
-#include "js/TypeDecls.h"
-#endif
-#include "js/ProfilingStack.h"
-#include "mozilla/UniquePtr.h"
-#include "mozilla/Vector.h"
-#include <stdint.h>
-
-class nsISupports;
-
-namespace mozilla {
-class TimeStamp;
-
-namespace dom {
-class Promise;
-} // namespace dom
-
-} // namespace mozilla
-
-class ProfilerBacktrace;
-class ProfilerMarkerPayload;
-
-// Returns a handle to pass on exit. This can check that we are popping the
-// correct callstack.
-inline void* mozilla_sampler_call_enter(const char *aInfo, js::ProfileEntry::Category aCategory,
- void *aFrameAddress = nullptr, bool aCopy = false,
- uint32_t line = 0);
-
-inline void mozilla_sampler_call_exit(void* handle);
-
-void mozilla_sampler_add_marker(const char *aInfo,
- ProfilerMarkerPayload *aPayload = nullptr);
-
-void mozilla_sampler_start(int aEntries, double aInterval,
- const char** aFeatures, uint32_t aFeatureCount,
- const char** aThreadNameFilters, uint32_t aFilterCount);
-
-void mozilla_sampler_stop();
-
-bool mozilla_sampler_is_paused();
-void mozilla_sampler_pause();
-void mozilla_sampler_resume();
-
-ProfilerBacktrace* mozilla_sampler_get_backtrace();
-void mozilla_sampler_free_backtrace(ProfilerBacktrace* aBacktrace);
-void mozilla_sampler_get_backtrace_noalloc(char *output, size_t outputSize);
-
-bool mozilla_sampler_is_active();
-
-bool mozilla_sampler_feature_active(const char* aName);
-
-void mozilla_sampler_responsiveness(const mozilla::TimeStamp& time);
-
-void mozilla_sampler_frame_number(int frameNumber);
-
-const double* mozilla_sampler_get_responsiveness();
-
-void mozilla_sampler_save();
-
-mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime);
-
-#ifndef SPS_STANDALONE
-JSObject *mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime);
-void mozilla_sampler_get_profile_data_async(double aSinceTime,
- mozilla::dom::Promise* aPromise);
-void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
- double* aInterval,
- mozilla::Vector<const char*>* aFilters,
- mozilla::Vector<const char*>* aFeatures);
-void mozilla_sampler_get_gatherer(nsISupports** aRetVal);
-#endif
-
-// Make this function easily callable from a debugger in a build without
-// debugging information (work around http://llvm.org/bugs/show_bug.cgi?id=22211)
-extern "C" {
- void mozilla_sampler_save_profile_to_file(const char* aFilename);
-}
-
-const char** mozilla_sampler_get_features();
-
-void mozilla_sampler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
- uint32_t *aGeneration);
-
-void mozilla_sampler_init(void* stackTop);
-
-void mozilla_sampler_shutdown();
-
-// Lock the profiler. When locked the profiler is (1) stopped,
-// (2) profile data is cleared, (3) profiler-locked is fired.
-// This is used to lock down the profiler during private browsing
-void mozilla_sampler_lock();
-
-// Unlock the profiler, leaving it stopped and fires profiler-unlocked.
-void mozilla_sampler_unlock();
-
-// Register/unregister threads with the profiler
-bool mozilla_sampler_register_thread(const char* name, void* stackTop);
-void mozilla_sampler_unregister_thread();
-
-void mozilla_sampler_sleep_start();
-void mozilla_sampler_sleep_end();
-bool mozilla_sampler_is_sleeping();
-
-double mozilla_sampler_time();
-double mozilla_sampler_time(const mozilla::TimeStamp& aTime);
-
-void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
- TracingMetadata aMetaData);
-
-void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
- ProfilerBacktrace* aCause,
- TracingMetadata aMetaData);
-
-void mozilla_sampler_log(const char *fmt, va_list args);
-
-#endif
-
diff --git a/tools/profiler/public/GeckoProfilerImpl.h b/tools/profiler/public/GeckoProfilerImpl.h
deleted file mode 100644
index 7011e5cdb..000000000
--- a/tools/profiler/public/GeckoProfilerImpl.h
+++ /dev/null
@@ -1,515 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-// IWYU pragma: private, include "GeckoProfiler.h"
-
-#ifndef TOOLS_SPS_SAMPLER_H_
-#define TOOLS_SPS_SAMPLER_H_
-
-#include <stdlib.h>
-#include <signal.h>
-#include <stdarg.h>
-#include "mozilla/Assertions.h"
-#include "mozilla/GuardObjects.h"
-#include "mozilla/Sprintf.h"
-#include "mozilla/ThreadLocal.h"
-#include "mozilla/UniquePtr.h"
-#ifndef SPS_STANDALONE
-#include "nscore.h"
-#include "nsISupports.h"
-#endif
-#include "GeckoProfilerFunc.h"
-#include "PseudoStack.h"
-#include "ProfilerBacktrace.h"
-
-// Make sure that we can use std::min here without the Windows headers messing with us.
-#ifdef min
-#undef min
-#endif
-
-class GeckoSampler;
-
-namespace mozilla {
-class TimeStamp;
-} // namespace mozilla
-
-extern MOZ_THREAD_LOCAL(PseudoStack *) tlsPseudoStack;
-extern MOZ_THREAD_LOCAL(GeckoSampler *) tlsTicker;
-extern MOZ_THREAD_LOCAL(void *) tlsStackTop;
-extern bool stack_key_initialized;
-
-#ifndef SAMPLE_FUNCTION_NAME
-# ifdef __GNUC__
-# define SAMPLE_FUNCTION_NAME __FUNCTION__
-# elif defined(_MSC_VER)
-# define SAMPLE_FUNCTION_NAME __FUNCTION__
-# else
-# define SAMPLE_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name.
-# endif
-#endif
-
-static inline
-void profiler_init(void* stackTop)
-{
- mozilla_sampler_init(stackTop);
-}
-
-static inline
-void profiler_shutdown()
-{
- mozilla_sampler_shutdown();
-}
-
-static inline
-void profiler_start(int aProfileEntries, double aInterval,
- const char** aFeatures, uint32_t aFeatureCount,
- const char** aThreadNameFilters, uint32_t aFilterCount)
-{
- mozilla_sampler_start(aProfileEntries, aInterval, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount);
-}
-
-static inline
-void profiler_stop()
-{
- mozilla_sampler_stop();
-}
-
-static inline
-bool profiler_is_paused()
-{
- return mozilla_sampler_is_paused();
-}
-
-static inline
-void profiler_pause()
-{
- mozilla_sampler_pause();
-}
-
-static inline
-void profiler_resume()
-{
- mozilla_sampler_resume();
-}
-
-static inline
-ProfilerBacktrace* profiler_get_backtrace()
-{
- return mozilla_sampler_get_backtrace();
-}
-
-static inline
-void profiler_free_backtrace(ProfilerBacktrace* aBacktrace)
-{
- mozilla_sampler_free_backtrace(aBacktrace);
-}
-
-static inline
-void profiler_get_backtrace_noalloc(char *output, size_t outputSize)
-{
- return mozilla_sampler_get_backtrace_noalloc(output, outputSize);
-}
-
-static inline
-bool profiler_is_active()
-{
- return mozilla_sampler_is_active();
-}
-
-static inline
-bool profiler_feature_active(const char* aName)
-{
- return mozilla_sampler_feature_active(aName);
-}
-
-static inline
-void profiler_responsiveness(const mozilla::TimeStamp& aTime)
-{
- mozilla_sampler_responsiveness(aTime);
-}
-
-static inline
-void profiler_set_frame_number(int frameNumber)
-{
- return mozilla_sampler_frame_number(frameNumber);
-}
-
-static inline
-mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0)
-{
- return mozilla_sampler_get_profile(aSinceTime);
-}
-
-#ifndef SPS_STANDALONE
-static inline
-JSObject* profiler_get_profile_jsobject(JSContext* aCx, double aSinceTime = 0)
-{
- return mozilla_sampler_get_profile_data(aCx, aSinceTime);
-}
-
-static inline
-void profiler_get_profile_jsobject_async(double aSinceTime = 0,
- mozilla::dom::Promise* aPromise = 0)
-{
- mozilla_sampler_get_profile_data_async(aSinceTime, aPromise);
-}
-
-static inline
-void profiler_get_start_params(int* aEntrySize,
- double* aInterval,
- mozilla::Vector<const char*>* aFilters,
- mozilla::Vector<const char*>* aFeatures)
-{
- mozilla_sampler_get_profiler_start_params(aEntrySize, aInterval, aFilters, aFeatures);
-}
-
-static inline
-void profiler_get_gatherer(nsISupports** aRetVal)
-{
- mozilla_sampler_get_gatherer(aRetVal);
-}
-
-#endif
-
-static inline
-void profiler_save_profile_to_file(const char* aFilename)
-{
- return mozilla_sampler_save_profile_to_file(aFilename);
-}
-
-static inline
-const char** profiler_get_features()
-{
- return mozilla_sampler_get_features();
-}
-
-static inline
-void profiler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
- uint32_t *aGeneration)
-{
- return mozilla_sampler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration);
-}
-
-static inline
-void profiler_lock()
-{
- return mozilla_sampler_lock();
-}
-
-static inline
-void profiler_unlock()
-{
- return mozilla_sampler_unlock();
-}
-
-static inline
-void profiler_register_thread(const char* name, void* guessStackTop)
-{
- mozilla_sampler_register_thread(name, guessStackTop);
-}
-
-static inline
-void profiler_unregister_thread()
-{
- mozilla_sampler_unregister_thread();
-}
-
-static inline
-void profiler_sleep_start()
-{
- mozilla_sampler_sleep_start();
-}
-
-static inline
-void profiler_sleep_end()
-{
- mozilla_sampler_sleep_end();
-}
-
-static inline
-bool profiler_is_sleeping()
-{
- return mozilla_sampler_is_sleeping();
-}
-
-#ifndef SPS_STANDALONE
-static inline
-void profiler_js_operation_callback()
-{
- PseudoStack *stack = tlsPseudoStack.get();
- if (!stack) {
- return;
- }
-
- stack->jsOperationCallback();
-}
-#endif
-
-static inline
-double profiler_time()
-{
- return mozilla_sampler_time();
-}
-
-static inline
-double profiler_time(const mozilla::TimeStamp& aTime)
-{
- return mozilla_sampler_time(aTime);
-}
-
-static inline
-bool profiler_in_privacy_mode()
-{
- PseudoStack *stack = tlsPseudoStack.get();
- if (!stack) {
- return false;
- }
- return stack->mPrivacyMode;
-}
-
-static inline void profiler_tracing(const char* aCategory, const char* aInfo,
- ProfilerBacktrace* aCause,
- TracingMetadata aMetaData = TRACING_DEFAULT)
-{
- // Don't insert a marker if we're not profiling to avoid
- // the heap copy (malloc).
- if (!stack_key_initialized || !profiler_is_active()) {
- delete aCause;
- return;
- }
-
- mozilla_sampler_tracing(aCategory, aInfo, aCause, aMetaData);
-}
-
-static inline void profiler_tracing(const char* aCategory, const char* aInfo,
- TracingMetadata aMetaData = TRACING_DEFAULT)
-{
- if (!stack_key_initialized)
- return;
-
- // Don't insert a marker if we're not profiling to avoid
- // the heap copy (malloc).
- if (!profiler_is_active()) {
- return;
- }
-
- mozilla_sampler_tracing(aCategory, aInfo, aMetaData);
-}
-
-#define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line
-#define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line)
-#define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__)
-
-// Uncomment this to turn on systrace or build with
-// ac_add_options --enable-systace
-//#define MOZ_USE_SYSTRACE
-#ifdef MOZ_USE_SYSTRACE
-#ifndef ATRACE_TAG
-# define ATRACE_TAG ATRACE_TAG_ALWAYS
-#endif
-// We need HAVE_ANDROID_OS to be defined for Trace.h.
-// If its not set we will set it temporary and remove it.
-# ifndef HAVE_ANDROID_OS
-# define HAVE_ANDROID_OS
-# define REMOVE_HAVE_ANDROID_OS
-# endif
-// Android source code will include <cutils/trace.h> before this. There is no
-// HAVE_ANDROID_OS defined in Firefox OS build at that time. Enabled it globally
-// will cause other build break. So atrace_begin and atrace_end are not defined.
-// It will cause a build-break when we include <utils/Trace.h>. Use undef
-// _LIBS_CUTILS_TRACE_H will force <cutils/trace.h> to define atrace_begin and
-// atrace_end with defined HAVE_ANDROID_OS again. Then there is no build-break.
-# undef _LIBS_CUTILS_TRACE_H
-# include <utils/Trace.h>
-# define MOZ_PLATFORM_TRACING(name) android::ScopedTrace SAMPLER_APPEND_LINE_NUMBER(scopedTrace)(ATRACE_TAG, name);
-# ifdef REMOVE_HAVE_ANDROID_OS
-# undef HAVE_ANDROID_OS
-# undef REMOVE_HAVE_ANDROID_OS
-# endif
-#else
-# define MOZ_PLATFORM_TRACING(name)
-#endif
-
-// we want the class and function name but can't easily get that using preprocessor macros
-// __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
-
-#define PROFILER_LABEL(name_space, info, category) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__)
-#define PROFILER_LABEL_FUNC(category) MOZ_PLATFORM_TRACING(SAMPLE_FUNCTION_NAME) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(SAMPLE_FUNCTION_NAME, category, __LINE__)
-#define PROFILER_LABEL_PRINTF(name_space, info, category, ...) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__, __VA_ARGS__)
-
-#define PROFILER_MARKER(info) mozilla_sampler_add_marker(info)
-#define PROFILER_MARKER_PAYLOAD(info, payload) mozilla_sampler_add_marker(info, payload)
-#define PROFILER_MAIN_THREAD_MARKER(info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info)
-
-#define PROFILER_MAIN_THREAD_LABEL(name_space, info, category) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__)
-#define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, category, ...) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__, __VA_ARGS__)
-
-
-#if !defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) && !defined(ARCH_ARMV6)
-# define PROFILE_DEFAULT_ENTRY 1000000
-#else
-# define PROFILE_DEFAULT_ENTRY 100000
-#endif
-
-// In the case of profiler_get_backtrace we know that we only need enough space
-// for a single backtrace.
-#define GET_BACKTRACE_DEFAULT_ENTRY 1000
-
-#if defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED)
-/* A 1ms sampling interval has been shown to be a large perf hit
- * (10fps) on memory-contrained (low-end) platforms, and additionally
- * to yield different results from the profiler. Where this is the
- * important case, b2g, there are also many gecko processes which
- * magnify these effects. */
-# define PROFILE_DEFAULT_INTERVAL 10
-#elif defined(ANDROID)
-// We use a lower frequency on Android, in order to make things work
-// more smoothly on phones. This value can be adjusted later with
-// some libunwind optimizations.
-// In one sample measurement on Galaxy Nexus, out of about 700 backtraces,
-// 60 of them took more than 25ms, and the average and standard deviation
-// were 6.17ms and 9.71ms respectively.
-
-// For now since we don't support stackwalking let's use 1ms since it's fast
-// enough.
-#define PROFILE_DEFAULT_INTERVAL 1
-#else
-#define PROFILE_DEFAULT_INTERVAL 1
-#endif
-#define PROFILE_DEFAULT_FEATURES NULL
-#define PROFILE_DEFAULT_FEATURE_COUNT 0
-
-namespace mozilla {
-
-class MOZ_RAII GeckoProfilerTracingRAII {
-public:
- GeckoProfilerTracingRAII(const char* aCategory, const char* aInfo,
- mozilla::UniquePtr<ProfilerBacktrace> aBacktrace
- MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
- : mCategory(aCategory)
- , mInfo(aInfo)
- {
- MOZ_GUARD_OBJECT_NOTIFIER_INIT;
- profiler_tracing(mCategory, mInfo, aBacktrace.release(), TRACING_INTERVAL_START);
- }
-
- ~GeckoProfilerTracingRAII() {
- profiler_tracing(mCategory, mInfo, TRACING_INTERVAL_END);
- }
-
-protected:
- MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
- const char* mCategory;
- const char* mInfo;
-};
-
-class MOZ_RAII SamplerStackFrameRAII {
-public:
- // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
- SamplerStackFrameRAII(const char *aInfo,
- js::ProfileEntry::Category aCategory, uint32_t line
- MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
- {
- MOZ_GUARD_OBJECT_NOTIFIER_INIT;
- mHandle = mozilla_sampler_call_enter(aInfo, aCategory, this, false, line);
- }
- ~SamplerStackFrameRAII() {
- mozilla_sampler_call_exit(mHandle);
- }
-private:
- MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
- void* mHandle;
-};
-
-static const int SAMPLER_MAX_STRING = 128;
-class MOZ_RAII SamplerStackFramePrintfRAII {
-public:
- // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
- SamplerStackFramePrintfRAII(const char *aInfo,
- js::ProfileEntry::Category aCategory, uint32_t line, const char *aFormat, ...)
- : mHandle(nullptr)
- {
- if (profiler_is_active() && !profiler_in_privacy_mode()) {
- va_list args;
- va_start(args, aFormat);
- char buff[SAMPLER_MAX_STRING];
-
- // We have to use seperate printf's because we're using
- // the vargs.
- VsprintfLiteral(buff, aFormat, args);
- SprintfLiteral(mDest, "%s %s", aInfo, buff);
-
- mHandle = mozilla_sampler_call_enter(mDest, aCategory, this, true, line);
- va_end(args);
- } else {
- mHandle = mozilla_sampler_call_enter(aInfo, aCategory, this, false, line);
- }
- }
- ~SamplerStackFramePrintfRAII() {
- mozilla_sampler_call_exit(mHandle);
- }
-private:
- char mDest[SAMPLER_MAX_STRING];
- void* mHandle;
-};
-
-} // namespace mozilla
-
-inline PseudoStack* mozilla_get_pseudo_stack(void)
-{
- if (!stack_key_initialized)
- return nullptr;
- return tlsPseudoStack.get();
-}
-
-inline void* mozilla_sampler_call_enter(const char *aInfo,
- js::ProfileEntry::Category aCategory, void *aFrameAddress, bool aCopy, uint32_t line)
-{
- // check if we've been initialized to avoid calling pthread_getspecific
- // with a null tlsStack which will return undefined results.
- if (!stack_key_initialized)
- return nullptr;
-
- PseudoStack *stack = tlsPseudoStack.get();
- // we can't infer whether 'stack' has been initialized
- // based on the value of stack_key_intiailized because
- // 'stack' is only intialized when a thread is being
- // profiled.
- if (!stack) {
- return stack;
- }
- stack->push(aInfo, aCategory, aFrameAddress, aCopy, line);
-
- // The handle is meant to support future changes
- // but for now it is simply use to save a call to
- // pthread_getspecific on exit. It also supports the
- // case where the sampler is initialized between
- // enter and exit.
- return stack;
-}
-
-inline void mozilla_sampler_call_exit(void *aHandle)
-{
- if (!aHandle)
- return;
-
- PseudoStack *stack = (PseudoStack*)aHandle;
- stack->popAndMaybeDelete();
-}
-
-void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload);
-
-static inline
-void profiler_log(const char *str)
-{
- profiler_tracing("log", str, TRACING_EVENT);
-}
-
-static inline
-void profiler_log(const char *fmt, va_list args)
-{
- mozilla_sampler_log(fmt, args);
-}
-
-#endif /* ndef TOOLS_SPS_SAMPLER_H_ */
diff --git a/tools/profiler/public/ProfileGatherer.h b/tools/profiler/public/ProfileGatherer.h
deleted file mode 100644
index 4e39a4f5c..000000000
--- a/tools/profiler/public/ProfileGatherer.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* 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/. */
-
-#ifndef MOZ_PROFILE_GATHERER_H
-#define MOZ_PROFILE_GATHERER_H
-
-#include "mozilla/dom/Promise.h"
-
-class GeckoSampler;
-
-namespace mozilla {
-
-class ProfileGatherer final : public nsIObserver
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSIOBSERVER
-
- explicit ProfileGatherer(GeckoSampler* aTicker);
- void WillGatherOOPProfile();
- void GatheredOOPProfile();
- void Start(double aSinceTime, mozilla::dom::Promise* aPromise);
- void Cancel();
- void OOPExitProfile(const nsCString& aProfile);
-
-private:
- ~ProfileGatherer() {};
- void Finish();
- void Reset();
-
- nsTArray<nsCString> mExitProfiles;
- RefPtr<mozilla::dom::Promise> mPromise;
- GeckoSampler* mTicker;
- double mSinceTime;
- uint32_t mPendingProfiles;
- bool mGathering;
-};
-
-} // namespace mozilla
-
-#endif
diff --git a/tools/profiler/public/ProfilerBacktrace.h b/tools/profiler/public/ProfilerBacktrace.h
deleted file mode 100644
index bcaab3563..000000000
--- a/tools/profiler/public/ProfilerBacktrace.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-#ifndef __PROFILER_BACKTRACE_H
-#define __PROFILER_BACKTRACE_H
-
-class SyncProfile;
-class SpliceableJSONWriter;
-class UniqueStacks;
-
-class ProfilerBacktrace
-{
-public:
- explicit ProfilerBacktrace(SyncProfile* aProfile);
- ~ProfilerBacktrace();
-
- // ProfilerBacktraces' stacks are deduplicated in the context of the
- // profile that contains the backtrace as a marker payload.
- //
- // That is, markers that contain backtraces should not need their own stack,
- // frame, and string tables. They should instead reuse their parent
- // profile's tables.
- void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks);
-
-private:
- ProfilerBacktrace(const ProfilerBacktrace&);
- ProfilerBacktrace& operator=(const ProfilerBacktrace&);
-
- SyncProfile* mProfile;
-};
-
-#endif // __PROFILER_BACKTRACE_H
-
diff --git a/tools/profiler/public/ProfilerMarkers.h b/tools/profiler/public/ProfilerMarkers.h
deleted file mode 100644
index 29711f210..000000000
--- a/tools/profiler/public/ProfilerMarkers.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef PROFILER_MARKERS_H
-#define PROFILER_MARKERS_H
-
-#include "mozilla/TimeStamp.h"
-#include "mozilla/Attributes.h"
-
-namespace mozilla {
-namespace layers {
-class Layer;
-} // namespace layers
-} // namespace mozilla
-
-class SpliceableJSONWriter;
-class UniqueStacks;
-
-/**
- * This is an abstract object that can be implied to supply
- * data to be attached with a profiler marker. Most data inserted
- * into a profile is stored in a circular buffer. This buffer
- * typically wraps around and overwrites most entries. Because
- * of this, this structure is designed to defer the work of
- * prepare the payload only when 'preparePayload' is called.
- *
- * Note when implementing that this object is typically constructed
- * on a particular thread but 'preparePayload' and the destructor
- * is called from the main thread.
- */
-class ProfilerMarkerPayload
-{
-public:
- /**
- * ProfilerMarkerPayload takes ownership of aStack
- */
- explicit ProfilerMarkerPayload(ProfilerBacktrace* aStack = nullptr);
- ProfilerMarkerPayload(const mozilla::TimeStamp& aStartTime,
- const mozilla::TimeStamp& aEndTime,
- ProfilerBacktrace* aStack = nullptr);
-
- /**
- * Called from the main thread
- */
- virtual ~ProfilerMarkerPayload();
-
- /**
- * Called from the main thread
- */
- virtual void StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks) = 0;
-
- mozilla::TimeStamp GetStartTime() const { return mStartTime; }
-
-protected:
- /**
- * Called from the main thread
- */
- void streamCommonProps(const char* aMarkerType, SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks);
-
- void SetStack(ProfilerBacktrace* aStack) { mStack = aStack; }
-
-private:
- mozilla::TimeStamp mStartTime;
- mozilla::TimeStamp mEndTime;
- ProfilerBacktrace* mStack;
-};
-
-class ProfilerMarkerTracing : public ProfilerMarkerPayload
-{
-public:
- ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData);
- ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData, ProfilerBacktrace* aCause);
-
- const char *GetCategory() const { return mCategory; }
- TracingMetadata GetMetaData() const { return mMetaData; }
-
- virtual void StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks) override;
-
-private:
- const char *mCategory;
- TracingMetadata mMetaData;
-};
-
-
-#ifndef SPS_STANDALONE
-#include "gfxASurface.h"
-class ProfilerMarkerImagePayload : public ProfilerMarkerPayload
-{
-public:
- explicit ProfilerMarkerImagePayload(gfxASurface *aImg);
-
- virtual void StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks) override;
-
-private:
- RefPtr<gfxASurface> mImg;
-};
-
-class IOMarkerPayload : public ProfilerMarkerPayload
-{
-public:
- IOMarkerPayload(const char* aSource, const char* aFilename, const mozilla::TimeStamp& aStartTime,
- const mozilla::TimeStamp& aEndTime,
- ProfilerBacktrace* aStack);
- ~IOMarkerPayload();
-
- virtual void StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks) override;
-
-private:
- const char* mSource;
- char* mFilename;
-};
-
-/**
- * Contains the translation applied to a 2d layer so we can
- * track the layer position at each frame.
- */
-class LayerTranslationPayload : public ProfilerMarkerPayload
-{
-public:
- LayerTranslationPayload(mozilla::layers::Layer* aLayer,
- mozilla::gfx::Point aPoint);
-
- virtual void StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks) override;
-
-private:
- mozilla::layers::Layer* mLayer;
- mozilla::gfx::Point mPoint;
-};
-
-#include "Units.h" // For ScreenIntPoint
-
-/**
- * Tracks when touch events are processed by gecko, not when
- * the touch actually occured in gonk/android.
- */
-class TouchDataPayload : public ProfilerMarkerPayload
-{
-public:
- explicit TouchDataPayload(const mozilla::ScreenIntPoint& aPoint);
- virtual ~TouchDataPayload() {}
-
- virtual void StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks) override;
-
-private:
- mozilla::ScreenIntPoint mPoint;
-};
-
-/**
- * Tracks when a vsync occurs according to the HardwareComposer.
- */
-class VsyncPayload : public ProfilerMarkerPayload
-{
-public:
- explicit VsyncPayload(mozilla::TimeStamp aVsyncTimestamp);
- virtual ~VsyncPayload() {}
-
- virtual void StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks) override;
-
-private:
- mozilla::TimeStamp mVsyncTimestamp;
-};
-
-class GPUMarkerPayload : public ProfilerMarkerPayload
-{
-public:
- GPUMarkerPayload(const mozilla::TimeStamp& aCpuTimeStart,
- const mozilla::TimeStamp& aCpuTimeEnd,
- uint64_t aGpuTimeStart,
- uint64_t aGpuTimeEnd);
- ~GPUMarkerPayload() {}
-
- virtual void StreamPayload(SpliceableJSONWriter& aWriter,
- UniqueStacks& aUniqueStacks) override;
-
-private:
- mozilla::TimeStamp mCpuTimeStart;
- mozilla::TimeStamp mCpuTimeEnd;
- uint64_t mGpuTimeStart;
- uint64_t mGpuTimeEnd;
-};
-#endif
-
-#endif // PROFILER_MARKERS_H
diff --git a/tools/profiler/public/PseudoStack.h b/tools/profiler/public/PseudoStack.h
deleted file mode 100644
index f9e3836ea..000000000
--- a/tools/profiler/public/PseudoStack.h
+++ /dev/null
@@ -1,469 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef PROFILER_PSEUDO_STACK_H_
-#define PROFILER_PSEUDO_STACK_H_
-
-#include "mozilla/ArrayUtils.h"
-#include <stdint.h>
-#include "js/ProfilingStack.h"
-#include <stdlib.h>
-#include "mozilla/Atomics.h"
-#ifndef SPS_STANDALONE
-#include "nsISupportsImpl.h"
-#endif
-
-/* we duplicate this code here to avoid header dependencies
- * which make it more difficult to include in other places */
-#if defined(_M_X64) || defined(__x86_64__)
-#define V8_HOST_ARCH_X64 1
-#elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
-#define V8_HOST_ARCH_IA32 1
-#elif defined(__ARMEL__)
-#define V8_HOST_ARCH_ARM 1
-#else
-#warning Please add support for your architecture in chromium_types.h
-#endif
-
-// STORE_SEQUENCER: Because signals can interrupt our profile modification
-// we need to make stores are not re-ordered by the compiler
-// or hardware to make sure the profile is consistent at
-// every point the signal can fire.
-#ifdef V8_HOST_ARCH_ARM
-// TODO Is there something cheaper that will prevent
-// memory stores from being reordered
-
-typedef void (*LinuxKernelMemoryBarrierFunc)(void);
-LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
- (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
-
-# define STORE_SEQUENCER() pLinuxKernelMemoryBarrier()
-#elif defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_X64)
-# if defined(_MSC_VER)
-# include <intrin.h>
-# define STORE_SEQUENCER() _ReadWriteBarrier();
-# elif defined(__INTEL_COMPILER)
-# define STORE_SEQUENCER() __memory_barrier();
-# elif __GNUC__
-# define STORE_SEQUENCER() asm volatile("" ::: "memory");
-# else
-# error "Memory clobber not supported for your compiler."
-# endif
-#else
-# error "Memory clobber not supported for your platform."
-#endif
-
-// We can't include <algorithm> because it causes issues on OS X, so we use
-// our own min function.
-static inline uint32_t sMin(uint32_t l, uint32_t r) {
- return l < r ? l : r;
-}
-
-// A stack entry exists to allow the JS engine to inform SPS of the current
-// backtrace, but also to instrument particular points in C++ in case stack
-// walking is not available on the platform we are running on.
-//
-// Each entry has a descriptive string, a relevant stack address, and some extra
-// information the JS engine might want to inform SPS of. This class inherits
-// from the JS engine's version of the entry to ensure that the size and layout
-// of the two representations are consistent.
-class StackEntry : public js::ProfileEntry
-{
-};
-
-class ProfilerMarkerPayload;
-template<typename T>
-class ProfilerLinkedList;
-class SpliceableJSONWriter;
-class UniqueStacks;
-
-class ProfilerMarker {
- friend class ProfilerLinkedList<ProfilerMarker>;
-public:
- explicit ProfilerMarker(const char* aMarkerName,
- ProfilerMarkerPayload* aPayload = nullptr,
- double aTime = 0);
-
- ~ProfilerMarker();
-
- const char* GetMarkerName() const {
- return mMarkerName;
- }
-
- void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks) const;
-
- void SetGeneration(uint32_t aGenID);
-
- bool HasExpired(uint32_t aGenID) const {
- return mGenID + 2 <= aGenID;
- }
-
- double GetTime() const;
-
-private:
- char* mMarkerName;
- ProfilerMarkerPayload* mPayload;
- ProfilerMarker* mNext;
- double mTime;
- uint32_t mGenID;
-};
-
-template<typename T>
-class ProfilerLinkedList {
-public:
- ProfilerLinkedList()
- : mHead(nullptr)
- , mTail(nullptr)
- {}
-
- void insert(T* elem)
- {
- if (!mTail) {
- mHead = elem;
- mTail = elem;
- } else {
- mTail->mNext = elem;
- mTail = elem;
- }
- elem->mNext = nullptr;
- }
-
- T* popHead()
- {
- if (!mHead) {
- MOZ_ASSERT(false);
- return nullptr;
- }
-
- T* head = mHead;
-
- mHead = head->mNext;
- if (!mHead) {
- mTail = nullptr;
- }
-
- return head;
- }
-
- const T* peek() {
- return mHead;
- }
-
-private:
- T* mHead;
- T* mTail;
-};
-
-typedef ProfilerLinkedList<ProfilerMarker> ProfilerMarkerLinkedList;
-
-template<typename T>
-class ProfilerSignalSafeLinkedList {
-public:
- ProfilerSignalSafeLinkedList()
- : mSignalLock(false)
- {}
-
- ~ProfilerSignalSafeLinkedList()
- {
- if (mSignalLock) {
- // Some thread is modifying the list. We should only be released on that
- // thread.
- abort();
- }
-
- while (mList.peek()) {
- delete mList.popHead();
- }
- }
-
- // Insert an item into the list.
- // Must only be called from the owning thread.
- // Must not be called while the list from accessList() is being accessed.
- // In the profiler, we ensure that by interrupting the profiled thread
- // (which is the one that owns this list and calls insert() on it) until
- // we're done reading the list from the signal handler.
- void insert(T* aElement) {
- MOZ_ASSERT(aElement);
-
- mSignalLock = true;
- STORE_SEQUENCER();
-
- mList.insert(aElement);
-
- STORE_SEQUENCER();
- mSignalLock = false;
- }
-
- // Called within signal, from any thread, possibly while insert() is in the
- // middle of modifying the list (on the owning thread). Will return null if
- // that is the case.
- // Function must be reentrant.
- ProfilerLinkedList<T>* accessList()
- {
- if (mSignalLock) {
- return nullptr;
- }
- return &mList;
- }
-
-private:
- ProfilerLinkedList<T> mList;
-
- // If this is set, then it's not safe to read the list because its contents
- // are being changed.
- volatile bool mSignalLock;
-};
-
-// Stub eventMarker function for js-engine event generation.
-void ProfilerJSEventMarker(const char *event);
-
-// the PseudoStack members are read by signal
-// handlers, so the mutation of them needs to be signal-safe.
-struct PseudoStack
-{
-public:
- // Create a new PseudoStack and acquire a reference to it.
- static PseudoStack *create()
- {
- return new PseudoStack();
- }
-
- // This is called on every profiler restart. Put things that should happen at that time here.
- void reinitializeOnResume() {
- // This is needed to cause an initial sample to be taken from sleeping threads. Otherwise sleeping
- // threads would not have any samples to copy forward while sleeping.
- mSleepId++;
- }
-
- void addMarker(const char* aMarkerStr, ProfilerMarkerPayload* aPayload, double aTime)
- {
- ProfilerMarker* marker = new ProfilerMarker(aMarkerStr, aPayload, aTime);
- mPendingMarkers.insert(marker);
- }
-
- // called within signal. Function must be reentrant
- ProfilerMarkerLinkedList* getPendingMarkers()
- {
- // The profiled thread is interrupted, so we can access the list safely.
- // Unless the profiled thread was in the middle of changing the list when
- // we interrupted it - in that case, accessList() will return null.
- return mPendingMarkers.accessList();
- }
-
- void push(const char *aName, js::ProfileEntry::Category aCategory, uint32_t line)
- {
- push(aName, aCategory, nullptr, false, line);
- }
-
- void push(const char *aName, js::ProfileEntry::Category aCategory,
- void *aStackAddress, bool aCopy, uint32_t line)
- {
- if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) {
- mStackPointer++;
- return;
- }
-
- // In order to ensure this object is kept alive while it is
- // active, we acquire a reference at the outermost push. This is
- // released by the corresponding pop.
- if (mStackPointer == 0) {
- ref();
- }
-
- volatile StackEntry &entry = mStack[mStackPointer];
-
- // Make sure we increment the pointer after the name has
- // been written such that mStack is always consistent.
- entry.initCppFrame(aStackAddress, line);
- entry.setLabel(aName);
- MOZ_ASSERT(entry.flags() == js::ProfileEntry::IS_CPP_ENTRY);
- entry.setCategory(aCategory);
-
- // Track if mLabel needs a copy.
- if (aCopy)
- entry.setFlag(js::ProfileEntry::FRAME_LABEL_COPY);
- else
- entry.unsetFlag(js::ProfileEntry::FRAME_LABEL_COPY);
-
- // Prevent the optimizer from re-ordering these instructions
- STORE_SEQUENCER();
- mStackPointer++;
- }
-
- // Pop the stack. If the stack is empty and all other references to
- // this PseudoStack have been dropped, then the PseudoStack is
- // deleted and "false" is returned. Otherwise "true" is returned.
- bool popAndMaybeDelete()
- {
- mStackPointer--;
- if (mStackPointer == 0) {
- // Release our self-owned reference count. See 'push'.
- deref();
- return false;
- } else {
- return true;
- }
- }
- bool isEmpty()
- {
- return mStackPointer == 0;
- }
- uint32_t stackSize() const
- {
- return sMin(mStackPointer, mozilla::sig_safe_t(mozilla::ArrayLength(mStack)));
- }
-
- void sampleContext(JSContext* context) {
-#ifndef SPS_STANDALONE
- if (mContext && !context) {
- // On JS shut down, flush the current buffer as stringifying JIT samples
- // requires a live JSContext.
- flushSamplerOnJSShutdown();
- }
-
- mContext = context;
-
- if (!context) {
- return;
- }
-
- static_assert(sizeof(mStack[0]) == sizeof(js::ProfileEntry),
- "mStack must be binary compatible with js::ProfileEntry.");
- js::SetContextProfilingStack(context,
- (js::ProfileEntry*) mStack,
- (uint32_t*) &mStackPointer,
- (uint32_t) mozilla::ArrayLength(mStack));
- if (mStartJSSampling)
- enableJSSampling();
-#endif
- }
-#ifndef SPS_STANDALONE
- void enableJSSampling() {
- if (mContext) {
- js::EnableContextProfilingStack(mContext, true);
- js::RegisterContextProfilingEventMarker(mContext, &ProfilerJSEventMarker);
- mStartJSSampling = false;
- } else {
- mStartJSSampling = true;
- }
- }
- void jsOperationCallback() {
- if (mStartJSSampling)
- enableJSSampling();
- }
- void disableJSSampling() {
- mStartJSSampling = false;
- if (mContext)
- js::EnableContextProfilingStack(mContext, false);
- }
-#endif
-
- // Keep a list of active checkpoints
- StackEntry volatile mStack[1024];
- private:
-
- // A PseudoStack can only be created via the "create" method.
- PseudoStack()
- : mStackPointer(0)
- , mSleepId(0)
- , mSleepIdObserved(0)
- , mSleeping(false)
- , mRefCnt(1)
-#ifndef SPS_STANDALONE
- , mContext(nullptr)
-#endif
- , mStartJSSampling(false)
- , mPrivacyMode(false)
- {
- MOZ_COUNT_CTOR(PseudoStack);
- }
-
- // A PseudoStack can only be deleted via deref.
- ~PseudoStack() {
- MOZ_COUNT_DTOR(PseudoStack);
- if (mStackPointer != 0) {
- // We're releasing the pseudostack while it's still in use.
- // The label macros keep a non ref counted reference to the
- // stack to avoid a TLS. If these are not all cleared we will
- // get a use-after-free so better to crash now.
- abort();
- }
- }
-
- // No copying.
- PseudoStack(const PseudoStack&) = delete;
- void operator=(const PseudoStack&) = delete;
-
- void flushSamplerOnJSShutdown();
-
- // Keep a list of pending markers that must be moved
- // to the circular buffer
- ProfilerSignalSafeLinkedList<ProfilerMarker> mPendingMarkers;
- // This may exceed the length of mStack, so instead use the stackSize() method
- // to determine the number of valid samples in mStack
- mozilla::sig_safe_t mStackPointer;
- // Incremented at every sleep/wake up of the thread
- int mSleepId;
- // Previous id observed. If this is not the same as mSleepId, this thread is not sleeping in the same place any more
- mozilla::Atomic<int> mSleepIdObserved;
- // Keeps tack of whether the thread is sleeping or not (1 when sleeping 0 when awake)
- mozilla::Atomic<int> mSleeping;
- // This class is reference counted because it must be kept alive by
- // the ThreadInfo, by the reference from tlsPseudoStack, and by the
- // current thread when callbacks are in progress.
- mozilla::Atomic<int> mRefCnt;
-
- public:
-#ifndef SPS_STANDALONE
- // The context which is being sampled
- JSContext *mContext;
-#endif
- // Start JS Profiling when possible
- bool mStartJSSampling;
- bool mPrivacyMode;
-
- enum SleepState {NOT_SLEEPING, SLEEPING_FIRST, SLEEPING_AGAIN};
-
- // The first time this is called per sleep cycle we return SLEEPING_FIRST
- // and any other subsequent call within the same sleep cycle we return SLEEPING_AGAIN
- SleepState observeSleeping() {
- if (mSleeping != 0) {
- if (mSleepIdObserved == mSleepId) {
- return SLEEPING_AGAIN;
- } else {
- mSleepIdObserved = mSleepId;
- return SLEEPING_FIRST;
- }
- } else {
- return NOT_SLEEPING;
- }
- }
-
-
- // Call this whenever the current thread sleeps or wakes up
- // Calling setSleeping with the same value twice in a row is an error
- void setSleeping(int sleeping) {
- MOZ_ASSERT(mSleeping != sleeping);
- mSleepId++;
- mSleeping = sleeping;
- }
-
- bool isSleeping() {
- return !!mSleeping;
- }
-
- void ref() {
- ++mRefCnt;
- }
-
- void deref() {
- int newValue = --mRefCnt;
- if (newValue == 0) {
- delete this;
- }
- }
-};
-
-#endif
diff --git a/tools/profiler/public/shared-libraries.h b/tools/profiler/public/shared-libraries.h
deleted file mode 100644
index b02a1fb08..000000000
--- a/tools/profiler/public/shared-libraries.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#ifndef SHARED_LIBRARIES_H_
-#define SHARED_LIBRARIES_H_
-
-#ifndef MOZ_ENABLE_PROFILER_SPS
-#error This header does not have a useful implementation on your platform!
-#endif
-
-#include <algorithm>
-#include <vector>
-#include <string>
-#include <stdlib.h>
-#include <stdint.h>
-#ifndef SPS_STANDALONE
-#include <nsID.h>
-#endif
-
-class SharedLibrary {
-public:
-
- SharedLibrary(uintptr_t aStart,
- uintptr_t aEnd,
- uintptr_t aOffset,
- const std::string& aBreakpadId,
- const std::string& aName)
- : mStart(aStart)
- , mEnd(aEnd)
- , mOffset(aOffset)
- , mBreakpadId(aBreakpadId)
- , mName(aName)
- {}
-
- SharedLibrary(const SharedLibrary& aEntry)
- : mStart(aEntry.mStart)
- , mEnd(aEntry.mEnd)
- , mOffset(aEntry.mOffset)
- , mBreakpadId(aEntry.mBreakpadId)
- , mName(aEntry.mName)
- {}
-
- SharedLibrary& operator=(const SharedLibrary& aEntry)
- {
- // Gracefully handle self assignment
- if (this == &aEntry) return *this;
-
- mStart = aEntry.mStart;
- mEnd = aEntry.mEnd;
- mOffset = aEntry.mOffset;
- mBreakpadId = aEntry.mBreakpadId;
- mName = aEntry.mName;
- return *this;
- }
-
- bool operator==(const SharedLibrary& other) const
- {
- return (mStart == other.mStart) &&
- (mEnd == other.mEnd) &&
- (mOffset == other.mOffset) &&
- (mName == other.mName) &&
- (mBreakpadId == other.mBreakpadId);
- }
-
- uintptr_t GetStart() const { return mStart; }
- uintptr_t GetEnd() const { return mEnd; }
- uintptr_t GetOffset() const { return mOffset; }
- const std::string &GetBreakpadId() const { return mBreakpadId; }
- const std::string &GetName() const { return mName; }
-
-private:
- SharedLibrary() {}
-
- uintptr_t mStart;
- uintptr_t mEnd;
- uintptr_t mOffset;
- std::string mBreakpadId;
- std::string mName;
-};
-
-static bool
-CompareAddresses(const SharedLibrary& first, const SharedLibrary& second)
-{
- return first.GetStart() < second.GetStart();
-}
-
-class SharedLibraryInfo {
-public:
- static SharedLibraryInfo GetInfoForSelf();
- SharedLibraryInfo() {}
-
- void AddSharedLibrary(SharedLibrary entry)
- {
- mEntries.push_back(entry);
- }
-
- const SharedLibrary& GetEntry(size_t i) const
- {
- return mEntries[i];
- }
-
- // Removes items in the range [first, last)
- // i.e. element at the "last" index is not removed
- void RemoveEntries(size_t first, size_t last)
- {
- mEntries.erase(mEntries.begin() + first, mEntries.begin() + last);
- }
-
- bool Contains(const SharedLibrary& searchItem) const
- {
- return (mEntries.end() !=
- std::find(mEntries.begin(), mEntries.end(), searchItem));
- }
-
- size_t GetSize() const
- {
- return mEntries.size();
- }
-
- void SortByAddress()
- {
- std::sort(mEntries.begin(), mEntries.end(), CompareAddresses);
- }
-
- void Clear()
- {
- mEntries.clear();
- }
-
-private:
- std::vector<SharedLibrary> mEntries;
-};
-
-#endif
diff --git a/tools/profiler/tests/gtest/LulTest.cpp b/tools/profiler/tests/gtest/LulTest.cpp
deleted file mode 100644
index 8a165ab34..000000000
--- a/tools/profiler/tests/gtest/LulTest.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "gtest/gtest.h"
-#include "mozilla/Atomics.h"
-#include "LulMain.h"
-#include "GeckoProfiler.h" // for TracingMetadata
-#include "platform-linux-lul.h" // for read_procmaps
-
-// Set this to 0 to make LUL be completely silent during tests.
-// Set it to 1 to get logging output from LUL, presumably for
-// the purpose of debugging it.
-#define DEBUG_LUL_TEST 0
-
-// LUL needs a callback for its logging sink.
-static void
-gtest_logging_sink_for_LulIntegration(const char* str) {
- if (DEBUG_LUL_TEST == 0) {
- return;
- }
- // Ignore any trailing \n, since LOG will add one anyway.
- size_t n = strlen(str);
- if (n > 0 && str[n-1] == '\n') {
- char* tmp = strdup(str);
- tmp[n-1] = 0;
- fprintf(stderr, "LUL-in-gtest: %s\n", tmp);
- free(tmp);
- } else {
- fprintf(stderr, "LUL-in-gtest: %s\n", str);
- }
-}
-
-TEST(LulIntegration, unwind_consistency) {
- // Set up LUL and get it to read unwind info for libxul.so, which is
- // all we care about here, plus (incidentally) practically every
- // other object in the process too.
- lul::LUL* lul = new lul::LUL(gtest_logging_sink_for_LulIntegration);
- read_procmaps(lul);
-
- // Run unwind tests and receive information about how many there
- // were and how many were successful.
- lul->EnableUnwinding();
- int nTests = 0, nTestsPassed = 0;
- RunLulUnitTests(&nTests, &nTestsPassed, lul);
- EXPECT_TRUE(nTests == 6) << "Unexpected number of tests";
- EXPECT_TRUE(nTestsPassed == nTests) << "Not all tests passed";
-
- delete lul;
-}
diff --git a/tools/profiler/tests/gtest/LulTestDwarf.cpp b/tools/profiler/tests/gtest/LulTestDwarf.cpp
deleted file mode 100644
index 5cfd71fd4..000000000
--- a/tools/profiler/tests/gtest/LulTestDwarf.cpp
+++ /dev/null
@@ -1,2597 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "gtest/gtest.h"
-#include "gmock/gmock.h"
-#include "LulCommonExt.h"
-#include "LulDwarfExt.h"
-#include "LulDwarfInt.h"
-#include "LulTestInfrastructure.h"
-
-using testing::Test;
-using testing::Return;
-using testing::Sequence;
-using testing::InSequence;
-using testing::_;
-using lul_test::CFISection;
-using lul_test::test_assembler::kBigEndian;
-using lul_test::test_assembler::kLittleEndian;
-using lul_test::test_assembler::Label;
-
-#define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) /**/
-#define PERHAPS_WRITE_EH_FRAME_FILE(name, section) /**/
-
-// Set this to 0 to make LUL be completely silent during tests.
-// Set it to 1 to get logging output from LUL, presumably for
-// the purpose of debugging it.
-#define DEBUG_LUL_TEST_DWARF 0
-
-// LUL needs a callback for its logging sink.
-static void
-gtest_logging_sink_for_LulTestDwarf(const char* str) {
- if (DEBUG_LUL_TEST_DWARF == 0) {
- return;
- }
- // Ignore any trailing \n, since LOG will add one anyway.
- size_t n = strlen(str);
- if (n > 0 && str[n-1] == '\n') {
- char* tmp = strdup(str);
- tmp[n-1] = 0;
- fprintf(stderr, "LUL-in-gtest: %s\n", tmp);
- free(tmp);
- } else {
- fprintf(stderr, "LUL-in-gtest: %s\n", str);
- }
-}
-
-namespace lul {
-
-class MockCallFrameInfoHandler : public CallFrameInfo::Handler {
- public:
- MOCK_METHOD6(Entry, bool(size_t offset, uint64 address, uint64 length,
- uint8 version, const std::string &augmentation,
- unsigned return_address));
- MOCK_METHOD2(UndefinedRule, bool(uint64 address, int reg));
- MOCK_METHOD2(SameValueRule, bool(uint64 address, int reg));
- MOCK_METHOD4(OffsetRule, bool(uint64 address, int reg, int base_register,
- long offset));
- MOCK_METHOD4(ValOffsetRule, bool(uint64 address, int reg, int base_register,
- long offset));
- MOCK_METHOD3(RegisterRule, bool(uint64 address, int reg, int base_register));
- MOCK_METHOD3(ExpressionRule, bool(uint64 address, int reg,
- const std::string &expression));
- MOCK_METHOD3(ValExpressionRule, bool(uint64 address, int reg,
- const std::string &expression));
- MOCK_METHOD0(End, bool());
- MOCK_METHOD2(PersonalityRoutine, bool(uint64 address, bool indirect));
- MOCK_METHOD2(LanguageSpecificDataArea, bool(uint64 address, bool indirect));
- MOCK_METHOD0(SignalHandler, bool());
-};
-
-class MockCallFrameErrorReporter : public CallFrameInfo::Reporter {
- public:
- MockCallFrameErrorReporter()
- : Reporter(gtest_logging_sink_for_LulTestDwarf,
- "mock filename", "mock section")
- { }
- MOCK_METHOD2(Incomplete, void(uint64, CallFrameInfo::EntryKind));
- MOCK_METHOD1(EarlyEHTerminator, void(uint64));
- MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64));
- MOCK_METHOD2(BadCIEId, void(uint64, uint64));
- MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version));
- MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &));
- MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8));
- MOCK_METHOD2(UnusablePointerEncoding, void(uint64, uint8));
- MOCK_METHOD2(RestoreInCIE, void(uint64, uint64));
- MOCK_METHOD3(BadInstruction, void(uint64, CallFrameInfo::EntryKind, uint64));
- MOCK_METHOD3(NoCFARule, void(uint64, CallFrameInfo::EntryKind, uint64));
- MOCK_METHOD3(EmptyStateStack, void(uint64, CallFrameInfo::EntryKind, uint64));
- MOCK_METHOD3(ClearingCFARule, void(uint64, CallFrameInfo::EntryKind, uint64));
-};
-
-struct CFIFixture {
-
- enum { kCFARegister = CallFrameInfo::Handler::kCFARegister };
-
- CFIFixture() {
- // Default expectations for the data handler.
- //
- // - Leave Entry and End without expectations, as it's probably a
- // good idea to set those explicitly in each test.
- //
- // - Expect the *Rule functions to not be called,
- // so that each test can simply list the calls they expect.
- //
- // I gather I could use StrictMock for this, but the manual seems
- // to suggest using that only as a last resort, and this isn't so
- // bad.
- EXPECT_CALL(handler, UndefinedRule(_, _)).Times(0);
- EXPECT_CALL(handler, SameValueRule(_, _)).Times(0);
- EXPECT_CALL(handler, OffsetRule(_, _, _, _)).Times(0);
- EXPECT_CALL(handler, ValOffsetRule(_, _, _, _)).Times(0);
- EXPECT_CALL(handler, RegisterRule(_, _, _)).Times(0);
- EXPECT_CALL(handler, ExpressionRule(_, _, _)).Times(0);
- EXPECT_CALL(handler, ValExpressionRule(_, _, _)).Times(0);
- EXPECT_CALL(handler, PersonalityRoutine(_, _)).Times(0);
- EXPECT_CALL(handler, LanguageSpecificDataArea(_, _)).Times(0);
- EXPECT_CALL(handler, SignalHandler()).Times(0);
-
- // Default expectations for the error/warning reporer.
- EXPECT_CALL(reporter, Incomplete(_, _)).Times(0);
- EXPECT_CALL(reporter, EarlyEHTerminator(_)).Times(0);
- EXPECT_CALL(reporter, CIEPointerOutOfRange(_, _)).Times(0);
- EXPECT_CALL(reporter, BadCIEId(_, _)).Times(0);
- EXPECT_CALL(reporter, UnrecognizedVersion(_, _)).Times(0);
- EXPECT_CALL(reporter, UnrecognizedAugmentation(_, _)).Times(0);
- EXPECT_CALL(reporter, InvalidPointerEncoding(_, _)).Times(0);
- EXPECT_CALL(reporter, UnusablePointerEncoding(_, _)).Times(0);
- EXPECT_CALL(reporter, RestoreInCIE(_, _)).Times(0);
- EXPECT_CALL(reporter, BadInstruction(_, _, _)).Times(0);
- EXPECT_CALL(reporter, NoCFARule(_, _, _)).Times(0);
- EXPECT_CALL(reporter, EmptyStateStack(_, _, _)).Times(0);
- EXPECT_CALL(reporter, ClearingCFARule(_, _, _)).Times(0);
- }
-
- MockCallFrameInfoHandler handler;
- MockCallFrameErrorReporter reporter;
-};
-
-class LulDwarfCFI: public CFIFixture, public Test { };
-
-TEST_F(LulDwarfCFI, EmptyRegion) {
- EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
- EXPECT_CALL(handler, End()).Times(0);
- static const char data[1] = { 42 };
-
- ByteReader reader(ENDIANNESS_BIG);
- CallFrameInfo parser(data, 0, &reader, &handler, &reporter);
- EXPECT_TRUE(parser.Start());
-}
-
-TEST_F(LulDwarfCFI, IncompleteLength32) {
- CFISection section(kBigEndian, 8);
- section
- // Not even long enough for an initial length.
- .D16(0xa0f)
- // Padding to keep valgrind happy. We subtract these off when we
- // construct the parser.
- .D16(0);
-
- EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
- EXPECT_CALL(handler, End()).Times(0);
-
- EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown))
- .WillOnce(Return());
-
- string contents;
- ASSERT_TRUE(section.GetContents(&contents));
-
- ByteReader reader(ENDIANNESS_BIG);
- reader.SetAddressSize(8);
- CallFrameInfo parser(contents.data(), contents.size() - 2,
- &reader, &handler, &reporter);
- EXPECT_FALSE(parser.Start());
-}
-
-TEST_F(LulDwarfCFI, IncompleteLength64) {
- CFISection section(kLittleEndian, 4);
- section
- // An incomplete 64-bit DWARF initial length.
- .D32(0xffffffff).D32(0x71fbaec2)
- // Padding to keep valgrind happy. We subtract these off when we
- // construct the parser.
- .D32(0);
-
- EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
- EXPECT_CALL(handler, End()).Times(0);
-
- EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown))
- .WillOnce(Return());
-
- string contents;
- ASSERT_TRUE(section.GetContents(&contents));
-
- ByteReader reader(ENDIANNESS_LITTLE);
- reader.SetAddressSize(4);
- CallFrameInfo parser(contents.data(), contents.size() - 4,
- &reader, &handler, &reporter);
- EXPECT_FALSE(parser.Start());
-}
-
-TEST_F(LulDwarfCFI, IncompleteId32) {
- CFISection section(kBigEndian, 8);
- section
- .D32(3) // Initial length, not long enough for id
- .D8(0xd7).D8(0xe5).D8(0xf1) // incomplete id
- .CIEHeader(8727, 3983, 8889, 3, "")
- .FinishEntry();
-
- EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
- EXPECT_CALL(handler, End()).Times(0);
-
- EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown))
- .WillOnce(Return());
-
- string contents;
- ASSERT_TRUE(section.GetContents(&contents));
-
- ByteReader reader(ENDIANNESS_BIG);
- reader.SetAddressSize(8);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_FALSE(parser.Start());
-}
-
-TEST_F(LulDwarfCFI, BadId32) {
- CFISection section(kBigEndian, 8);
- section
- .D32(0x100) // Initial length
- .D32(0xe802fade) // bogus ID
- .Append(0x100 - 4, 0x42); // make the length true
- section
- .CIEHeader(1672, 9872, 8529, 3, "")
- .FinishEntry();
-
- EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
- EXPECT_CALL(handler, End()).Times(0);
-
- EXPECT_CALL(reporter, CIEPointerOutOfRange(_, 0xe802fade))
- .WillOnce(Return());
-
- string contents;
- ASSERT_TRUE(section.GetContents(&contents));
-
- ByteReader reader(ENDIANNESS_BIG);
- reader.SetAddressSize(8);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_FALSE(parser.Start());
-}
-
-// A lone CIE shouldn't cause any handler calls.
-TEST_F(LulDwarfCFI, SingleCIE) {
- CFISection section(kLittleEndian, 4);
- section.CIEHeader(0xffe799a8, 0x3398dcdd, 0x6e9683de, 3, "");
- section.Append(10, lul::DW_CFA_nop);
- section.FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("SingleCIE", section);
-
- EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
- EXPECT_CALL(handler, End()).Times(0);
-
- string contents;
- EXPECT_TRUE(section.GetContents(&contents));
- ByteReader reader(ENDIANNESS_LITTLE);
- reader.SetAddressSize(4);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_TRUE(parser.Start());
-}
-
-// One FDE, one CIE.
-TEST_F(LulDwarfCFI, OneFDE) {
- CFISection section(kBigEndian, 4);
- Label cie;
- section
- .Mark(&cie)
- .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "")
- .FinishEntry()
- .FDEHeader(cie, 0x7714740d, 0x3d5a10cd)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("OneFDE", section);
-
- {
- InSequence s;
- EXPECT_CALL(handler,
- Entry(_, 0x7714740d, 0x3d5a10cd, 3, "", 0x6b6efb87))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
-
- string contents;
- EXPECT_TRUE(section.GetContents(&contents));
- ByteReader reader(ENDIANNESS_BIG);
- reader.SetAddressSize(4);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_TRUE(parser.Start());
-}
-
-// Two FDEs share a CIE.
-TEST_F(LulDwarfCFI, TwoFDEsOneCIE) {
- CFISection section(kBigEndian, 4);
- Label cie;
- section
- // First FDE. readelf complains about this one because it makes
- // a forward reference to its CIE.
- .FDEHeader(cie, 0xa42744df, 0xa3b42121)
- .FinishEntry()
- // CIE.
- .Mark(&cie)
- .CIEHeader(0x04f7dc7b, 0x3d00c05f, 0xbd43cb59, 3, "")
- .FinishEntry()
- // Second FDE.
- .FDEHeader(cie, 0x6057d391, 0x700f608d)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsOneCIE", section);
-
- {
- InSequence s;
- EXPECT_CALL(handler,
- Entry(_, 0xa42744df, 0xa3b42121, 3, "", 0xbd43cb59))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
- {
- InSequence s;
- EXPECT_CALL(handler,
- Entry(_, 0x6057d391, 0x700f608d, 3, "", 0xbd43cb59))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
-
- string contents;
- EXPECT_TRUE(section.GetContents(&contents));
- ByteReader reader(ENDIANNESS_BIG);
- reader.SetAddressSize(4);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_TRUE(parser.Start());
-}
-
-// Two FDEs, two CIEs.
-TEST_F(LulDwarfCFI, TwoFDEsTwoCIEs) {
- CFISection section(kLittleEndian, 8);
- Label cie1, cie2;
- section
- // First CIE.
- .Mark(&cie1)
- .CIEHeader(0x694d5d45, 0x4233221b, 0xbf45e65a, 3, "")
- .FinishEntry()
- // First FDE which cites second CIE. readelf complains about
- // this one because it makes a forward reference to its CIE.
- .FDEHeader(cie2, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL)
- .FinishEntry()
- // Second FDE, which cites first CIE.
- .FDEHeader(cie1, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL)
- .FinishEntry()
- // Second CIE.
- .Mark(&cie2)
- .CIEHeader(0xfba3fad7, 0x6287e1fd, 0x61d2c581, 2, "")
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsTwoCIEs", section);
-
- {
- InSequence s;
- EXPECT_CALL(handler,
- Entry(_, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL, 2,
- "", 0x61d2c581))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
- {
- InSequence s;
- EXPECT_CALL(handler,
- Entry(_, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL, 3,
- "", 0xbf45e65a))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
-
- string contents;
- EXPECT_TRUE(section.GetContents(&contents));
- ByteReader reader(ENDIANNESS_LITTLE);
- reader.SetAddressSize(8);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_TRUE(parser.Start());
-}
-
-// An FDE whose CIE specifies a version we don't recognize.
-TEST_F(LulDwarfCFI, BadVersion) {
- CFISection section(kBigEndian, 4);
- Label cie1, cie2;
- section
- .Mark(&cie1)
- .CIEHeader(0xca878cf0, 0x7698ec04, 0x7b616f54, 0x52, "")
- .FinishEntry()
- // We should skip this entry, as its CIE specifies a version we
- // don't recognize.
- .FDEHeader(cie1, 0x08852292, 0x2204004a)
- .FinishEntry()
- // Despite the above, we should visit this entry.
- .Mark(&cie2)
- .CIEHeader(0x7c3ae7c9, 0xb9b9a512, 0x96cb3264, 3, "")
- .FinishEntry()
- .FDEHeader(cie2, 0x2094735a, 0x6e875501)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("BadVersion", section);
-
- EXPECT_CALL(reporter, UnrecognizedVersion(_, 0x52))
- .WillOnce(Return());
-
- {
- InSequence s;
- // We should see no mention of the first FDE, but we should get
- // a call to Entry for the second.
- EXPECT_CALL(handler, Entry(_, 0x2094735a, 0x6e875501, 3, "",
- 0x96cb3264))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .WillOnce(Return(true));
- }
-
- string contents;
- EXPECT_TRUE(section.GetContents(&contents));
- ByteReader reader(ENDIANNESS_BIG);
- reader.SetAddressSize(4);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_FALSE(parser.Start());
-}
-
-// An FDE whose CIE specifies an augmentation we don't recognize.
-TEST_F(LulDwarfCFI, BadAugmentation) {
- CFISection section(kBigEndian, 4);
- Label cie1, cie2;
- section
- .Mark(&cie1)
- .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "spaniels!")
- .FinishEntry()
- // We should skip this entry, as its CIE specifies an
- // augmentation we don't recognize.
- .FDEHeader(cie1, 0x7714740d, 0x3d5a10cd)
- .FinishEntry()
- // Despite the above, we should visit this entry.
- .Mark(&cie2)
- .CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 3, "")
- .FinishEntry()
- .FDEHeader(cie2, 0x7bf0fda0, 0xcbcd28d8)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("BadAugmentation", section);
-
- EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "spaniels!"))
- .WillOnce(Return());
-
- {
- InSequence s;
- // We should see no mention of the first FDE, but we should get
- // a call to Entry for the second.
- EXPECT_CALL(handler, Entry(_, 0x7bf0fda0, 0xcbcd28d8, 3, "",
- 0xf2f519b2))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .WillOnce(Return(true));
- }
-
- string contents;
- EXPECT_TRUE(section.GetContents(&contents));
- ByteReader reader(ENDIANNESS_BIG);
- reader.SetAddressSize(4);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_FALSE(parser.Start());
-}
-
-// The return address column field is a byte in CFI version 1
-// (DWARF2), but a ULEB128 value in version 3 (DWARF3).
-TEST_F(LulDwarfCFI, CIEVersion1ReturnColumn) {
- CFISection section(kBigEndian, 4);
- Label cie;
- section
- // CIE, using the version 1 format: return column is a ubyte.
- .Mark(&cie)
- // Use a value for the return column that is parsed differently
- // as a ubyte and as a ULEB128.
- .CIEHeader(0xbcdea24f, 0x5be28286, 0x9f, 1, "")
- .FinishEntry()
- // FDE, citing that CIE.
- .FDEHeader(cie, 0xb8d347b5, 0x825e55dc)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion1ReturnColumn", section);
-
- {
- InSequence s;
- EXPECT_CALL(handler, Entry(_, 0xb8d347b5, 0x825e55dc, 1, "", 0x9f))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
-
- string contents;
- EXPECT_TRUE(section.GetContents(&contents));
- ByteReader reader(ENDIANNESS_BIG);
- reader.SetAddressSize(4);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_TRUE(parser.Start());
-}
-
-// The return address column field is a byte in CFI version 1
-// (DWARF2), but a ULEB128 value in version 3 (DWARF3).
-TEST_F(LulDwarfCFI, CIEVersion3ReturnColumn) {
- CFISection section(kBigEndian, 4);
- Label cie;
- section
- // CIE, using the version 3 format: return column is a ULEB128.
- .Mark(&cie)
- // Use a value for the return column that is parsed differently
- // as a ubyte and as a ULEB128.
- .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 3, "")
- .FinishEntry()
- // FDE, citing that CIE.
- .FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section);
-
- {
- InSequence s;
- EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 3, "", 0x89))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
-
- string contents;
- EXPECT_TRUE(section.GetContents(&contents));
- ByteReader reader(ENDIANNESS_BIG);
- reader.SetAddressSize(4);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter);
- EXPECT_TRUE(parser.Start());
-}
-
-struct CFIInsnFixture: public CFIFixture {
- CFIInsnFixture() : CFIFixture() {
- data_factor = 0xb6f;
- return_register = 0x9be1ed9f;
- version = 3;
- cfa_base_register = 0x383a3aa;
- cfa_offset = 0xf748;
- }
-
- // Prepare SECTION to receive FDE instructions.
- //
- // - Append a stock CIE header that establishes the fixture's
- // code_factor, data_factor, return_register, version, and
- // augmentation values.
- // - Have the CIE set up a CFA rule using cfa_base_register and
- // cfa_offset.
- // - Append a stock FDE header, referring to the above CIE, for the
- // fde_size bytes at fde_start. Choose fde_start and fde_size
- // appropriately for the section's address size.
- // - Set appropriate expectations on handler in sequence s for the
- // frame description entry and the CIE's CFA rule.
- //
- // On return, SECTION is ready to have FDE instructions appended to
- // it, and its FinishEntry member called.
- void StockCIEAndFDE(CFISection *section) {
- // Choose appropriate constants for our address size.
- if (section->AddressSize() == 4) {
- fde_start = 0xc628ecfbU;
- fde_size = 0x5dee04a2;
- code_factor = 0x60b;
- } else {
- assert(section->AddressSize() == 8);
- fde_start = 0x0005c57ce7806bd3ULL;
- fde_size = 0x2699521b5e333100ULL;
- code_factor = 0x01008e32855274a8ULL;
- }
-
- // Create the CIE.
- (*section)
- .Mark(&cie_label)
- .CIEHeader(code_factor, data_factor, return_register, version,
- "")
- .D8(lul::DW_CFA_def_cfa)
- .ULEB128(cfa_base_register)
- .ULEB128(cfa_offset)
- .FinishEntry();
-
- // Create the FDE.
- section->FDEHeader(cie_label, fde_start, fde_size);
-
- // Expect an Entry call for the FDE and a ValOffsetRule call for the
- // CIE's CFA rule.
- EXPECT_CALL(handler, Entry(_, fde_start, fde_size, version, "",
- return_register))
- .InSequence(s)
- .WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(fde_start, kCFARegister,
- cfa_base_register, cfa_offset))
- .InSequence(s)
- .WillOnce(Return(true));
- }
-
- // Run the contents of SECTION through a CallFrameInfo parser,
- // expecting parser.Start to return SUCCEEDS. Caller may optionally
- // supply, via READER, its own ByteReader. If that's absent, a
- // local one is used.
- void ParseSection(CFISection *section,
- bool succeeds = true, ByteReader* reader = nullptr) {
- string contents;
- EXPECT_TRUE(section->GetContents(&contents));
- lul::Endianness endianness;
- if (section->endianness() == kBigEndian)
- endianness = ENDIANNESS_BIG;
- else {
- assert(section->endianness() == kLittleEndian);
- endianness = ENDIANNESS_LITTLE;
- }
- ByteReader local_reader(endianness);
- ByteReader* reader_to_use = reader ? reader : &local_reader;
- reader_to_use->SetAddressSize(section->AddressSize());
- CallFrameInfo parser(contents.data(), contents.size(),
- reader_to_use, &handler, &reporter);
- if (succeeds)
- EXPECT_TRUE(parser.Start());
- else
- EXPECT_FALSE(parser.Start());
- }
-
- Label cie_label;
- Sequence s;
- uint64 code_factor;
- int data_factor;
- unsigned return_register;
- unsigned version;
- unsigned cfa_base_register;
- int cfa_offset;
- uint64 fde_start, fde_size;
-};
-
-class LulDwarfCFIInsn: public CFIInsnFixture, public Test { };
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_set_loc) {
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_set_loc).D32(0xb1ee3e7a)
- // Use DW_CFA_def_cfa to force a handler call that we can use to
- // check the effect of the DW_CFA_set_loc.
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x4defb431).ULEB128(0x6d17b0ee)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_set_loc", section);
-
- EXPECT_CALL(handler,
- ValOffsetRule(0xb1ee3e7a, kCFARegister, 0x4defb431, 0x6d17b0ee))
- .InSequence(s)
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_advance_loc) {
- CFISection section(kBigEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_advance_loc | 0x2a)
- // Use DW_CFA_def_cfa to force a handler call that we can use to
- // check the effect of the DW_CFA_advance_loc.
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x5bbb3715).ULEB128(0x0186c7bf)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc", section);
-
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start + 0x2a * code_factor,
- kCFARegister, 0x5bbb3715, 0x0186c7bf))
- .InSequence(s)
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_advance_loc1) {
- CFISection section(kLittleEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_advance_loc1).D8(0xd8)
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x69d5696a).ULEB128(0x1eb7fc93)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc1", section);
-
- EXPECT_CALL(handler,
- ValOffsetRule((fde_start + 0xd8 * code_factor),
- kCFARegister, 0x69d5696a, 0x1eb7fc93))
- .InSequence(s)
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_advance_loc2) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_advance_loc2).D16(0x3adb)
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x3a368bed).ULEB128(0x3194ee37)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc2", section);
-
- EXPECT_CALL(handler,
- ValOffsetRule((fde_start + 0x3adb * code_factor),
- kCFARegister, 0x3a368bed, 0x3194ee37))
- .InSequence(s)
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_advance_loc4) {
- CFISection section(kBigEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_advance_loc4).D32(0x15813c88)
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x135270c5).ULEB128(0x24bad7cb)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc4", section);
-
- EXPECT_CALL(handler,
- ValOffsetRule((fde_start + 0x15813c88ULL * code_factor),
- kCFARegister, 0x135270c5, 0x24bad7cb))
- .InSequence(s)
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_MIPS_advance_loc8) {
- code_factor = 0x2d;
- CFISection section(kBigEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_MIPS_advance_loc8).D64(0x3c4f3945b92c14ULL)
- .D8(lul::DW_CFA_def_cfa).ULEB128(0xe17ed602).ULEB128(0x3d162e7f)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc8", section);
-
- EXPECT_CALL(handler,
- ValOffsetRule((fde_start + 0x3c4f3945b92c14ULL * code_factor),
- kCFARegister, 0xe17ed602, 0x3d162e7f))
- .InSequence(s)
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_def_cfa) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x4e363a85).ULEB128(0x815f9aa7)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_def_cfa", section);
-
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, kCFARegister, 0x4e363a85, 0x815f9aa7))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_def_cfa_sf) {
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_def_cfa_sf).ULEB128(0x8ccb32b7).LEB128(0x9ea)
- .D8(lul::DW_CFA_def_cfa_sf).ULEB128(0x9b40f5da).LEB128(-0x40a2)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, kCFARegister, 0x8ccb32b7,
- 0x9ea * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, kCFARegister, 0x9b40f5da,
- -0x40a2 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_def_cfa_register) {
- CFISection section(kLittleEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_def_cfa_register).ULEB128(0x3e7e9363)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, kCFARegister, 0x3e7e9363, cfa_offset))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-// DW_CFA_def_cfa_register should have no effect when applied to a
-// non-base/offset rule.
-TEST_F(LulDwarfCFIInsn, DW_CFA_def_cfa_registerBadRule) {
- ByteReader reader(ENDIANNESS_BIG);
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_def_cfa_expression).Block("needle in a haystack")
- .D8(lul::DW_CFA_def_cfa_register).ULEB128(0xf1b49e49)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValExpressionRule(fde_start, kCFARegister,
- "needle in a haystack"))
- .WillRepeatedly(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_def_cfa_offset) {
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, kCFARegister, cfa_base_register,
- 0x1e8e3b9b))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_def_cfa_offset_sf) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_def_cfa_offset_sf).LEB128(0x970)
- .D8(lul::DW_CFA_def_cfa_offset_sf).LEB128(-0x2cd)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, kCFARegister, cfa_base_register,
- 0x970 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, kCFARegister, cfa_base_register,
- -0x2cd * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-// DW_CFA_def_cfa_offset should have no effect when applied to a
-// non-base/offset rule.
-TEST_F(LulDwarfCFIInsn, DW_CFA_def_cfa_offsetBadRule) {
- ByteReader reader(ENDIANNESS_BIG);
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_def_cfa_expression).Block("six ways to Sunday")
- .D8(lul::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValExpressionRule(fde_start, kCFARegister,
- "six ways to Sunday"))
- .WillRepeatedly(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_def_cfa_expression) {
- ByteReader reader(ENDIANNESS_LITTLE);
- CFISection section(kLittleEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_def_cfa_expression).Block("eating crow")
- .FinishEntry();
-
- EXPECT_CALL(handler, ValExpressionRule(fde_start, kCFARegister,
- "eating crow"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_undefined) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_undefined).ULEB128(0x300ce45d)
- .FinishEntry();
-
- EXPECT_CALL(handler, UndefinedRule(fde_start, 0x300ce45d))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_same_value) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_same_value).ULEB128(0x3865a760)
- .FinishEntry();
-
- EXPECT_CALL(handler, SameValueRule(fde_start, 0x3865a760))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_offset) {
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_offset | 0x2c).ULEB128(0x9f6)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- OffsetRule(fde_start, 0x2c, kCFARegister, 0x9f6 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_offset_extended) {
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_offset_extended).ULEB128(0x402b).ULEB128(0xb48)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- OffsetRule(fde_start,
- 0x402b, kCFARegister, 0xb48 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_offset_extended_sf) {
- CFISection section(kBigEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_offset_extended_sf)
- .ULEB128(0x997c23ee).LEB128(0x2d00)
- .D8(lul::DW_CFA_offset_extended_sf)
- .ULEB128(0x9519eb82).LEB128(-0xa77)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- OffsetRule(fde_start, 0x997c23ee,
- kCFARegister, 0x2d00 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler,
- OffsetRule(fde_start, 0x9519eb82,
- kCFARegister, -0xa77 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_val_offset) {
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_val_offset).ULEB128(0x623562fe).ULEB128(0x673)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, 0x623562fe,
- kCFARegister, 0x673 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_val_offset_sf) {
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_val_offset_sf).ULEB128(0x6f4f).LEB128(0xaab)
- .D8(lul::DW_CFA_val_offset_sf).ULEB128(0x2483).LEB128(-0x8a2)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, 0x6f4f,
- kCFARegister, 0xaab * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, 0x2483,
- kCFARegister, -0x8a2 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_register) {
- CFISection section(kLittleEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_register).ULEB128(0x278d18f9).ULEB128(0x1a684414)
- .FinishEntry();
-
- EXPECT_CALL(handler, RegisterRule(fde_start, 0x278d18f9, 0x1a684414))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_expression) {
- ByteReader reader(ENDIANNESS_BIG);
- CFISection section(kBigEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_expression).ULEB128(0xa1619fb2)
- .Block("plus ça change, plus c'est la même chose")
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ExpressionRule(fde_start, 0xa1619fb2,
- "plus ça change, plus c'est la même chose"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_val_expression) {
- ByteReader reader(ENDIANNESS_BIG);
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_val_expression).ULEB128(0xc5e4a9e3)
- .Block("he who has the gold makes the rules")
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValExpressionRule(fde_start, 0xc5e4a9e3,
- "he who has the gold makes the rules"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_restore) {
- CFISection section(kLittleEndian, 8);
- code_factor = 0x01bd188a9b1fa083ULL;
- data_factor = -0x1ac8;
- return_register = 0x8c35b049;
- version = 2;
- fde_start = 0x2d70fe998298bbb1ULL;
- fde_size = 0x46ccc2e63cf0b108ULL;
- Label cie;
- section
- .Mark(&cie)
- .CIEHeader(code_factor, data_factor, return_register, version,
- "")
- // Provide a CFA rule, because register rules require them.
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x6ca1d50e).ULEB128(0x372e38e8)
- // Provide an offset(N) rule for register 0x3c.
- .D8(lul::DW_CFA_offset | 0x3c).ULEB128(0xb348)
- .FinishEntry()
- // In the FDE...
- .FDEHeader(cie, fde_start, fde_size)
- // At a second address, provide a new offset(N) rule for register 0x3c.
- .D8(lul::DW_CFA_advance_loc | 0x13)
- .D8(lul::DW_CFA_offset | 0x3c).ULEB128(0x9a50)
- // At a third address, restore the original rule for register 0x3c.
- .D8(lul::DW_CFA_advance_loc | 0x01)
- .D8(lul::DW_CFA_restore | 0x3c)
- .FinishEntry();
-
- {
- InSequence s;
- EXPECT_CALL(handler,
- Entry(_, fde_start, fde_size, version, "", return_register))
- .WillOnce(Return(true));
- // CIE's CFA rule.
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start,
- kCFARegister, 0x6ca1d50e, 0x372e38e8))
- .WillOnce(Return(true));
- // CIE's rule for register 0x3c.
- EXPECT_CALL(handler,
- OffsetRule(fde_start, 0x3c,
- kCFARegister, 0xb348 * data_factor))
- .WillOnce(Return(true));
- // FDE's rule for register 0x3c.
- EXPECT_CALL(handler,
- OffsetRule(fde_start + 0x13 * code_factor, 0x3c,
- kCFARegister, 0x9a50 * data_factor))
- .WillOnce(Return(true));
- // Restore CIE's rule for register 0x3c.
- EXPECT_CALL(handler,
- OffsetRule(fde_start + (0x13 + 0x01) * code_factor, 0x3c,
- kCFARegister, 0xb348 * data_factor))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_restoreNoRule) {
- CFISection section(kBigEndian, 4);
- code_factor = 0x005f78143c1c3b82ULL;
- data_factor = 0x25d0;
- return_register = 0xe8;
- version = 1;
- fde_start = 0x4062e30f;
- fde_size = 0x5302a389;
- Label cie;
- section
- .Mark(&cie)
- .CIEHeader(code_factor, data_factor, return_register, version, "")
- // Provide a CFA rule, because register rules require them.
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x470aa334).ULEB128(0x099ef127)
- .FinishEntry()
- // In the FDE...
- .FDEHeader(cie, fde_start, fde_size)
- // At a second address, provide an offset(N) rule for register 0x2c.
- .D8(lul::DW_CFA_advance_loc | 0x7)
- .D8(lul::DW_CFA_offset | 0x2c).ULEB128(0x1f47)
- // At a third address, restore the (missing) CIE rule for register 0x2c.
- .D8(lul::DW_CFA_advance_loc | 0xb)
- .D8(lul::DW_CFA_restore | 0x2c)
- .FinishEntry();
-
- {
- InSequence s;
- EXPECT_CALL(handler,
- Entry(_, fde_start, fde_size, version, "", return_register))
- .WillOnce(Return(true));
- // CIE's CFA rule.
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start,
- kCFARegister, 0x470aa334, 0x099ef127))
- .WillOnce(Return(true));
- // FDE's rule for register 0x2c.
- EXPECT_CALL(handler,
- OffsetRule(fde_start + 0x7 * code_factor, 0x2c,
- kCFARegister, 0x1f47 * data_factor))
- .WillOnce(Return(true));
- // Restore CIE's (missing) rule for register 0x2c.
- EXPECT_CALL(handler,
- SameValueRule(fde_start + (0x7 + 0xb) * code_factor, 0x2c))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_restore_extended) {
- CFISection section(kBigEndian, 4);
- code_factor = 0x126e;
- data_factor = -0xd8b;
- return_register = 0x77711787;
- version = 3;
- fde_start = 0x01f55a45;
- fde_size = 0x452adb80;
- Label cie;
- section
- .Mark(&cie)
- .CIEHeader(code_factor, data_factor, return_register, version,
- "", true /* dwarf64 */ )
- // Provide a CFA rule, because register rules require them.
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x56fa0edd).ULEB128(0x097f78a5)
- // Provide an offset(N) rule for register 0x0f9b8a1c.
- .D8(lul::DW_CFA_offset_extended)
- .ULEB128(0x0f9b8a1c).ULEB128(0xc979)
- .FinishEntry()
- // In the FDE...
- .FDEHeader(cie, fde_start, fde_size)
- // At a second address, provide a new offset(N) rule for reg 0x0f9b8a1c.
- .D8(lul::DW_CFA_advance_loc | 0x3)
- .D8(lul::DW_CFA_offset_extended)
- .ULEB128(0x0f9b8a1c).ULEB128(0x3b7b)
- // At a third address, restore the original rule for register 0x0f9b8a1c.
- .D8(lul::DW_CFA_advance_loc | 0x04)
- .D8(lul::DW_CFA_restore_extended).ULEB128(0x0f9b8a1c)
- .FinishEntry();
-
- {
- InSequence s;
- EXPECT_CALL(handler,
- Entry(_, fde_start, fde_size, version, "", return_register))
- .WillOnce(Return(true));
- // CIE's CFA rule.
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, kCFARegister, 0x56fa0edd, 0x097f78a5))
- .WillOnce(Return(true));
- // CIE's rule for register 0x0f9b8a1c.
- EXPECT_CALL(handler,
- OffsetRule(fde_start, 0x0f9b8a1c, kCFARegister,
- 0xc979 * data_factor))
- .WillOnce(Return(true));
- // FDE's rule for register 0x0f9b8a1c.
- EXPECT_CALL(handler,
- OffsetRule(fde_start + 0x3 * code_factor, 0x0f9b8a1c,
- kCFARegister, 0x3b7b * data_factor))
- .WillOnce(Return(true));
- // Restore CIE's rule for register 0x0f9b8a1c.
- EXPECT_CALL(handler,
- OffsetRule(fde_start + (0x3 + 0x4) * code_factor, 0x0f9b8a1c,
- kCFARegister, 0xc979 * data_factor))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
- }
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_remember_and_restore_state) {
- CFISection section(kLittleEndian, 8);
- StockCIEAndFDE(&section);
-
- // We create a state, save it, modify it, and then restore. We
- // refer to the state that is overridden the restore as the
- // "outgoing" state, and the restored state the "incoming" state.
- //
- // Register outgoing incoming expect
- // 1 offset(N) no rule new "same value" rule
- // 2 register(R) offset(N) report changed rule
- // 3 offset(N) offset(M) report changed offset
- // 4 offset(N) offset(N) no report
- // 5 offset(N) no rule new "same value" rule
- section
- // Create the "incoming" state, which we will save and later restore.
- .D8(lul::DW_CFA_offset | 2).ULEB128(0x9806)
- .D8(lul::DW_CFA_offset | 3).ULEB128(0x995d)
- .D8(lul::DW_CFA_offset | 4).ULEB128(0x7055)
- .D8(lul::DW_CFA_remember_state)
- // Advance to a new instruction; an implementation could legitimately
- // ignore all but the final rule for a given register at a given address.
- .D8(lul::DW_CFA_advance_loc | 1)
- // Create the "outgoing" state, which we will discard.
- .D8(lul::DW_CFA_offset | 1).ULEB128(0xea1a)
- .D8(lul::DW_CFA_register).ULEB128(2).ULEB128(0x1d2a3767)
- .D8(lul::DW_CFA_offset | 3).ULEB128(0xdd29)
- .D8(lul::DW_CFA_offset | 5).ULEB128(0xf1ce)
- // At a third address, restore the incoming state.
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- uint64 addr = fde_start;
-
- // Expect the incoming rules to be reported.
- EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, OffsetRule(addr, 4, kCFARegister, 0x7055 * data_factor))
- .InSequence(s).WillOnce(Return(true));
-
- addr += code_factor;
-
- // After the save, we establish the outgoing rule set.
- EXPECT_CALL(handler, OffsetRule(addr, 1, kCFARegister, 0xea1a * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, RegisterRule(addr, 2, 0x1d2a3767))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0xdd29 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, OffsetRule(addr, 5, kCFARegister, 0xf1ce * data_factor))
- .InSequence(s).WillOnce(Return(true));
-
- addr += code_factor;
-
- // Finally, after the restore, expect to see the differences from
- // the outgoing to the incoming rules reported.
- EXPECT_CALL(handler, SameValueRule(addr, 1))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, SameValueRule(addr, 5))
- .InSequence(s).WillOnce(Return(true));
-
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-// Check that restoring a rule set reports changes to the CFA rule.
-TEST_F(LulDwarfCFIInsn, DW_CFA_remember_and_restore_stateCFA) {
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
-
- section
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_def_cfa_offset).ULEB128(0x90481102)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, kCFARegister,
- cfa_base_register, 0x90481102))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor * 2, kCFARegister,
- cfa_base_register, cfa_offset))
- .InSequence(s).WillOnce(Return(true));
-
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_nop) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_nop)
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x3fb8d4f1).ULEB128(0x078dc67b)
- .D8(lul::DW_CFA_nop)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- ValOffsetRule(fde_start, kCFARegister, 0x3fb8d4f1, 0x078dc67b))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_GNU_window_save) {
- CFISection section(kBigEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_GNU_window_save)
- .FinishEntry();
-
- // Don't include all the rules in any particular sequence.
-
- // The caller's %o0-%o7 have become the callee's %i0-%i7. This is
- // the GCC register numbering.
- for (int i = 8; i < 16; i++)
- EXPECT_CALL(handler, RegisterRule(fde_start, i, i + 16))
- .WillOnce(Return(true));
- // The caller's %l0-%l7 and %i0-%i7 have been saved at the top of
- // its frame.
- for (int i = 16; i < 32; i++)
- EXPECT_CALL(handler, OffsetRule(fde_start, i, kCFARegister, (i-16) * 4))
- .WillOnce(Return(true));
-
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_GNU_args_size) {
- CFISection section(kLittleEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_GNU_args_size).ULEB128(0xeddfa520)
- // Verify that we see this, meaning we parsed the above properly.
- .D8(lul::DW_CFA_offset | 0x23).ULEB128(0x269)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- OffsetRule(fde_start, 0x23, kCFARegister, 0x269 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIInsn, DW_CFA_GNU_negative_offset_extended) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_GNU_negative_offset_extended)
- .ULEB128(0x430cc87a).ULEB128(0x613)
- .FinishEntry();
-
- EXPECT_CALL(handler,
- OffsetRule(fde_start, 0x430cc87a,
- kCFARegister, -0x613 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-// Three FDEs: skip the second
-TEST_F(LulDwarfCFIInsn, SkipFDE) {
- CFISection section(kBigEndian, 4);
- Label cie;
- section
- // CIE, used by all FDEs.
- .Mark(&cie)
- .CIEHeader(0x010269f2, 0x9177, 0xedca5849, 2, "")
- .D8(lul::DW_CFA_def_cfa).ULEB128(0x42ed390b).ULEB128(0x98f43aad)
- .FinishEntry()
- // First FDE.
- .FDEHeader(cie, 0xa870ebdd, 0x60f6aa4)
- .D8(lul::DW_CFA_register).ULEB128(0x3a860351).ULEB128(0x6c9a6bcf)
- .FinishEntry()
- // Second FDE.
- .FDEHeader(cie, 0xc534f7c0, 0xf6552e9, true /* dwarf64 */)
- .D8(lul::DW_CFA_register).ULEB128(0x1b62c234).ULEB128(0x26586b18)
- .FinishEntry()
- // Third FDE.
- .FDEHeader(cie, 0xf681cfc8, 0x7e4594e)
- .D8(lul::DW_CFA_register).ULEB128(0x26c53934).ULEB128(0x18eeb8a4)
- .FinishEntry();
-
- {
- InSequence s;
-
- // Process the first FDE.
- EXPECT_CALL(handler, Entry(_, 0xa870ebdd, 0x60f6aa4, 2, "", 0xedca5849))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(0xa870ebdd, kCFARegister,
- 0x42ed390b, 0x98f43aad))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, RegisterRule(0xa870ebdd, 0x3a860351, 0x6c9a6bcf))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .WillOnce(Return(true));
-
- // Skip the second FDE.
- EXPECT_CALL(handler, Entry(_, 0xc534f7c0, 0xf6552e9, 2, "", 0xedca5849))
- .WillOnce(Return(false));
-
- // Process the third FDE.
- EXPECT_CALL(handler, Entry(_, 0xf681cfc8, 0x7e4594e, 2, "", 0xedca5849))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(0xf681cfc8, kCFARegister,
- 0x42ed390b, 0x98f43aad))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, RegisterRule(0xf681cfc8, 0x26c53934, 0x18eeb8a4))
- .WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .WillOnce(Return(true));
- }
-
- ParseSection(&section);
-}
-
-// Quit processing in the middle of an entry's instructions.
-TEST_F(LulDwarfCFIInsn, QuitMidentry) {
- CFISection section(kLittleEndian, 8);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_register).ULEB128(0xe0cf850d).ULEB128(0x15aab431)
- .D8(lul::DW_CFA_expression).ULEB128(0x46750aa5).Block("meat")
- .FinishEntry();
-
- EXPECT_CALL(handler, RegisterRule(fde_start, 0xe0cf850d, 0x15aab431))
- .InSequence(s).WillOnce(Return(false));
- EXPECT_CALL(handler, End())
- .InSequence(s).WillOnce(Return(true));
-
- ParseSection(&section, false);
-}
-
-class LulDwarfCFIRestore: public CFIInsnFixture, public Test { };
-
-TEST_F(LulDwarfCFIRestore, RestoreUndefinedRuleUnchanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_undefined).ULEB128(0x0bac878e)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, UndefinedRule(fde_start, 0x0bac878e))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreUndefinedRuleChanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_undefined).ULEB128(0x7dedff5f)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_same_value).ULEB128(0x7dedff5f)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, UndefinedRule(fde_start, 0x7dedff5f))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, SameValueRule(fde_start + code_factor, 0x7dedff5f))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(fde_start + 2 * code_factor, 0x7dedff5f))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreSameValueRuleUnchanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_same_value).ULEB128(0xadbc9b3a)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, SameValueRule(fde_start, 0xadbc9b3a))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreSameValueRuleChanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_same_value).ULEB128(0x3d90dcb5)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_undefined).ULEB128(0x3d90dcb5)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, SameValueRule(fde_start, 0x3d90dcb5))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x3d90dcb5))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, SameValueRule(fde_start + 2 * code_factor, 0x3d90dcb5))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreOffsetRuleUnchanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_offset | 0x14).ULEB128(0xb6f)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, OffsetRule(fde_start, 0x14,
- kCFARegister, 0xb6f * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreOffsetRuleChanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_offset | 0x21).ULEB128(0xeb7)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_undefined).ULEB128(0x21)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, OffsetRule(fde_start, 0x21,
- kCFARegister, 0xeb7 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x21))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21,
- kCFARegister, 0xeb7 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreOffsetRuleChangedOffset) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_offset | 0x21).ULEB128(0x134)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_offset | 0x21).ULEB128(0xf4f)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, OffsetRule(fde_start, 0x21,
- kCFARegister, 0x134 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, OffsetRule(fde_start + code_factor, 0x21,
- kCFARegister, 0xf4f * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21,
- kCFARegister, 0x134 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreValOffsetRuleUnchanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_val_offset).ULEB128(0x829caee6).ULEB128(0xe4c)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x829caee6,
- kCFARegister, 0xe4c * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreValOffsetRuleChanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_val_offset).ULEB128(0xf17c36d6).ULEB128(0xeb7)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_undefined).ULEB128(0xf17c36d6)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, ValOffsetRule(fde_start, 0xf17c36d6,
- kCFARegister, 0xeb7 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xf17c36d6))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0xf17c36d6,
- kCFARegister, 0xeb7 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreValOffsetRuleChangedValOffset) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0x562)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0xe88)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x2cf0ab1b,
- kCFARegister, 0x562 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, 0x2cf0ab1b,
- kCFARegister, 0xe88 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0x2cf0ab1b,
- kCFARegister, 0x562 * data_factor))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreRegisterRuleUnchanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_register).ULEB128(0x77514acc).ULEB128(0x464de4ce)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, RegisterRule(fde_start, 0x77514acc, 0x464de4ce))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreRegisterRuleChanged) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_register).ULEB128(0xe39acce5).ULEB128(0x095f1559)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_undefined).ULEB128(0xe39acce5)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, RegisterRule(fde_start, 0xe39acce5, 0x095f1559))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xe39acce5))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xe39acce5,
- 0x095f1559))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreRegisterRuleChangedRegister) {
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0x16607d6a)
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0xbabb4742)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, RegisterRule(fde_start, 0xd40e21b1, 0x16607d6a))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, RegisterRule(fde_start + code_factor, 0xd40e21b1,
- 0xbabb4742))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xd40e21b1,
- 0x16607d6a))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreExpressionRuleUnchanged) {
- ByteReader reader(ENDIANNESS_LITTLE);
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_expression).ULEB128(0x666ae152).Block("dwarf")
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, ExpressionRule(fde_start, 0x666ae152, "dwarf"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreExpressionRuleChanged) {
- ByteReader reader(ENDIANNESS_LITTLE);
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_expression).ULEB128(0xb5ca5c46).Block("elf")
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_undefined).ULEB128(0xb5ca5c46)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, ExpressionRule(fde_start, 0xb5ca5c46, "elf"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46,
- "elf"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreExpressionRuleChangedExpression) {
- ByteReader reader(ENDIANNESS_LITTLE);
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_expression).ULEB128(0x500f5739).Block("smurf")
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_expression).ULEB128(0x500f5739).Block("orc")
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, ExpressionRule(fde_start, 0x500f5739, "smurf"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ExpressionRule(fde_start + code_factor, 0x500f5739,
- "orc"))
- .InSequence(s).WillOnce(Return(true));
- // Expectations are not wishes.
- EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0x500f5739,
- "smurf"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreValExpressionRuleUnchanged) {
- ByteReader reader(ENDIANNESS_LITTLE);
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_val_expression).ULEB128(0x666ae152)
- .Block("hideous")
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x666ae152, "hideous"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreValExpressionRuleChanged) {
- ByteReader reader(ENDIANNESS_LITTLE);
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_val_expression).ULEB128(0xb5ca5c46)
- .Block("revolting")
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_undefined).ULEB128(0xb5ca5c46)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChanged", section);
-
- EXPECT_CALL(handler, ValExpressionRule(fde_start, 0xb5ca5c46, "revolting"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46,
- "revolting"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-TEST_F(LulDwarfCFIRestore, RestoreValExpressionRuleChangedValExpression) {
- ByteReader reader(ENDIANNESS_LITTLE);
- CFISection section(kLittleEndian, 4);
- StockCIEAndFDE(&section);
- section
- .D8(lul::DW_CFA_val_expression).ULEB128(0x500f5739)
- .Block("repulsive")
- .D8(lul::DW_CFA_remember_state)
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_val_expression).ULEB128(0x500f5739)
- .Block("nauseous")
- .D8(lul::DW_CFA_advance_loc | 1)
- .D8(lul::DW_CFA_restore_state)
- .FinishEntry();
-
- PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChangedValExpression",
- section);
-
- EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x500f5739, "repulsive"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ValExpressionRule(fde_start + code_factor, 0x500f5739,
- "nauseous"))
- .InSequence(s).WillOnce(Return(true));
- // Expectations are not wishes.
- EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0x500f5739,
- "repulsive"))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End()).WillOnce(Return(true));
-
- ParseSection(&section, true, &reader);
-}
-
-struct EHFrameFixture: public CFIInsnFixture {
- EHFrameFixture()
- : CFIInsnFixture(), section(kBigEndian, 4, true) {
- encoded_pointer_bases.cfi = 0x7f496cb2;
- encoded_pointer_bases.text = 0x540f67b6;
- encoded_pointer_bases.data = 0xe3eab768;
- section.SetEncodedPointerBases(encoded_pointer_bases);
- }
- CFISection section;
- CFISection::EncodedPointerBases encoded_pointer_bases;
-
- // Parse CFIInsnFixture::ParseSection, but parse the section as
- // .eh_frame data, supplying stock base addresses.
- void ParseEHFrameSection(CFISection *section, bool succeeds = true) {
- EXPECT_TRUE(section->ContainsEHFrame());
- string contents;
- EXPECT_TRUE(section->GetContents(&contents));
- lul::Endianness endianness;
- if (section->endianness() == kBigEndian)
- endianness = ENDIANNESS_BIG;
- else {
- assert(section->endianness() == kLittleEndian);
- endianness = ENDIANNESS_LITTLE;
- }
- ByteReader reader(endianness);
- reader.SetAddressSize(section->AddressSize());
- reader.SetCFIDataBase(encoded_pointer_bases.cfi, contents.data());
- reader.SetTextBase(encoded_pointer_bases.text);
- reader.SetDataBase(encoded_pointer_bases.data);
- CallFrameInfo parser(contents.data(), contents.size(),
- &reader, &handler, &reporter, true);
- if (succeeds)
- EXPECT_TRUE(parser.Start());
- else
- EXPECT_FALSE(parser.Start());
- }
-
-};
-
-class LulDwarfEHFrame: public EHFrameFixture, public Test { };
-
-// A simple CIE, an FDE, and a terminator.
-TEST_F(LulDwarfEHFrame, Terminator) {
- Label cie;
- section
- .Mark(&cie)
- .CIEHeader(9968, 2466, 67, 1, "")
- .D8(lul::DW_CFA_def_cfa).ULEB128(3772).ULEB128(1372)
- .FinishEntry()
- .FDEHeader(cie, 0x848037a1, 0x7b30475e)
- .D8(lul::DW_CFA_set_loc).D32(0x17713850)
- .D8(lul::DW_CFA_undefined).ULEB128(5721)
- .FinishEntry()
- .D32(0) // Terminate the sequence.
- // This FDE should be ignored.
- .FDEHeader(cie, 0xf19629fe, 0x439fb09b)
- .FinishEntry();
-
- PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.Terminator", section);
-
- EXPECT_CALL(handler, Entry(_, 0x848037a1, 0x7b30475e, 1, "", 67))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(0x848037a1, kCFARegister, 3772, 1372))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(0x17713850, 5721))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(reporter, EarlyEHTerminator(_))
- .InSequence(s).WillOnce(Return());
-
- ParseEHFrameSection(&section);
-}
-
-// The parser should recognize the Linux Standards Base 'z' augmentations.
-TEST_F(LulDwarfEHFrame, SimpleFDE) {
- lul::DwarfPointerEncoding lsda_encoding =
- lul::DwarfPointerEncoding(lul::DW_EH_PE_indirect
- | lul::DW_EH_PE_datarel
- | lul::DW_EH_PE_sdata2);
- lul::DwarfPointerEncoding fde_encoding =
- lul::DwarfPointerEncoding(lul::DW_EH_PE_textrel
- | lul::DW_EH_PE_udata2);
-
- section.SetPointerEncoding(fde_encoding);
- section.SetEncodedPointerBases(encoded_pointer_bases);
- Label cie;
- section
- .Mark(&cie)
- .CIEHeader(4873, 7012, 100, 1, "zSLPR")
- .ULEB128(7) // Augmentation data length
- .D8(lsda_encoding) // LSDA pointer format
- .D8(lul::DW_EH_PE_pcrel) // personality pointer format
- .EncodedPointer(0x97baa00, lul::DW_EH_PE_pcrel) // and value
- .D8(fde_encoding) // FDE pointer format
- .D8(lul::DW_CFA_def_cfa).ULEB128(6706).ULEB128(31)
- .FinishEntry()
- .FDEHeader(cie, 0x540f6b56, 0xf686)
- .ULEB128(2) // Augmentation data length
- .EncodedPointer(0xe3eab475, lsda_encoding) // LSDA pointer, signed
- .D8(lul::DW_CFA_set_loc)
- .EncodedPointer(0x540fa4ce, fde_encoding)
- .D8(lul::DW_CFA_undefined).ULEB128(0x675e)
- .FinishEntry()
- .D32(0); // terminator
-
- PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.SimpleFDE", section);
-
- EXPECT_CALL(handler, Entry(_, 0x540f6b56, 0xf686, 1, "zSLPR", 100))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, PersonalityRoutine(0x97baa00, false))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, LanguageSpecificDataArea(0xe3eab475, true))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, SignalHandler())
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(0x540f6b56, kCFARegister, 6706, 31))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(0x540fa4ce, 0x675e))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .InSequence(s).WillOnce(Return(true));
-
- ParseEHFrameSection(&section);
-}
-
-// Check that we can handle an empty 'z' augmentation.
-TEST_F(LulDwarfEHFrame, EmptyZ) {
- Label cie;
- section
- .Mark(&cie)
- .CIEHeader(5955, 5805, 228, 1, "z")
- .ULEB128(0) // Augmentation data length
- .D8(lul::DW_CFA_def_cfa).ULEB128(3629).ULEB128(247)
- .FinishEntry()
- .FDEHeader(cie, 0xda007738, 0xfb55c641)
- .ULEB128(0) // Augmentation data length
- .D8(lul::DW_CFA_advance_loc1).D8(11)
- .D8(lul::DW_CFA_undefined).ULEB128(3769)
- .FinishEntry();
-
- PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.EmptyZ", section);
-
- EXPECT_CALL(handler, Entry(_, 0xda007738, 0xfb55c641, 1, "z", 228))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, ValOffsetRule(0xda007738, kCFARegister, 3629, 247))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, UndefinedRule(0xda007738 + 11 * 5955, 3769))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .InSequence(s).WillOnce(Return(true));
-
- ParseEHFrameSection(&section);
-}
-
-// Check that we recognize bad 'z' augmentation characters.
-TEST_F(LulDwarfEHFrame, BadZ) {
- Label cie;
- section
- .Mark(&cie)
- .CIEHeader(6937, 1045, 142, 1, "zQ")
- .ULEB128(0) // Augmentation data length
- .D8(lul::DW_CFA_def_cfa).ULEB128(9006).ULEB128(7725)
- .FinishEntry()
- .FDEHeader(cie, 0x1293efa8, 0x236f53f2)
- .ULEB128(0) // Augmentation data length
- .D8(lul::DW_CFA_advance_loc | 12)
- .D8(lul::DW_CFA_register).ULEB128(5667).ULEB128(3462)
- .FinishEntry();
-
- PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.BadZ", section);
-
- EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "zQ"))
- .WillOnce(Return());
-
- ParseEHFrameSection(&section, false);
-}
-
-TEST_F(LulDwarfEHFrame, zL) {
- Label cie;
- lul::DwarfPointerEncoding lsda_encoding =
- lul::DwarfPointerEncoding(lul::DW_EH_PE_funcrel | lul::DW_EH_PE_udata2);
- section
- .Mark(&cie)
- .CIEHeader(9285, 9959, 54, 1, "zL")
- .ULEB128(1) // Augmentation data length
- .D8(lsda_encoding) // encoding for LSDA pointer in FDE
-
- .FinishEntry()
- .FDEHeader(cie, 0xd40091aa, 0x9aa6e746)
- .ULEB128(2) // Augmentation data length
- .EncodedPointer(0xd40099cd, lsda_encoding) // LSDA pointer
- .FinishEntry()
- .D32(0); // terminator
-
- PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zL", section);
-
- EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zL", 54))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, LanguageSpecificDataArea(0xd40099cd, false))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .InSequence(s).WillOnce(Return(true));
-
- ParseEHFrameSection(&section);
-}
-
-TEST_F(LulDwarfEHFrame, zP) {
- Label cie;
- lul::DwarfPointerEncoding personality_encoding =
- lul::DwarfPointerEncoding(lul::DW_EH_PE_datarel | lul::DW_EH_PE_udata2);
- section
- .Mark(&cie)
- .CIEHeader(1097, 6313, 17, 1, "zP")
- .ULEB128(3) // Augmentation data length
- .D8(personality_encoding) // encoding for personality routine
- .EncodedPointer(0xe3eaccac, personality_encoding) // value
- .FinishEntry()
- .FDEHeader(cie, 0x0c8350c9, 0xbef11087)
- .ULEB128(0) // Augmentation data length
- .FinishEntry()
- .D32(0); // terminator
-
- PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zP", section);
-
- EXPECT_CALL(handler, Entry(_, 0x0c8350c9, 0xbef11087, 1, "zP", 17))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, PersonalityRoutine(0xe3eaccac, false))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .InSequence(s).WillOnce(Return(true));
-
- ParseEHFrameSection(&section);
-}
-
-TEST_F(LulDwarfEHFrame, zR) {
- Label cie;
- lul::DwarfPointerEncoding pointer_encoding =
- lul::DwarfPointerEncoding(lul::DW_EH_PE_textrel | lul::DW_EH_PE_sdata2);
- section.SetPointerEncoding(pointer_encoding);
- section
- .Mark(&cie)
- .CIEHeader(8011, 5496, 75, 1, "zR")
- .ULEB128(1) // Augmentation data length
- .D8(pointer_encoding) // encoding for FDE addresses
- .FinishEntry()
- .FDEHeader(cie, 0x540f9431, 0xbd0)
- .ULEB128(0) // Augmentation data length
- .FinishEntry()
- .D32(0); // terminator
-
- PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zR", section);
-
- EXPECT_CALL(handler, Entry(_, 0x540f9431, 0xbd0, 1, "zR", 75))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .InSequence(s).WillOnce(Return(true));
-
- ParseEHFrameSection(&section);
-}
-
-TEST_F(LulDwarfEHFrame, zS) {
- Label cie;
- section
- .Mark(&cie)
- .CIEHeader(9217, 7694, 57, 1, "zS")
- .ULEB128(0) // Augmentation data length
- .FinishEntry()
- .FDEHeader(cie, 0xd40091aa, 0x9aa6e746)
- .ULEB128(0) // Augmentation data length
- .FinishEntry()
- .D32(0); // terminator
-
- PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zS", section);
-
- EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zS", 57))
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, SignalHandler())
- .InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(handler, End())
- .InSequence(s).WillOnce(Return(true));
-
- ParseEHFrameSection(&section);
-}
-
-// These tests require manual inspection of the test output.
-struct CFIReporterFixture {
- CFIReporterFixture() : reporter(gtest_logging_sink_for_LulTestDwarf,
- "test file name", "test section name") { }
- CallFrameInfo::Reporter reporter;
-};
-
-class LulDwarfCFIReporter: public CFIReporterFixture, public Test { };
-
-TEST_F(LulDwarfCFIReporter, Incomplete) {
- reporter.Incomplete(0x0102030405060708ULL, CallFrameInfo::kUnknown);
-}
-
-TEST_F(LulDwarfCFIReporter, EarlyEHTerminator) {
- reporter.EarlyEHTerminator(0x0102030405060708ULL);
-}
-
-TEST_F(LulDwarfCFIReporter, CIEPointerOutOfRange) {
- reporter.CIEPointerOutOfRange(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
-}
-
-TEST_F(LulDwarfCFIReporter, BadCIEId) {
- reporter.BadCIEId(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
-}
-
-TEST_F(LulDwarfCFIReporter, UnrecognizedVersion) {
- reporter.UnrecognizedVersion(0x0123456789abcdefULL, 43);
-}
-
-TEST_F(LulDwarfCFIReporter, UnrecognizedAugmentation) {
- reporter.UnrecognizedAugmentation(0x0123456789abcdefULL, "poodles");
-}
-
-TEST_F(LulDwarfCFIReporter, InvalidPointerEncoding) {
- reporter.InvalidPointerEncoding(0x0123456789abcdefULL, 0x42);
-}
-
-TEST_F(LulDwarfCFIReporter, UnusablePointerEncoding) {
- reporter.UnusablePointerEncoding(0x0123456789abcdefULL, 0x42);
-}
-
-TEST_F(LulDwarfCFIReporter, RestoreInCIE) {
- reporter.RestoreInCIE(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
-}
-
-TEST_F(LulDwarfCFIReporter, BadInstruction) {
- reporter.BadInstruction(0x0123456789abcdefULL, CallFrameInfo::kFDE,
- 0xfedcba9876543210ULL);
-}
-
-TEST_F(LulDwarfCFIReporter, NoCFARule) {
- reporter.NoCFARule(0x0123456789abcdefULL, CallFrameInfo::kCIE,
- 0xfedcba9876543210ULL);
-}
-
-TEST_F(LulDwarfCFIReporter, EmptyStateStack) {
- reporter.EmptyStateStack(0x0123456789abcdefULL, CallFrameInfo::kTerminator,
- 0xfedcba9876543210ULL);
-}
-
-TEST_F(LulDwarfCFIReporter, ClearingCFARule) {
- reporter.ClearingCFARule(0x0123456789abcdefULL, CallFrameInfo::kFDE,
- 0xfedcba9876543210ULL);
-}
-class LulDwarfExpr : public Test { };
-
-class MockSummariser : public Summariser {
-public:
- MockSummariser() : Summariser(nullptr, 0, nullptr) {}
- MOCK_METHOD2(Entry, void(uintptr_t, uintptr_t));
- MOCK_METHOD0(End, void());
- MOCK_METHOD5(Rule, void(uintptr_t, int, LExprHow, int16_t, int64_t));
- MOCK_METHOD1(AddPfxInstr, uint32_t(PfxInstr));
-};
-
-TEST_F(LulDwarfExpr, SimpleTransliteration) {
- MockSummariser summ;
- ByteReader reader(ENDIANNESS_LITTLE);
-
- CFISection section(kLittleEndian, 8);
- section
- .D8(DW_OP_lit0)
- .D8(DW_OP_lit31)
- .D8(DW_OP_breg0 + 17).LEB128(-1234)
- .D8(DW_OP_const4s).D32(0xFEDC9876)
- .D8(DW_OP_deref)
- .D8(DW_OP_and)
- .D8(DW_OP_plus)
- .D8(DW_OP_minus)
- .D8(DW_OP_shl)
- .D8(DW_OP_ge);
- string expr;
- bool ok = section.GetContents(&expr);
- EXPECT_TRUE(ok);
-
- {
- InSequence s;
- // required start marker
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_Start, 0)));
- // DW_OP_lit0
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_SImm32, 0)));
- // DW_OP_lit31
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_SImm32, 31)));
- // DW_OP_breg17 -1234
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_DwReg, 17)));
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_SImm32, -1234)));
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_Add)));
- // DW_OP_const4s 0xFEDC9876
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_SImm32, 0xFEDC9876)));
- // DW_OP_deref
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_Deref)));
- // DW_OP_and
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_And)));
- // DW_OP_plus
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_Add)));
- // DW_OP_minus
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_Sub)));
- // DW_OP_shl
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_Shl)));
- // DW_OP_ge
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_CmpGES)));
- // required end marker
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_End)));
- }
-
- int32_t ix = parseDwarfExpr(&summ, &reader, expr, false, false, false);
- EXPECT_TRUE(ix >= 0);
-}
-
-TEST_F(LulDwarfExpr, UnknownOpcode) {
- MockSummariser summ;
- ByteReader reader(ENDIANNESS_LITTLE);
-
- CFISection section(kLittleEndian, 8);
- section
- .D8(DW_OP_lo_user - 1);
- string expr;
- bool ok = section.GetContents(&expr);
- EXPECT_TRUE(ok);
-
- {
- InSequence s;
- // required start marker
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_Start, 0)));
- }
-
- int32_t ix = parseDwarfExpr(&summ, &reader, expr, false, false, false);
- EXPECT_TRUE(ix == -1);
-}
-
-TEST_F(LulDwarfExpr, ExpressionOverrun) {
- MockSummariser summ;
- ByteReader reader(ENDIANNESS_LITTLE);
-
- CFISection section(kLittleEndian, 8);
- section
- .D8(DW_OP_const4s).D8(0x12).D8(0x34).D8(0x56);
- string expr;
- bool ok = section.GetContents(&expr);
- EXPECT_TRUE(ok);
-
- {
- InSequence s;
- // required start marker
- EXPECT_CALL(summ, AddPfxInstr(PfxInstr(PX_Start, 0)));
- // DW_OP_const4s followed by 3 (a.k.a. not enough) bytes
- // We expect PfxInstr(PX_Simm32, not-known-for-sure-32-bit-immediate)
- // Hence must use _ as the argument.
- EXPECT_CALL(summ, AddPfxInstr(_));
- }
-
- int32_t ix = parseDwarfExpr(&summ, &reader, expr, false, false, false);
- EXPECT_TRUE(ix == -1);
-}
-
-// We'll need to mention specific Dwarf registers in the EvaluatePfxExpr tests,
-// and those names are arch-specific, so a bit of macro magic is helpful.
-#if defined(LUL_ARCH_arm)
-# define TESTED_REG_STRUCT_NAME r11
-# define TESTED_REG_DWARF_NAME DW_REG_ARM_R11
-#elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
-# define TESTED_REG_STRUCT_NAME xbp
-# define TESTED_REG_DWARF_NAME DW_REG_INTEL_XBP
-#else
-# error "Unknown plat"
-#endif
-
-struct EvaluatePfxExprFixture {
- // Creates:
- // initial stack, AVMA 0x12345678, at offset 4 bytes = 0xdeadbeef
- // initial regs, with XBP = 0x14141356
- // initial CFA = 0x5432ABCD
- EvaluatePfxExprFixture() {
- // The test stack.
- si.mStartAvma = 0x12345678;
- si.mLen = 0;
-# define XX(_byte) do { si.mContents[si.mLen++] = (_byte); } while (0)
- XX(0x55); XX(0x55); XX(0x55); XX(0x55);
- if (sizeof(void*) == 8) {
- // le64
- XX(0xEF); XX(0xBE); XX(0xAD); XX(0xDE); XX(0); XX(0); XX(0); XX(0);
- } else {
- // le32
- XX(0xEF); XX(0xBE); XX(0xAD); XX(0xDE);
- }
- XX(0xAA); XX(0xAA); XX(0xAA); XX(0xAA);
-# undef XX
- // The initial CFA.
- initialCFA = TaggedUWord(0x5432ABCD);
- // The initial register state.
- memset(&regs, 0, sizeof(regs));
- regs.TESTED_REG_STRUCT_NAME = TaggedUWord(0x14141356);
- }
-
- StackImage si;
- TaggedUWord initialCFA;
- UnwindRegs regs;
-};
-
-class LulDwarfEvaluatePfxExpr : public EvaluatePfxExprFixture, public Test { };
-
-TEST_F(LulDwarfEvaluatePfxExpr, NormalEvaluation) {
- vector<PfxInstr> instrs;
- // Put some junk at the start of the insn sequence.
- instrs.push_back(PfxInstr(PX_End));
- instrs.push_back(PfxInstr(PX_End));
-
- // Now the real sequence
- // stack is empty
- instrs.push_back(PfxInstr(PX_Start, 1));
- // 0x5432ABCD
- instrs.push_back(PfxInstr(PX_SImm32, 0x31415927));
- // 0x5432ABCD 0x31415927
- instrs.push_back(PfxInstr(PX_DwReg, TESTED_REG_DWARF_NAME));
- // 0x5432ABCD 0x31415927 0x14141356
- instrs.push_back(PfxInstr(PX_SImm32, 42));
- // 0x5432ABCD 0x31415927 0x14141356 42
- instrs.push_back(PfxInstr(PX_Sub));
- // 0x5432ABCD 0x31415927 0x1414132c
- instrs.push_back(PfxInstr(PX_Add));
- // 0x5432ABCD 0x45556c53
- instrs.push_back(PfxInstr(PX_SImm32, si.mStartAvma + 4));
- // 0x5432ABCD 0x45556c53 0x1234567c
- instrs.push_back(PfxInstr(PX_Deref));
- // 0x5432ABCD 0x45556c53 0xdeadbeef
- instrs.push_back(PfxInstr(PX_SImm32, 0xFE01DC23));
- // 0x5432ABCD 0x45556c53 0xdeadbeef 0xFE01DC23
- instrs.push_back(PfxInstr(PX_And));
- // 0x5432ABCD 0x45556c53 0xde019c23
- instrs.push_back(PfxInstr(PX_SImm32, 7));
- // 0x5432ABCD 0x45556c53 0xde019c23 7
- instrs.push_back(PfxInstr(PX_Shl));
- // 0x5432ABCD 0x45556c53 0x6f00ce1180
- instrs.push_back(PfxInstr(PX_SImm32, 0x7fffffff));
- // 0x5432ABCD 0x45556c53 0x6f00ce1180 7fffffff
- instrs.push_back(PfxInstr(PX_And));
- // 0x5432ABCD 0x45556c53 0x00ce1180
- instrs.push_back(PfxInstr(PX_Add));
- // 0x5432ABCD 0x46237dd3
- instrs.push_back(PfxInstr(PX_Sub));
- // 0xe0f2dfa
-
- instrs.push_back(PfxInstr(PX_End));
-
- TaggedUWord res = EvaluatePfxExpr(2/*offset of start insn*/,
- &regs, initialCFA, &si, instrs);
- EXPECT_TRUE(res.Valid());
- EXPECT_TRUE(res.Value() == 0xe0f2dfa);
-}
-
-TEST_F(LulDwarfEvaluatePfxExpr, EmptySequence) {
- vector<PfxInstr> instrs;
- TaggedUWord res = EvaluatePfxExpr(0, &regs, initialCFA, &si, instrs);
- EXPECT_FALSE(res.Valid());
-}
-
-TEST_F(LulDwarfEvaluatePfxExpr, BogusStartPoint) {
- vector<PfxInstr> instrs;
- instrs.push_back(PfxInstr(PX_SImm32, 42));
- instrs.push_back(PfxInstr(PX_SImm32, 24));
- instrs.push_back(PfxInstr(PX_SImm32, 4224));
- TaggedUWord res = EvaluatePfxExpr(1, &regs, initialCFA, &si, instrs);
- EXPECT_FALSE(res.Valid());
-}
-
-TEST_F(LulDwarfEvaluatePfxExpr, MissingEndMarker) {
- vector<PfxInstr> instrs;
- instrs.push_back(PfxInstr(PX_Start, 0));
- instrs.push_back(PfxInstr(PX_SImm32, 24));
- TaggedUWord res = EvaluatePfxExpr(0, &regs, initialCFA, &si, instrs);
- EXPECT_FALSE(res.Valid());
-}
-
-TEST_F(LulDwarfEvaluatePfxExpr, StackUnderflow) {
- vector<PfxInstr> instrs;
- instrs.push_back(PfxInstr(PX_Start, 0));
- instrs.push_back(PfxInstr(PX_End));
- TaggedUWord res = EvaluatePfxExpr(0, &regs, initialCFA, &si, instrs);
- EXPECT_FALSE(res.Valid());
-}
-
-TEST_F(LulDwarfEvaluatePfxExpr, StackNoUnderflow) {
- vector<PfxInstr> instrs;
- instrs.push_back(PfxInstr(PX_Start, 1/*push the initial CFA*/));
- instrs.push_back(PfxInstr(PX_End));
- TaggedUWord res = EvaluatePfxExpr(0, &regs, initialCFA, &si, instrs);
- EXPECT_TRUE(res.Valid());
- EXPECT_TRUE(res == initialCFA);
-}
-
-TEST_F(LulDwarfEvaluatePfxExpr, StackOverflow) {
- vector<PfxInstr> instrs;
- instrs.push_back(PfxInstr(PX_Start, 0));
- for (int i = 0; i < 10+1; i++) {
- instrs.push_back(PfxInstr(PX_SImm32, i + 100));
- }
- instrs.push_back(PfxInstr(PX_End));
- TaggedUWord res = EvaluatePfxExpr(0, &regs, initialCFA, &si, instrs);
- EXPECT_FALSE(res.Valid());
-}
-
-TEST_F(LulDwarfEvaluatePfxExpr, StackNoOverflow) {
- vector<PfxInstr> instrs;
- instrs.push_back(PfxInstr(PX_Start, 0));
- for (int i = 0; i < 10+0; i++) {
- instrs.push_back(PfxInstr(PX_SImm32, i + 100));
- }
- instrs.push_back(PfxInstr(PX_End));
- TaggedUWord res = EvaluatePfxExpr(0, &regs, initialCFA, &si, instrs);
- EXPECT_TRUE(res.Valid());
- EXPECT_TRUE(res == TaggedUWord(109));
-}
-
-TEST_F(LulDwarfEvaluatePfxExpr, OutOfRangeShl) {
- vector<PfxInstr> instrs;
- instrs.push_back(PfxInstr(PX_Start, 0));
- instrs.push_back(PfxInstr(PX_SImm32, 1234));
- instrs.push_back(PfxInstr(PX_SImm32, 5678));
- instrs.push_back(PfxInstr(PX_Shl));
- TaggedUWord res = EvaluatePfxExpr(0, &regs, initialCFA, &si, instrs);
- EXPECT_TRUE(!res.Valid());
-}
-
-TEST_F(LulDwarfEvaluatePfxExpr, TestCmpGES) {
- const int32_t argsL[6] = { 0, 0, 1, -2, -1, -2 };
- const int32_t argsR[6] = { 0, 1, 0, -2, -2, -1 };
- // expecting: t f t t t f = 101110 = 0x2E
- vector<PfxInstr> instrs;
- instrs.push_back(PfxInstr(PX_Start, 0));
- // The "running total"
- instrs.push_back(PfxInstr(PX_SImm32, 0));
- for (unsigned int i = 0; i < sizeof(argsL)/sizeof(argsL[0]); i++) {
- // Shift the "running total" at the bottom of the stack left by one bit
- instrs.push_back(PfxInstr(PX_SImm32, 1));
- instrs.push_back(PfxInstr(PX_Shl));
- // Push both test args and do the comparison
- instrs.push_back(PfxInstr(PX_SImm32, argsL[i]));
- instrs.push_back(PfxInstr(PX_SImm32, argsR[i]));
- instrs.push_back(PfxInstr(PX_CmpGES));
- // Or the result into the running total
- instrs.push_back(PfxInstr(PX_Or));
- }
- instrs.push_back(PfxInstr(PX_End));
- TaggedUWord res = EvaluatePfxExpr(0, &regs, initialCFA, &si, instrs);
- EXPECT_TRUE(res.Valid());
- EXPECT_TRUE(res == TaggedUWord(0x2E));
-}
-
-} // namespace lul
diff --git a/tools/profiler/tests/gtest/LulTestInfrastructure.cpp b/tools/profiler/tests/gtest/LulTestInfrastructure.cpp
deleted file mode 100644
index ba8e2e41e..000000000
--- a/tools/profiler/tests/gtest/LulTestInfrastructure.cpp
+++ /dev/null
@@ -1,491 +0,0 @@
-// Copyright (c) 2010, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
-
-// Derived from:
-// test_assembler.cc: Implementation of google_breakpad::TestAssembler.
-// See test_assembler.h for details.
-
-// Derived from:
-// cfi_assembler.cc: Implementation of google_breakpad::CFISection class.
-// See cfi_assembler.h for details.
-
-#include "LulTestInfrastructure.h"
-
-namespace lul_test {
-namespace test_assembler {
-
-using std::back_insert_iterator;
-
-Label::Label() : value_(new Binding()) { }
-Label::Label(uint64_t value) : value_(new Binding(value)) { }
-Label::Label(const Label &label) {
- value_ = label.value_;
- value_->Acquire();
-}
-Label::~Label() {
- if (value_->Release()) delete value_;
-}
-
-Label &Label::operator=(uint64_t value) {
- value_->Set(NULL, value);
- return *this;
-}
-
-Label &Label::operator=(const Label &label) {
- value_->Set(label.value_, 0);
- return *this;
-}
-
-Label Label::operator+(uint64_t addend) const {
- Label l;
- l.value_->Set(this->value_, addend);
- return l;
-}
-
-Label Label::operator-(uint64_t subtrahend) const {
- Label l;
- l.value_->Set(this->value_, -subtrahend);
- return l;
-}
-
-// When NDEBUG is #defined, assert doesn't evaluate its argument. This
-// means you can't simply use assert to check the return value of a
-// function with necessary side effects.
-//
-// ALWAYS_EVALUATE_AND_ASSERT(x) evaluates x regardless of whether
-// NDEBUG is #defined; when NDEBUG is not #defined, it further asserts
-// that x is true.
-#ifdef NDEBUG
-#define ALWAYS_EVALUATE_AND_ASSERT(x) x
-#else
-#define ALWAYS_EVALUATE_AND_ASSERT(x) assert(x)
-#endif
-
-uint64_t Label::operator-(const Label &label) const {
- uint64_t offset;
- ALWAYS_EVALUATE_AND_ASSERT(IsKnownOffsetFrom(label, &offset));
- return offset;
-}
-
-bool Label::IsKnownConstant(uint64_t *value_p) const {
- Binding *base;
- uint64_t addend;
- value_->Get(&base, &addend);
- if (base != NULL) return false;
- if (value_p) *value_p = addend;
- return true;
-}
-
-bool Label::IsKnownOffsetFrom(const Label &label, uint64_t *offset_p) const
-{
- Binding *label_base, *this_base;
- uint64_t label_addend, this_addend;
- label.value_->Get(&label_base, &label_addend);
- value_->Get(&this_base, &this_addend);
- // If this and label are related, Get will find their final
- // common ancestor, regardless of how indirect the relation is. This
- // comparison also handles the constant vs. constant case.
- if (this_base != label_base) return false;
- if (offset_p) *offset_p = this_addend - label_addend;
- return true;
-}
-
-Label::Binding::Binding() : base_(this), addend_(), reference_count_(1) { }
-
-Label::Binding::Binding(uint64_t addend)
- : base_(NULL), addend_(addend), reference_count_(1) { }
-
-Label::Binding::~Binding() {
- assert(reference_count_ == 0);
- if (base_ && base_ != this && base_->Release())
- delete base_;
-}
-
-void Label::Binding::Set(Binding *binding, uint64_t addend) {
- if (!base_ && !binding) {
- // We're equating two constants. This could be okay.
- assert(addend_ == addend);
- } else if (!base_) {
- // We are a known constant, but BINDING may not be, so turn the
- // tables and try to set BINDING's value instead.
- binding->Set(NULL, addend_ - addend);
- } else {
- if (binding) {
- // Find binding's final value. Since the final value is always either
- // completely unconstrained or a constant, never a reference to
- // another variable (otherwise, it wouldn't be final), this
- // guarantees we won't create cycles here, even for code like this:
- // l = m, m = n, n = l;
- uint64_t binding_addend;
- binding->Get(&binding, &binding_addend);
- addend += binding_addend;
- }
-
- // It seems likely that setting a binding to itself is a bug
- // (although I can imagine this might turn out to be helpful to
- // permit).
- assert(binding != this);
-
- if (base_ != this) {
- // Set the other bindings on our chain as well. Note that this
- // is sufficient even though binding relationships form trees:
- // All binding operations traverse their chains to the end, and
- // all bindings related to us share some tail of our chain, so
- // they will see the changes we make here.
- base_->Set(binding, addend - addend_);
- // We're not going to use base_ any more.
- if (base_->Release()) delete base_;
- }
-
- // Adopt BINDING as our base. Note that it should be correct to
- // acquire here, after the release above, even though the usual
- // reference-counting rules call for acquiring first, and then
- // releasing: the self-reference assertion above should have
- // complained if BINDING were 'this' or anywhere along our chain,
- // so we didn't release BINDING.
- if (binding) binding->Acquire();
- base_ = binding;
- addend_ = addend;
- }
-}
-
-void Label::Binding::Get(Binding **base, uint64_t *addend) {
- if (base_ && base_ != this) {
- // Recurse to find the end of our reference chain (the root of our
- // tree), and then rewrite every binding along the chain to refer
- // to it directly, adjusting addends appropriately. (This is why
- // this member function isn't this-const.)
- Binding *final_base;
- uint64_t final_addend;
- base_->Get(&final_base, &final_addend);
- if (final_base) final_base->Acquire();
- if (base_->Release()) delete base_;
- base_ = final_base;
- addend_ += final_addend;
- }
- *base = base_;
- *addend = addend_;
-}
-
-template<typename Inserter>
-static inline void InsertEndian(test_assembler::Endianness endianness,
- size_t size, uint64_t number, Inserter dest) {
- assert(size > 0);
- if (endianness == kLittleEndian) {
- for (size_t i = 0; i < size; i++) {
- *dest++ = (char) (number & 0xff);
- number >>= 8;
- }
- } else {
- assert(endianness == kBigEndian);
- // The loop condition is odd, but it's correct for size_t.
- for (size_t i = size - 1; i < size; i--)
- *dest++ = (char) ((number >> (i * 8)) & 0xff);
- }
-}
-
-Section &Section::Append(Endianness endianness, size_t size, uint64_t number) {
- InsertEndian(endianness, size, number,
- back_insert_iterator<string>(contents_));
- return *this;
-}
-
-Section &Section::Append(Endianness endianness, size_t size,
- const Label &label) {
- // If this label's value is known, there's no reason to waste an
- // entry in references_ on it.
- uint64_t value;
- if (label.IsKnownConstant(&value))
- return Append(endianness, size, value);
-
- // This will get caught when the references are resolved, but it's
- // nicer to find out earlier.
- assert(endianness != kUnsetEndian);
-
- references_.push_back(Reference(contents_.size(), endianness, size, label));
- contents_.append(size, 0);
- return *this;
-}
-
-#define ENDIANNESS_L kLittleEndian
-#define ENDIANNESS_B kBigEndian
-#define ENDIANNESS(e) ENDIANNESS_ ## e
-
-#define DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \
- Section &Section::e ## bits(uint ## bits ## _t v) { \
- InsertEndian(ENDIANNESS(e), bits / 8, v, \
- back_insert_iterator<string>(contents_)); \
- return *this; \
- }
-
-#define DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) \
- Section &Section::e ## bits(const Label &v) { \
- return Append(ENDIANNESS(e), bits / 8, v); \
- }
-
-// Define L16, B32, and friends.
-#define DEFINE_SHORT_APPEND_ENDIAN(e, bits) \
- DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \
- DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits)
-
-DEFINE_SHORT_APPEND_LABEL_ENDIAN(L, 8);
-DEFINE_SHORT_APPEND_LABEL_ENDIAN(B, 8);
-DEFINE_SHORT_APPEND_ENDIAN(L, 16);
-DEFINE_SHORT_APPEND_ENDIAN(L, 32);
-DEFINE_SHORT_APPEND_ENDIAN(L, 64);
-DEFINE_SHORT_APPEND_ENDIAN(B, 16);
-DEFINE_SHORT_APPEND_ENDIAN(B, 32);
-DEFINE_SHORT_APPEND_ENDIAN(B, 64);
-
-#define DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \
- Section &Section::D ## bits(uint ## bits ## _t v) { \
- InsertEndian(endianness_, bits / 8, v, \
- back_insert_iterator<string>(contents_)); \
- return *this; \
- }
-#define DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) \
- Section &Section::D ## bits(const Label &v) { \
- return Append(endianness_, bits / 8, v); \
- }
-#define DEFINE_SHORT_APPEND_DEFAULT(bits) \
- DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \
- DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits)
-
-DEFINE_SHORT_APPEND_LABEL_DEFAULT(8)
-DEFINE_SHORT_APPEND_DEFAULT(16);
-DEFINE_SHORT_APPEND_DEFAULT(32);
-DEFINE_SHORT_APPEND_DEFAULT(64);
-
-Section &Section::LEB128(long long value) {
- while (value < -0x40 || 0x3f < value) {
- contents_ += (value & 0x7f) | 0x80;
- if (value < 0)
- value = (value >> 7) | ~(((unsigned long long) -1) >> 7);
- else
- value = (value >> 7);
- }
- contents_ += value & 0x7f;
- return *this;
-}
-
-Section &Section::ULEB128(uint64_t value) {
- while (value > 0x7f) {
- contents_ += (value & 0x7f) | 0x80;
- value = (value >> 7);
- }
- contents_ += value;
- return *this;
-}
-
-Section &Section::Align(size_t alignment, uint8_t pad_byte) {
- // ALIGNMENT must be a power of two.
- assert(((alignment - 1) & alignment) == 0);
- size_t new_size = (contents_.size() + alignment - 1) & ~(alignment - 1);
- contents_.append(new_size - contents_.size(), pad_byte);
- assert((contents_.size() & (alignment - 1)) == 0);
- return *this;
-}
-
-bool Section::GetContents(string *contents) {
- // For each label reference, find the label's value, and patch it into
- // the section's contents.
- for (size_t i = 0; i < references_.size(); i++) {
- Reference &r = references_[i];
- uint64_t value;
- if (!r.label.IsKnownConstant(&value)) {
- fprintf(stderr, "Undefined label #%zu at offset 0x%zx\n", i, r.offset);
- return false;
- }
- assert(r.offset < contents_.size());
- assert(contents_.size() - r.offset >= r.size);
- InsertEndian(r.endianness, r.size, value, contents_.begin() + r.offset);
- }
- contents->clear();
- std::swap(contents_, *contents);
- references_.clear();
- return true;
-}
-
-} // namespace test_assembler
-} // namespace lul_test
-
-
-namespace lul_test {
-
-CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
- int data_alignment_factor,
- unsigned return_address_register,
- uint8_t version,
- const string &augmentation,
- bool dwarf64) {
- assert(!entry_length_);
- entry_length_ = new PendingLength();
- in_fde_ = false;
-
- if (dwarf64) {
- D32(kDwarf64InitialLengthMarker);
- D64(entry_length_->length);
- entry_length_->start = Here();
- D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier);
- } else {
- D32(entry_length_->length);
- entry_length_->start = Here();
- D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier);
- }
- D8(version);
- AppendCString(augmentation);
- ULEB128(code_alignment_factor);
- LEB128(data_alignment_factor);
- if (version == 1)
- D8(return_address_register);
- else
- ULEB128(return_address_register);
- return *this;
-}
-
-CFISection &CFISection::FDEHeader(Label cie_pointer,
- uint64_t initial_location,
- uint64_t address_range,
- bool dwarf64) {
- assert(!entry_length_);
- entry_length_ = new PendingLength();
- in_fde_ = true;
- fde_start_address_ = initial_location;
-
- if (dwarf64) {
- D32(0xffffffff);
- D64(entry_length_->length);
- entry_length_->start = Here();
- if (eh_frame_)
- D64(Here() - cie_pointer);
- else
- D64(cie_pointer);
- } else {
- D32(entry_length_->length);
- entry_length_->start = Here();
- if (eh_frame_)
- D32(Here() - cie_pointer);
- else
- D32(cie_pointer);
- }
- EncodedPointer(initial_location);
- // The FDE length in an .eh_frame section uses the same encoding as the
- // initial location, but ignores the base address (selected by the upper
- // nybble of the encoding), as it's a length, not an address that can be
- // made relative.
- EncodedPointer(address_range,
- DwarfPointerEncoding(pointer_encoding_ & 0x0f));
- return *this;
-}
-
-CFISection &CFISection::FinishEntry() {
- assert(entry_length_);
- Align(address_size_, lul::DW_CFA_nop);
- entry_length_->length = Here() - entry_length_->start;
- delete entry_length_;
- entry_length_ = NULL;
- in_fde_ = false;
- return *this;
-}
-
-CFISection &CFISection::EncodedPointer(uint64_t address,
- DwarfPointerEncoding encoding,
- const EncodedPointerBases &bases) {
- // Omitted data is extremely easy to emit.
- if (encoding == lul::DW_EH_PE_omit)
- return *this;
-
- // If (encoding & lul::DW_EH_PE_indirect) != 0, then we assume
- // that ADDRESS is the address at which the pointer is stored --- in
- // other words, that bit has no effect on how we write the pointer.
- encoding = DwarfPointerEncoding(encoding & ~lul::DW_EH_PE_indirect);
-
- // Find the base address to which this pointer is relative. The upper
- // nybble of the encoding specifies this.
- uint64_t base;
- switch (encoding & 0xf0) {
- case lul::DW_EH_PE_absptr: base = 0; break;
- case lul::DW_EH_PE_pcrel: base = bases.cfi + Size(); break;
- case lul::DW_EH_PE_textrel: base = bases.text; break;
- case lul::DW_EH_PE_datarel: base = bases.data; break;
- case lul::DW_EH_PE_funcrel: base = fde_start_address_; break;
- case lul::DW_EH_PE_aligned: base = 0; break;
- default: abort();
- };
-
- // Make ADDRESS relative. Yes, this is appropriate even for "absptr"
- // values; see gcc/unwind-pe.h.
- address -= base;
-
- // Align the pointer, if required.
- if ((encoding & 0xf0) == lul::DW_EH_PE_aligned)
- Align(AddressSize());
-
- // Append ADDRESS to this section in the appropriate form. For the
- // fixed-width forms, we don't need to differentiate between signed and
- // unsigned encodings, because ADDRESS has already been extended to 64
- // bits before it was passed to us.
- switch (encoding & 0x0f) {
- case lul::DW_EH_PE_absptr:
- Address(address);
- break;
-
- case lul::DW_EH_PE_uleb128:
- ULEB128(address);
- break;
-
- case lul::DW_EH_PE_sleb128:
- LEB128(address);
- break;
-
- case lul::DW_EH_PE_udata2:
- case lul::DW_EH_PE_sdata2:
- D16(address);
- break;
-
- case lul::DW_EH_PE_udata4:
- case lul::DW_EH_PE_sdata4:
- D32(address);
- break;
-
- case lul::DW_EH_PE_udata8:
- case lul::DW_EH_PE_sdata8:
- D64(address);
- break;
-
- default:
- abort();
- }
-
- return *this;
-};
-
-} // namespace lul_test
diff --git a/tools/profiler/tests/gtest/LulTestInfrastructure.h b/tools/profiler/tests/gtest/LulTestInfrastructure.h
deleted file mode 100644
index 37b1b7d49..000000000
--- a/tools/profiler/tests/gtest/LulTestInfrastructure.h
+++ /dev/null
@@ -1,666 +0,0 @@
-// -*- mode: C++ -*-
-
-// Copyright (c) 2010, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
-
-// Derived from:
-// cfi_assembler.h: Define CFISection, a class for creating properly
-// (and improperly) formatted DWARF CFI data for unit tests.
-
-// Derived from:
-// test-assembler.h: interface to class for building complex binary streams.
-
-// To test the Breakpad symbol dumper and processor thoroughly, for
-// all combinations of host system and minidump processor
-// architecture, we need to be able to easily generate complex test
-// data like debugging information and minidump files.
-//
-// For example, if we want our unit tests to provide full code
-// coverage for stack walking, it may be difficult to persuade the
-// compiler to generate every possible sort of stack walking
-// information that we want to support; there are probably DWARF CFI
-// opcodes that GCC never emits. Similarly, if we want to test our
-// error handling, we will need to generate damaged minidumps or
-// debugging information that (we hope) the client or compiler will
-// never produce on its own.
-//
-// google_breakpad::TestAssembler provides a predictable and
-// (relatively) simple way to generate complex formatted data streams
-// like minidumps and CFI. Furthermore, because TestAssembler is
-// portable, developers without access to (say) Visual Studio or a
-// SPARC assembler can still work on test data for those targets.
-
-#ifndef LUL_TEST_INFRASTRUCTURE_H
-#define LUL_TEST_INFRASTRUCTURE_H
-
-#include <string>
-#include <vector>
-
-using std::string;
-using std::vector;
-
-namespace lul_test {
-namespace test_assembler {
-
-// A Label represents a value not yet known that we need to store in a
-// section. As long as all the labels a section refers to are defined
-// by the time we retrieve its contents as bytes, we can use undefined
-// labels freely in that section's construction.
-//
-// A label can be in one of three states:
-// - undefined,
-// - defined as the sum of some other label and a constant, or
-// - a constant.
-//
-// A label's value never changes, but it can accumulate constraints.
-// Adding labels and integers is permitted, and yields a label.
-// Subtracting a constant from a label is permitted, and also yields a
-// label. Subtracting two labels that have some relationship to each
-// other is permitted, and yields a constant.
-//
-// For example:
-//
-// Label a; // a's value is undefined
-// Label b; // b's value is undefined
-// {
-// Label c = a + 4; // okay, even though a's value is unknown
-// b = c + 4; // also okay; b is now a+8
-// }
-// Label d = b - 2; // okay; d == a+6, even though c is gone
-// d.Value(); // error: d's value is not yet known
-// d - a; // is 6, even though their values are not known
-// a = 12; // now b == 20, and d == 18
-// d.Value(); // 18: no longer an error
-// b.Value(); // 20
-// d = 10; // error: d is already defined.
-//
-// Label objects' lifetimes are unconstrained: notice that, in the
-// above example, even though a and b are only related through c, and
-// c goes out of scope, the assignment to a sets b's value as well. In
-// particular, it's not necessary to ensure that a Label lives beyond
-// Sections that refer to it.
-class Label {
- public:
- Label(); // An undefined label.
- explicit Label(uint64_t value); // A label with a fixed value
- Label(const Label &value); // A label equal to another.
- ~Label();
-
- Label &operator=(uint64_t value);
- Label &operator=(const Label &value);
- Label operator+(uint64_t addend) const;
- Label operator-(uint64_t subtrahend) const;
- uint64_t operator-(const Label &subtrahend) const;
-
- // We could also provide == and != that work on undefined, but
- // related, labels.
-
- // Return true if this label's value is known. If VALUE_P is given,
- // set *VALUE_P to the known value if returning true.
- bool IsKnownConstant(uint64_t *value_p = NULL) const;
-
- // Return true if the offset from LABEL to this label is known. If
- // OFFSET_P is given, set *OFFSET_P to the offset when returning true.
- //
- // You can think of l.KnownOffsetFrom(m, &d) as being like 'd = l-m',
- // except that it also returns a value indicating whether the
- // subtraction is possible given what we currently know of l and m.
- // It can be possible even if we don't know l and m's values. For
- // example:
- //
- // Label l, m;
- // m = l + 10;
- // l.IsKnownConstant(); // false
- // m.IsKnownConstant(); // false
- // uint64_t d;
- // l.IsKnownOffsetFrom(m, &d); // true, and sets d to -10.
- // l-m // -10
- // m-l // 10
- // m.Value() // error: m's value is not known
- bool IsKnownOffsetFrom(const Label &label, uint64_t *offset_p = NULL) const;
-
- private:
- // A label's value, or if that is not yet known, how the value is
- // related to other labels' values. A binding may be:
- // - a known constant,
- // - constrained to be equal to some other binding plus a constant, or
- // - unconstrained, and free to take on any value.
- //
- // Many labels may point to a single binding, and each binding may
- // refer to another, so bindings and labels form trees whose leaves
- // are labels, whose interior nodes (and roots) are bindings, and
- // where links point from children to parents. Bindings are
- // reference counted, allowing labels to be lightweight, copyable,
- // assignable, placed in containers, and so on.
- class Binding {
- public:
- Binding();
- explicit Binding(uint64_t addend);
- ~Binding();
-
- // Increment our reference count.
- void Acquire() { reference_count_++; };
- // Decrement our reference count, and return true if it is zero.
- bool Release() { return --reference_count_ == 0; }
-
- // Set this binding to be equal to BINDING + ADDEND. If BINDING is
- // NULL, then set this binding to the known constant ADDEND.
- // Update every binding on this binding's chain to point directly
- // to BINDING, or to be a constant, with addends adjusted
- // appropriately.
- void Set(Binding *binding, uint64_t value);
-
- // Return what we know about the value of this binding.
- // - If this binding's value is a known constant, set BASE to
- // NULL, and set ADDEND to its value.
- // - If this binding is not a known constant but related to other
- // bindings, set BASE to the binding at the end of the relation
- // chain (which will always be unconstrained), and set ADDEND to the
- // value to add to that binding's value to get this binding's
- // value.
- // - If this binding is unconstrained, set BASE to this, and leave
- // ADDEND unchanged.
- void Get(Binding **base, uint64_t *addend);
-
- private:
- // There are three cases:
- //
- // - A binding representing a known constant value has base_ NULL,
- // and addend_ equal to the value.
- //
- // - A binding representing a completely unconstrained value has
- // base_ pointing to this; addend_ is unused.
- //
- // - A binding whose value is related to some other binding's
- // value has base_ pointing to that other binding, and addend_
- // set to the amount to add to that binding's value to get this
- // binding's value. We only represent relationships of the form
- // x = y+c.
- //
- // Thus, the bind_ links form a chain terminating in either a
- // known constant value or a completely unconstrained value. Most
- // operations on bindings do path compression: they change every
- // binding on the chain to point directly to the final value,
- // adjusting addends as appropriate.
- Binding *base_;
- uint64_t addend_;
-
- // The number of Labels and Bindings pointing to this binding.
- // (When a binding points to itself, indicating a completely
- // unconstrained binding, that doesn't count as a reference.)
- int reference_count_;
- };
-
- // This label's value.
- Binding *value_;
-};
-
-// Conventions for representing larger numbers as sequences of bytes.
-enum Endianness {
- kBigEndian, // Big-endian: the most significant byte comes first.
- kLittleEndian, // Little-endian: the least significant byte comes first.
- kUnsetEndian, // used internally
-};
-
-// A section is a sequence of bytes, constructed by appending bytes
-// to the end. Sections have a convenient and flexible set of member
-// functions for appending data in various formats: big-endian and
-// little-endian signed and unsigned values of different sizes;
-// LEB128 and ULEB128 values (see below), and raw blocks of bytes.
-//
-// If you need to append a value to a section that is not convenient
-// to compute immediately, you can create a label, append the
-// label's value to the section, and then set the label's value
-// later, when it's convenient to do so. Once a label's value is
-// known, the section class takes care of updating all previously
-// appended references to it.
-//
-// Once all the labels to which a section refers have had their
-// values determined, you can get a copy of the section's contents
-// as a string.
-//
-// Note that there is no specified "start of section" label. This is
-// because there are typically several different meanings for "the
-// start of a section": the offset of the section within an object
-// file, the address in memory at which the section's content appear,
-// and so on. It's up to the code that uses the Section class to
-// keep track of these explicitly, as they depend on the application.
-class Section {
- public:
- explicit Section(Endianness endianness = kUnsetEndian)
- : endianness_(endianness) { };
-
- // A base class destructor should be either public and virtual,
- // or protected and nonvirtual.
- virtual ~Section() { };
-
- // Return the default endianness of this section.
- Endianness endianness() const { return endianness_; }
-
- // Append the SIZE bytes at DATA to the end of this section. Return
- // a reference to this section.
- Section &Append(const string &data) {
- contents_.append(data);
- return *this;
- };
-
- // Append SIZE copies of BYTE to the end of this section. Return a
- // reference to this section.
- Section &Append(size_t size, uint8_t byte) {
- contents_.append(size, (char) byte);
- return *this;
- }
-
- // Append NUMBER to this section. ENDIANNESS is the endianness to
- // use to write the number. SIZE is the length of the number in
- // bytes. Return a reference to this section.
- Section &Append(Endianness endianness, size_t size, uint64_t number);
- Section &Append(Endianness endianness, size_t size, const Label &label);
-
- // Append SECTION to the end of this section. The labels SECTION
- // refers to need not be defined yet.
- //
- // Note that this has no effect on any Labels' values, or on
- // SECTION. If placing SECTION within 'this' provides new
- // constraints on existing labels' values, then it's up to the
- // caller to fiddle with those labels as needed.
- Section &Append(const Section &section);
-
- // Append the contents of DATA as a series of bytes terminated by
- // a NULL character.
- Section &AppendCString(const string &data) {
- Append(data);
- contents_ += '\0';
- return *this;
- }
-
- // Append VALUE or LABEL to this section, with the given bit width and
- // endianness. Return a reference to this section.
- //
- // The names of these functions have the form <ENDIANNESS><BITWIDTH>:
- // <ENDIANNESS> is either 'L' (little-endian, least significant byte first),
- // 'B' (big-endian, most significant byte first), or
- // 'D' (default, the section's default endianness)
- // <BITWIDTH> is 8, 16, 32, or 64.
- //
- // Since endianness doesn't matter for a single byte, all the
- // <BITWIDTH>=8 functions are equivalent.
- //
- // These can be used to write both signed and unsigned values, as
- // the compiler will properly sign-extend a signed value before
- // passing it to the function, at which point the function's
- // behavior is the same either way.
- Section &L8(uint8_t value) { contents_ += value; return *this; }
- Section &B8(uint8_t value) { contents_ += value; return *this; }
- Section &D8(uint8_t value) { contents_ += value; return *this; }
- Section &L16(uint16_t), &L32(uint32_t), &L64(uint64_t),
- &B16(uint16_t), &B32(uint32_t), &B64(uint64_t),
- &D16(uint16_t), &D32(uint32_t), &D64(uint64_t);
- Section &L8(const Label &label), &L16(const Label &label),
- &L32(const Label &label), &L64(const Label &label),
- &B8(const Label &label), &B16(const Label &label),
- &B32(const Label &label), &B64(const Label &label),
- &D8(const Label &label), &D16(const Label &label),
- &D32(const Label &label), &D64(const Label &label);
-
- // Append VALUE in a signed LEB128 (Little-Endian Base 128) form.
- //
- // The signed LEB128 representation of an integer N is a variable
- // number of bytes:
- //
- // - If N is between -0x40 and 0x3f, then its signed LEB128
- // representation is a single byte whose value is N.
- //
- // - Otherwise, its signed LEB128 representation is (N & 0x7f) |
- // 0x80, followed by the signed LEB128 representation of N / 128,
- // rounded towards negative infinity.
- //
- // In other words, we break VALUE into groups of seven bits, put
- // them in little-endian order, and then write them as eight-bit
- // bytes with the high bit on all but the last.
- //
- // Note that VALUE cannot be a Label (we would have to implement
- // relaxation).
- Section &LEB128(long long value);
-
- // Append VALUE in unsigned LEB128 (Little-Endian Base 128) form.
- //
- // The unsigned LEB128 representation of an integer N is a variable
- // number of bytes:
- //
- // - If N is between 0 and 0x7f, then its unsigned LEB128
- // representation is a single byte whose value is N.
- //
- // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) |
- // 0x80, followed by the unsigned LEB128 representation of N /
- // 128, rounded towards negative infinity.
- //
- // Note that VALUE cannot be a Label (we would have to implement
- // relaxation).
- Section &ULEB128(uint64_t value);
-
- // Jump to the next location aligned on an ALIGNMENT-byte boundary,
- // relative to the start of the section. Fill the gap with PAD_BYTE.
- // ALIGNMENT must be a power of two. Return a reference to this
- // section.
- Section &Align(size_t alignment, uint8_t pad_byte = 0);
-
- // Return the current size of the section.
- size_t Size() const { return contents_.size(); }
-
- // Return a label representing the start of the section.
- //
- // It is up to the user whether this label represents the section's
- // position in an object file, the section's address in memory, or
- // what have you; some applications may need both, in which case
- // this simple-minded interface won't be enough. This class only
- // provides a single start label, for use with the Here and Mark
- // member functions.
- //
- // Ideally, we'd provide this in a subclass that actually knows more
- // about the application at hand and can provide an appropriate
- // collection of start labels. But then the appending member
- // functions like Append and D32 would return a reference to the
- // base class, not the derived class, and the chaining won't work.
- // Since the only value here is in pretty notation, that's a fatal
- // flaw.
- Label start() const { return start_; }
-
- // Return a label representing the point at which the next Appended
- // item will appear in the section, relative to start().
- Label Here() const { return start_ + Size(); }
-
- // Set *LABEL to Here, and return a reference to this section.
- Section &Mark(Label *label) { *label = Here(); return *this; }
-
- // If there are no undefined label references left in this
- // section, set CONTENTS to the contents of this section, as a
- // string, and clear this section. Return true on success, or false
- // if there were still undefined labels.
- bool GetContents(string *contents);
-
- private:
- // Used internally. A reference to a label's value.
- struct Reference {
- Reference(size_t set_offset, Endianness set_endianness, size_t set_size,
- const Label &set_label)
- : offset(set_offset), endianness(set_endianness), size(set_size),
- label(set_label) { }
-
- // The offset of the reference within the section.
- size_t offset;
-
- // The endianness of the reference.
- Endianness endianness;
-
- // The size of the reference.
- size_t size;
-
- // The label to which this is a reference.
- Label label;
- };
-
- // The default endianness of this section.
- Endianness endianness_;
-
- // The contents of the section.
- string contents_;
-
- // References to labels within those contents.
- vector<Reference> references_;
-
- // A label referring to the beginning of the section.
- Label start_;
-};
-
-} // namespace test_assembler
-} // namespace lul_test
-
-
-namespace lul_test {
-
-using lul::DwarfPointerEncoding;
-using lul_test::test_assembler::Endianness;
-using lul_test::test_assembler::Label;
-using lul_test::test_assembler::Section;
-
-class CFISection: public Section {
- public:
-
- // CFI augmentation strings beginning with 'z', defined by the
- // Linux/IA-64 C++ ABI, can specify interesting encodings for
- // addresses appearing in FDE headers and call frame instructions (and
- // for additional fields whose presence the augmentation string
- // specifies). In particular, pointers can be specified to be relative
- // to various base address: the start of the .text section, the
- // location holding the address itself, and so on. These allow the
- // frame data to be position-independent even when they live in
- // write-protected pages. These variants are specified at the
- // following two URLs:
- //
- // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
- // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
- //
- // CFISection leaves the production of well-formed 'z'-augmented CIEs and
- // FDEs to the user, but does provide EncodedPointer, to emit
- // properly-encoded addresses for a given pointer encoding.
- // EncodedPointer uses an instance of this structure to find the base
- // addresses it should use; you can establish a default for all encoded
- // pointers appended to this section with SetEncodedPointerBases.
- struct EncodedPointerBases {
- EncodedPointerBases() : cfi(), text(), data() { }
-
- // The starting address of this CFI section in memory, for
- // DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data
- // that has is loaded into the program's address space.
- uint64_t cfi;
-
- // The starting address of this file's .text section, for DW_EH_PE_textrel.
- uint64_t text;
-
- // The starting address of this file's .got or .eh_frame_hdr section,
- // for DW_EH_PE_datarel.
- uint64_t data;
- };
-
- // Create a CFISection whose endianness is ENDIANNESS, and where
- // machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is
- // true, use the .eh_frame format, as described by the Linux
- // Standards Base Core Specification, instead of the DWARF CFI
- // format.
- CFISection(Endianness endianness, size_t address_size,
- bool eh_frame = false)
- : Section(endianness), address_size_(address_size), eh_frame_(eh_frame),
- pointer_encoding_(lul::DW_EH_PE_absptr),
- encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) {
- // The 'start', 'Here', and 'Mark' members of a CFISection all refer
- // to section offsets.
- start() = 0;
- }
-
- // Return this CFISection's address size.
- size_t AddressSize() const { return address_size_; }
-
- // Return true if this CFISection uses the .eh_frame format, or
- // false if it contains ordinary DWARF CFI data.
- bool ContainsEHFrame() const { return eh_frame_; }
-
- // Use ENCODING for pointers in calls to FDEHeader and EncodedPointer.
- void SetPointerEncoding(DwarfPointerEncoding encoding) {
- pointer_encoding_ = encoding;
- }
-
- // Use the addresses in BASES as the base addresses for encoded
- // pointers in subsequent calls to FDEHeader or EncodedPointer.
- // This function makes a copy of BASES.
- void SetEncodedPointerBases(const EncodedPointerBases &bases) {
- encoded_pointer_bases_ = bases;
- }
-
- // Append a Common Information Entry header to this section with the
- // given values. If dwarf64 is true, use the 64-bit DWARF initial
- // length format for the CIE's initial length. Return a reference to
- // this section. You should call FinishEntry after writing the last
- // instruction for the CIE.
- //
- // Before calling this function, you will typically want to use Mark
- // or Here to make a label to pass to FDEHeader that refers to this
- // CIE's position in the section.
- CFISection &CIEHeader(uint64_t code_alignment_factor,
- int data_alignment_factor,
- unsigned return_address_register,
- uint8_t version = 3,
- const string &augmentation = "",
- bool dwarf64 = false);
-
- // Append a Frame Description Entry header to this section with the
- // given values. If dwarf64 is true, use the 64-bit DWARF initial
- // length format for the CIE's initial length. Return a reference to
- // this section. You should call FinishEntry after writing the last
- // instruction for the CIE.
- //
- // This function doesn't support entries that are longer than
- // 0xffffff00 bytes. (The "initial length" is always a 32-bit
- // value.) Nor does it support .debug_frame sections longer than
- // 0xffffff00 bytes.
- CFISection &FDEHeader(Label cie_pointer,
- uint64_t initial_location,
- uint64_t address_range,
- bool dwarf64 = false);
-
- // Note the current position as the end of the last CIE or FDE we
- // started, after padding with DW_CFA_nops for alignment. This
- // defines the label representing the entry's length, cited in the
- // entry's header. Return a reference to this section.
- CFISection &FinishEntry();
-
- // Append the contents of BLOCK as a DW_FORM_block value: an
- // unsigned LEB128 length, followed by that many bytes of data.
- CFISection &Block(const string &block) {
- ULEB128(block.size());
- Append(block);
- return *this;
- }
-
- // Append ADDRESS to this section, in the appropriate size and
- // endianness. Return a reference to this section.
- CFISection &Address(uint64_t address) {
- Section::Append(endianness(), address_size_, address);
- return *this;
- }
-
- // Append ADDRESS to this section, using ENCODING and BASES. ENCODING
- // defaults to this section's default encoding, established by
- // SetPointerEncoding. BASES defaults to this section's bases, set by
- // SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the
- // encoding, assume that ADDRESS is where the true address is stored.
- // Return a reference to this section.
- //
- // (C++ doesn't let me use default arguments here, because I want to
- // refer to members of *this in the default argument expression.)
- CFISection &EncodedPointer(uint64_t address) {
- return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_);
- }
- CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) {
- return EncodedPointer(address, encoding, encoded_pointer_bases_);
- }
- CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding,
- const EncodedPointerBases &bases);
-
- // Restate some member functions, to keep chaining working nicely.
- CFISection &Mark(Label *label) { Section::Mark(label); return *this; }
- CFISection &D8(uint8_t v) { Section::D8(v); return *this; }
- CFISection &D16(uint16_t v) { Section::D16(v); return *this; }
- CFISection &D16(Label v) { Section::D16(v); return *this; }
- CFISection &D32(uint32_t v) { Section::D32(v); return *this; }
- CFISection &D32(const Label &v) { Section::D32(v); return *this; }
- CFISection &D64(uint64_t v) { Section::D64(v); return *this; }
- CFISection &D64(const Label &v) { Section::D64(v); return *this; }
- CFISection &LEB128(long long v) { Section::LEB128(v); return *this; }
- CFISection &ULEB128(uint64_t v) { Section::ULEB128(v); return *this; }
-
- private:
- // A length value that we've appended to the section, but is not yet
- // known. LENGTH is the appended value; START is a label referring
- // to the start of the data whose length was cited.
- struct PendingLength {
- Label length;
- Label start;
- };
-
- // Constants used in CFI/.eh_frame data:
-
- // If the first four bytes of an "initial length" are this constant, then
- // the data uses the 64-bit DWARF format, and the length itself is the
- // subsequent eight bytes.
- static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU;
-
- // The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data.
- static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0;
- static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0;
- static const uint32_t kEHFrame32CIEIdentifier = 0;
- static const uint64_t kEHFrame64CIEIdentifier = 0;
-
- // The size of a machine address for the data in this section.
- size_t address_size_;
-
- // If true, we are generating a Linux .eh_frame section, instead of
- // a standard DWARF .debug_frame section.
- bool eh_frame_;
-
- // The encoding to use for FDE pointers.
- DwarfPointerEncoding pointer_encoding_;
-
- // The base addresses to use when emitting encoded pointers.
- EncodedPointerBases encoded_pointer_bases_;
-
- // The length value for the current entry.
- //
- // Oddly, this must be dynamically allocated. Labels never get new
- // values; they only acquire constraints on the value they already
- // have, or assert if you assign them something incompatible. So
- // each header needs truly fresh Label objects to cite in their
- // headers and track their positions. The alternative is explicit
- // destructor invocation and a placement new. Ick.
- PendingLength *entry_length_;
-
- // True if we are currently emitting an FDE --- that is, we have
- // called FDEHeader but have not yet called FinishEntry.
- bool in_fde_;
-
- // If in_fde_ is true, this is its starting address. We use this for
- // emitting DW_EH_PE_funcrel pointers.
- uint64_t fde_start_address_;
-};
-
-} // namespace lul_test
-
-#endif // LUL_TEST_INFRASTRUCTURE_H
diff --git a/tools/profiler/tests/gtest/ThreadProfileTest.cpp b/tools/profiler/tests/gtest/ThreadProfileTest.cpp
deleted file mode 100644
index 4399a5bc2..000000000
--- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "gtest/gtest.h"
-
-#include "ProfileEntry.h"
-#include "ThreadProfile.h"
-
-// Make sure we can initialize our ThreadProfile
-TEST(ThreadProfile, Initialization) {
- PseudoStack* stack = PseudoStack::create();
- Thread::tid_t tid = 1000;
- ThreadInfo info("testThread", tid, true, stack, nullptr);
- RefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
- ThreadProfile tp(&info, pb);
-}
-
-// Make sure we can record one tag and read it
-TEST(ThreadProfile, InsertOneTag) {
- PseudoStack* stack = PseudoStack::create();
- Thread::tid_t tid = 1000;
- ThreadInfo info("testThread", tid, true, stack, nullptr);
- RefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
- pb->addTag(ProfileEntry('t', 123.1));
- ASSERT_TRUE(pb->mEntries != nullptr);
- ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagName == 't');
- ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagDouble == 123.1);
-}
-
-// See if we can insert some tags
-TEST(ThreadProfile, InsertTagsNoWrap) {
- PseudoStack* stack = PseudoStack::create();
- Thread::tid_t tid = 1000;
- ThreadInfo info("testThread", tid, true, stack, nullptr);
- RefPtr<ProfileBuffer> pb = new ProfileBuffer(100);
- int test_size = 50;
- for (int i = 0; i < test_size; i++) {
- pb->addTag(ProfileEntry('t', i));
- }
- ASSERT_TRUE(pb->mEntries != nullptr);
- int readPos = pb->mReadPos;
- while (readPos != pb->mWritePos) {
- ASSERT_TRUE(pb->mEntries[readPos].mTagName == 't');
- ASSERT_TRUE(pb->mEntries[readPos].mTagInt == readPos);
- readPos = (readPos + 1) % pb->mEntrySize;
- }
-}
-
-// See if wrapping works as it should in the basic case
-TEST(ThreadProfile, InsertTagsWrap) {
- PseudoStack* stack = PseudoStack::create();
- Thread::tid_t tid = 1000;
- // we can fit only 24 tags in this buffer because of the empty slot
- int tags = 24;
- int buffer_size = tags + 1;
- ThreadInfo info("testThread", tid, true, stack, nullptr);
- RefPtr<ProfileBuffer> pb = new ProfileBuffer(buffer_size);
- int test_size = 43;
- for (int i = 0; i < test_size; i++) {
- pb->addTag(ProfileEntry('t', i));
- }
- ASSERT_TRUE(pb->mEntries != nullptr);
- int readPos = pb->mReadPos;
- int ctr = 0;
- while (readPos != pb->mWritePos) {
- ASSERT_TRUE(pb->mEntries[readPos].mTagName == 't');
- // the first few tags were discarded when we wrapped
- ASSERT_TRUE(pb->mEntries[readPos].mTagInt == ctr + (test_size - tags));
- ctr++;
- readPos = (readPos + 1) % pb->mEntrySize;
- }
-}
-
diff --git a/tools/profiler/tests/gtest/moz.build b/tools/profiler/tests/gtest/moz.build
deleted file mode 100644
index 33aded164..000000000
--- a/tools/profiler/tests/gtest/moz.build
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
- UNIFIED_SOURCES += [
- 'LulTestDwarf.cpp',
- 'LulTestInfrastructure.cpp',
- ]
- if CONFIG['CPU_ARCH'] != 'x86':
- UNIFIED_SOURCES += [
- 'LulTest.cpp',
- ]
-
-LOCAL_INCLUDES += [
- '/tools/profiler/core',
- '/tools/profiler/gecko',
- '/tools/profiler/lul',
-]
-
-UNIFIED_SOURCES += [
- 'ThreadProfileTest.cpp',
-]
-
-FINAL_LIBRARY = 'xul-gtest'
-
-if CONFIG['GNU_CXX']:
- CXXFLAGS += ['-Wno-error=shadow']