mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 867757 - Part 1: Allow markers to carry payload. r=aklotz
--HG-- extra : rebase_source : 31904aa5f084283bfc23ecfc53762d275967e34b
This commit is contained in:
parent
3a1e811e43
commit
c2c963e37e
@ -181,10 +181,13 @@ void TableTicker::UnwinderTick(TickSample* sample)
|
||||
|
||||
// Marker(s) come before the sample
|
||||
PseudoStack* stack = currThreadProfile.GetPseudoStack();
|
||||
for (int i = 0; stack->getMarker(i) != NULL; i++) {
|
||||
utb__addEntry( utb, ProfileEntry('m', stack->getMarker(i)) );
|
||||
ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
|
||||
while (pendingMarkersList && pendingMarkersList->peek()) {
|
||||
ProfilerMarker* marker = pendingMarkersList->popHead();
|
||||
stack->addStoredMarker(marker);
|
||||
utb__addEntry( utb, ProfileEntry('m', marker) );
|
||||
}
|
||||
stack->mQueueClearMarker = true;
|
||||
stack->updateGeneration(currThreadProfile.GetGenerationID());
|
||||
|
||||
bool recordSample = true;
|
||||
if (mJankOnly) {
|
||||
|
@ -76,6 +76,7 @@ class TimeStamp;
|
||||
// only recorded if a sample is collected while it is active, marker will always
|
||||
// be collected.
|
||||
#define PROFILER_MARKER(info) do {} while (0)
|
||||
#define PROFILER_MARKER_PAYLOAD(info, payload) do {} while (0)
|
||||
|
||||
// Main thread specilization to avoid TLS lookup for performance critical use.
|
||||
#define PROFILER_MAIN_THREAD_LABEL(name_space, info) do {} while (0)
|
||||
|
@ -18,12 +18,14 @@ class TimeStamp;
|
||||
using mozilla::TimeStamp;
|
||||
using mozilla::TimeDuration;
|
||||
|
||||
class ProfilerMarkerPayload;
|
||||
|
||||
// Returns a handle to pass on exit. This can check that we are popping the
|
||||
// correct callstack.
|
||||
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL,
|
||||
bool aCopy = false, uint32_t line = 0);
|
||||
inline void mozilla_sampler_call_exit(void* handle);
|
||||
inline void mozilla_sampler_add_marker(const char *aInfo);
|
||||
inline void mozilla_sampler_add_marker(const char *aInfo, ProfilerMarkerPayload *aPayload = nullptr);
|
||||
|
||||
void mozilla_sampler_start(int aEntries, double aInterval,
|
||||
const char** aFeatures, uint32_t aFeatureCount,
|
||||
|
@ -193,6 +193,7 @@ bool profiler_in_privacy_mode()
|
||||
#define PROFILER_LABEL(name_space, info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__)
|
||||
#define PROFILER_LABEL_PRINTF(name_space, info, ...) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__)
|
||||
#define PROFILER_MARKER(info) mozilla_sampler_add_marker(info)
|
||||
#define PROFILER_MARKER_PAYLOAD(info, payload) mozilla_sampler_add_marker(info, payload)
|
||||
#define PROFILER_MAIN_THREAD_LABEL(name_space, info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__)
|
||||
#define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, ...) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__)
|
||||
#define PROFILER_MAIN_THREAD_MARKER(info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info)
|
||||
@ -327,7 +328,7 @@ inline void mozilla_sampler_call_exit(void *aHandle)
|
||||
stack->pop();
|
||||
}
|
||||
|
||||
inline void mozilla_sampler_add_marker(const char *aMarker)
|
||||
inline void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload)
|
||||
{
|
||||
if (!stack_key_initialized)
|
||||
return;
|
||||
@ -347,7 +348,7 @@ inline void mozilla_sampler_add_marker(const char *aMarker)
|
||||
if (!stack) {
|
||||
return;
|
||||
}
|
||||
stack->addMarker(aMarker);
|
||||
stack->addMarker(aMarker, aPayload);
|
||||
}
|
||||
|
||||
#endif /* ndef TOOLS_SPS_SAMPLER_H_ */
|
||||
|
@ -34,6 +34,11 @@ ProfileEntry::ProfileEntry(char aTagName, const char *aTagData)
|
||||
, mTagName(aTagName)
|
||||
{ }
|
||||
|
||||
ProfileEntry::ProfileEntry(char aTagName, ProfilerMarker *aTagMarker)
|
||||
: mTagMarker(aTagMarker)
|
||||
, mTagName(aTagName)
|
||||
{ }
|
||||
|
||||
ProfileEntry::ProfileEntry(char aTagName, void *aTagPtr)
|
||||
: mTagPtr(aTagPtr)
|
||||
, mTagName(aTagName)
|
||||
@ -92,7 +97,9 @@ void ProfileEntry::log()
|
||||
// mTagChar (char) h
|
||||
// mTagFloat (double) r,t
|
||||
switch (mTagName) {
|
||||
case 'm': case 'c': case 's':
|
||||
case 'm':
|
||||
LOGF("%c \"%s\"", mTagName, mTagMarker->GetMarkerName()); break;
|
||||
case 'c': case 's':
|
||||
LOGF("%c \"%s\"", mTagName, mTagData); break;
|
||||
case 'd': case 'l': case 'L': case 'S':
|
||||
LOGF("%c %p", mTagName, mTagPtr); break;
|
||||
@ -152,6 +159,7 @@ ThreadProfile::ThreadProfile(const char* aName, int aEntrySize,
|
||||
, mStackTop(aStackTop)
|
||||
{
|
||||
mEntries = new ProfileEntry[mEntrySize];
|
||||
mGeneration = 0;
|
||||
}
|
||||
|
||||
ThreadProfile::~ThreadProfile()
|
||||
@ -164,7 +172,11 @@ void ThreadProfile::addTag(ProfileEntry aTag)
|
||||
{
|
||||
// Called from signal, call only reentrant functions
|
||||
mEntries[mWritePos] = aTag;
|
||||
mWritePos = (mWritePos + 1) % mEntrySize;
|
||||
mWritePos = mWritePos + 1;
|
||||
if (mWritePos >= mEntrySize) {
|
||||
mPendingGenerationFlush++;
|
||||
mWritePos = mWritePos % mEntrySize;
|
||||
}
|
||||
if (mWritePos == mReadPos) {
|
||||
// Keep one slot open
|
||||
mEntries[mReadPos] = ProfileEntry();
|
||||
@ -181,6 +193,8 @@ void ThreadProfile::addTag(ProfileEntry aTag)
|
||||
void ThreadProfile::flush()
|
||||
{
|
||||
mLastFlushPos = mWritePos;
|
||||
mGeneration += mPendingGenerationFlush;
|
||||
mPendingGenerationFlush = 0;
|
||||
}
|
||||
|
||||
// discards all of the entries since the last flush()
|
||||
@ -236,6 +250,7 @@ void ThreadProfile::flush()
|
||||
void ThreadProfile::erase()
|
||||
{
|
||||
mWritePos = mLastFlushPos;
|
||||
mPendingGenerationFlush = 0;
|
||||
}
|
||||
|
||||
char* ThreadProfile::processDynamicTag(int readPos,
|
||||
@ -325,7 +340,7 @@ void ThreadProfile::BuildJSObject(Builder& b, typename Builder::ObjectHandle pro
|
||||
|
||||
typename Builder::RootedObject sample(b.context());
|
||||
typename Builder::RootedArray frames(b.context());
|
||||
typename Builder::RootedArray marker(b.context());
|
||||
typename Builder::RootedArray markers(b.context());
|
||||
|
||||
int readPos = mReadPos;
|
||||
while (readPos != mLastFlushPos) {
|
||||
@ -353,16 +368,16 @@ void ThreadProfile::BuildJSObject(Builder& b, typename Builder::ObjectHandle pro
|
||||
b.DefineProperty(sample, "frames", frames);
|
||||
b.ArrayPush(samples, sample);
|
||||
// Created lazily
|
||||
marker = nullptr;
|
||||
markers = nullptr;
|
||||
break;
|
||||
case 'm':
|
||||
{
|
||||
if (sample) {
|
||||
if (!marker) {
|
||||
marker = b.CreateArray();
|
||||
b.DefineProperty(sample, "marker", marker);
|
||||
if (!markers) {
|
||||
markers = b.CreateArray();
|
||||
b.DefineProperty(sample, "marker", markers);
|
||||
}
|
||||
b.ArrayPush(marker, tagStringData);
|
||||
entry.getMarker()->BuildJSObject(b, markers);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -21,6 +21,7 @@ public:
|
||||
// aTagData must not need release (i.e. be a string from the text segment)
|
||||
ProfileEntry(char aTagName, const char *aTagData);
|
||||
ProfileEntry(char aTagName, void *aTagPtr);
|
||||
ProfileEntry(char aTagName, ProfilerMarker *aTagMarker);
|
||||
ProfileEntry(char aTagName, double aTagFloat);
|
||||
ProfileEntry(char aTagName, uintptr_t aTagOffset);
|
||||
ProfileEntry(char aTagName, Address aTagAddress);
|
||||
@ -32,6 +33,10 @@ public:
|
||||
bool is_ent(char tagName);
|
||||
void* get_tagPtr();
|
||||
void log();
|
||||
const ProfilerMarker* getMarker() {
|
||||
MOZ_ASSERT(mTagName == 'm');
|
||||
return mTagMarker;
|
||||
}
|
||||
|
||||
char getTagName() const { return mTagName; }
|
||||
|
||||
@ -41,6 +46,7 @@ private:
|
||||
const char* mTagData;
|
||||
char mTagChars[sizeof(void*)];
|
||||
void* mTagPtr;
|
||||
ProfilerMarker* mTagMarker;
|
||||
double mTagFloat;
|
||||
Address mTagAddress;
|
||||
uintptr_t mTagOffset;
|
||||
@ -77,6 +83,10 @@ public:
|
||||
int ThreadId() const { return mThreadId; }
|
||||
|
||||
PlatformData* GetPlatformData() { return mPlatformData; }
|
||||
int GetGenerationID() const { return mGeneration; }
|
||||
bool HasGenerationExpired(int aGenID) {
|
||||
return aGenID + 2 <= mGeneration;
|
||||
}
|
||||
void* GetStackTop() const { return mStackTop; }
|
||||
private:
|
||||
// Circular buffer 'Keep One Slot Open' implementation
|
||||
@ -92,6 +102,8 @@ private:
|
||||
int mThreadId;
|
||||
bool mIsMainThread;
|
||||
PlatformData* mPlatformData; // Platform specific data.
|
||||
int mGeneration;
|
||||
int mPendingGenerationFlush;
|
||||
void* const mStackTop;
|
||||
};
|
||||
|
||||
|
28
tools/profiler/ProfilerMarkers.cpp
Normal file
28
tools/profiler/ProfilerMarkers.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
/* -*- 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 "ProfilerMarkers.h"
|
||||
#include "gfxASurface.h"
|
||||
|
||||
ProfilerMarkerImagePayload::ProfilerMarkerImagePayload(gfxASurface *aImg)
|
||||
: mImg(aImg)
|
||||
{}
|
||||
|
||||
template<typename Builder>
|
||||
typename Builder::Object
|
||||
ProfilerMarkerImagePayload::preparePayloadImp(Builder& b)
|
||||
{
|
||||
typename Builder::RootedObject data(b.context(), b.CreateObject());
|
||||
b.DefineProperty(data, "type", "innerHTML");
|
||||
// TODO: Finish me
|
||||
//b.DefineProperty(data, "innerHTML", "<img src=''/>");
|
||||
return data;
|
||||
}
|
||||
|
||||
template JSCustomObjectBuilder::Object
|
||||
ProfilerMarkerImagePayload::preparePayloadImp<JSCustomObjectBuilder>(JSCustomObjectBuilder& b);
|
||||
template JSObjectBuilder::Object
|
||||
ProfilerMarkerImagePayload::preparePayloadImp<JSObjectBuilder>(JSObjectBuilder& b);
|
||||
|
75
tools/profiler/ProfilerMarkers.h
Normal file
75
tools/profiler/ProfilerMarkers.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef PROFILER_MARKERS_H
|
||||
#define PROFILER_MARKERS_H
|
||||
|
||||
#include "JSCustomObjectBuilder.h"
|
||||
#include "JSObjectBuilder.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
/**
|
||||
* This is an abstract object that can be implied to supply
|
||||
* data to be attached with a profiler marker. Most data inserted
|
||||
* into a profile is stored in a circular buffer. This buffer
|
||||
* typically wraps around and overwrites most entries. Because
|
||||
* of this, this structure is designed to defer the work of
|
||||
* prepare the payload only when 'preparePayload' is called.
|
||||
*
|
||||
* Note when implementing that this object is typically constructed
|
||||
* on a particular thread but 'preparePayload' and the destructor
|
||||
* is called from the main thread.
|
||||
*/
|
||||
class ProfilerMarkerPayload {
|
||||
public:
|
||||
ProfilerMarkerPayload() {}
|
||||
|
||||
/**
|
||||
* Called from the main thread
|
||||
*/
|
||||
virtual ~ProfilerMarkerPayload() {}
|
||||
|
||||
/**
|
||||
* Called from the main thread
|
||||
*/
|
||||
template<typename Builder>
|
||||
typename Builder::Object PreparePayload(Builder& b)
|
||||
{
|
||||
return preparePayload(b);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called from the main thread
|
||||
*/
|
||||
virtual JSCustomObjectBuilder::Object
|
||||
preparePayload(JSCustomObjectBuilder& b) = 0;
|
||||
|
||||
/**
|
||||
* Called from the main thread
|
||||
*/
|
||||
virtual JSObjectBuilder::Object
|
||||
preparePayload(JSObjectBuilder& b) = 0;
|
||||
};
|
||||
|
||||
class gfxASurface;
|
||||
class ProfilerMarkerImagePayload : public ProfilerMarkerPayload {
|
||||
public:
|
||||
ProfilerMarkerImagePayload(gfxASurface *aImg);
|
||||
|
||||
protected:
|
||||
virtual JSCustomObjectBuilder::Object
|
||||
preparePayload(JSCustomObjectBuilder& b) { return preparePayloadImp(b); }
|
||||
virtual JSObjectBuilder::Object
|
||||
preparePayload(JSObjectBuilder& b) { return preparePayloadImp(b); }
|
||||
|
||||
private:
|
||||
template<typename Builder>
|
||||
typename Builder::Object preparePayloadImp(Builder& b);
|
||||
|
||||
nsRefPtr<gfxASurface> mImg;
|
||||
};
|
||||
|
||||
#endif // PROFILER_MARKERS_H
|
@ -100,6 +100,112 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class ProfilerMarkerPayload;
|
||||
class ProfilerMarkerLinkedList;
|
||||
class JSAObjectBuilder;
|
||||
class JSCustomArray;
|
||||
class ThreadProfile;
|
||||
class ProfilerMarker {
|
||||
friend class ProfilerMarkerLinkedList;
|
||||
public:
|
||||
ProfilerMarker(const char* aMarkerName,
|
||||
ProfilerMarkerPayload* aPayload = nullptr);
|
||||
|
||||
~ProfilerMarker();
|
||||
|
||||
const char* GetMarkerName() const {
|
||||
return mMarkerName;
|
||||
}
|
||||
|
||||
template<typename Builder> void
|
||||
BuildJSObject(Builder& b, typename Builder::ArrayHandle markers) const;
|
||||
|
||||
void SetGeneration(int aGenID);
|
||||
|
||||
bool HasExpired(int aGenID) const {
|
||||
return mGenID + 2 <= aGenID;
|
||||
}
|
||||
|
||||
private:
|
||||
char* mMarkerName;
|
||||
ProfilerMarkerPayload* mPayload;
|
||||
ProfilerMarker* mNext;
|
||||
int mGenID;
|
||||
};
|
||||
|
||||
class ProfilerMarkerLinkedList {
|
||||
public:
|
||||
ProfilerMarkerLinkedList()
|
||||
: mHead(nullptr)
|
||||
, mTail(nullptr)
|
||||
{}
|
||||
|
||||
void insert(ProfilerMarker* elem);
|
||||
ProfilerMarker* popHead();
|
||||
|
||||
const ProfilerMarker* peek() {
|
||||
return mHead;
|
||||
}
|
||||
|
||||
private:
|
||||
ProfilerMarker* mHead;
|
||||
ProfilerMarker* mTail;
|
||||
};
|
||||
|
||||
class PendingMarkers {
|
||||
public:
|
||||
PendingMarkers()
|
||||
: mSignalLock(false)
|
||||
{}
|
||||
|
||||
~PendingMarkers();
|
||||
|
||||
void addMarker(ProfilerMarker *aMarker);
|
||||
|
||||
void updateGeneration(int aGenID);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
// called within signal. Function must be reentrant
|
||||
ProfilerMarkerLinkedList* getPendingMarkers()
|
||||
{
|
||||
// if mSignalLock then the stack is inconsistent because it's being
|
||||
// modified by the profiled thread. Post pone these markers
|
||||
// for the next sample. The odds of a livelock are nearly impossible
|
||||
// and would show up in a profile as many sample in 'addMarker' thus
|
||||
// we ignore this scenario.
|
||||
if (mSignalLock) {
|
||||
return nullptr;
|
||||
}
|
||||
return &mPendingMarkers;
|
||||
}
|
||||
|
||||
void clearMarkers()
|
||||
{
|
||||
while (mPendingMarkers.peek()) {
|
||||
delete mPendingMarkers.popHead();
|
||||
}
|
||||
while (mStoredMarkers.peek()) {
|
||||
delete mStoredMarkers.popHead();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Keep a list of active markers to be applied to the next sample taken
|
||||
ProfilerMarkerLinkedList mPendingMarkers;
|
||||
ProfilerMarkerLinkedList mStoredMarkers;
|
||||
// If this is set then it's not safe to read mStackPointer from the signal handler
|
||||
volatile bool mSignalLock;
|
||||
// We don't want to modify _markers from within the signal so we allow
|
||||
// it to queue a clear operation.
|
||||
volatile mozilla::sig_safe_t mGenID;
|
||||
};
|
||||
|
||||
// the PseudoStack members are read by signal
|
||||
// handlers, so the mutation of them needs to be signal-safe.
|
||||
struct PseudoStack
|
||||
@ -107,18 +213,13 @@ struct PseudoStack
|
||||
public:
|
||||
PseudoStack()
|
||||
: mStackPointer(0)
|
||||
, mSignalLock(false)
|
||||
, mMarkerPointer(0)
|
||||
, mQueueClearMarker(false)
|
||||
, mRuntime(nullptr)
|
||||
, mStartJSSampling(false)
|
||||
, mPrivacyMode(false)
|
||||
{ }
|
||||
|
||||
~PseudoStack() {
|
||||
clearMarkers();
|
||||
if (mStackPointer != 0 || mSignalLock != false ||
|
||||
mMarkerPointer != 0) {
|
||||
if (mStackPointer != 0) {
|
||||
// We're releasing the pseudostack while it's still in use.
|
||||
// The label macros keep a non ref counted reference to the
|
||||
// stack to avoid a TLS. If these are not all cleared we will
|
||||
@ -127,53 +228,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void addMarker(const char *aMarker)
|
||||
void addMarker(const char *aMarkerStr, ProfilerMarkerPayload *aPayload)
|
||||
{
|
||||
char* markerCopy = strdup(aMarker);
|
||||
mSignalLock = true;
|
||||
STORE_SEQUENCER();
|
||||
ProfilerMarker* marker = new ProfilerMarker(aMarkerStr, aPayload);
|
||||
mPendingMarkers.addMarker(marker);
|
||||
}
|
||||
|
||||
if (mQueueClearMarker) {
|
||||
clearMarkers();
|
||||
}
|
||||
if (!aMarker) {
|
||||
return; //discard
|
||||
}
|
||||
if (size_t(mMarkerPointer) == mozilla::ArrayLength(mMarkers)) {
|
||||
return; //array full, silently drop
|
||||
}
|
||||
mMarkers[mMarkerPointer] = markerCopy;
|
||||
mMarkerPointer++;
|
||||
void addStoredMarker(ProfilerMarker *aStoredMarker) {
|
||||
mPendingMarkers.addStoredMarker(aStoredMarker);
|
||||
}
|
||||
|
||||
mSignalLock = false;
|
||||
STORE_SEQUENCER();
|
||||
void updateGeneration(int aGenID) {
|
||||
mPendingMarkers.updateGeneration(aGenID);
|
||||
}
|
||||
|
||||
// called within signal. Function must be reentrant
|
||||
const char* getMarker(int aMarkerId)
|
||||
ProfilerMarkerLinkedList* getPendingMarkers()
|
||||
{
|
||||
// if mSignalLock then the stack is inconsistent because it's being
|
||||
// modified by the profiled thread. Post pone these markers
|
||||
// for the next sample. The odds of a livelock are nearly impossible
|
||||
// and would show up in a profile as many sample in 'addMarker' thus
|
||||
// we ignore this scenario.
|
||||
// if mQueueClearMarker then we've the sampler thread has already
|
||||
// thread the markers then they are pending deletion.
|
||||
if (mSignalLock || mQueueClearMarker || aMarkerId < 0 ||
|
||||
static_cast<mozilla::sig_safe_t>(aMarkerId) >= mMarkerPointer) {
|
||||
return nullptr;
|
||||
}
|
||||
return mMarkers[aMarkerId];
|
||||
}
|
||||
|
||||
// called within signal. Function must be reentrant
|
||||
void clearMarkers()
|
||||
{
|
||||
for (mozilla::sig_safe_t i = 0; i < mMarkerPointer; i++) {
|
||||
free(mMarkers[i]);
|
||||
}
|
||||
mMarkerPointer = 0;
|
||||
mQueueClearMarker = false;
|
||||
return mPendingMarkers.getPendingMarkers();
|
||||
}
|
||||
|
||||
void push(const char *aName, uint32_t line)
|
||||
@ -246,19 +318,14 @@ public:
|
||||
|
||||
// Keep a list of active checkpoints
|
||||
StackEntry volatile mStack[1024];
|
||||
// Keep a list of active markers to be applied to the next sample taken
|
||||
char* mMarkers[1024];
|
||||
private:
|
||||
// Keep a list of pending markers that must be moved
|
||||
// to the circular buffer
|
||||
PendingMarkers mPendingMarkers;
|
||||
// This may exceed the length of mStack, so instead use the stackSize() method
|
||||
// to determine the number of valid samples in mStack
|
||||
mozilla::sig_safe_t mStackPointer;
|
||||
// If this is set then it's not safe to read mStackPointer from the signal handler
|
||||
volatile bool mSignalLock;
|
||||
public:
|
||||
volatile mozilla::sig_safe_t mMarkerPointer;
|
||||
// We don't want to modify _markers from within the signal so we allow
|
||||
// it to queue a clear operation.
|
||||
volatile mozilla::sig_safe_t mQueueClearMarker;
|
||||
// The runtime which is being sampled
|
||||
JSRuntime *mRuntime;
|
||||
// Start JS Profiling when possible
|
||||
|
@ -511,10 +511,13 @@ void TableTicker::InplaceTick(TickSample* sample)
|
||||
|
||||
// Marker(s) come before the sample
|
||||
PseudoStack* stack = currThreadProfile.GetPseudoStack();
|
||||
for (int i = 0; stack->getMarker(i) != NULL; i++) {
|
||||
addDynamicTag(currThreadProfile, 'm', stack->getMarker(i));
|
||||
ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
|
||||
while (pendingMarkersList && pendingMarkersList->peek()) {
|
||||
ProfilerMarker* marker = pendingMarkersList->popHead();
|
||||
stack->addStoredMarker(marker);
|
||||
currThreadProfile.addTag(ProfileEntry('m', marker));
|
||||
}
|
||||
stack->mQueueClearMarker = true;
|
||||
stack->updateGeneration(currThreadProfile.GetGenerationID());
|
||||
|
||||
bool recordSample = true;
|
||||
if (mJankOnly) {
|
||||
|
@ -122,8 +122,6 @@ class TableTicker: public Sampler {
|
||||
aInfo->GetPlatformData(),
|
||||
aInfo->IsMainThread(),
|
||||
aInfo->StackTop());
|
||||
profile->addTag(ProfileEntry('m', "Start"));
|
||||
|
||||
aInfo->SetProfile(profile);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
|
||||
EXPORTS += [
|
||||
'GeckoProfilerFunc.h',
|
||||
'GeckoProfilerImpl.h',
|
||||
'ProfilerMarkers.h',
|
||||
'PseudoStack.h',
|
||||
'shared-libraries.h',
|
||||
]
|
||||
@ -39,6 +40,7 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
|
||||
'IOInterposer.cpp',
|
||||
'NSPRInterposer.cpp',
|
||||
'ProfilerIOInterposeObserver.cpp',
|
||||
'ProfilerMarkers.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "ProfilerMarkers.h"
|
||||
|
||||
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
|
||||
#include "AndroidBridge.h"
|
||||
@ -92,6 +93,113 @@ ThreadInfo::~ThreadInfo() {
|
||||
Sampler::FreePlatformData(mPlatformData);
|
||||
}
|
||||
|
||||
ProfilerMarker::ProfilerMarker(const char* aMarkerName,
|
||||
ProfilerMarkerPayload* aPayload)
|
||||
: mMarkerName(strdup(aMarkerName))
|
||||
, mPayload(aPayload)
|
||||
{
|
||||
}
|
||||
|
||||
ProfilerMarker::~ProfilerMarker() {
|
||||
free(mMarkerName);
|
||||
delete mPayload;
|
||||
}
|
||||
|
||||
void
|
||||
ProfilerMarker::SetGeneration(int aGenID) {
|
||||
mGenID = aGenID;
|
||||
}
|
||||
|
||||
template<typename Builder> void
|
||||
ProfilerMarker::BuildJSObject(Builder& b, typename Builder::ArrayHandle markers) const {
|
||||
typename Builder::RootedObject marker(b.context(), b.CreateObject());
|
||||
b.DefineProperty(marker, "name", GetMarkerName());
|
||||
// TODO: Store the callsite for this marker if available:
|
||||
// if have location data
|
||||
// b.DefineProperty(marker, "location", ...);
|
||||
if (mPayload) {
|
||||
typename Builder::RootedObject markerData(b.context(),
|
||||
mPayload->PreparePayload(b));
|
||||
b.DefineProperty(marker, "data", markerData);
|
||||
}
|
||||
b.ArrayPush(markers, marker);
|
||||
}
|
||||
|
||||
template void
|
||||
ProfilerMarker::BuildJSObject<JSCustomObjectBuilder>(JSCustomObjectBuilder& b,
|
||||
JSCustomObjectBuilder::ArrayHandle markers) const;
|
||||
template void
|
||||
ProfilerMarker::BuildJSObject<JSObjectBuilder>(JSObjectBuilder& b,
|
||||
JSObjectBuilder::ArrayHandle markers) const;
|
||||
|
||||
void
|
||||
ProfilerMarkerLinkedList::insert(ProfilerMarker* elem) {
|
||||
if (!mTail) {
|
||||
mHead = elem;
|
||||
mTail = elem;
|
||||
} else {
|
||||
mTail->mNext = elem;
|
||||
mTail = elem;
|
||||
}
|
||||
elem->mNext = nullptr;
|
||||
}
|
||||
|
||||
ProfilerMarker*
|
||||
ProfilerMarkerLinkedList::popHead() {
|
||||
if (!mHead) {
|
||||
MOZ_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ProfilerMarker* head = mHead;
|
||||
|
||||
mHead = head->mNext;
|
||||
if (!mHead) {
|
||||
mTail = nullptr;
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
PendingMarkers::~PendingMarkers() {
|
||||
clearMarkers();
|
||||
if (mSignalLock != false) {
|
||||
// We're releasing the pseudostack while it's still in use.
|
||||
// The label macros keep a non ref counted reference to the
|
||||
// stack to avoid a TLS. If these are not all cleared we will
|
||||
// get a use-after-free so better to crash now.
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PendingMarkers::addMarker(ProfilerMarker *aMarker) {
|
||||
mSignalLock = true;
|
||||
STORE_SEQUENCER();
|
||||
|
||||
MOZ_ASSERT(aMarker);
|
||||
mPendingMarkers.insert(aMarker);
|
||||
|
||||
// Clear markers that have been overwritten
|
||||
while (mStoredMarkers.peek() &&
|
||||
mStoredMarkers.peek()->HasExpired(mGenID)) {
|
||||
delete mStoredMarkers.popHead();
|
||||
}
|
||||
STORE_SEQUENCER();
|
||||
mSignalLock = false;
|
||||
}
|
||||
|
||||
void
|
||||
PendingMarkers::updateGeneration(int aGenID) {
|
||||
mGenID = aGenID;
|
||||
}
|
||||
|
||||
void
|
||||
PendingMarkers::addStoredMarker(ProfilerMarker *aStoredMarker) {
|
||||
aStoredMarker->SetGeneration(mGenID);
|
||||
mStoredMarkers.insert(aStoredMarker);
|
||||
}
|
||||
|
||||
bool sps_version2()
|
||||
{
|
||||
static int version = 0; // Raced on, potentially
|
||||
|
Loading…
Reference in New Issue
Block a user