Bug 1178892 - Split the profiler into Core & Gecko files and break apart monolithic headers. r=mstange

--HG--
rename : tools/profiler/EHABIStackWalk.cpp => tools/profiler/core/EHABIStackWalk.cpp
rename : tools/profiler/EHABIStackWalk.h => tools/profiler/core/EHABIStackWalk.h
rename : tools/profiler/TableTicker.cpp => tools/profiler/core/GeckoSampler.cpp
rename : tools/profiler/TableTicker.h => tools/profiler/core/GeckoSampler.h
rename : tools/profiler/IntelPowerGadget.cpp => tools/profiler/core/IntelPowerGadget.cpp
rename : tools/profiler/IntelPowerGadget.h => tools/profiler/core/IntelPowerGadget.h
rename : tools/profiler/PlatformMacros.h => tools/profiler/core/PlatformMacros.h
rename : tools/profiler/ProfileEntry.cpp => tools/profiler/core/ProfileEntry.cpp
rename : tools/profiler/ProfileEntry.h => tools/profiler/core/ProfileEntry.h
rename : tools/profiler/ProfileJSONWriter.cpp => tools/profiler/core/ProfileJSONWriter.cpp
rename : tools/profiler/ProfileJSONWriter.h => tools/profiler/core/ProfileJSONWriter.h
rename : tools/profiler/ProfilerBacktrace.cpp => tools/profiler/core/ProfilerBacktrace.cpp
rename : tools/profiler/ProfilerMarkers.cpp => tools/profiler/core/ProfilerMarkers.cpp
rename : tools/profiler/SyncProfile.cpp => tools/profiler/core/SyncProfile.cpp
rename : tools/profiler/SyncProfile.h => tools/profiler/core/SyncProfile.h
rename : tools/profiler/platform-linux.cc => tools/profiler/core/platform-linux.cc
rename : tools/profiler/platform-macos.cc => tools/profiler/core/platform-macos.cc
rename : tools/profiler/platform-win32.cc => tools/profiler/core/platform-win32.cc
rename : tools/profiler/platform.cpp => tools/profiler/core/platform.cpp
rename : tools/profiler/platform.h => tools/profiler/core/platform.h
rename : tools/profiler/shared-libraries-linux.cc => tools/profiler/core/shared-libraries-linux.cc
rename : tools/profiler/shared-libraries-macos.cc => tools/profiler/core/shared-libraries-macos.cc
rename : tools/profiler/shared-libraries-win32.cc => tools/profiler/core/shared-libraries-win32.cc
rename : tools/profiler/shim_mac_dump_syms.h => tools/profiler/core/shim_mac_dump_syms.h
rename : tools/profiler/shim_mac_dump_syms.mm => tools/profiler/core/shim_mac_dump_syms.mm
rename : tools/profiler/v8-support.h => tools/profiler/core/v8-support.h
rename : tools/profiler/ProfileGatherer.cpp => tools/profiler/gecko/ProfileGatherer.cpp
rename : tools/profiler/Profiler.jsm => tools/profiler/gecko/Profiler.jsm
rename : tools/profiler/ProfilerIOInterposeObserver.cpp => tools/profiler/gecko/ProfilerIOInterposeObserver.cpp
rename : tools/profiler/ProfilerIOInterposeObserver.h => tools/profiler/gecko/ProfilerIOInterposeObserver.h
rename : tools/profiler/SaveProfileTask.cpp => tools/profiler/gecko/SaveProfileTask.cpp
rename : tools/profiler/SaveProfileTask.h => tools/profiler/gecko/SaveProfileTask.h
rename : tools/profiler/ThreadResponsiveness.cpp => tools/profiler/gecko/ThreadResponsiveness.cpp
rename : tools/profiler/ThreadResponsiveness.h => tools/profiler/gecko/ThreadResponsiveness.h
rename : tools/profiler/local_debug_info_symbolizer.cc => tools/profiler/gecko/local_debug_info_symbolizer.cc
rename : tools/profiler/local_debug_info_symbolizer.h => tools/profiler/gecko/local_debug_info_symbolizer.h
rename : tools/profiler/nsIProfileSaveEvent.idl => tools/profiler/gecko/nsIProfileSaveEvent.idl
rename : tools/profiler/nsIProfiler.idl => tools/profiler/gecko/nsIProfiler.idl
rename : tools/profiler/nsProfiler.cpp => tools/profiler/gecko/nsProfiler.cpp
rename : tools/profiler/nsProfiler.h => tools/profiler/gecko/nsProfiler.h
rename : tools/profiler/nsProfilerCIID.h => tools/profiler/gecko/nsProfilerCIID.h
rename : tools/profiler/nsProfilerFactory.cpp => tools/profiler/gecko/nsProfilerFactory.cpp
rename : tools/profiler/nsProfilerStartParams.cpp => tools/profiler/gecko/nsProfilerStartParams.cpp
rename : tools/profiler/nsProfilerStartParams.h => tools/profiler/gecko/nsProfilerStartParams.h
rename : tools/profiler/AutoObjectMapper.cpp => tools/profiler/lul/AutoObjectMapper.cpp
rename : tools/profiler/AutoObjectMapper.h => tools/profiler/lul/AutoObjectMapper.h
rename : tools/profiler/LulCommon.cpp => tools/profiler/lul/LulCommon.cpp
rename : tools/profiler/LulCommonExt.h => tools/profiler/lul/LulCommonExt.h
rename : tools/profiler/LulDwarf.cpp => tools/profiler/lul/LulDwarf.cpp
rename : tools/profiler/LulDwarfExt.h => tools/profiler/lul/LulDwarfExt.h
rename : tools/profiler/LulDwarfInt.h => tools/profiler/lul/LulDwarfInt.h
rename : tools/profiler/LulDwarfSummariser.cpp => tools/profiler/lul/LulDwarfSummariser.cpp
rename : tools/profiler/LulDwarfSummariser.h => tools/profiler/lul/LulDwarfSummariser.h
rename : tools/profiler/LulElf.cpp => tools/profiler/lul/LulElf.cpp
rename : tools/profiler/LulElfExt.h => tools/profiler/lul/LulElfExt.h
rename : tools/profiler/LulElfInt.h => tools/profiler/lul/LulElfInt.h
rename : tools/profiler/LulMain.cpp => tools/profiler/lul/LulMain.cpp
rename : tools/profiler/LulMain.h => tools/profiler/lul/LulMain.h
rename : tools/profiler/LulMainInt.h => tools/profiler/lul/LulMainInt.h
rename : tools/profiler/LulPlatformMacros.h => tools/profiler/lul/LulPlatformMacros.h
rename : tools/profiler/platform-linux-lul.cpp => tools/profiler/lul/platform-linux-lul.cpp
rename : tools/profiler/platform-linux-lul.h => tools/profiler/lul/platform-linux-lul.h
rename : tools/profiler/GeckoProfiler.h => tools/profiler/public/GeckoProfiler.h
rename : tools/profiler/GeckoProfilerFunc.h => tools/profiler/public/GeckoProfilerFunc.h
rename : tools/profiler/GeckoProfilerImpl.h => tools/profiler/public/GeckoProfilerImpl.h
rename : tools/profiler/ProfileGatherer.h => tools/profiler/public/ProfileGatherer.h
rename : tools/profiler/ProfilerBacktrace.h => tools/profiler/public/ProfilerBacktrace.h
rename : tools/profiler/ProfilerMarkers.h => tools/profiler/public/ProfilerMarkers.h
rename : tools/profiler/PseudoStack.h => tools/profiler/public/PseudoStack.h
rename : tools/profiler/shared-libraries.h => tools/profiler/public/shared-libraries.h
rename : tools/profiler/GeckoTaskTracer.cpp => tools/profiler/tasktracer/GeckoTaskTracer.cpp
rename : tools/profiler/GeckoTaskTracer.h => tools/profiler/tasktracer/GeckoTaskTracer.h
rename : tools/profiler/GeckoTaskTracerImpl.h => tools/profiler/tasktracer/GeckoTaskTracerImpl.h
rename : tools/profiler/SourceEventTypeMap.h => tools/profiler/tasktracer/SourceEventTypeMap.h
rename : tools/profiler/TracedTaskCommon.cpp => tools/profiler/tasktracer/TracedTaskCommon.cpp
rename : tools/profiler/TracedTaskCommon.h => tools/profiler/tasktracer/TracedTaskCommon.h
extra : commitid : EGgqHIgsN6z
This commit is contained in:
Benoit Girard 2015-06-30 15:03:45 -04:00
parent 39a5778c7e
commit daba448963
86 changed files with 761 additions and 697 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1178215 requires clobber for libvpx file moves.
Bug 1178892 requires clobber for profiler file moves.

View File

@ -21,7 +21,7 @@
#include "platform.h"
#include "shared-libraries.h"
#include "mozilla/StackWalk.h"
#include "TableTicker.h"
#include "GeckoSampler.h"
// JSON
#include "ProfileJSONWriter.h"
@ -40,6 +40,8 @@
#include "mozilla/Services.h"
#include "PlatformMacros.h"
#include "nsTArray.h"
#include "mozilla/ProfileGatherer.h"
#endif
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
@ -79,8 +81,8 @@ pid_t gettid();
#ifndef SPS_STANDALONE
#if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
# define USE_LUL_STACKWALK
# include "LulMain.h"
# include "platform-linux-lul.h"
# include "lul/LulMain.h"
# include "lul/platform-linux-lul.h"
#endif
#endif
@ -176,7 +178,7 @@ hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature)
return false;
}
TableTicker::TableTicker(double aInterval, int aEntrySize,
GeckoSampler::GeckoSampler(double aInterval, int aEntrySize,
const char** aFeatures, uint32_t aFeatureCount,
const char** aThreadNameFilters, uint32_t aFilterCount)
: Sampler(aInterval, true, aEntrySize)
@ -241,7 +243,7 @@ TableTicker::TableTicker(double aInterval, int aEntrySize,
#endif
}
TableTicker::~TableTicker()
GeckoSampler::~GeckoSampler()
{
if (IsActive())
Stop();
@ -273,7 +275,7 @@ TableTicker::~TableTicker()
#endif
}
void TableTicker::HandleSaveRequest()
void GeckoSampler::HandleSaveRequest()
{
if (!mSaveRequested)
return;
@ -287,12 +289,12 @@ void TableTicker::HandleSaveRequest()
#endif
}
void TableTicker::DeleteExpiredMarkers()
void GeckoSampler::DeleteExpiredMarkers()
{
mBuffer->deleteExpiredStoredMarkers();
}
void TableTicker::StreamTaskTracer(SpliceableJSONWriter& aWriter)
void GeckoSampler::StreamTaskTracer(SpliceableJSONWriter& aWriter)
{
#ifdef MOZ_TASK_TRACER
aWriter.StartArrayProperty("data");
@ -324,7 +326,7 @@ void TableTicker::StreamTaskTracer(SpliceableJSONWriter& aWriter)
}
void TableTicker::StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter)
void GeckoSampler::StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter)
{
aWriter.IntProperty("version", 3);
aWriter.DoubleProperty("interval", interval());
@ -378,14 +380,14 @@ void TableTicker::StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter)
#endif
}
void TableTicker::ToStreamAsJSON(std::ostream& stream, double aSinceTime)
void GeckoSampler::ToStreamAsJSON(std::ostream& stream, double aSinceTime)
{
SpliceableJSONWriter b(mozilla::MakeUnique<OStreamJSONWriteFunc>(stream));
StreamJSON(b, aSinceTime);
}
#ifndef SPS_STANDALONE
JSObject* TableTicker::ToJSObject(JSContext *aCx, double aSinceTime)
JSObject* GeckoSampler::ToJSObject(JSContext *aCx, double aSinceTime)
{
JS::RootedValue val(aCx);
{
@ -398,25 +400,25 @@ JSObject* TableTicker::ToJSObject(JSContext *aCx, double aSinceTime)
}
#endif
UniquePtr<char[]> TableTicker::ToJSON(double aSinceTime)
UniquePtr<char[]> GeckoSampler::ToJSON(double aSinceTime)
{
SpliceableChunkedJSONWriter b;
StreamJSON(b, aSinceTime);
return b.WriteFunc()->CopyData();
}
void TableTicker::ToJSObjectAsync(double aSinceTime,
Promise* aPromise)
void GeckoSampler::ToJSObjectAsync(double aSinceTime,
mozilla::dom::Promise* aPromise)
{
if (NS_WARN_IF(mGatherer)) {
return;
}
mGatherer = new ProfileGatherer(this, aSinceTime, aPromise);
mGatherer = new mozilla::ProfileGatherer(this, aSinceTime, aPromise);
mGatherer->Start();
}
void TableTicker::ProfileGathered()
void GeckoSampler::ProfileGathered()
{
mGatherer = nullptr;
}
@ -491,7 +493,7 @@ void BuildJavaThreadJSObject(SpliceableJSONWriter& aWriter)
}
#endif
void TableTicker::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
void GeckoSampler::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
{
aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
{
@ -561,7 +563,7 @@ void TableTicker::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
aWriter.End();
}
void TableTicker::FlushOnJSShutdown(JSRuntime* aRuntime)
void GeckoSampler::FlushOnJSShutdown(JSRuntime* aRuntime)
{
#ifndef SPS_STANDALONE
SetPaused(true);
@ -594,7 +596,7 @@ void PseudoStack::flushSamplerOnJSShutdown()
{
#ifndef SPS_STANDALONE
MOZ_ASSERT(mRuntime);
TableTicker* t = tlsTicker.get();
GeckoSampler* t = tlsTicker.get();
if (t) {
t->FlushOnJSShutdown(mRuntime);
}
@ -921,7 +923,7 @@ void StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP,
nativeStack->count++;
}
void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
void GeckoSampler::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
#ifndef XP_MACOSX
uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData());
@ -974,7 +976,7 @@ void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample
#ifdef USE_EHABI_STACKWALK
void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
void GeckoSampler::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
void *pc_array[1000];
void *sp_array[1000];
@ -1042,7 +1044,7 @@ void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample
#ifdef USE_LUL_STACKWALK
void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
void GeckoSampler::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
const mcontext_t* mc
= &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
@ -1162,13 +1164,13 @@ void doSampleStackTrace(ThreadProfile &aProfile, TickSample *aSample, bool aAddL
#endif
}
void TableTicker::Tick(TickSample* sample)
void GeckoSampler::Tick(TickSample* sample)
{
// Don't allow for ticks to happen within other ticks.
InplaceTick(sample);
}
void TableTicker::InplaceTick(TickSample* sample)
void GeckoSampler::InplaceTick(TickSample* sample)
{
ThreadProfile& currThreadProfile = *sample->threadProfile;
@ -1251,7 +1253,7 @@ SyncProfile* NewSyncProfile()
} // anonymous namespace
SyncProfile* TableTicker::GetBacktrace()
SyncProfile* GeckoSampler::GetBacktrace()
{
SyncProfile* profile = NewSyncProfile();
@ -1278,7 +1280,7 @@ SyncProfile* TableTicker::GetBacktrace()
}
void
TableTicker::GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration)
GeckoSampler::GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration)
{
*aCurrentPosition = mBuffer->mWritePos;
*aTotalSize = mBuffer->mEntrySize;

View File

@ -3,12 +3,14 @@
* 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 TableTicker_h
#define TableTicker_h
#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
@ -41,12 +43,12 @@ extern mozilla::TimeStamp sLastTracerEvent;
extern int sFrameNumber;
extern int sLastFrameNumber;
class TableTicker: public Sampler {
class GeckoSampler: public Sampler {
public:
TableTicker(double aInterval, int aEntrySize,
GeckoSampler(double aInterval, int aEntrySize,
const char** aFeatures, uint32_t aFeatureCount,
const char** aThreadNameFilters, uint32_t aFilterCount);
~TableTicker();
~GeckoSampler();
void RegisterThread(ThreadInfo* aInfo) {
if (!aInfo->IsMainThread() && !mProfileThreads) {

View File

@ -0,0 +1,89 @@
/* -*- 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;
}

View File

@ -0,0 +1,60 @@
/* -*- 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"
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,
JSRuntime* rt, 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

View File

@ -101,91 +101,6 @@ void* ProfileEntry::get_tagPtr() {
// END ProfileEntry
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// BEGIN ProfileBuffer
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;
}
class JSONSchemaWriter
{
JSONWriter& mWriter;
@ -913,259 +828,5 @@ void ProfileBuffer::DuplicateLastSample(int aThreadId)
////////////////////////////////////////////////////////////////////////
// BEGIN ThreadProfile
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->mRuntime);
#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->mRuntime,
#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
// JSRuntime destruction.
MOZ_ASSERT(mPseudoStack->mRuntime);
// 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->mRuntime);
#else
mUniqueStacks.emplace(nullptr);
#endif
{
SpliceableChunkedJSONWriter b;
b.StartBareList();
{
mBuffer->StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
#ifndef SPS_STANDALONE
mPseudoStack->mRuntime,
#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 mRuntime 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);
}
// END ThreadProfile
////////////////////////////////////////////////////////////////////////

View File

@ -311,52 +311,6 @@ private:
#endif
};
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,
JSRuntime* rt, 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;
};
//
// ThreadProfile JSON Format
// -------------------------
@ -450,95 +404,4 @@ public:
// }
//
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
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 mozilla::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 /* ndef MOZ_PROFILE_ENTRY_H */

View File

@ -8,6 +8,7 @@
#define __SYNCPROFILE_H
#include "ProfileEntry.h"
#include "ThreadProfile.h"
class SyncProfile : public ThreadProfile
{

View File

@ -0,0 +1,38 @@
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)
{
#ifndef SPS_STANDALONE
mThread = NS_GetCurrentThread();
#endif
}
ThreadInfo::~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();
}
}

View File

@ -0,0 +1,66 @@
/* -*- 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; }
#ifdef MOZ_NUWA_PROCESS
void SetThreadId(int aThreadId) { mThreadId = aThreadId; }
#endif
#ifndef SPS_STANDALONE
/**
* May be null for the main thread if the profiler was started during startup
*/
nsIThread* GetThread() const { return mThread.get(); }
#endif
private:
char* mName;
int mThreadId;
const bool mIsMainThread;
PseudoStack* mPseudoStack;
PlatformData* mPlatformData;
ThreadProfile* mProfile;
void* const 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

View File

@ -0,0 +1,260 @@
/* -*- 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->mRuntime);
#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->mRuntime,
#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
// JSRuntime destruction.
MOZ_ASSERT(mPseudoStack->mRuntime);
// 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->mRuntime);
#else
mUniqueStacks.emplace(nullptr);
#endif
{
SpliceableChunkedJSONWriter b;
b.StartBareList();
{
mBuffer->StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
#ifndef SPS_STANDALONE
mPseudoStack->mRuntime,
#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 mRuntime 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);
}

View File

@ -0,0 +1,104 @@
/* -*- 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
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 mozilla::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

View File

@ -73,7 +73,7 @@
#include "mozilla/DebugOnly.h"
#include "ProfileEntry.h"
#include "nsThreadUtils.h"
#include "TableTicker.h"
#include "GeckoSampler.h"
#include "ThreadResponsiveness.h"
#if defined(__ARM_EABI__) && defined(MOZ_WIDGET_GONK)
@ -82,8 +82,8 @@
# include "EHABIStackWalk.h"
#elif defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
# define USE_LUL_STACKWALK
# include "LulMain.h"
# include "platform-linux-lul.h"
# include "lul/LulMain.h"
# include "lul/platform-linux-lul.h"
#endif
// Memory profile
@ -273,7 +273,7 @@ int tgkill(pid_t tgid, pid_t tid, int signalno) {
return syscall(SYS_tgkill, tgid, tid, signalno);
}
class PlatformData : public Malloced {
class PlatformData {
public:
PlatformData()
{}

View File

@ -38,7 +38,7 @@
#endif
#include "platform.h"
#include "TableTicker.h"
#include "GeckoSampler.h"
#include "mozilla/TimeStamp.h"
using mozilla::TimeStamp;
@ -137,7 +137,7 @@ void Thread::Join() {
pthread_join(thread_, NULL);
}
class PlatformData : public Malloced {
class PlatformData {
public:
PlatformData() : profiled_thread_(mach_thread_self())
{

View File

@ -30,14 +30,14 @@
#include <mmsystem.h>
#include <process.h>
#include "platform.h"
#include "TableTicker.h"
#include "GeckoSampler.h"
#include "ThreadResponsiveness.h"
#include "ProfileEntry.h"
// Memory profile
#include "nsMemoryReporterManager.h"
class PlatformData : public Malloced {
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

View File

@ -19,7 +19,7 @@
#include "mozilla/ThreadLocal.h"
#include "mozilla/TimeStamp.h"
#include "PseudoStack.h"
#include "TableTicker.h"
#include "GeckoSampler.h"
#ifndef SPS_STANDALONE
#include "nsIObserverService.h"
#include "nsDirectoryServiceUtils.h"
@ -38,13 +38,13 @@
#ifndef SPS_STANDALONE
#if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
# define USE_LUL_STACKWALK
# include "LulMain.h"
# include "platform-linux-lul.h"
# include "lul/LulMain.h"
# include "lul/platform-linux-lul.h"
#endif
#endif
mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
mozilla::ThreadLocal<TableTicker *> tlsTicker;
mozilla::ThreadLocal<GeckoSampler *> tlsTicker;
mozilla::ThreadLocal<void *> tlsStackTop;
// We need to track whether we've been initialized otherwise
// we end up using tlsStack without initializing it.
@ -83,7 +83,7 @@ static int sProfileEntries; /* how many entries do we store? */
std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = nullptr;
mozilla::UniquePtr< ::Mutex> Sampler::sRegisteredThreadsMutex;
TableTicker* Sampler::sActiveSampler;
GeckoSampler* Sampler::sActiveSampler;
#ifndef SPS_STANDALONE
static mozilla::StaticAutoPtr<mozilla::ProfilerIOInterposeObserver>
@ -129,43 +129,6 @@ void Sampler::Shutdown() {
#endif
}
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)
{
#ifndef SPS_STANDALONE
mThread = NS_GetCurrentThread();
#endif
}
ThreadInfo::~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();
}
}
StackOwningThreadInfo::StackOwningThreadInfo(const char* aName, int aThreadId,
bool aIsMainThread,
PseudoStack* aPseudoStack,
@ -547,7 +510,7 @@ void mozilla_sampler_shutdown()
return;
// Save the profile on shutdown if requested.
TableTicker *t = tlsTicker.get();
GeckoSampler *t = tlsTicker.get();
if (t) {
const char *val = getenv("MOZ_PROFILER_SHUTDOWN");
if (val) {
@ -579,7 +542,7 @@ void mozilla_sampler_shutdown()
void mozilla_sampler_save()
{
TableTicker *t = tlsTicker.get();
GeckoSampler *t = tlsTicker.get();
if (!t) {
return;
}
@ -592,7 +555,7 @@ void mozilla_sampler_save()
mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime)
{
TableTicker *t = tlsTicker.get();
GeckoSampler *t = tlsTicker.get();
if (!t) {
return nullptr;
}
@ -603,7 +566,7 @@ mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime)
#ifndef SPS_STANDALONE
JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, double aSinceTime)
{
TableTicker *t = tlsTicker.get();
GeckoSampler *t = tlsTicker.get();
if (!t) {
return nullptr;
}
@ -614,7 +577,7 @@ JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, double aSinceTime)
void mozilla_sampler_get_profile_data_async(double aSinceTime,
mozilla::dom::Promise* aPromise)
{
TableTicker *t = tlsTicker.get();
GeckoSampler *t = tlsTicker.get();
if (NS_WARN_IF(!t)) {
return;
}
@ -625,7 +588,7 @@ void mozilla_sampler_get_profile_data_async(double aSinceTime,
void mozilla_sampler_save_profile_to_file(const char* aFilename)
{
TableTicker *t = tlsTicker.get();
GeckoSampler *t = tlsTicker.get();
if (!t) {
return;
}
@ -702,7 +665,7 @@ void mozilla_sampler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTota
if (!stack_key_initialized)
return;
TableTicker *t = tlsTicker.get();
GeckoSampler *t = tlsTicker.get();
if (!t)
return;
@ -732,8 +695,8 @@ void mozilla_sampler_start(int aProfileEntries, double aInterval,
// Reset the current state if the profiler is running
profiler_stop();
TableTicker* t;
t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
GeckoSampler* t;
t = new GeckoSampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
aFeatures, aFeatureCount,
aThreadNameFilters, aFilterCount);
@ -827,7 +790,7 @@ void mozilla_sampler_stop()
if (!stack_key_initialized)
return;
TableTicker *t = tlsTicker.get();
GeckoSampler *t = tlsTicker.get();
if (!t) {
LOG("END mozilla_sampler_stop-early");
return;
@ -1037,7 +1000,7 @@ ProfilerBacktrace* mozilla_sampler_get_backtrace()
return nullptr;
}
TableTicker* t = tlsTicker.get();
GeckoSampler* t = tlsTicker.get();
if (!t) {
return nullptr;
}
@ -1157,5 +1120,3 @@ mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) {
// END externally visible functions
////////////////////////////////////////////////////////////////////////

View File

@ -58,6 +58,17 @@
#include "v8-support.h"
#include <vector>
// We need a definition of gettid(), but glibc doesn't provide a
// wrapper for it.
#if defined(__GLIBC__)
#include <unistd.h>
#include <sys/syscall.h>
static inline pid_t gettid()
{
return (pid_t) syscall(SYS_gettid);
}
#endif
#ifdef XP_WIN
#include <windows.h>
#endif
@ -96,6 +107,8 @@ bool moz_profiler_verbose();
#define ENABLE_SPS_LEAF_DATA
#endif
typedef int32_t Atomic32;
extern mozilla::TimeStamp sStartTime;
typedef uint8_t* Address;
@ -300,7 +313,7 @@ class TickSample {
class ThreadInfo;
class PlatformData;
class TableTicker;
class GeckoSampler;
class SyncProfile;
class Sampler {
public:
@ -370,8 +383,8 @@ class Sampler {
// Should only be called on shutdown
static void Shutdown();
static TableTicker* GetActiveSampler() { return sActiveSampler; }
static void SetActiveSampler(TableTicker* sampler) { sActiveSampler = sampler; }
static GeckoSampler* GetActiveSampler() { return sActiveSampler; }
static void SetActiveSampler(GeckoSampler* sampler) { sActiveSampler = sampler; }
static mozilla::UniquePtr<Mutex> sRegisteredThreadsMutex;
@ -390,7 +403,7 @@ class Sampler {
protected:
static std::vector<ThreadInfo*>* sRegisteredThreads;
static TableTicker* sActiveSampler;
static GeckoSampler* sActiveSampler;
private:
void SetActive(bool value) { NoBarrier_Store(&active_, value); }
@ -411,58 +424,4 @@ class Sampler {
#endif
};
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; }
#ifdef MOZ_NUWA_PROCESS
void SetThreadId(int aThreadId) { mThreadId = aThreadId; }
#endif
#ifndef SPS_STANDALONE
/**
* May be null for the main thread if the profiler was started during startup
*/
nsIThread* GetThread() const { return mThread.get(); }
#endif
private:
char* mName;
int mThreadId;
const bool mIsMainThread;
PseudoStack* mPseudoStack;
PlatformData* mPlatformData;
ThreadProfile* mProfile;
void* const 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 /* ndef TOOLS_PLATFORM_H_ */

View File

@ -5,7 +5,7 @@
#include "mozilla/ProfileGatherer.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "TableTicker.h"
#include "GeckoSampler.h"
using mozilla::dom::AutoJSAPI;
using mozilla::dom::Promise;
@ -14,7 +14,7 @@ namespace mozilla {
NS_IMPL_ISUPPORTS0(ProfileGatherer)
ProfileGatherer::ProfileGatherer(TableTicker* aTicker,
ProfileGatherer::ProfileGatherer(GeckoSampler* aTicker,
double aSinceTime,
Promise* aPromise)
: mPromise(aPromise)
@ -74,7 +74,7 @@ ProfileGatherer::Finish()
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mPromise->GlobalJSObject()))) {
// We're really hosed if we can't get a JS context for some reason.
// We'll tell the TableTicker that we've gathered the profile just
// We'll tell the GeckoSampler that we've gathered the profile just
// so that it can drop the reference to this ProfileGatherer and maybe
// the user can try again.
mTicker->ProfileGathered();

View File

@ -29,7 +29,6 @@
// Set this to 1 for verbose logging
#define DEBUG_MAIN 0
namespace lul {
using std::string;

View File

@ -6,6 +6,8 @@
#ifndef MOZ_PLATFORM_LINUX_LUL_H
#define MOZ_PLATFORM_LINUX_LUL_H
#include "platform.h"
// Find out, in a platform-dependent way, where the code modules got
// mapped in the process' virtual address space, and get |aLUL| to
// load unwind info for them.
@ -16,17 +18,6 @@ read_procmaps(lul::LUL* aLUL);
void
logging_sink_for_LUL(const char* str);
// We need a definition of gettid(), but glibc doesn't provide a
// wrapper for it.
#if defined(__GLIBC__)
#include <unistd.h>
#include <sys/syscall.h>
static inline pid_t gettid()
{
return (pid_t) syscall(SYS_gettid);
}
#endif
// A singleton instance of the library.
extern lul::LUL* sLUL;

View File

@ -9,75 +9,78 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
XPIDL_MODULE = 'profiler'
XPIDL_SOURCES += [
'nsIProfiler.idl',
'nsIProfileSaveEvent.idl',
'gecko/nsIProfiler.idl',
'gecko/nsIProfileSaveEvent.idl',
]
EXPORTS += [
'GeckoProfilerFunc.h',
'GeckoProfilerImpl.h',
'ProfilerBacktrace.h',
'ProfilerMarkers.h',
'PseudoStack.h',
'shared-libraries.h',
'public/GeckoProfilerFunc.h',
'public/GeckoProfilerImpl.h',
'public/ProfilerBacktrace.h',
'public/ProfilerMarkers.h',
'public/PseudoStack.h',
'public/shared-libraries.h',
]
EXPORTS.mozilla += [
'ProfileGatherer.h',
'public/ProfileGatherer.h',
]
EXTRA_JS_MODULES += [
'Profiler.jsm',
'gecko/Profiler.jsm',
]
UNIFIED_SOURCES += [
'nsProfiler.cpp',
'nsProfilerFactory.cpp',
'nsProfilerStartParams.cpp',
'platform.cpp',
'ProfileEntry.cpp',
'ProfileGatherer.cpp',
'ProfileJSONWriter.cpp',
'ProfilerBacktrace.cpp',
'ProfilerIOInterposeObserver.cpp',
'ProfilerMarkers.cpp',
'SaveProfileTask.cpp',
'SyncProfile.cpp',
'TableTicker.cpp',
'ThreadResponsiveness.cpp',
'core/GeckoSampler.cpp',
'core/platform.cpp',
'core/ProfileBuffer.cpp',
'core/ProfileEntry.cpp',
'core/ProfileJSONWriter.cpp',
'core/ProfilerBacktrace.cpp',
'core/ProfilerMarkers.cpp',
'core/SyncProfile.cpp',
'core/ThreadInfo.cpp',
'core/ThreadProfile.cpp',
'gecko/nsProfiler.cpp',
'gecko/nsProfilerFactory.cpp',
'gecko/nsProfilerStartParams.cpp',
'gecko/ProfileGatherer.cpp',
'gecko/ProfilerIOInterposeObserver.cpp',
'gecko/SaveProfileTask.cpp',
'gecko/ThreadResponsiveness.cpp',
]
# This file cannot be built in unified mode because of name clashes with mozglue headers on Android.
SOURCES += [
'local_debug_info_symbolizer.cc',
'gecko/local_debug_info_symbolizer.cc',
]
if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
UNIFIED_SOURCES += [
'AutoObjectMapper.cpp',
'LulCommon.cpp',
'LulDwarf.cpp',
'LulDwarfSummariser.cpp',
'LulElf.cpp',
'LulMain.cpp',
'platform-linux-lul.cpp',
'lul/AutoObjectMapper.cpp',
'lul/LulCommon.cpp',
'lul/LulDwarf.cpp',
'lul/LulDwarfSummariser.cpp',
'lul/LulElf.cpp',
'lul/LulMain.cpp',
'lul/platform-linux-lul.cpp',
]
# These files cannot be built in unified mode because of name clashes with mozglue headers on Android.
SOURCES += [
'platform-linux.cc',
'shared-libraries-linux.cc',
'core/platform-linux.cc',
'core/shared-libraries-linux.cc',
]
if CONFIG['CPU_ARCH'] == 'arm':
SOURCES += [
'EHABIStackWalk.cpp',
'core/EHABIStackWalk.cpp',
]
elif CONFIG['OS_TARGET'] == 'Darwin':
UNIFIED_SOURCES += [
'platform-macos.cc',
'shared-libraries-macos.cc',
'shim_mac_dump_syms.mm',
'core/platform-macos.cc',
'core/shared-libraries-macos.cc',
'core/shim_mac_dump_syms.mm',
]
elif CONFIG['OS_TARGET'] == 'WINNT':
SOURCES += [
'IntelPowerGadget.cpp',
'platform-win32.cc',
'shared-libraries-win32.cc',
'core/IntelPowerGadget.cpp',
'core/platform-win32.cc',
'core/shared-libraries-win32.cc',
]
LOCAL_INCLUDES += [
@ -85,6 +88,8 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
'/ipc/chromium/src',
'/mozglue/linker',
'/toolkit/crashreporter/google-breakpad/src',
'/tools/profiler/core/',
'/tools/profiler/gecko/',
'/xpcom/base',
]
@ -106,18 +111,18 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
FINAL_LIBRARY = 'xul'
EXPORTS += [
'GeckoProfiler.h',
'public/GeckoProfiler.h',
]
if CONFIG['MOZ_TASK_TRACER']:
EXPORTS += [
'GeckoTaskTracer.h',
'GeckoTaskTracerImpl.h',
'TracedTaskCommon.h',
'public/GeckoTaskTracer.h',
'public/GeckoTaskTracerImpl.h',
'public/TracedTaskCommon.h',
]
UNIFIED_SOURCES += [
'GeckoTaskTracer.cpp',
'TracedTaskCommon.cpp',
'tasktracer/GeckoTaskTracer.cpp',
'tasktracer/TracedTaskCommon.cpp',
]
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']

View File

@ -38,14 +38,14 @@
#undef min
#endif
class TableTicker;
class GeckoSampler;
namespace mozilla {
class TimeStamp;
}
extern mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
extern mozilla::ThreadLocal<TableTicker *> tlsTicker;
extern mozilla::ThreadLocal<GeckoSampler *> tlsTicker;
extern mozilla::ThreadLocal<void *> tlsStackTop;
extern bool stack_key_initialized;

View File

@ -7,7 +7,7 @@
#include "mozilla/dom/Promise.h"
class TableTicker;
class GeckoSampler;
namespace mozilla {
@ -16,7 +16,7 @@ class ProfileGatherer final : public nsISupports
public:
NS_DECL_ISUPPORTS
ProfileGatherer(TableTicker* aTicker,
ProfileGatherer(GeckoSampler* aTicker,
double aSinceTime,
mozilla::dom::Promise* aPromise);
void WillGatherOOPProfile();
@ -28,7 +28,7 @@ private:
void Finish();
nsRefPtr<mozilla::dom::Promise> mPromise;
TableTicker* mTicker;
GeckoSampler* mTicker;
double mSinceTime;
uint32_t mPendingProfiles;
};

View File

@ -6,6 +6,7 @@
#include "gtest/gtest.h"
#include "ProfileEntry.h"
#include "ThreadProfile.h"
// Make sure we can initialize our ThreadProfile
TEST(ThreadProfile, Initialization) {

View File

@ -12,7 +12,9 @@ if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
]
LOCAL_INCLUDES += [
'/tools/profiler',
'/tools/profiler/core',
'/tools/profiler/gecko',
'/tools/profiler/lul',
]
UNIFIED_SOURCES += [