summaryrefslogtreecommitdiffstats
path: root/tools/profiler/core
diff options
context:
space:
mode:
Diffstat (limited to 'tools/profiler/core')
-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
30 files changed, 0 insertions, 8953 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;
-}
-