mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
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:
parent
39a5778c7e
commit
daba448963
2
CLOBBER
2
CLOBBER
@ -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.
|
||||
|
@ -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;
|
@ -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) {
|
89
tools/profiler/core/ProfileBuffer.cpp
Normal file
89
tools/profiler/core/ProfileBuffer.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
60
tools/profiler/core/ProfileBuffer.h
Normal file
60
tools/profiler/core/ProfileBuffer.h
Normal 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
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////
|
@ -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 */
|
@ -8,6 +8,7 @@
|
||||
#define __SYNCPROFILE_H
|
||||
|
||||
#include "ProfileEntry.h"
|
||||
#include "ThreadProfile.h"
|
||||
|
||||
class SyncProfile : public ThreadProfile
|
||||
{
|
38
tools/profiler/core/ThreadInfo.cpp
Normal file
38
tools/profiler/core/ThreadInfo.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
66
tools/profiler/core/ThreadInfo.h
Normal file
66
tools/profiler/core/ThreadInfo.h
Normal 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
|
260
tools/profiler/core/ThreadProfile.cpp
Normal file
260
tools/profiler/core/ThreadProfile.cpp
Normal 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);
|
||||
}
|
||||
|
104
tools/profiler/core/ThreadProfile.h
Normal file
104
tools/profiler/core/ThreadProfile.h
Normal 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
|
@ -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()
|
||||
{}
|
@ -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())
|
||||
{
|
@ -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
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -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_ */
|
@ -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();
|
@ -29,7 +29,6 @@
|
||||
// Set this to 1 for verbose logging
|
||||
#define DEBUG_MAIN 0
|
||||
|
||||
|
||||
namespace lul {
|
||||
|
||||
using std::string;
|
@ -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;
|
||||
|
@ -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']
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
@ -6,6 +6,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ProfileEntry.h"
|
||||
#include "ThreadProfile.h"
|
||||
|
||||
// Make sure we can initialize our ThreadProfile
|
||||
TEST(ThreadProfile, Initialization) {
|
||||
|
@ -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 += [
|
||||
|
Loading…
Reference in New Issue
Block a user