diff --git a/tools/profiler/BreakpadSampler.cpp b/tools/profiler/BreakpadSampler.cpp index 0f3a64e3858..96ba1d66dd8 100644 --- a/tools/profiler/BreakpadSampler.cpp +++ b/tools/profiler/BreakpadSampler.cpp @@ -155,17 +155,16 @@ void genPseudoBacktraceEntries(/*MODIFIED*/UnwinderThreadBuffer* utb, // RUNS IN SIGHANDLER CONTEXT static void populateBuffer(UnwinderThreadBuffer* utb, TickSample* sample, - UTB_RELEASE_FUNC releaseFunction, bool jankOnly) + UTB_RELEASE_FUNC releaseFunction) { ThreadProfile& sampledThreadProfile = *sample->threadProfile; PseudoStack* stack = sampledThreadProfile.GetPseudoStack(); - stack->updateGeneration(sampledThreadProfile.GetGenerationID()); /* Manufacture the ProfileEntries that we will give to the unwinder thread, and park them in |utb|. */ bool recordSample = true; - /* Don't process the PeudoStack's markers or honour jankOnly if we're + /* Don't process the PeudoStack's markers if we're immediately sampling the current thread. */ if (!sample->isSamplingCurrentThread) { // LinkedUWTBuffers before markers @@ -178,30 +177,9 @@ void populateBuffer(UnwinderThreadBuffer* utb, TickSample* sample, ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers(); while (pendingMarkersList && pendingMarkersList->peek()) { ProfilerMarker* marker = pendingMarkersList->popHead(); - stack->addStoredMarker(marker); + sampledThreadProfile.addStoredMarker(marker); utb__addEntry( utb, ProfileEntry('m', marker) ); } - if (jankOnly) { - // if we are on a different event we can discard any temporary samples - // we've kept around - if (sLastSampledEventGeneration != sCurrentEventGeneration) { - // XXX: we also probably want to add an entry to the profile to help - // distinguish which samples are part of the same event. That, or record - // the event generation in each sample - sampledThreadProfile.erase(); - } - sLastSampledEventGeneration = sCurrentEventGeneration; - - recordSample = false; - // only record the events when we have a we haven't seen a tracer - // event for 100ms - if (!sLastTracerEvent.IsNull()) { - mozilla::TimeDuration delta = sample->timestamp - sLastTracerEvent; - if (delta.ToMilliseconds() > 100.0) { - recordSample = true; - } - } - } } // JRS 2012-Sept-27: this logic used to involve mUseStackWalk. @@ -318,7 +296,7 @@ void sampleCurrent(TickSample* sample) return; } UnwinderThreadBuffer* utb = syncBuf->GetBuffer(); - populateBuffer(utb, sample, &utb__finish_sync_buffer, false); + populateBuffer(utb, sample, &utb__finish_sync_buffer); } // RUNS IN SIGHANDLER CONTEXT @@ -344,7 +322,7 @@ void TableTicker::UnwinderTick(TickSample* sample) if (!utb) return; - populateBuffer(utb, sample, &uwt__release_full_buffer, mJankOnly); + populateBuffer(utb, sample, &uwt__release_full_buffer); } // END take samples diff --git a/tools/profiler/ProfileEntry.cpp b/tools/profiler/ProfileEntry.cpp index 247f39e9ff3..8f17736a3d9 100644 --- a/tools/profiler/ProfileEntry.cpp +++ b/tools/profiler/ProfileEntry.cpp @@ -94,7 +94,7 @@ void ProfileEntry::log() // mTagMarker (ProfilerMarker*) m // mTagData (const char*) c,s // mTagPtr (void*) d,l,L,B (immediate backtrace), S(start-of-stack) - // mTagInt (int) n,f,y + // mTagInt (int) n,f,y,T (thread id) // mTagChar (char) h // mTagFloat (double) r,t,p,R (resident memory), U (unshared memory) switch (mTagName) { @@ -104,7 +104,7 @@ void ProfileEntry::log() LOGF("%c \"%s\"", mTagName, mTagData); break; case 'd': case 'l': case 'L': case 'B': case 'S': LOGF("%c %p", mTagName, mTagPtr); break; - case 'n': case 'f': case 'y': + case 'n': case 'f': case 'y': case 'T': LOGF("%c %d", mTagName, mTagInt); break; case 'h': LOGF("%c \'%c\'", mTagName, mTagChar); break; @@ -139,126 +139,50 @@ std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry) //////////////////////////////////////////////////////////////////////// -// BEGIN ThreadProfile +// BEGIN ProfileBuffer -#define DYNAMIC_MAX_STRING 512 - -ThreadProfile::ThreadProfile(ThreadInfo* aInfo, int aEntrySize) - : mThreadInfo(aInfo) +ProfileBuffer::ProfileBuffer(int aEntrySize) + : mEntries(MakeUnique(aEntrySize)) , mWritePos(0) - , mLastFlushPos(0) , mReadPos(0) , mEntrySize(aEntrySize) - , mPseudoStack(aInfo->Stack()) - , mMutex("ThreadProfile::mMutex") - , mThreadId(aInfo->ThreadId()) - , mIsMainThread(aInfo->IsMainThread()) - , mPlatformData(aInfo->GetPlatformData()) , mGeneration(0) - , mPendingGenerationFlush(0) - , mStackTop(aInfo->StackTop()) - , mRespInfo(this) -#ifdef XP_LINUX - , mRssMemory(0) - , mUssMemory(0) -#endif { - MOZ_COUNT_CTOR(ThreadProfile); - mEntries = new ProfileEntry[mEntrySize]; } -ThreadProfile::~ThreadProfile() +// Called from signal, call only reentrant functions +void ProfileBuffer::addTag(const ProfileEntry& aTag) { - MOZ_COUNT_DTOR(ThreadProfile); - delete[] mEntries; -} - -void ThreadProfile::addTag(ProfileEntry aTag) -{ - // Called from signal, call only reentrant functions - mEntries[mWritePos] = aTag; - mWritePos = mWritePos + 1; - if (mWritePos >= mEntrySize) { - mPendingGenerationFlush++; - mWritePos = mWritePos % mEntrySize; + mEntries[mWritePos++] = aTag; + if (mWritePos == mEntrySize) { + mGeneration++; + mWritePos = 0; } if (mWritePos == mReadPos) { - // Keep one slot open + // Keep one slot open. mEntries[mReadPos] = ProfileEntry(); mReadPos = (mReadPos + 1) % mEntrySize; } - // we also need to move the flush pos to ensure we - // do not pass it - if (mWritePos == mLastFlushPos) { - mLastFlushPos = (mLastFlushPos + 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. + int generation = mGeneration; + while (mStoredMarkers.peek() && + mStoredMarkers.peek()->HasExpired(generation)) { + delete mStoredMarkers.popHead(); } } -// flush the new entries -void ThreadProfile::flush() -{ - mLastFlushPos = mWritePos; - mGeneration += mPendingGenerationFlush; - mPendingGenerationFlush = 0; -} +#define DYNAMIC_MAX_STRING 512 -// discards all of the entries since the last flush() -// NOTE: that if mWritePos happens to wrap around past -// mLastFlushPos we actually only discard mWritePos - mLastFlushPos entries -// -// r = mReadPos -// w = mWritePos -// f = mLastFlushPos -// -// r f w -// |-----------------------------| -// | abcdefghijklmnopq | -> 'abcdefghijklmnopq' -// |-----------------------------| -// -// -// mWritePos and mReadPos have passed mLastFlushPos -// f -// w r -// |-----------------------------| -// |ABCDEFGHIJKLMNOPQRSqrstuvwxyz| -// |-----------------------------| -// w -// r -// |-----------------------------| -// |ABCDEFGHIJKLMNOPQRSqrstuvwxyz| -> '' -// |-----------------------------| -// -// -// mWritePos will end up the same as mReadPos -// r -// w f -// |-----------------------------| -// |ABCDEFGHIJKLMklmnopqrstuvwxyz| -// |-----------------------------| -// r -// w -// |-----------------------------| -// |ABCDEFGHIJKLMklmnopqrstuvwxyz| -> '' -// |-----------------------------| -// -// -// mWritePos has moved past mReadPos -// w r f -// |-----------------------------| -// |ABCDEFdefghijklmnopqrstuvwxyz| -// |-----------------------------| -// r w -// |-----------------------------| -// |ABCDEFdefghijklmnopqrstuvwxyz| -> 'defghijkl' -// |-----------------------------| - -void ThreadProfile::erase() -{ - mWritePos = mLastFlushPos; - mPendingGenerationFlush = 0; -} - -char* ThreadProfile::processDynamicTag(int readPos, +char* ProfileBuffer::processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff) { int readAheadPos = (readPos + 1) % mEntrySize; @@ -266,7 +190,7 @@ char* ThreadProfile::processDynamicTag(int readPos, // Read the string stored in mTagData until the null character is seen bool seenNullByte = false; - while (readAheadPos != mLastFlushPos && !seenNullByte) { + while (readAheadPos != mWritePos && !seenNullByte) { (*tagsConsumed)++; ProfileEntry readAheadEntry = mEntries[readAheadPos]; for (size_t pos = 0; pos < sizeof(void*); pos++) { @@ -283,16 +207,25 @@ char* ThreadProfile::processDynamicTag(int readPos, return tagBuff; } -void ThreadProfile::IterateTags(IterateTagsCallback aCallback) +void ProfileBuffer::IterateTagsForThread(IterateTagsCallback aCallback, int aThreadId) { MOZ_ASSERT(aCallback); int readPos = mReadPos; - while (readPos != mLastFlushPos) { - // Number of tag consumed - int incBy = 1; + int currentThreadID = -1; + + while (readPos != mWritePos) { const ProfileEntry& entry = mEntries[readPos]; + if (entry.mTagName == 'T') { + currentThreadID = entry.mTagInt; + readPos = (readPos + 1) % mEntrySize; + continue; + } + + // Number of tags consumed + int incBy = 1; + // Read ahead to the next tag, if it's a 'd' tag process it now const char* tagStringData = entry.mTagData; int readAheadPos = (readPos + 1) % mEntrySize; @@ -300,47 +233,31 @@ void ThreadProfile::IterateTags(IterateTagsCallback aCallback) // Make sure the string is always null terminated if it fills up DYNAMIC_MAX_STRING-2 tagBuff[DYNAMIC_MAX_STRING-1] = '\0'; - if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') { + if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'd') { tagStringData = processDynamicTag(readPos, &incBy, tagBuff); } - aCallback(entry, tagStringData); + if (currentThreadID == aThreadId) { + aCallback(entry, tagStringData); + } readPos = (readPos + incBy) % mEntrySize; } } -void ThreadProfile::ToStreamAsJSON(std::ostream& stream) +void ProfileBuffer::StreamSamplesToJSObject(JSStreamWriter& b, int aThreadId) { - JSStreamWriter b(stream); - StreamJSObject(b); -} - -void ThreadProfile::StreamJSObject(JSStreamWriter& b) -{ - b.BeginObject(); - // Thread meta data - if (XRE_GetProcessType() == GeckoProcessType_Plugin) { - // TODO Add the proper plugin name - b.NameValue("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. - b.NameValue("name", "Content"); - } else { - b.NameValue("name", Name()); - } - b.NameValue("tid", static_cast(mThreadId)); - - b.Name("samples"); - b.BeginArray(); - - bool sample = false; - int readPos = mReadPos; - while (readPos != mLastFlushPos) { - // Number of tag consumed - ProfileEntry entry = mEntries[readPos]; + b.BeginArray(); + bool sample = false; + int readPos = mReadPos; + int currentThreadID = -1; + while (readPos != mWritePos) { + ProfileEntry entry = mEntries[readPos]; + if (entry.mTagName == 'T') { + currentThreadID = entry.mTagInt; + } + if (currentThreadID == aThreadId) { switch (entry.mTagName) { case 'r': { @@ -407,7 +324,7 @@ void ThreadProfile::StreamJSObject(JSStreamWriter& b) int framePos = (readPos + 1) % mEntrySize; ProfileEntry frame = mEntries[framePos]; - while (framePos != mLastFlushPos && frame.mTagName != 's') { + while (framePos != mWritePos && frame.mTagName != 's' && frame.mTagName != 'T') { int incBy = 1; frame = mEntries[framePos]; @@ -419,7 +336,7 @@ void ThreadProfile::StreamJSObject(JSStreamWriter& b) // DYNAMIC_MAX_STRING-2 tagBuff[DYNAMIC_MAX_STRING-1] = '\0'; - if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') { + if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'd') { tagStringData = processDynamicTag(framePos, &incBy, tagBuff); } @@ -440,13 +357,13 @@ void ThreadProfile::StreamJSObject(JSStreamWriter& b) b.BeginObject(); b.NameValue("location", tagStringData); readAheadPos = (framePos + incBy) % mEntrySize; - if (readAheadPos != mLastFlushPos && + if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'n') { b.NameValue("line", mEntries[readAheadPos].mTagInt); incBy++; } readAheadPos = (framePos + incBy) % mEntrySize; - if (readAheadPos != mLastFlushPos && + if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'y') { b.NameValue("category", mEntries[readAheadPos].mTagInt); incBy++; @@ -459,24 +376,176 @@ void ThreadProfile::StreamJSObject(JSStreamWriter& b) } break; } - readPos = (readPos + 1) % mEntrySize; } - if (sample) { - b.EndObject(); + readPos = (readPos + 1) % mEntrySize; + } + if (sample) { + b.EndObject(); + } + b.EndArray(); +} + +void ProfileBuffer::StreamMarkersToJSObject(JSStreamWriter& b, int aThreadId) +{ + b.BeginArray(); + int readPos = mReadPos; + int currentThreadID = -1; + while (readPos != mWritePos) { + ProfileEntry entry = mEntries[readPos]; + if (entry.mTagName == 'T') { + currentThreadID = entry.mTagInt; + } else if (currentThreadID == aThreadId && entry.mTagName == 'm') { + entry.getMarker()->StreamJSObject(b); } - b.EndArray(); + readPos = (readPos + 1) % mEntrySize; + } + b.EndArray(); +} + +int ProfileBuffer::FindLastSampleOfThread(int aThreadId) +{ + // We search backwards from mWritePos-1 to mReadPos. + // Adding mEntrySize makes the result of the modulus positive. + for (int readPos = (mWritePos + mEntrySize - 1) % mEntrySize; + readPos != (mReadPos + mEntrySize - 1) % mEntrySize; + readPos = (readPos + mEntrySize - 1) % mEntrySize) { + ProfileEntry entry = mEntries[readPos]; + if (entry.mTagName == 'T' && entry.mTagInt == aThreadId) { + return readPos; + } + } + + return -1; +} + +void ProfileBuffer::DuplicateLastSample(int aThreadId) +{ + int lastSampleStartPos = FindLastSampleOfThread(aThreadId); + if (lastSampleStartPos == -1) { + return; + } + + MOZ_ASSERT(mEntries[lastSampleStartPos].mTagName == 'T'); + + addTag(mEntries[lastSampleStartPos]); + + // Go through the whole entry and duplicate it, until we find the next one. + for (int readPos = (lastSampleStartPos + 1) % mEntrySize; + readPos != mWritePos; + readPos = (readPos + 1) % mEntrySize) { + switch (mEntries[readPos].mTagName) { + case 'T': + // We're done. + return; + case 't': + // Copy with new time + addTag(ProfileEntry('t', static_cast((mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds()))); + break; + case 'm': + // Don't copy markers + break; + // Copy anything else we don't know about + // L, B, S, c, s, d, l, f, h, r, t, p + default: + addTag(mEntries[readPos]); + break; + } + } +} + +std::ostream& +ProfileBuffer::StreamToOStream(std::ostream& stream, int aThreadId) const +{ + int readPos = mReadPos; + int currentThreadID = -1; + while (readPos != mWritePos) { + ProfileEntry entry = mEntries[readPos]; + if (entry.mTagName == 'T') { + currentThreadID = entry.mTagInt; + } else if (currentThreadID == aThreadId) { + stream << mEntries[readPos]; + } + readPos = (readPos + 1) % mEntrySize; + } + return stream; +} + +// END ProfileBuffer +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// +// BEGIN ThreadProfile + +ThreadProfile::ThreadProfile(ThreadInfo* aInfo, ProfileBuffer* aBuffer) + : mThreadInfo(aInfo) + , mBuffer(aBuffer) + , mPseudoStack(aInfo->Stack()) + , mMutex("ThreadProfile::mMutex") + , mThreadId(int(aInfo->ThreadId())) + , mIsMainThread(aInfo->IsMainThread()) + , mPlatformData(aInfo->GetPlatformData()) + , mStackTop(aInfo->StackTop()) + , mRespInfo(this) +#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::IterateTags(IterateTagsCallback aCallback) +{ + mBuffer->IterateTagsForThread(aCallback, mThreadId); +} + +void ThreadProfile::ToStreamAsJSON(std::ostream& stream) +{ + JSStreamWriter b(stream); + StreamJSObject(b); +} + +void ThreadProfile::StreamJSObject(JSStreamWriter& b) +{ + b.BeginObject(); + // Thread meta data + if (XRE_GetProcessType() == GeckoProcessType_Plugin) { + // TODO Add the proper plugin name + b.NameValue("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. + b.NameValue("name", "Content"); + } else { + b.NameValue("name", Name()); + } + b.NameValue("tid", static_cast(mThreadId)); + + b.Name("samples"); + mBuffer->StreamSamplesToJSObject(b, mThreadId); b.Name("markers"); - b.BeginArray(); - readPos = mReadPos; - while (readPos != mLastFlushPos) { - ProfileEntry entry = mEntries[readPos]; - if (entry.mTagName == 'm') { - entry.getMarker()->StreamJSObject(b); - } - readPos = (readPos + 1) % mEntrySize; - } - b.EndArray(); + mBuffer->StreamMarkersToJSObject(b, mThreadId); b.EndObject(); } @@ -516,46 +585,14 @@ mozilla::Mutex* ThreadProfile::GetMutex() return &mMutex; } -void ThreadProfile::DuplicateLastSample() { - // Scan the whole buffer (even unflushed parts) - // Adding mEntrySize makes the result of the modulus positive - // We search backwards from mWritePos-1 to mReadPos - for (int readPos = (mWritePos + mEntrySize - 1) % mEntrySize; - readPos != (mReadPos + mEntrySize - 1) % mEntrySize; - readPos = (readPos + mEntrySize - 1) % mEntrySize) { - if (mEntries[readPos].mTagName == 's') { - // Found the start of the last entry at position readPos - int copyEndIdx = mWritePos; - // Go through the whole entry and duplicate it - for (;readPos != copyEndIdx; readPos = (readPos + 1) % mEntrySize) { - switch (mEntries[readPos].mTagName) { - // Copy with new time - case 't': - addTag(ProfileEntry('t', static_cast((mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds()))); - break; - // Don't copy markers - case 'm': - break; - // Copy anything else we don't know about - // L, B, S, c, s, d, l, f, h, r, t, p - default: - addTag(mEntries[readPos]); - break; - } - } - break; - } - } +void ThreadProfile::DuplicateLastSample() +{ + mBuffer->DuplicateLastSample(mThreadId); } std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile) { - int readPos = profile.mReadPos; - while (readPos != profile.mLastFlushPos) { - stream << profile.mEntries[readPos]; - readPos = (readPos + 1) % profile.mEntrySize; - } - return stream; + return profile.mBuffer->StreamToOStream(stream, profile.mThreadId); } // END ThreadProfile diff --git a/tools/profiler/ProfileEntry.h b/tools/profiler/ProfileEntry.h index da9b15a2df6..36f93f2e166 100644 --- a/tools/profiler/ProfileEntry.h +++ b/tools/profiler/ProfileEntry.h @@ -12,8 +12,10 @@ #include "platform.h" #include "JSStreamWriter.h" #include "ProfilerBacktrace.h" +#include "nsRefPtr.h" #include "mozilla/Mutex.h" #include "gtest/MozGtestFriend.h" +#include "mozilla/UniquePtr.h" class ThreadProfile; @@ -52,7 +54,7 @@ private: FRIEND_TEST(ThreadProfile, InsertTagsNoWrap); FRIEND_TEST(ThreadProfile, InsertTagsWrap); FRIEND_TEST(ThreadProfile, MemoryMeasure); - friend class ThreadProfile; + friend class ProfileBuffer; union { const char* mTagData; char mTagChars[sizeof(void*)]; @@ -71,15 +73,64 @@ private: typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagStringData); +class ProfileBuffer { +public: + NS_INLINE_DECL_REFCOUNTING(ProfileBuffer) + + explicit ProfileBuffer(int aEntrySize); + + void addTag(const ProfileEntry& aTag); + void IterateTagsForThread(IterateTagsCallback aCallback, int aThreadId); + void StreamSamplesToJSObject(JSStreamWriter& b, int aThreadId); + void StreamMarkersToJSObject(JSStreamWriter& b, int aThreadId); + void DuplicateLastSample(int aThreadId); + + void addStoredMarker(ProfilerMarker* aStoredMarker); + void deleteExpiredStoredMarkers(); + + std::ostream& StreamToOStream(std::ostream& stream, int aThreadId) const; + +protected: + char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff); + int FindLastSampleOfThread(int aThreadId); + + ~ProfileBuffer() {} + +public: + // Circular buffer 'Keep One Slot Open' implementation for simplicity + mozilla::UniquePtr 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. + int mGeneration; + + // Markers that marker entries in the buffer might refer to. + ProfilerMarkerLinkedList mStoredMarkers; +}; + class ThreadProfile { public: - ThreadProfile(ThreadInfo* aThreadInfo, int aEntrySize); + ThreadProfile(ThreadInfo* aThreadInfo, ProfileBuffer* aBuffer); virtual ~ThreadProfile(); - void addTag(ProfileEntry aTag); - void flush(); - void erase(); - char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff); + 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); + void IterateTags(IterateTagsCallback aCallback); friend std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile); @@ -94,13 +145,9 @@ public: bool IsMainThread() const { return mIsMainThread; } const char* Name() const { return mThreadInfo->Name(); } - Thread::tid_t ThreadId() const { return mThreadId; } + int ThreadId() const { return mThreadId; } PlatformData* GetPlatformData() const { return mPlatformData; } - int GetGenerationID() const { return mGeneration; } - bool HasGenerationExpired(int aGenID) const { - return aGenID + 2 <= mGeneration; - } void* GetStackTop() const { return mStackTop; } void DuplicateLastSample(); @@ -118,20 +165,14 @@ private: FRIEND_TEST(ThreadProfile, InsertTagsWrap); FRIEND_TEST(ThreadProfile, MemoryMeasure); ThreadInfo* mThreadInfo; - // Circular buffer 'Keep One Slot Open' implementation - // for simplicity - ProfileEntry* mEntries; - int mWritePos; // points to the next entry we will write to - int mLastFlushPos; // points to the next entry since the last flush() - int mReadPos; // points to the next entry we will read to - int mEntrySize; + + const nsRefPtr mBuffer; + PseudoStack* mPseudoStack; mozilla::Mutex mMutex; - Thread::tid_t mThreadId; + int mThreadId; bool mIsMainThread; PlatformData* mPlatformData; // Platform specific data. - int mGeneration; - int mPendingGenerationFlush; void* const mStackTop; ThreadResponsiveness mRespInfo; diff --git a/tools/profiler/PseudoStack.h b/tools/profiler/PseudoStack.h index 6beaf4efce8..96859a1f227 100644 --- a/tools/profiler/PseudoStack.h +++ b/tools/profiler/PseudoStack.h @@ -176,90 +176,62 @@ private: typedef ProfilerLinkedList ProfilerMarkerLinkedList; typedef ProfilerLinkedList UWTBufferLinkedList; -class PendingMarkers { +template +class ProfilerSignalSafeLinkedList { public: - PendingMarkers() + ProfilerSignalSafeLinkedList() : 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() + ~ProfilerSignalSafeLinkedList() { - // 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; + // Some thread is modifying the list. We should only be released on that + // thread. + abort(); } - return &mPendingMarkers; - } - void clearMarkers() - { - while (mPendingMarkers.peek()) { - delete mPendingMarkers.popHead(); - } - while (mStoredMarkers.peek()) { - delete mStoredMarkers.popHead(); + while (mList.peek()) { + delete mList.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; -}; + // Insert an item into the list. + // Must only be called from the owning thread. + // Must not be called while the list from accessList() is being accessed. + // In the profiler, we ensure that by interrupting the profiled thread + // (which is the one that owns this list and calls insert() on it) until + // we're done reading the list from the signal handler. + void insert(T* aElement) { + MOZ_ASSERT(aElement); -class PendingUWTBuffers -{ -public: - PendingUWTBuffers() - : mSignalLock(false) - { - } - - void addLinkedUWTBuffer(LinkedUWTBuffer* aBuff) - { - MOZ_ASSERT(aBuff); mSignalLock = true; STORE_SEQUENCER(); - mPendingUWTBuffers.insert(aBuff); + + mList.insert(aElement); + STORE_SEQUENCER(); mSignalLock = false; } - // called within signal. Function must be reentrant - UWTBufferLinkedList* getLinkedUWTBuffers() + // Called within signal, from any thread, possibly while insert() is in the + // middle of modifying the list (on the owning thread). Will return null if + // that is the case. + // Function must be reentrant. + ProfilerLinkedList* accessList() { if (mSignalLock) { return nullptr; } - return &mPendingUWTBuffers; + return &mList; } private: - UWTBufferLinkedList mPendingUWTBuffers; - volatile bool mSignalLock; + ProfilerLinkedList mList; + + // If this is set, then it's not safe to read the list because its contents + // are being changed. + volatile bool mSignalLock; }; // Stub eventMarker function for js-engine event generation. @@ -285,32 +257,27 @@ public: void addLinkedUWTBuffer(LinkedUWTBuffer* aBuff) { - mPendingUWTBuffers.addLinkedUWTBuffer(aBuff); + mPendingUWTBuffers.insert(aBuff); } UWTBufferLinkedList* getLinkedUWTBuffers() { - return mPendingUWTBuffers.getLinkedUWTBuffers(); + return mPendingUWTBuffers.accessList(); } void addMarker(const char *aMarkerStr, ProfilerMarkerPayload *aPayload, float aTime) { ProfilerMarker* marker = new ProfilerMarker(aMarkerStr, aPayload, aTime); - mPendingMarkers.addMarker(marker); - } - - void addStoredMarker(ProfilerMarker *aStoredMarker) { - mPendingMarkers.addStoredMarker(aStoredMarker); - } - - void updateGeneration(int aGenID) { - mPendingMarkers.updateGeneration(aGenID); + mPendingMarkers.insert(marker); } // called within signal. Function must be reentrant ProfilerMarkerLinkedList* getPendingMarkers() { - return mPendingMarkers.getPendingMarkers(); + // The profiled thread is interrupted, so we can access the list safely. + // Unless the profiled thread was in the middle of changing the list when + // we interrupted it - in that case, accessList() will return null. + return mPendingMarkers.accessList(); } void push(const char *aName, js::ProfileEntry::Category aCategory, uint32_t line) @@ -450,9 +417,9 @@ public: // Keep a list of pending markers that must be moved // to the circular buffer - PendingMarkers mPendingMarkers; + ProfilerSignalSafeLinkedList mPendingMarkers; // List of LinkedUWTBuffers that must be processed on the next tick - PendingUWTBuffers mPendingUWTBuffers; + ProfilerSignalSafeLinkedList mPendingUWTBuffers; // 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; diff --git a/tools/profiler/SyncProfile.cpp b/tools/profiler/SyncProfile.cpp index d7190b280b6..b6e3029f493 100644 --- a/tools/profiler/SyncProfile.cpp +++ b/tools/profiler/SyncProfile.cpp @@ -8,7 +8,7 @@ #include "UnwinderThread2.h" SyncProfile::SyncProfile(ThreadInfo* aInfo, int aEntrySize) - : ThreadProfile(aInfo, aEntrySize) + : ThreadProfile(aInfo, new ProfileBuffer(aEntrySize)) , mOwnerState(REFERENCED) , mUtb(nullptr) { @@ -57,7 +57,6 @@ SyncProfile::EndUnwind() utb__end_sync_buffer_unwind(mUtb); } if (mOwnerState != ORPHANED) { - flush(); mOwnerState = OWNED; } // Save mOwnerState before we release the mutex diff --git a/tools/profiler/TableTicker.cpp b/tools/profiler/TableTicker.cpp index cc60059054d..a6ffa013ef1 100644 --- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -107,6 +107,10 @@ void TableTicker::HandleSaveRequest() NS_DispatchToMainThread(runnable); } +void TableTicker::DeleteExpiredMarkers() +{ + mBuffer->deleteExpiredStoredMarkers(); +} void TableTicker::StreamTaskTracer(JSStreamWriter& b) { @@ -153,7 +157,6 @@ void TableTicker::StreamMetaJSCustomObject(JSStreamWriter& b) b.NameValue("version", 2); b.NameValue("interval", interval()); b.NameValue("stackwalk", mUseStackWalk); - b.NameValue("jank", mJankOnly); b.NameValue("processType", XRE_GetProcessType()); mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - sStartTime; @@ -753,52 +756,9 @@ void TableTicker::InplaceTick(TickSample* sample) { ThreadProfile& currThreadProfile = *sample->threadProfile; + currThreadProfile.addTag(ProfileEntry('T', currThreadProfile.ThreadId())); + PseudoStack* stack = currThreadProfile.GetPseudoStack(); - stack->updateGeneration(currThreadProfile.GetGenerationID()); - bool recordSample = true; -#if defined(XP_WIN) - bool powerSample = false; -#endif - - /* Don't process the PeudoStack's markers or honour jankOnly if we're - immediately sampling the current thread. */ - if (!sample->isSamplingCurrentThread) { - // Marker(s) come before the sample - ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers(); - while (pendingMarkersList && pendingMarkersList->peek()) { - ProfilerMarker* marker = pendingMarkersList->popHead(); - stack->addStoredMarker(marker); - currThreadProfile.addTag(ProfileEntry('m', marker)); - } - -#if defined(XP_WIN) - if (mProfilePower) { - mIntelPowerGadget->TakeSample(); - powerSample = true; - } -#endif - - if (mJankOnly) { - // if we are on a different event we can discard any temporary samples - // we've kept around - if (sLastSampledEventGeneration != sCurrentEventGeneration) { - // XXX: we also probably want to add an entry to the profile to help - // distinguish which samples are part of the same event. That, or record - // the event generation in each sample - currThreadProfile.erase(); - } - sLastSampledEventGeneration = sCurrentEventGeneration; - - recordSample = false; - // only record the events when we have a we haven't seen a tracer event for 100ms - if (!sLastTracerEvent.IsNull()) { - mozilla::TimeDuration delta = sample->timestamp - sLastTracerEvent; - if (delta.ToMilliseconds() > 100.0) { - recordSample = true; - } - } - } - } #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) if (mUseStackWalk) { @@ -810,8 +770,16 @@ void TableTicker::InplaceTick(TickSample* sample) doSampleStackTrace(currThreadProfile, sample, mAddLeafAddresses); #endif - if (recordSample) - currThreadProfile.flush(); + // Don't process the PeudoStack's markers if we're + // synchronously sampling the current thread. + if (!sample->isSamplingCurrentThread) { + ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers(); + while (pendingMarkersList && pendingMarkersList->peek()) { + ProfilerMarker* marker = pendingMarkersList->popHead(); + currThreadProfile.addStoredMarker(marker); + currThreadProfile.addTag(ProfileEntry('m', marker)); + } + } if (sample && currThreadProfile.GetThreadResponsiveness()->HasData()) { mozilla::TimeDuration delta = currThreadProfile.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp); @@ -834,7 +802,8 @@ void TableTicker::InplaceTick(TickSample* sample) } #if defined(XP_WIN) - if (powerSample) { + if (mProfilePower) { + mIntelPowerGadget->TakeSample(); currThreadProfile.addTag(ProfileEntry('p', static_cast(mIntelPowerGadget->GetTotalPackagePowerInWatts()))); } #endif diff --git a/tools/profiler/TableTicker.h b/tools/profiler/TableTicker.h index 09c888f9315..83f80b4487f 100644 --- a/tools/profiler/TableTicker.h +++ b/tools/profiler/TableTicker.h @@ -42,8 +42,6 @@ threadSelected(ThreadInfo* aInfo, char** aThreadNameFilters, uint32_t aFeatureCo extern mozilla::TimeStamp sLastTracerEvent; extern int sFrameNumber; extern int sLastFrameNumber; -extern unsigned int sCurrentEventGeneration; -extern unsigned int sLastSampledEventGeneration; class BreakpadSampler; @@ -54,6 +52,7 @@ class TableTicker: public Sampler { const char** aThreadNameFilters, uint32_t aFilterCount) : Sampler(aInterval, true, aEntrySize) , mPrimaryThreadProfile(nullptr) + , mBuffer(new ProfileBuffer(aEntrySize)) , mSaveRequested(false) , mUnwinderThread(false) , mFilterCount(aFilterCount) @@ -63,8 +62,6 @@ class TableTicker: public Sampler { { mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk"); - //XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point - mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank"); mProfileJS = hasFeature(aFeatures, aFeatureCount, "js"); mProfileJava = hasFeature(aFeatures, aFeatureCount, "java"); mProfileGPU = hasFeature(aFeatures, aFeatureCount, "gpu"); @@ -156,7 +153,7 @@ class TableTicker: public Sampler { return; } - ThreadProfile* profile = new ThreadProfile(aInfo, EntrySize()); + ThreadProfile* profile = new ThreadProfile(aInfo, mBuffer); aInfo->SetProfile(profile); } @@ -178,6 +175,7 @@ class TableTicker: public Sampler { } virtual void HandleSaveRequest(); + virtual void DeleteExpiredMarkers() MOZ_OVERRIDE; ThreadProfile* GetPrimaryThreadProfile() { @@ -227,10 +225,10 @@ protected: // This represent the application's main thread (SAMPLER_INIT) ThreadProfile* mPrimaryThreadProfile; + nsRefPtr mBuffer; bool mSaveRequested; bool mAddLeafAddresses; bool mUseStackWalk; - bool mJankOnly; bool mProfileJS; bool mProfileGPU; bool mProfileThreads; diff --git a/tools/profiler/UnwinderThread2.cpp b/tools/profiler/UnwinderThread2.cpp index 166d1b0be81..9a8916b450a 100644 --- a/tools/profiler/UnwinderThread2.cpp +++ b/tools/profiler/UnwinderThread2.cpp @@ -1186,7 +1186,7 @@ static void process_buffer(UnwinderThreadBuffer* buff, int oldest_ix) for (k = 0; k < buff->entsUsed; k++) { ProfileEntry ent = utb_get_profent(buff, k); // action flush-hints - if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; } + if (ent.is_ent_hint('F')) { continue; } // skip ones we can't copy if (ent.is_ent_hint() || ent.is_ent('S')) { continue; } // handle GetBacktrace() @@ -1223,7 +1223,7 @@ static void process_buffer(UnwinderThreadBuffer* buff, int oldest_ix) continue; } // action flush-hints - if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; } + if (ent.is_ent_hint('F')) { continue; } // skip ones we can't copy if (ent.is_ent_hint() || ent.is_ent('S')) { continue; } // handle GetBacktrace() @@ -1249,7 +1249,7 @@ static void process_buffer(UnwinderThreadBuffer* buff, int oldest_ix) buff->aProfile->addTag( ProfileEntry('s', "(root)") ); } // action flush-hints - if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; } + if (ent.is_ent_hint('F')) { continue; } // skip ones we can't copy if (ent.is_ent_hint() || ent.is_ent('S')) { continue; } // handle GetBacktrace() @@ -1281,7 +1281,7 @@ static void process_buffer(UnwinderThreadBuffer* buff, int oldest_ix) for (k = 0; k < ix_first_hP; k++) { ProfileEntry ent = utb_get_profent(buff, k); // action flush-hints - if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; } + if (ent.is_ent_hint('F')) { continue; } // skip ones we can't copy if (ent.is_ent_hint() || ent.is_ent('S')) { continue; } // handle GetBacktrace() @@ -1400,7 +1400,7 @@ static void process_buffer(UnwinderThreadBuffer* buff, int oldest_ix) for (k = ix_last_hQ+1; k < buff->entsUsed; k++) { ProfileEntry ent = utb_get_profent(buff, k); // action flush-hints - if (ent.is_ent_hint('F')) { buff->aProfile->flush(); continue; } + if (ent.is_ent_hint('F')) { continue; } // skip ones we can't copy if (ent.is_ent_hint() || ent.is_ent('S')) { continue; } // and copy everything else @@ -1418,11 +1418,7 @@ static void process_buffer(UnwinderThreadBuffer* buff, int oldest_ix) for (k = 0; k < buff->entsUsed; k++) { ProfileEntry ent = utb_get_profent(buff, k); if (show) ent.log(); - if (ent.is_ent_hint('F')) { - /* This is a flush-hint */ - buff->aProfile->flush(); - } - else if (ent.is_ent_hint('N')) { + if (ent.is_ent_hint('N')) { /* This is a do-a-native-unwind-right-now hint */ MOZ_ASSERT(buff->haveNativeInfo); PCandSP* pairs = nullptr; diff --git a/tools/profiler/platform-linux.cc b/tools/profiler/platform-linux.cc index e13fbaa153d..2ea2534bdee 100644 --- a/tools/profiler/platform-linux.cc +++ b/tools/profiler/platform-linux.cc @@ -294,6 +294,7 @@ static void* SignalSender(void* arg) { while (SamplerRegistry::sampler->IsActive()) { SamplerRegistry::sampler->HandleSaveRequest(); + SamplerRegistry::sampler->DeleteExpiredMarkers(); if (!SamplerRegistry::sampler->IsPaused()) { mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); @@ -311,8 +312,6 @@ static void* SignalSender(void* arg) { PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); if (sleeping == PseudoStack::SLEEPING_AGAIN) { info->Profile()->DuplicateLastSample(); - //XXX: This causes flushes regardless of jank-only mode - info->Profile()->flush(); continue; } diff --git a/tools/profiler/platform-macos.cc b/tools/profiler/platform-macos.cc index 003d960fd05..bcd91f76130 100644 --- a/tools/profiler/platform-macos.cc +++ b/tools/profiler/platform-macos.cc @@ -203,6 +203,7 @@ class SamplerThread : public Thread { // Implement Thread::Run(). virtual void Run() { while (SamplerRegistry::sampler->IsActive()) { + SamplerRegistry::sampler->DeleteExpiredMarkers(); if (!SamplerRegistry::sampler->IsPaused()) { mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); std::vector threads = @@ -218,8 +219,6 @@ class SamplerThread : public Thread { PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); if (sleeping == PseudoStack::SLEEPING_AGAIN) { info->Profile()->DuplicateLastSample(); - //XXX: This causes flushes regardless of jank-only mode - info->Profile()->flush(); continue; } diff --git a/tools/profiler/platform-win32.cc b/tools/profiler/platform-win32.cc index c20c45b7c5c..289c21f4630 100644 --- a/tools/profiler/platform-win32.cc +++ b/tools/profiler/platform-win32.cc @@ -120,6 +120,8 @@ class SamplerThread : public Thread { ::timeBeginPeriod(interval_); while (sampler_->IsActive()) { + sampler_->DeleteExpiredMarkers(); + if (!sampler_->IsPaused()) { mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); std::vector threads = @@ -135,8 +137,6 @@ class SamplerThread : public Thread { PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); if (sleeping == PseudoStack::SLEEPING_AGAIN) { info->Profile()->DuplicateLastSample(); - //XXX: This causes flushes regardless of jank-only mode - info->Profile()->flush(); continue; } diff --git a/tools/profiler/platform.cpp b/tools/profiler/platform.cpp index 0e3237d44e1..eb17bd5b93f 100644 --- a/tools/profiler/platform.cpp +++ b/tools/profiler/platform.cpp @@ -56,13 +56,6 @@ const char* PROFILER_ENTRIES = "MOZ_PROFILER_ENTRIES"; const char* PROFILER_STACK = "MOZ_PROFILER_STACK_SCAN"; const char* PROFILER_FEATURES = "MOZ_PROFILING_FEATURES"; -/* used to keep track of the last event that we sampled during */ -unsigned int sLastSampledEventGeneration = 0; - -/* a counter that's incremented everytime we get responsiveness event - * note: it might also be worth trackplaing everytime we go around - * the event loop */ -unsigned int sCurrentEventGeneration = 0; /* we don't need to worry about overflow because we only treat the * case of them being the same as special. i.e. we only run into * a problem if 2^32 events happen between samples that we need @@ -203,45 +196,6 @@ void ProfilerMarker::StreamJSObject(JSStreamWriter& b) const { b.EndObject(); } -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 @@ -939,8 +893,6 @@ bool mozilla_sampler_is_active() void mozilla_sampler_responsiveness(const mozilla::TimeStamp& aTime) { - sCurrentEventGeneration++; - sLastTracerEvent = aTime; } diff --git a/tools/profiler/platform.h b/tools/profiler/platform.h index 5e8a401cbf9..cf1f3928346 100644 --- a/tools/profiler/platform.h +++ b/tools/profiler/platform.h @@ -309,6 +309,8 @@ class Sampler { virtual void RequestSave() = 0; // Process any outstanding request outside a signal handler. virtual void HandleSaveRequest() = 0; + // Delete markers which are no longer part of the profile due to buffer wraparound. + virtual void DeleteExpiredMarkers() = 0; // Start and stop sampler. void Start(); diff --git a/tools/profiler/tests/gtest/ThreadProfileTest.cpp b/tools/profiler/tests/gtest/ThreadProfileTest.cpp index d8ac6d08918..a1a18877b23 100644 --- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp +++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp @@ -12,7 +12,8 @@ TEST(ThreadProfile, Initialization) { PseudoStack* stack = PseudoStack::create(); Thread::tid_t tid = 1000; ThreadInfo info("testThread", tid, true, stack, nullptr); - ThreadProfile tp(&info, 10); + nsRefPtr pb = new ProfileBuffer(10); + ThreadProfile tp(&info, pb); } // Make sure we can record one tag and read it @@ -20,11 +21,11 @@ TEST(ThreadProfile, InsertOneTag) { PseudoStack* stack = PseudoStack::create(); Thread::tid_t tid = 1000; ThreadInfo info("testThread", tid, true, stack, nullptr); - ThreadProfile tp(&info, 10); - tp.addTag(ProfileEntry('t', 123.1f)); - ASSERT_TRUE(tp.mEntries != nullptr); - ASSERT_TRUE(tp.mEntries[tp.mReadPos].mTagName == 't'); - ASSERT_TRUE(tp.mEntries[tp.mReadPos].mTagFloat == 123.1f); + nsRefPtr pb = new ProfileBuffer(10); + pb->addTag(ProfileEntry('t', 123.1f)); + ASSERT_TRUE(pb->mEntries != nullptr); + ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagName == 't'); + ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagFloat == 123.1f); } // See if we can insert some tags @@ -32,17 +33,17 @@ TEST(ThreadProfile, InsertTagsNoWrap) { PseudoStack* stack = PseudoStack::create(); Thread::tid_t tid = 1000; ThreadInfo info("testThread", tid, true, stack, nullptr); - ThreadProfile tp(&info, 100); + nsRefPtr pb = new ProfileBuffer(100); int test_size = 50; for (int i = 0; i < test_size; i++) { - tp.addTag(ProfileEntry('t', i)); + pb->addTag(ProfileEntry('t', i)); } - ASSERT_TRUE(tp.mEntries != nullptr); - int readPos = tp.mReadPos; - while (readPos != tp.mWritePos) { - ASSERT_TRUE(tp.mEntries[readPos].mTagName == 't'); - ASSERT_TRUE(tp.mEntries[readPos].mTagInt == readPos); - readPos = (readPos + 1) % tp.mEntrySize; + ASSERT_TRUE(pb->mEntries != nullptr); + int readPos = pb->mReadPos; + while (readPos != pb->mWritePos) { + ASSERT_TRUE(pb->mEntries[readPos].mTagName == 't'); + ASSERT_TRUE(pb->mEntries[readPos].mTagInt == readPos); + readPos = (readPos + 1) % pb->mEntrySize; } } @@ -54,20 +55,20 @@ TEST(ThreadProfile, InsertTagsWrap) { int tags = 24; int buffer_size = tags + 1; ThreadInfo info("testThread", tid, true, stack, nullptr); - ThreadProfile tp(&info, buffer_size); + nsRefPtr pb = new ProfileBuffer(buffer_size); int test_size = 43; for (int i = 0; i < test_size; i++) { - tp.addTag(ProfileEntry('t', i)); + pb->addTag(ProfileEntry('t', i)); } - ASSERT_TRUE(tp.mEntries != nullptr); - int readPos = tp.mReadPos; + ASSERT_TRUE(pb->mEntries != nullptr); + int readPos = pb->mReadPos; int ctr = 0; - while (readPos != tp.mWritePos) { - ASSERT_TRUE(tp.mEntries[readPos].mTagName == 't'); + while (readPos != pb->mWritePos) { + ASSERT_TRUE(pb->mEntries[readPos].mTagName == 't'); // the first few tags were discarded when we wrapped - ASSERT_TRUE(tp.mEntries[readPos].mTagInt == ctr + (test_size - tags)); + ASSERT_TRUE(pb->mEntries[readPos].mTagInt == ctr + (test_size - tags)); ctr++; - readPos = (readPos + 1) % tp.mEntrySize; + readPos = (readPos + 1) % pb->mEntrySize; } }