author | Benoit Girard <b56girard@gmail.com> |
Thu, 18 Jun 2015 01:05:42 -0400 | |
changeset 249683 | d1d45ce7cbf53564f5549cf8cf8ce0eca2976cfd |
parent 249682 | d8c58dd8dabc156c3948a532d4bba47ccacb9977 |
child 249684 | 54ad73e3a027524c74c2deff9bda18b65a9a536b |
push id | 28936 |
push user | ryanvm@gmail.com |
push date | Fri, 19 Jun 2015 20:34:42 +0000 |
treeherder | mozilla-central@c319f262ce3e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mstange |
bugs | 1172186 |
milestone | 41.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/public/ProfilingFrameIterator.h +++ b/js/public/ProfilingFrameIterator.h @@ -6,16 +6,17 @@ #ifndef js_ProfilingFrameIterator_h #define js_ProfilingFrameIterator_h #include "mozilla/Alignment.h" #include "mozilla/Maybe.h" #include "jsbytecode.h" +#include "js/TypeDecls.h" #include "js/Utility.h" struct JSRuntime; class JSScript; namespace js { class Activation; class AsmJSProfilingFrameIterator;
--- a/js/public/ProfilingStack.h +++ b/js/public/ProfilingStack.h @@ -4,16 +4,17 @@ * 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 js_ProfilingStack_h #define js_ProfilingStack_h #include "jsbytecode.h" #include "jstypes.h" +#include "js/TypeDecls.h" #include "js/Utility.h" struct JSRuntime; namespace js { // A call stack can be specified to the JS engine such that all JS entry/exits
--- a/tools/profiler/GeckoProfiler.h +++ b/tools/profiler/GeckoProfiler.h @@ -44,17 +44,19 @@ * application is responding to the event loop. Lower is better. * 't' - Elapse time since recording started. * */ #ifndef SAMPLER_H #define SAMPLER_H +#ifndef SPS_STANDALONE #include "js/TypeDecls.h" +#endif #include "mozilla/UniquePtr.h" namespace mozilla { class TimeStamp; namespace dom { class Promise; } // namespace dom @@ -170,19 +172,21 @@ static inline mozilla::UniquePtr<char[]> } // Get the profile encoded as a JSON object. static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx, double aSinceTime = 0) { return nullptr; } +#ifndef SPS_STANDALONE // Get the profile encoded as a JSON object. static inline void profiler_get_profile_jsobject_async(double aSinceTime = 0, mozilla::dom::Promise* = 0) {} +#endif // Get the profile and write it into a file static inline void profiler_save_profile_to_file(char* aFilename) { } // Get the features supported by the profiler that are accepted by profiler_init. // Returns a null terminated char* array. static inline char** profiler_get_features() { return nullptr; }
--- a/tools/profiler/GeckoProfilerFunc.h +++ b/tools/profiler/GeckoProfilerFunc.h @@ -1,17 +1,19 @@ /* -*- 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_FUNCS_H #define PROFILER_FUNCS_H +#ifndef SPS_STANDALONE #include "js/TypeDecls.h" +#endif #include "js/ProfilingStack.h" #include "mozilla/UniquePtr.h" #include <stdint.h> namespace mozilla { class TimeStamp; namespace dom { @@ -56,19 +58,21 @@ void mozilla_sampler_responsiveness(cons void mozilla_sampler_frame_number(int frameNumber); const double* mozilla_sampler_get_responsiveness(); void mozilla_sampler_save(); mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime); +#ifndef SPS_STANDALONE JSObject *mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime); void mozilla_sampler_get_profile_data_async(double aSinceTime, mozilla::dom::Promise* aPromise); +#endif // Make this function easily callable from a debugger in a build without // debugging information (work around http://llvm.org/bugs/show_bug.cgi?id=22211) extern "C" { void mozilla_sampler_save_profile_to_file(const char* aFilename); } const char** mozilla_sampler_get_features();
--- a/tools/profiler/GeckoProfilerImpl.h +++ b/tools/profiler/GeckoProfilerImpl.h @@ -9,20 +9,22 @@ #include <stdlib.h> #include <signal.h> #include <stdarg.h> #include "mozilla/ThreadLocal.h" #include "mozilla/Assertions.h" #include "mozilla/GuardObjects.h" #include "mozilla/UniquePtr.h" +#ifndef SPS_STANDALONE #include "nscore.h" +#include "nsISupports.h" +#endif #include "GeckoProfilerFunc.h" #include "PseudoStack.h" -#include "nsISupports.h" #include "ProfilerBacktrace.h" #ifdef MOZ_TASK_TRACER #include "GeckoTaskTracer.h" #endif /* QT has a #define for the word "slots" and jsfriendapi.h has a struct with * this variable name, causing compilation problems. Alleviate this for now by @@ -144,28 +146,30 @@ void profiler_set_frame_number(int frame } static inline mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0) { return mozilla_sampler_get_profile(aSinceTime); } +#ifndef SPS_STANDALONE static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx, double aSinceTime = 0) { return mozilla_sampler_get_profile_data(aCx, aSinceTime); } static inline void profiler_get_profile_jsobject_async(double aSinceTime = 0, mozilla::dom::Promise* aPromise = 0) { mozilla_sampler_get_profile_data_async(aSinceTime, aPromise); } +#endif static inline void profiler_save_profile_to_file(const char* aFilename) { return mozilla_sampler_save_profile_to_file(aFilename); } static inline @@ -212,26 +216,28 @@ void profiler_sleep_start() } static inline void profiler_sleep_end() { mozilla_sampler_sleep_end(); } +#ifndef SPS_STANDALONE static inline void profiler_js_operation_callback() { PseudoStack *stack = tlsPseudoStack.get(); if (!stack) { return; } stack->jsOperationCallback(); } +#endif static inline double profiler_time() { return mozilla_sampler_time(); } static inline
--- a/tools/profiler/ProfileEntry.cpp +++ b/tools/profiler/ProfileEntry.cpp @@ -1,23 +1,26 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <ostream> #include "platform.h" +#include "mozilla/HashFunctions.h" + +#ifndef SPS_STANDALONE #include "nsThreadUtils.h" #include "nsXULAppAPI.h" -#include "mozilla/HashFunctions.h" // JS #include "jsapi.h" #include "jsfriendapi.h" #include "js/TrackedOptimizationInfo.h" +#endif // Self #include "ProfileEntry.h" #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf _snprintf #endif @@ -200,16 +203,17 @@ public: mWriter.IntProperty(aName, mIndex++); } ~JSONSchemaWriter() { mWriter.EndObject(); } }; +#ifndef SPS_STANDALONE class StreamOptimizationTypeInfoOp : public JS::ForEachTrackedOptimizationTypeInfoOp { JSONWriter& mWriter; UniqueJSONStrings& mUniqueStrings; bool mStartedTypeList; public: StreamOptimizationTypeInfoOp(JSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings) @@ -301,25 +305,30 @@ public: } void operator()(const JS::ForEachProfiledFrameOp::FrameHandle& aFrameHandle) override { UniqueStacks::OnStackFrameKey frameKey(mReturnAddress, mDepth, aFrameHandle); mStack.AppendFrame(frameKey); mDepth++; } }; +#endif uint32_t UniqueJSONStrings::GetOrAddIndex(const char* aStr) { uint32_t index; - if (mStringToIndexMap.Get(aStr, &index)) { - return index; + StringKey key(aStr); + + auto it = mStringToIndexMap.find(key); + + if (it != mStringToIndexMap.end()) { + return it->second; } - index = mStringToIndexMap.Count(); - mStringToIndexMap.Put(aStr, index); + index = mStringToIndexMap.size(); + mStringToIndexMap[key] = index; mStringTableWriter.StringElement(aStr); return index; } bool UniqueStacks::FrameKey::operator==(const FrameKey& aOther) const { return mLocation == aOther.mLocation && mLine == aOther.mLine && @@ -340,31 +349,29 @@ UniqueStacks::Stack::Stack(UniqueStacks& { } void UniqueStacks::Stack::AppendFrame(const OnStackFrameKey& aFrame) { // Compute the prefix hash and index before mutating mStack. uint32_t prefixHash = mStack.Hash(); uint32_t prefix = mUniqueStacks.GetOrAddStackIndex(mStack); - mStack.mPrefixHash = Some(prefixHash); - mStack.mPrefix = Some(prefix); - mStack.mFrame = mUniqueStacks.GetOrAddFrameIndex(aFrame); + mStack.UpdateHash(prefixHash, prefix, mUniqueStacks.GetOrAddFrameIndex(aFrame)); } uint32_t UniqueStacks::Stack::GetOrAddIndex() const { return mUniqueStacks.GetOrAddStackIndex(mStack); } uint32_t UniqueStacks::FrameKey::Hash() const { uint32_t hash = 0; - if (!mLocation.IsEmpty()) { - hash = mozilla::HashString(mLocation.get()); + if (!mLocation.empty()) { + hash = mozilla::HashString(mLocation.c_str()); } if (mLine.isSome()) { hash = mozilla::AddToHash(hash, *mLine); } if (mCategory.isSome()) { hash = mozilla::AddToHash(hash, *mCategory); } if (mJITAddress.isSome()) { @@ -392,30 +399,65 @@ UniqueStacks::Stack UniqueStacks::BeginS UniqueStacks::UniqueStacks(JSRuntime* aRuntime) : mRuntime(aRuntime) , mFrameCount(0) { mFrameTableWriter.StartBareList(); mStackTableWriter.StartBareList(); } +#ifdef SPS_STANDALONE +uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack) +{ + uint32_t index; + auto it = mStackToIndexMap.find(aStack); + + if (it != mStackToIndexMap.end()) { + return it->second; + } + + index = mStackToIndexMap.size(); + mStackToIndexMap[aStack] = index; + StreamStack(aStack); + return index; +} +#else uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack) { uint32_t index; if (mStackToIndexMap.Get(aStack, &index)) { MOZ_ASSERT(index < mStackToIndexMap.Count()); return index; } index = mStackToIndexMap.Count(); mStackToIndexMap.Put(aStack, index); StreamStack(aStack); return index; } +#endif +#ifdef SPS_STANDALONE +uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame) +{ + uint32_t index; + auto it = mFrameToIndexMap.find(aFrame); + if (it != mFrameToIndexMap.end()) { + MOZ_ASSERT(it->second < mFrameCount); + return it->second; + } + + // A manual count is used instead of mFrameToIndexMap.Count() due to + // forwarding of canonical JIT frames above. + index = mFrameCount++; + mFrameToIndexMap[aFrame] = index; + StreamFrame(aFrame); + return index; +} +#else uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame) { uint32_t index; if (mFrameToIndexMap.Get(aFrame, &index)) { MOZ_ASSERT(index < mFrameCount); return index; } @@ -432,30 +474,34 @@ uint32_t UniqueStacks::GetOrAddFrameInde // A manual count is used instead of mFrameToIndexMap.Count() due to // forwarding of canonical JIT frames above. index = mFrameCount++; mFrameToIndexMap.Put(aFrame, index); StreamFrame(aFrame); return index; } +#endif uint32_t UniqueStacks::LookupJITFrameDepth(void* aAddr) { uint32_t depth; - if (mJITFrameDepthMap.Get(aAddr, &depth)) { + + auto it = mJITFrameDepthMap.find(aAddr); + if (it != mJITFrameDepthMap.end()) { + depth = it->second; MOZ_ASSERT(depth > 0); return depth; } return 0; } void UniqueStacks::AddJITFrameDepth(void* aAddr, unsigned depth) { - mJITFrameDepthMap.Put(aAddr, depth); + mJITFrameDepthMap[aAddr] = depth; } void UniqueStacks::SpliceFrameTableElements(SpliceableJSONWriter& aWriter) { mFrameTableWriter.EndBareList(); aWriter.TakeAndSplice(mFrameTableWriter.WriteFunc()); } @@ -483,30 +529,36 @@ void UniqueStacks::StreamStack(const Sta } void UniqueStacks::StreamFrame(const OnStackFrameKey& aFrame) { // Schema: // [location, implementation, optimizations, line, category] mFrameTableWriter.StartArrayElement(); +#ifndef SPS_STANDALONE if (!aFrame.mJITFrameHandle) { - mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.get()); +#else + { +#endif + mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.c_str()); if (aFrame.mLine.isSome()) { mFrameTableWriter.NullElement(); // implementation mFrameTableWriter.NullElement(); // optimizations mFrameTableWriter.IntElement(*aFrame.mLine); } if (aFrame.mCategory.isSome()) { if (aFrame.mLine.isNothing()) { mFrameTableWriter.NullElement(); // line } mFrameTableWriter.IntElement(*aFrame.mCategory); } - } else { + } +#ifndef SPS_STANDALONE + else { const JS::ForEachProfiledFrameOp::FrameHandle& jitFrame = *aFrame.mJITFrameHandle; mUniqueStrings.WriteElement(mFrameTableWriter, jitFrame.label()); JS::ProfilingFrameIterator::FrameKind frameKind = jitFrame.frameKind(); MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion || frameKind == JS::ProfilingFrameIterator::Frame_Baseline); mUniqueStrings.WriteElement(mFrameTableWriter, @@ -552,16 +604,17 @@ void UniqueStacks::StreamFrame(const OnS unsigned line, column; line = JS_PCToLineNumber(script, pc, &column); mFrameTableWriter.IntProperty("line", line); mFrameTableWriter.IntProperty("column", column); } mFrameTableWriter.EndObject(); } } +#endif mFrameTableWriter.EndArray(); } struct ProfileSample { uint32_t mStack; Maybe<double> mTime; Maybe<double> mResponsiveness; @@ -738,30 +791,32 @@ void ProfileBuffer::StreamSamplesToJSON( } readAheadPos = (framePos + incBy) % mEntrySize; if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'y') { frameKey.mCategory = Some((unsigned) mEntries[readAheadPos].mTagInt); incBy++; } stack.AppendFrame(frameKey); +#ifndef SPS_STANDALONE } else if (frame.mTagName == 'J') { // A JIT frame may expand to multiple frames due to inlining. void* pc = frame.mTagPtr; unsigned depth = aUniqueStacks.LookupJITFrameDepth(pc); if (depth == 0) { StreamJSFramesOp framesOp(pc, stack); JS::ForEachProfiledFrame(aRuntime, pc, framesOp); aUniqueStacks.AddJITFrameDepth(pc, framesOp.depth()); } else { for (unsigned i = 0; i < depth; i++) { UniqueStacks::OnStackFrameKey inlineFrameKey(pc, i); stack.AppendFrame(inlineFrameKey); } } +#endif } framePos = (framePos + incBy) % mEntrySize; } sample->mStack = stack.GetOrAddIndex(); break; } } @@ -849,22 +904,24 @@ void ProfileBuffer::DuplicateLastSample( //////////////////////////////////////////////////////////////////////// // BEGIN ThreadProfile ThreadProfile::ThreadProfile(ThreadInfo* aInfo, ProfileBuffer* aBuffer) : mThreadInfo(aInfo) , mBuffer(aBuffer) , mPseudoStack(aInfo->Stack()) - , mMutex("ThreadProfile::mMutex") + , 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); @@ -886,17 +943,21 @@ void ThreadProfile::addTag(const Profile 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"); { @@ -942,27 +1003,32 @@ void ThreadProfile::StreamJSON(Spliceabl 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"); @@ -979,17 +1045,22 @@ void ThreadProfile::StreamSamplesAndMark // 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, - mPseudoStack->mRuntime, aUniqueStacks); +#ifndef SPS_STANDALONE + mPseudoStack->mRuntime, +#else + nullptr, +#endif + aUniqueStacks); } aWriter.EndArray(); } aWriter.EndObject(); aWriter.StartObjectProperty("markers"); { { @@ -1021,24 +1092,33 @@ void ThreadProfile::FlushSamplesAndMarke // 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, - mPseudoStack->mRuntime, *mUniqueStacks); +#ifndef SPS_STANDALONE + mPseudoStack->mRuntime, +#else + nullptr, +#endif + *mUniqueStacks); } b.EndBareList(); mSavedStreamedSamples = b.WriteFunc()->CopyData(); } { SpliceableChunkedJSONWriter b; b.StartBareList(); @@ -1056,27 +1136,27 @@ void ThreadProfile::FlushSamplesAndMarke PseudoStack* ThreadProfile::GetPseudoStack() { return mPseudoStack; } void ThreadProfile::BeginUnwind() { - mMutex.Lock(); + mMutex->Lock(); } void ThreadProfile::EndUnwind() { - mMutex.Unlock(); + mMutex->Unlock(); } -mozilla::Mutex* ThreadProfile::GetMutex() +Mutex& ThreadProfile::GetMutex() { - return &mMutex; + return *mMutex.get(); } void ThreadProfile::DuplicateLastSample() { mBuffer->DuplicateLastSample(mThreadId); } // END ThreadProfile
--- a/tools/profiler/ProfileEntry.h +++ b/tools/profiler/ProfileEntry.h @@ -7,33 +7,38 @@ #ifndef MOZ_PROFILE_ENTRY_H #define MOZ_PROFILE_ENTRY_H #include <ostream> #include "GeckoProfiler.h" #include "platform.h" #include "ProfileJSONWriter.h" #include "ProfilerBacktrace.h" -#include "nsRefPtr.h" -#include "nsHashKeys.h" -#include "nsDataHashtable.h" +#include "mozilla/RefPtr.h" +#include <string> +#include <map> +#ifndef SPS_STANDALONE #include "js/ProfilingFrameIterator.h" #include "js/TrackedOptimizationInfo.h" +#include "nsHashKeys.h" +#include "nsDataHashtable.h" +#endif #include "mozilla/Maybe.h" -#include "mozilla/Mutex.h" #include "mozilla/Vector.h" +#ifndef SPS_STANDALONE #include "gtest/MozGtestFriend.h" +#else +#define FRIEND_TEST(a, b) // TODO Support standalone gtest +#endif +#include "mozilla/HashFunctions.h" #include "mozilla/UniquePtr.h" class ThreadProfile; -// NB: Packing this structure has been shown to cause SIGBUS issues on ARM. -#ifndef __arm__ #pragma pack(push, 1) -#endif class ProfileEntry { public: ProfileEntry(); // aTagData must not need release (i.e. be a string from the text segment) ProfileEntry(char aTagName, const char *aTagData); @@ -71,19 +76,17 @@ private: Address mTagAddress; uintptr_t mTagOffset; int mTagInt; char mTagChar; }; char mTagName; }; -#ifndef __arm__ #pragma pack(pop) -#endif class UniqueJSONStrings { public: UniqueJSONStrings() { mStringTableWriter.StartBareList(); } @@ -96,89 +99,152 @@ public: } void WriteElement(mozilla::JSONWriter& aWriter, const char* aStr) { aWriter.IntElement(GetOrAddIndex(aStr)); } uint32_t GetOrAddIndex(const char* aStr); + struct StringKey { + + explicit StringKey(const char* aStr) + : mStr(strdup(aStr)) + { + mHash = mozilla::HashString(mStr); + } + + StringKey(const StringKey& aOther) + : mStr(strdup(aOther.mStr)) + { + mHash = aOther.mHash; + } + + ~StringKey() { + free(mStr); + } + + uint32_t Hash() const; + bool operator==(const StringKey& aOther) const { + return strcmp(mStr, aOther.mStr) == 0; + } + bool operator<(const StringKey& aOther) const { + return mHash < aOther.mHash; + } + + private: + uint32_t mHash; + char* mStr; + }; private: SpliceableChunkedJSONWriter mStringTableWriter; - nsDataHashtable<nsCharPtrHashKey, uint32_t> mStringToIndexMap; + std::map<StringKey, uint32_t> mStringToIndexMap; }; class UniqueStacks { public: struct FrameKey { - nsCString mLocation; + std::string mLocation; mozilla::Maybe<unsigned> mLine; mozilla::Maybe<unsigned> mCategory; mozilla::Maybe<void*> mJITAddress; mozilla::Maybe<uint32_t> mJITDepth; explicit FrameKey(const char* aLocation) : mLocation(aLocation) - { } + { + mHash = Hash(); + } FrameKey(const FrameKey& aToCopy) : mLocation(aToCopy.mLocation) , mLine(aToCopy.mLine) , mCategory(aToCopy.mCategory) , mJITAddress(aToCopy.mJITAddress) , mJITDepth(aToCopy.mJITDepth) - { } + { + mHash = Hash(); + } FrameKey(void* aJITAddress, uint32_t aJITDepth) : mJITAddress(mozilla::Some(aJITAddress)) , mJITDepth(mozilla::Some(aJITDepth)) - { } + { + mHash = Hash(); + } uint32_t Hash() const; bool operator==(const FrameKey& aOther) const; + bool operator<(const FrameKey& aOther) const { + return mHash < aOther.mHash; + } + + private: + uint32_t mHash; }; // A FrameKey that holds a scoped reference to a JIT FrameHandle. struct MOZ_STACK_CLASS OnStackFrameKey : public FrameKey { - const JS::ForEachProfiledFrameOp::FrameHandle* mJITFrameHandle; - explicit OnStackFrameKey(const char* aLocation) : FrameKey(aLocation) +#ifndef SPS_STANDALONE , mJITFrameHandle(nullptr) +#endif { } OnStackFrameKey(const OnStackFrameKey& aToCopy) : FrameKey(aToCopy) +#ifndef SPS_STANDALONE , mJITFrameHandle(aToCopy.mJITFrameHandle) +#endif { } +#ifndef SPS_STANDALONE + const JS::ForEachProfiledFrameOp::FrameHandle* mJITFrameHandle; + OnStackFrameKey(void* aJITAddress, unsigned aJITDepth) : FrameKey(aJITAddress, aJITDepth) , mJITFrameHandle(nullptr) { } OnStackFrameKey(void* aJITAddress, unsigned aJITDepth, const JS::ForEachProfiledFrameOp::FrameHandle& aJITFrameHandle) : FrameKey(aJITAddress, aJITDepth) , mJITFrameHandle(&aJITFrameHandle) { } +#endif }; struct StackKey { mozilla::Maybe<uint32_t> mPrefixHash; mozilla::Maybe<uint32_t> mPrefix; uint32_t mFrame; explicit StackKey(uint32_t aFrame) : mFrame(aFrame) - { } + { + mHash = Hash(); + } uint32_t Hash() const; bool operator==(const StackKey& aOther) const; + bool operator<(const StackKey& aOther) const { + return mHash < aOther.mHash; + } + + void UpdateHash(uint32_t aPrefixHash, uint32_t aPrefix, uint32_t aFrame) { + mPrefixHash = mozilla::Some(aPrefixHash); + mPrefix = mozilla::Some(aPrefix); + mFrame = aFrame; + mHash = Hash(); + } + + private: + uint32_t mHash; }; class Stack { public: Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot); void AppendFrame(const OnStackFrameKey& aFrame); uint32_t GetOrAddIndex() const; @@ -207,32 +273,46 @@ public: private: JSRuntime* mRuntime; // To avoid incurring JitcodeGlobalTable lookup costs for every JIT frame, // we cache the depth of frames keyed by JIT code address. If an address a // maps to a depth d, then frames keyed by a for depths 0 to d are // guaranteed to be in mFrameToIndexMap. - nsDataHashtable<nsVoidPtrHashKey, uint32_t> mJITFrameDepthMap; + std::map<void*, uint32_t> mJITFrameDepthMap; uint32_t mFrameCount; SpliceableChunkedJSONWriter mFrameTableWriter; +#ifdef SPS_STANDALNOE + std::map<FrameKey, uint32_t> mFrameToIndexMap; +#else nsDataHashtable<nsGenericHashKey<FrameKey>, uint32_t> mFrameToIndexMap; +#endif SpliceableChunkedJSONWriter mStackTableWriter; + + // This sucks but this is really performance critical, nsDataHashtable is way faster + // than map/unordered_map but nsDataHashtable is tied to xpcom so we ifdef + // until we can find a better solution. +#ifdef SPS_STANDALONE + std::map<StackKey, uint32_t> mStackToIndexMap; +#else nsDataHashtable<nsGenericHashKey<StackKey>, uint32_t> mStackToIndexMap; +#endif }; -class ProfileBuffer { +class ProfileBuffer : public mozilla::RefCounted<ProfileBuffer> { public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProfileBuffer) + 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); @@ -240,18 +320,16 @@ public: // 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); - ~ProfileBuffer(); - 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; @@ -370,17 +448,17 @@ public: /** * 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(); - mozilla::Mutex* GetMutex(); + 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(); @@ -392,17 +470,19 @@ public: 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; @@ -415,33 +495,35 @@ protected: private: FRIEND_TEST(ThreadProfile, InsertOneTag); FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer); FRIEND_TEST(ThreadProfile, InsertTagsNoWrap); FRIEND_TEST(ThreadProfile, InsertTagsWrap); FRIEND_TEST(ThreadProfile, MemoryMeasure); ThreadInfo* mThreadInfo; - const nsRefPtr<ProfileBuffer> mBuffer; + 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::Mutex mMutex; + 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;
--- a/tools/profiler/ProfileJSONWriter.cpp +++ b/tools/profiler/ProfileJSONWriter.cpp @@ -7,37 +7,35 @@ #include "ProfileJSONWriter.h" void ChunkedJSONWriteFunc::Write(const char* aStr) { MOZ_ASSERT(mChunkPtr >= mChunkList.back().get() && mChunkPtr <= mChunkEnd); MOZ_ASSERT(mChunkEnd >= mChunkList.back().get() + mChunkLengths.back()); - MOZ_ASSERT(*mChunkPtr == '\0'); size_t len = strlen(aStr); // Most strings to be written are small, but subprocess profiles (e.g., // from the content process in e10s) may be huge. If the string is larger // than a chunk, allocate its own chunk. char* newPtr; if (len >= kChunkSize) { - AllocChunk(len + 1); + AllocChunk(len); newPtr = mChunkPtr + len; } else { newPtr = mChunkPtr + len; - if (newPtr >= mChunkEnd) { + if (newPtr > mChunkEnd) { AllocChunk(kChunkSize); newPtr = mChunkPtr + len; } } memcpy(mChunkPtr, aStr, len); - *newPtr = '\0'; mChunkPtr = newPtr; mChunkLengths.back() += len; } mozilla::UniquePtr<char[]> ChunkedJSONWriteFunc::CopyData() const { MOZ_ASSERT(mChunkLengths.length() == mChunkList.length()); @@ -74,17 +72,16 @@ ChunkedJSONWriteFunc::Take(ChunkedJSONWr void ChunkedJSONWriteFunc::AllocChunk(size_t aChunkSize) { MOZ_ASSERT(mChunkLengths.length() == mChunkList.length()); mozilla::UniquePtr<char[]> newChunk = mozilla::MakeUnique<char[]>(aChunkSize); mChunkPtr = newChunk.get(); mChunkEnd = mChunkPtr + aChunkSize; - *mChunkPtr = '\0'; MOZ_ALWAYS_TRUE(mChunkLengths.append(0)); MOZ_ALWAYS_TRUE(mChunkList.append(mozilla::Move(newChunk))); } void SpliceableJSONWriter::TakeAndSplice(ChunkedJSONWriteFunc* aFunc) { Separator();
--- a/tools/profiler/ProfilerBacktrace.cpp +++ b/tools/profiler/ProfilerBacktrace.cpp @@ -23,11 +23,11 @@ ProfilerBacktrace::~ProfilerBacktrace() delete mProfile; } } void ProfilerBacktrace::StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks) { - mozilla::MutexAutoLock lock(*mProfile->GetMutex()); + ::MutexAutoLock lock(mProfile->GetMutex()); mProfile->StreamJSON(aWriter, aUniqueStacks); }
--- a/tools/profiler/ProfilerMarkers.cpp +++ b/tools/profiler/ProfilerMarkers.cpp @@ -1,20 +1,22 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GeckoProfiler.h" #include "ProfilerBacktrace.h" #include "ProfilerMarkers.h" +#include "SyncProfile.h" +#ifndef SPS_STANDALONE #include "gfxASurface.h" -#include "SyncProfile.h" #include "Layers.h" #include "prprf.h" +#endif ProfilerMarkerPayload::ProfilerMarkerPayload(ProfilerBacktrace* aStack) : mStack(aStack) {} ProfilerMarkerPayload::ProfilerMarkerPayload(const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime, ProfilerBacktrace* aStack) @@ -82,16 +84,17 @@ ProfilerMarkerTracing::StreamPayload(Spl if (GetMetaData() == TRACING_INTERVAL_START) { aWriter.StringProperty("interval", "start"); } else if (GetMetaData() == TRACING_INTERVAL_END) { aWriter.StringProperty("interval", "end"); } } } +#ifndef SPS_STANDALONE GPUMarkerPayload::GPUMarkerPayload( const mozilla::TimeStamp& aCpuTimeStart, const mozilla::TimeStamp& aCpuTimeEnd, uint64_t aGpuTimeStart, uint64_t aGpuTimeEnd) : ProfilerMarkerPayload(aCpuTimeStart, aCpuTimeEnd) , mCpuTimeStart(aCpuTimeStart) @@ -199,8 +202,9 @@ VsyncPayload::VsyncPayload(mozilla::Time } void VsyncPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks) { aWriter.DoubleProperty("vsync", profiler_time(mVsyncTimestamp)); aWriter.StringProperty("category", "VsyncTimestamp"); } +#endif
--- a/tools/profiler/ProfilerMarkers.h +++ b/tools/profiler/ProfilerMarkers.h @@ -3,18 +3,16 @@ * 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 "mozilla/TimeStamp.h" #include "mozilla/Attributes.h" -#include "nsAutoPtr.h" -#include "Units.h" // For ScreenIntPoint namespace mozilla { namespace layers { class Layer; } // layers } // mozilla class SpliceableJSONWriter; @@ -84,16 +82,17 @@ public: UniqueStacks& aUniqueStacks) override; private: const char *mCategory; TracingMetadata mMetaData; }; +#ifndef SPS_STANDALONE #include "gfxASurface.h" class ProfilerMarkerImagePayload : public ProfilerMarkerPayload { public: explicit ProfilerMarkerImagePayload(gfxASurface *aImg); virtual void StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks) override; @@ -131,16 +130,18 @@ public: virtual void StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks) override; private: mozilla::layers::Layer* mLayer; mozilla::gfx::Point mPoint; }; +#include "Units.h" // For ScreenIntPoint + /** * Tracks when touch events are processed by gecko, not when * the touch actually occured in gonk/android. */ class TouchDataPayload : public ProfilerMarkerPayload { public: explicit TouchDataPayload(const mozilla::ScreenIntPoint& aPoint); @@ -182,10 +183,11 @@ public: UniqueStacks& aUniqueStacks) override; private: mozilla::TimeStamp mCpuTimeStart; mozilla::TimeStamp mCpuTimeEnd; uint64_t mGpuTimeStart; uint64_t mGpuTimeEnd; }; +#endif #endif // PROFILER_MARKERS_H
--- a/tools/profiler/PseudoStack.h +++ b/tools/profiler/PseudoStack.h @@ -6,17 +6,19 @@ #ifndef PROFILER_PSEUDO_STACK_H_ #define PROFILER_PSEUDO_STACK_H_ #include "mozilla/ArrayUtils.h" #include <stdint.h> #include "js/ProfilingStack.h" #include <stdlib.h> #include "mozilla/Atomics.h" +#ifndef SPS_STANDALONE #include "nsISupportsImpl.h" +#endif /* we duplicate this code here to avoid header dependencies * which make it more difficult to include in other places */ #if defined(_M_X64) || defined(__x86_64__) #define V8_HOST_ARCH_X64 1 #elif defined(_M_IX86) || defined(__i386__) || defined(__i386) #define V8_HOST_ARCH_IA32 1 #elif defined(__ARMEL__) @@ -315,16 +317,17 @@ public: return mStackPointer == 0; } uint32_t stackSize() const { return sMin(mStackPointer, mozilla::sig_safe_t(mozilla::ArrayLength(mStack))); } void sampleRuntime(JSRuntime* runtime) { +#ifndef SPS_STANDALONE if (mRuntime && !runtime) { // On JS shut down, flush the current buffer as stringifying JIT samples // requires a live JSRuntime. flushSamplerOnJSShutdown(); } mRuntime = runtime; @@ -335,17 +338,19 @@ public: static_assert(sizeof(mStack[0]) == sizeof(js::ProfileEntry), "mStack must be binary compatible with js::ProfileEntry."); js::SetRuntimeProfilingStack(runtime, (js::ProfileEntry*) mStack, (uint32_t*) &mStackPointer, (uint32_t) mozilla::ArrayLength(mStack)); if (mStartJSSampling) enableJSSampling(); +#endif } +#ifndef SPS_STANDALONE void enableJSSampling() { if (mRuntime) { js::EnableRuntimeProfilingStack(mRuntime, true); js::RegisterRuntimeProfilingEventMarker(mRuntime, &ProfilerJSEventMarker); mStartJSSampling = false; } else { mStartJSSampling = true; } @@ -354,29 +359,32 @@ public: if (mStartJSSampling) enableJSSampling(); } void disableJSSampling() { mStartJSSampling = false; if (mRuntime) js::EnableRuntimeProfilingStack(mRuntime, false); } +#endif // Keep a list of active checkpoints StackEntry volatile mStack[1024]; private: // A PseudoStack can only be created via the "create" method. PseudoStack() : mStackPointer(0) , mSleepId(0) , mSleepIdObserved(0) , mSleeping(false) , mRefCnt(1) +#ifndef SPS_STANDALONE , mRuntime(nullptr) +#endif , mStartJSSampling(false) , mPrivacyMode(false) { } // A PseudoStack can only be deleted via deref. ~PseudoStack() { if (mStackPointer != 0) { // We're releasing the pseudostack while it's still in use. @@ -406,18 +414,20 @@ public: // Keeps tack of whether the thread is sleeping or not (1 when sleeping 0 when awake) mozilla::Atomic<int> mSleeping; // This class is reference counted because it must be kept alive by // the ThreadInfo, by the reference from tlsPseudoStack, and by the // current thread when callbacks are in progress. mozilla::Atomic<int> mRefCnt; public: +#ifndef SPS_STANDALONE // The runtime which is being sampled JSRuntime *mRuntime; +#endif // Start JS Profiling when possible bool mStartJSSampling; bool mPrivacyMode; enum SleepState {NOT_SLEEPING, SLEEPING_FIRST, SLEEPING_AGAIN}; // The first time this is called per sleep cycle we return SLEEPING_FIRST // and any other subsequent call within the same sleep cycle we return SLEEPING_AGAIN
--- a/tools/profiler/SyncProfile.cpp +++ b/tools/profiler/SyncProfile.cpp @@ -20,31 +20,28 @@ SyncProfile::~SyncProfile() // SyncProfile owns the ThreadInfo; see NewSyncProfile. ThreadInfo* info = GetThreadInfo(); delete info; } bool SyncProfile::ShouldDestroy() { - GetMutex()->AssertNotCurrentThreadOwns(); - mozilla::MutexAutoLock lock(*GetMutex()); + ::MutexAutoLock lock(GetMutex()); if (mOwnerState == OWNED) { mOwnerState = OWNER_DESTROYING; return true; } mOwnerState = ORPHANED; return false; } void SyncProfile::EndUnwind() { - // Mutex must be held when this is called - GetMutex()->AssertCurrentThreadOwns(); if (mOwnerState != ORPHANED) { mOwnerState = OWNED; } // Save mOwnerState before we release the mutex OwnerState ownerState = mOwnerState; ThreadProfile::EndUnwind(); if (ownerState == ORPHANED) { delete this;
--- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -4,52 +4,58 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <algorithm> #include <string> #include <stdio.h> #include <fstream> #include <sstream> #include "GeckoProfiler.h" +#ifndef SPS_STANDALONE #include "SaveProfileTask.h" +#include "nsThreadUtils.h" +#include "prenv.h" +#include "prtime.h" +#include "nsXULAppAPI.h" +#endif #include "ProfileEntry.h" #include "SyncProfile.h" #include "platform.h" -#include "nsThreadUtils.h" -#include "prenv.h" -#include "prtime.h" #include "shared-libraries.h" #include "mozilla/StackWalk.h" #include "TableTicker.h" -#include "nsXULAppAPI.h" // JSON #include "ProfileJSONWriter.h" +#ifndef SPS_STANDALONE // Meta #include "nsXPCOM.h" #include "nsXPCOMCID.h" #include "nsIHttpProtocolHandler.h" #include "nsServiceManagerUtils.h" #include "nsIXULRuntime.h" #include "nsIXULAppInfo.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "nsIObserverService.h" #include "mozilla/Services.h" #include "PlatformMacros.h" #include "nsTArray.h" +#endif #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) #include "AndroidBridge.h" #endif +#ifndef SPS_STANDALONE // JS #include "jsfriendapi.h" #include "js/ProfilingFrameIterator.h" +#endif #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN)) #define USE_NS_STACKWALK #endif #if defined(XP_WIN) typedef CONTEXT tickcontext_t; #elif defined(LINUX) @@ -65,21 +71,23 @@ pid_t gettid(); #if defined(SPS_ARCH_arm) && defined(MOZ_WIDGET_GONK) // Should also work on other Android and ARM Linux, but not tested there yet. #define USE_EHABI_STACKWALK #endif #ifdef USE_EHABI_STACKWALK #include "EHABIStackWalk.h" #endif +#ifndef SPS_STANDALONE #if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux) # define USE_LUL_STACKWALK # include "LulMain.h" # include "platform-linux-lul.h" #endif +#endif using std::string; using namespace mozilla; #ifndef MAXPATHLEN #ifdef PATH_MAX #define MAXPATHLEN PATH_MAX #elif defined(MAX_PATH) @@ -98,17 +106,71 @@ using namespace mozilla; #else # define VALGRIND_MAKE_MEM_DEFINED(_addr,_len) ((void)0) #endif /////////////////////////////////////////////////////////////////////// // BEGIN SaveProfileTask et al -std::string GetSharedLibraryInfoString(); +static void +AddSharedLibraryInfoToStream(std::ostream& aStream, const SharedLibrary& aLib) +{ + aStream << "{"; + aStream << "\"start\":" << aLib.GetStart(); + aStream << ",\"end\":" << aLib.GetEnd(); + aStream << ",\"offset\":" << aLib.GetOffset(); + aStream << ",\"name\":\"" << aLib.GetName() << "\""; + const std::string &breakpadId = aLib.GetBreakpadId(); + aStream << ",\"breakpadId\":\"" << breakpadId << "\""; +#ifdef XP_WIN + // FIXME: remove this XP_WIN code when the profiler plugin has switched to + // using breakpadId. + std::string pdbSignature = breakpadId.substr(0, 32); + std::string pdbAgeStr = breakpadId.substr(32, breakpadId.size() - 1); + + std::stringstream stream; + stream << pdbAgeStr; + + unsigned pdbAge; + stream << std::hex; + stream >> pdbAge; + +#ifdef DEBUG + std::ostringstream oStream; + oStream << pdbSignature << std::hex << std::uppercase << pdbAge; + MOZ_ASSERT(breakpadId == oStream.str()); +#endif + + aStream << ",\"pdbSignature\":\"" << pdbSignature << "\""; + aStream << ",\"pdbAge\":" << pdbAge; + aStream << ",\"pdbName\":\"" << aLib.GetName() << "\""; +#endif + aStream << "}"; +} + +std::string +GetSharedLibraryInfoStringInternal() +{ + SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf(); + if (info.GetSize() == 0) + return "[]"; + + std::ostringstream os; + os << "["; + AddSharedLibraryInfoToStream(os, info.GetEntry(0)); + + for (size_t i = 1; i < info.GetSize(); i++) { + os << ","; + AddSharedLibraryInfoToStream(os, info.GetEntry(i)); + } + + os << "]"; + return os.str(); +} static bool hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) { for(size_t i = 0; i < aFeatureCount; i++) { if (strcmp(aFeatures[i], aFeature) == 0) return true; } return false; @@ -155,17 +217,17 @@ TableTicker::TableTicker(double aInterva for (uint32_t i = 0; i < aFilterCount; ++i) { mThreadNameFilters[i] = aThreadNameFilters[i]; } bool ignore; sStartTime = mozilla::TimeStamp::ProcessCreation(ignore); { - mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); + ::MutexAutoLock lock(*sRegisteredThreadsMutex); // Create ThreadProfile for each registered thread for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); RegisterThread(info); } @@ -183,17 +245,17 @@ TableTicker::~TableTicker() { if (IsActive()) Stop(); SetActiveSampler(nullptr); // Destroy ThreadProfile for all threads { - mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); + ::MutexAutoLock lock(*sRegisteredThreadsMutex); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); ThreadProfile* profile = info->Profile(); if (profile) { delete profile; info->SetProfile(nullptr); } @@ -212,20 +274,22 @@ TableTicker::~TableTicker() } void TableTicker::HandleSaveRequest() { if (!mSaveRequested) return; mSaveRequested = false; +#ifndef SPS_STANDALONE // TODO: Use use the ipc/chromium Tasks here to support processes // without XPCOM. nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask(); NS_DispatchToMainThread(runnable); +#endif } void TableTicker::DeleteExpiredMarkers() { mBuffer->deleteExpiredStoredMarkers(); } void TableTicker::StreamTaskTracer(SpliceableJSONWriter& aWriter) @@ -234,17 +298,17 @@ void TableTicker::StreamTaskTracer(Splic aWriter.StartArrayProperty("data"); nsAutoPtr<nsTArray<nsCString>> data(mozilla::tasktracer::GetLoggedData(sStartTime)); for (uint32_t i = 0; i < data->Length(); ++i) { aWriter.StringElement((data->ElementAt(i)).get()); } aWriter.EndArray(); aWriter.StartArrayProperty("threads"); - mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); + ::MutexAutoLock lock(*sRegisteredThreadsMutex); for (size_t i = 0; i < sRegisteredThreads->size(); i++) { // Thread meta data ThreadInfo* info = sRegisteredThreads->at(i); aWriter.StartObjectElement(); if (XRE_GetProcessType() == GeckoProcessType_Plugin) { // TODO Add the proper plugin name aWriter.StringProperty("name", "Plugin"); } else { @@ -260,21 +324,23 @@ void TableTicker::StreamTaskTracer(Splic } void TableTicker::StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter) { aWriter.IntProperty("version", 3); aWriter.DoubleProperty("interval", interval()); aWriter.IntProperty("stackwalk", mUseStackWalk); - aWriter.IntProperty("processType", XRE_GetProcessType()); +#ifndef SPS_STANDALONE mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - sStartTime; aWriter.DoubleProperty("startTime", static_cast<double>(PR_Now()/1000.0 - delta.ToMilliseconds())); + aWriter.IntProperty("processType", XRE_GetProcessType()); + nsresult res; nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res); if (!NS_FAILED(res)) { nsAutoCString string; res = http->GetPlatform(string); if (!NS_FAILED(res)) aWriter.StringProperty("platform", string.Data()); @@ -304,25 +370,27 @@ void TableTicker::StreamMetaJSCustomObje nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1"); if (appInfo) { nsAutoCString string; res = appInfo->GetName(string); if (!NS_FAILED(res)) aWriter.StringProperty("product", string.Data()); } +#endif } void TableTicker::ToStreamAsJSON(std::ostream& stream, double aSinceTime) { SpliceableJSONWriter b(mozilla::MakeUnique<OStreamJSONWriteFunc>(stream)); StreamJSON(b, aSinceTime); } -JSObject* TableTicker::ToJSObject(JSContext* aCx, double aSinceTime) +#ifndef SPS_STANDALONE +JSObject* TableTicker::ToJSObject(JSContext *aCx, double aSinceTime) { JS::RootedValue val(aCx); { UniquePtr<char[]> buf = ToJSON(aSinceTime); NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get())); bool rv = JS_ParseJSON(aCx, static_cast<const char16_t*>(js_string.get()), js_string.Length(), &val); if (!rv) { @@ -348,16 +416,17 @@ JSObject* TableTicker::ToJSObject(JSCont } } } #endif } } return &val.toObject(); } +#endif UniquePtr<char[]> TableTicker::ToJSON(double aSinceTime) { SpliceableChunkedJSONWriter b; StreamJSON(b, aSinceTime); return b.WriteFunc()->CopyData(); } @@ -395,17 +464,16 @@ void SubProcessCallback(const char* aPro closure->mWriter->StringElement(aProfile); } #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) static void BuildJavaThreadJSObject(SpliceableJSONWriter& aWriter) { - aWriter.Start(SpliceableJSONWriter::SingleLineStyle); aWriter.StringProperty("name", "Java Main Thread"); aWriter.StartArrayProperty("samples"); // for each sample for (int sampleId = 0; true; sampleId++) { bool firstRun = true; // for each frame @@ -440,26 +508,25 @@ void BuildJavaThreadJSObject(SpliceableJ } // if we found no frames for this sample, we are done if (firstRun) { break; } } aWriter.EndArray(); - aWriter.End(); } #endif void TableTicker::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime) { aWriter.Start(SpliceableJSONWriter::SingleLineStyle); { // Put shared library info - aWriter.StringProperty("libs", GetSharedLibraryInfoString().c_str()); + aWriter.StringProperty("libs", GetSharedLibraryInfoStringInternal().c_str()); // Put meta data aWriter.StartObjectProperty("meta"); StreamMetaJSCustomObject(aWriter); aWriter.EndObject(); // Data of TaskTracer doesn't belong in the circular buffer. if (TaskTracer()) { @@ -468,32 +535,33 @@ void TableTicker::StreamJSON(SpliceableJ } // Lists the samples for each ThreadProfile aWriter.StartArrayProperty("threads"); { SetPaused(true); { - mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); + ::MutexAutoLock lock(*sRegisteredThreadsMutex); for (size_t i = 0; i < sRegisteredThreads->size(); i++) { // Thread not being profiled, skip it if (!sRegisteredThreads->at(i)->Profile()) continue; // Note that we intentionally include ThreadProfile which // have been marked for pending delete. - MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex()); + ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex()); sRegisteredThreads->at(i)->Profile()->StreamJSON(aWriter, aSinceTime); } } +#ifndef SPS_STANDALONE if (Sampler::CanNotifyObservers()) { // Send a event asking any subprocesses (plugins) to // give us their information SubprocessClosure closure(&aWriter); nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) { nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure); os->NotifyObservers(pse, "profiler-subprocess", nullptr); @@ -504,58 +572,63 @@ void TableTicker::StreamJSON(SpliceableJ if (ProfileJava()) { mozilla::widget::GeckoJavaSampler::PauseJavaProfiling(); BuildJavaThreadJSObject(aWriter); mozilla::widget::GeckoJavaSampler::UnpauseJavaProfiling(); } #endif +#endif SetPaused(false); } aWriter.EndArray(); } aWriter.End(); } void TableTicker::FlushOnJSShutdown(JSRuntime* aRuntime) { +#ifndef SPS_STANDALONE SetPaused(true); { - mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); + ::MutexAutoLock lock(*sRegisteredThreadsMutex); for (size_t i = 0; i < sRegisteredThreads->size(); i++) { // Thread not being profiled, skip it. if (!sRegisteredThreads->at(i)->Profile() || sRegisteredThreads->at(i)->IsPendingDelete()) { continue; } // Thread not profiling the runtime that's going away, skip it. if (sRegisteredThreads->at(i)->Profile()->GetPseudoStack()->mRuntime != aRuntime) { continue; } - MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex()); + ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex()); sRegisteredThreads->at(i)->Profile()->FlushSamplesAndMarkers(); } } SetPaused(false); +#endif } void PseudoStack::flushSamplerOnJSShutdown() { +#ifndef SPS_STANDALONE MOZ_ASSERT(mRuntime); TableTicker* t = tlsTicker.get(); if (t) { t->FlushOnJSShutdown(mRuntime); } +#endif } // END SaveProfileTask et al //////////////////////////////////////////////////////////////////////// static void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr) { @@ -590,16 +663,17 @@ void addPseudoEntry(volatile StackEntry // First entry has tagName 's' (start) // Check for magic pointer bit 1 to indicate copy const char* sampleLabel = entry.label(); if (entry.isCopyLabel()) { // Store the string using 1 or more 'd' (dynamic) tags // that will happen to the preceding tag addDynamicTag(aProfile, 'c', sampleLabel); +#ifndef SPS_STANDALONE if (entry.isJs()) { if (!entry.pc()) { // The JIT only allows the top-most entry to have a nullptr pc MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]); // If stack-walking was disabled, then that's just unfortunate if (lastpc) { jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(), lastpc); @@ -608,16 +682,17 @@ void addPseudoEntry(volatile StackEntry } } } else { lineno = JS_PCToLineNumber(entry.script(), entry.pc()); } } else { lineno = entry.line(); } +#endif } else { aProfile.addTag(ProfileEntry('c', sampleLabel)); // XXX: Bug 1010578. Don't assume a CPP entry and try to get the // line for js entries as well. if (entry.isCpp()) { lineno = entry.line(); } @@ -676,16 +751,17 @@ void mergeStacksIntoProfile(ThreadProfil // entries. uint32_t startBufferGen; if (aSample->isSamplingCurrentThread) { startBufferGen = UINT32_MAX; } else { startBufferGen = aProfile.bufferGeneration(); } uint32_t jsCount = 0; +#ifndef SPS_STANDALONE JS::ProfilingFrameIterator::Frame jsFrames[1000]; // Only walk jit stack if profiling frame iterator is turned on. if (pseudoStack->mRuntime && JS::IsProfilingEnabledForRuntime(pseudoStack->mRuntime)) { AutoWalkJSStack autoWalkJSStack; const uint32_t maxFrames = mozilla::ArrayLength(jsFrames); if (aSample && autoWalkJSStack.walkAllowed) { JS::ProfilingFrameIterator::RegisterState registerState; @@ -709,16 +785,17 @@ void mergeStacksIntoProfile(ThreadProfil mozilla::Maybe<JS::ProfilingFrameIterator::Frame> frame = jsIter.getPhysicalFrameWithoutLabel(); if (frame.isSome()) jsFrames[jsCount++] = frame.value(); } } } } +#endif // Start the sample with a root entry. aProfile.addTag(ProfileEntry('s', "(root)")); // While the pseudo-stack array is ordered oldest-to-youngest, the JS and // native arrays are ordered youngest-to-oldest. We must add frames to // aProfile oldest-to-youngest. Thus, iterate over the pseudo-stack forwards // and JS and native arrays backwards. Note: this means the terminating @@ -738,34 +815,38 @@ void mergeStacksIntoProfile(ThreadProfil uint8_t *nativeStackAddr = nullptr; if (pseudoIndex != pseudoCount) { volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex]; if (pseudoFrame.isCpp()) lastPseudoCppStackAddr = (uint8_t *) pseudoFrame.stackAddress(); +#ifndef SPS_STANDALONE // Skip any pseudo-stack JS frames which are marked isOSR // Pseudostack frames are marked isOSR when the JS interpreter // enters a jit frame on a loop edge (via on-stack-replacement, // or OSR). To avoid both the pseudoframe and jit frame being // recorded (and showing up twice), the interpreter marks the // interpreter pseudostack entry with the OSR flag to ensure that // it doesn't get counted. if (pseudoFrame.isJs() && pseudoFrame.isOSR()) { pseudoIndex++; continue; } +#endif MOZ_ASSERT(lastPseudoCppStackAddr); pseudoStackAddr = lastPseudoCppStackAddr; } +#ifndef SPS_STANDALONE if (jsIndex >= 0) jsStackAddr = (uint8_t *) jsFrames[jsIndex].stackAddress; +#endif if (nativeIndex >= 0) nativeStackAddr = (uint8_t *) aNativeStack.sp_array[nativeIndex]; // If there's a native stack entry which has the same SP as a // pseudo stack entry, pretend we didn't see the native stack // entry. Ditto for a native stack entry which has the same SP as // a JS stack entry. In effect this means pseudo or JS entries @@ -788,16 +869,17 @@ void mergeStacksIntoProfile(ThreadProfil if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) { MOZ_ASSERT(pseudoIndex < pseudoCount); volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex]; addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr); pseudoIndex++; continue; } +#ifndef SPS_STANDALONE // Check to see if JS jit stack frame is top-most if (jsStackAddr > nativeStackAddr) { MOZ_ASSERT(jsIndex >= 0); const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex]; // Stringifying non-asm.js JIT frames is delayed until streaming // time. To re-lookup the entry in the JitcodeGlobalTable, we need to // store the JIT code address ('J') in the circular buffer. @@ -818,40 +900,43 @@ void mergeStacksIntoProfile(ThreadProfil MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion || jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline); aProfile.addTag(ProfileEntry('J', jsFrames[jsIndex].returnAddress)); } jsIndex--; continue; } +#endif // If we reach here, there must be a native stack entry and it must be the // greatest entry. if (nativeStackAddr) { MOZ_ASSERT(nativeIndex >= 0); aProfile .addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex])); } if (nativeIndex >= 0) { nativeIndex--; } } +#ifndef SPS_STANDALONE // Update the JS runtime with the current profile sample buffer generation. // // Do not do this for synchronous sampling, which create their own // ProfileBuffers. if (!aSample->isSamplingCurrentThread && pseudoStack->mRuntime) { MOZ_ASSERT(aProfile.bufferGeneration() >= startBufferGen); uint32_t lapCount = aProfile.bufferGeneration() - startBufferGen; JS::UpdateJSRuntimeProfilerSampleBufferGen(pseudoStack->mRuntime, aProfile.bufferGeneration(), lapCount); } +#endif } #ifdef USE_NS_STACKWALK static void StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure) { NativeStack* nativeStack = static_cast<NativeStack*>(aClosure); @@ -1138,20 +1223,22 @@ void TableTicker::InplaceTick(TickSample ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers(); while (pendingMarkersList && pendingMarkersList->peek()) { ProfilerMarker* marker = pendingMarkersList->popHead(); currThreadProfile.addStoredMarker(marker); currThreadProfile.addTag(ProfileEntry('m', marker)); } } +#ifndef SPS_STANDALONE if (sample && currThreadProfile.GetThreadResponsiveness()->HasData()) { mozilla::TimeDuration delta = currThreadProfile.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp); currThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds())); } +#endif // rssMemory is equal to 0 when we are not recording. if (sample && sample->rssMemory != 0) { currThreadProfile.addTag(ProfileEntry('R', static_cast<double>(sample->rssMemory))); } // ussMemory is equal to 0 when we are not recording. if (sample && sample->ussMemory != 0) { @@ -1177,17 +1264,17 @@ SyncProfile* NewSyncProfile() { PseudoStack* stack = tlsPseudoStack.get(); if (!stack) { MOZ_ASSERT(stack); return nullptr; } Thread::tid_t tid = Thread::GetCurrentId(); - ThreadInfo* info = new ThreadInfo("SyncProfile", tid, NS_IsMainThread(), stack, nullptr); + ThreadInfo* info = new ThreadInfo("SyncProfile", tid, false, stack, nullptr); SyncProfile* profile = new SyncProfile(info, GET_BACKTRACE_DEFAULT_ENTRY); return profile; } } // anonymous namespace SyncProfile* TableTicker::GetBacktrace() {
--- a/tools/profiler/TableTicker.h +++ b/tools/profiler/TableTicker.h @@ -3,19 +3,20 @@ * 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 #include "platform.h" #include "ProfileEntry.h" -#include "mozilla/Mutex.h" #include "mozilla/Vector.h" +#ifndef SPS_STANDALONE #include "IntelPowerGadget.h" +#endif #ifdef MOZ_TASK_TRACER #include "GeckoTaskTracer.h" #endif namespace mozilla { class ProfileGatherer; } // namespace mozilla @@ -78,32 +79,34 @@ class TableTicker: public Sampler { } virtual void HandleSaveRequest() override; virtual void DeleteExpiredMarkers() override; ThreadProfile* GetPrimaryThreadProfile() { if (!mPrimaryThreadProfile) { - mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); + ::MutexAutoLock lock(*sRegisteredThreadsMutex); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (info->IsMainThread() && !info->IsPendingDelete()) { mPrimaryThreadProfile = info->Profile(); break; } } } return mPrimaryThreadProfile; } void ToStreamAsJSON(std::ostream& stream, double aSinceTime = 0); - virtual JSObject *ToJSObject(JSContext* aCx, double aSinceTime = 0); +#ifndef SPS_STANDALONE + virtual JSObject *ToJSObject(JSContext *aCx, double aSinceTime = 0); +#endif mozilla::UniquePtr<char[]> ToJSON(double aSinceTime = 0); virtual void ToJSObjectAsync(double aSinceTime = 0, mozilla::dom::Promise* aPromise = 0); void StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter); void StreamTaskTracer(SpliceableJSONWriter& aWriter); void FlushOnJSShutdown(JSRuntime* aRuntime); bool ProfileJS() const { return mProfileJS; } bool ProfileJava() const { return mProfileJava; } bool ProfileGPU() const { return mProfileGPU; } @@ -127,17 +130,17 @@ protected: // Not implemented on platforms which do not support backtracing void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample); void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime); // This represent the application's main thread (SAMPLER_INIT) ThreadProfile* mPrimaryThreadProfile; - nsRefPtr<ProfileBuffer> mBuffer; + mozilla::RefPtr<ProfileBuffer> mBuffer; bool mSaveRequested; bool mAddLeafAddresses; bool mUseStackWalk; bool mProfileJS; bool mProfileGPU; bool mProfileThreads; bool mProfileJava; bool mProfilePower;
--- a/tools/profiler/nsProfiler.cpp +++ b/tools/profiler/nsProfiler.cpp @@ -129,70 +129,22 @@ nsProfiler::GetProfile(double aSinceTime char *profileStr = static_cast<char *> (nsMemory::Clone(profile.get(), (len + 1) * sizeof(char))); profileStr[len] = '\0'; *aProfile = profileStr; } return NS_OK; } -static void -AddSharedLibraryInfoToStream(std::ostream& aStream, const SharedLibrary& aLib) -{ - aStream << "{"; - aStream << "\"start\":" << aLib.GetStart(); - aStream << ",\"end\":" << aLib.GetEnd(); - aStream << ",\"offset\":" << aLib.GetOffset(); - aStream << ",\"name\":\"" << aLib.GetName() << "\""; - const std::string &breakpadId = aLib.GetBreakpadId(); - aStream << ",\"breakpadId\":\"" << breakpadId << "\""; -#ifdef XP_WIN - // FIXME: remove this XP_WIN code when the profiler plugin has switched to - // using breakpadId. - std::string pdbSignature = breakpadId.substr(0, 32); - std::string pdbAgeStr = breakpadId.substr(32, breakpadId.size() - 1); - - std::stringstream stream; - stream << pdbAgeStr; - - unsigned pdbAge; - stream << std::hex; - stream >> pdbAge; - -#ifdef DEBUG - std::ostringstream oStream; - oStream << pdbSignature << std::hex << std::uppercase << pdbAge; - MOZ_ASSERT(breakpadId == oStream.str()); -#endif - - aStream << ",\"pdbSignature\":\"" << pdbSignature << "\""; - aStream << ",\"pdbAge\":" << pdbAge; - aStream << ",\"pdbName\":\"" << aLib.GetName() << "\""; -#endif - aStream << "}"; -} +std::string GetSharedLibraryInfoStringInternal(); std::string GetSharedLibraryInfoString() { - SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf(); - if (info.GetSize() == 0) - return "[]"; - - std::ostringstream os; - os << "["; - AddSharedLibraryInfoToStream(os, info.GetEntry(0)); - - for (size_t i = 1; i < info.GetSize(); i++) { - os << ","; - AddSharedLibraryInfoToStream(os, info.GetEntry(i)); - } - - os << "]"; - return os.str(); + return GetSharedLibraryInfoStringInternal(); } NS_IMETHODIMP nsProfiler::GetSharedLibraryInformation(nsAString& aOutString) { aOutString.Assign(NS_ConvertUTF8toUTF16(GetSharedLibraryInfoString().c_str())); return NS_OK; }
--- a/tools/profiler/platform-linux.cc +++ b/tools/profiler/platform-linux.cc @@ -315,17 +315,17 @@ static void* SignalSender(void* arg) { TimeDuration lastSleepOverhead = 0; TimeStamp sampleStart = TimeStamp::Now(); while (SamplerRegistry::sampler->IsActive()) { SamplerRegistry::sampler->HandleSaveRequest(); SamplerRegistry::sampler->DeleteExpiredMarkers(); if (!SamplerRegistry::sampler->IsPaused()) { - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); std::vector<ThreadInfo*> threads = SamplerRegistry::sampler->GetRegisteredThreads(); bool isFirstProfiledThread = true; for (uint32_t i = 0; i < threads.size(); i++) { ThreadInfo* info = threads[i]; // This will be null if we're not interested in profiling this thread. @@ -508,17 +508,17 @@ UpdateThreadId(void* aThreadInfo) { bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread, void* stackTop) { if (!Sampler::sRegisteredThreadsMutex) return false; - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); int id = gettid(); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (info->ThreadId() == id && !info->IsPendingDelete()) { // Thread already registered. This means the first unregister will be // too early. ASSERT(false); @@ -556,17 +556,17 @@ bool Sampler::RegisterCurrentThread(cons void Sampler::UnregisterCurrentThread() { if (!Sampler::sRegisteredThreadsMutex) return; tlsStackTop.set(nullptr); - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); int id = gettid(); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (info->ThreadId() == id && !info->IsPendingDelete()) { if (profiler_is_active()) { // We still want to show the results of this thread if you
--- a/tools/profiler/platform-macos.cc +++ b/tools/profiler/platform-macos.cc @@ -24,25 +24,30 @@ #include <sys/types.h> #include <sys/sysctl.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <math.h> +#ifndef SPS_STANDALONE #include "ThreadResponsiveness.h" #include "nsThreadUtils.h" +// Memory profile +#include "nsMemoryReporterManager.h" +#endif + #include "platform.h" #include "TableTicker.h" #include "mozilla/TimeStamp.h" -// Memory profile -#include "nsMemoryReporterManager.h" +using mozilla::TimeStamp; +using mozilla::TimeDuration; // this port is based off of v8 svn revision 9837 // XXX: this is a very stubbed out implementation // that only supports a single Sampler struct SamplerRegistry { static void AddActiveSampler(Sampler *sampler) { ASSERT(!SamplerRegistry::sampler); @@ -200,34 +205,36 @@ class SamplerThread : public Thread { // Implement Thread::Run(). virtual void Run() { TimeDuration lastSleepOverhead = 0; TimeStamp sampleStart = TimeStamp::Now(); while (SamplerRegistry::sampler->IsActive()) { SamplerRegistry::sampler->DeleteExpiredMarkers(); if (!SamplerRegistry::sampler->IsPaused()) { - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); std::vector<ThreadInfo*> threads = SamplerRegistry::sampler->GetRegisteredThreads(); bool isFirstProfiledThread = true; for (uint32_t i = 0; i < threads.size(); i++) { ThreadInfo* info = threads[i]; // This will be null if we're not interested in profiling this thread. if (!info->Profile() || info->IsPendingDelete()) continue; PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); if (sleeping == PseudoStack::SLEEPING_AGAIN) { info->Profile()->DuplicateLastSample(); continue; } +#ifndef SPS_STANDALONE info->Profile()->GetThreadResponsiveness()->Update(); +#endif ThreadProfile* thread_profile = info->Profile(); SampleContext(SamplerRegistry::sampler, thread_profile, isFirstProfiledThread); isFirstProfiledThread = false; } } @@ -246,24 +253,25 @@ class SamplerThread : public Thread { bool isFirstProfiledThread) { thread_act_t profiled_thread = thread_profile->GetPlatformData()->profiled_thread(); TickSample sample_obj; TickSample* sample = &sample_obj; + // Unique Set Size is not supported on Mac. + sample->ussMemory = 0; + sample->rssMemory = 0; + +#ifndef SPS_STANDALONE if (isFirstProfiledThread && Sampler::GetActiveSampler()->ProfileMemory()) { sample->rssMemory = nsMemoryReporterManager::ResidentFast(); - } else { - sample->rssMemory = 0; } - - // Unique Set Size is not supported on Mac. - sample->ussMemory = 0; +#endif // We're using thread_suspend on OS X because pthread_kill (which is what // we're using on Linux) has less consistent performance and causes // strange crashes, see bug 1166778 and bug 1166808. if (KERN_SUCCESS != thread_suspend(profiled_thread)) return; #if V8_HOST_ARCH_X64 @@ -365,17 +373,17 @@ Thread::GetCurrentId() bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread, void* stackTop) { if (!Sampler::sRegisteredThreadsMutex) return false; - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); int id = gettid(); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (info->ThreadId() == id && !info->IsPendingDelete()) { // Thread already registered. This means the first unregister will be // too early. ASSERT(false); @@ -399,17 +407,17 @@ bool Sampler::RegisterCurrentThread(cons void Sampler::UnregisterCurrentThread() { if (!Sampler::sRegisteredThreadsMutex) return; tlsStackTop.set(nullptr); - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); int id = gettid(); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (info->ThreadId() == id && !info->IsPendingDelete()) { if (profiler_is_active()) { // We still want to show the results of this thread if you
--- a/tools/profiler/platform-win32.cc +++ b/tools/profiler/platform-win32.cc @@ -117,17 +117,17 @@ class SamplerThread : public Thread { // adjust the resolution to match. if (interval_ < 10) ::timeBeginPeriod(interval_); while (sampler_->IsActive()) { sampler_->DeleteExpiredMarkers(); if (!sampler_->IsPaused()) { - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); std::vector<ThreadInfo*> threads = sampler_->GetRegisteredThreads(); bool isFirstProfiledThread = true; for (uint32_t i = 0; i < threads.size(); i++) { ThreadInfo* info = threads[i]; // This will be null if we're not interested in profiling this thread. if (!info->Profile() || info->IsPendingDelete()) @@ -310,17 +310,17 @@ void OS::Sleep(int milliseconds) { bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread, void* stackTop) { if (!Sampler::sRegisteredThreadsMutex) return false; - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); int id = GetCurrentThreadId(); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (info->ThreadId() == id && !info->IsPendingDelete()) { // Thread already registered. This means the first unregister will be // too early. @@ -345,17 +345,17 @@ bool Sampler::RegisterCurrentThread(cons void Sampler::UnregisterCurrentThread() { if (!Sampler::sRegisteredThreadsMutex) return; tlsStackTop.set(nullptr); - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); int id = GetCurrentThreadId(); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (info->ThreadId() == id && !info->IsPendingDelete()) { if (profiler_is_active()) { // We still want to show the results of this thread if you
--- a/tools/profiler/platform.cpp +++ b/tools/profiler/platform.cpp @@ -2,44 +2,51 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <ostream> #include <fstream> #include <sstream> #include <errno.h> -#include "ProfilerIOInterposeObserver.h" #include "platform.h" #include "PlatformMacros.h" -#include "prenv.h" #include "mozilla/ArrayUtils.h" +#include "mozilla/UniquePtr.h" +#include "GeckoProfiler.h" +#ifndef SPS_STANDALONE +#include "ProfilerIOInterposeObserver.h" #include "mozilla/StaticPtr.h" +#endif #include "mozilla/ThreadLocal.h" #include "mozilla/TimeStamp.h" #include "PseudoStack.h" #include "TableTicker.h" +#ifndef SPS_STANDALONE #include "nsIObserverService.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" +#include "nsXULAppAPI.h" #include "nsProfilerStartParams.h" #include "mozilla/Services.h" #include "nsThreadUtils.h" +#endif #include "ProfilerMarkers.h" -#include "nsXULAppAPI.h" #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) #include "AndroidBridge.h" #endif +#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" #endif +#endif mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack; mozilla::ThreadLocal<TableTicker *> tlsTicker; mozilla::ThreadLocal<void *> tlsStackTop; // We need to track whether we've been initialized otherwise // we end up using tlsStack without initializing it. // Because tlsStack is totally opaque to us we can't reuse // it as the flag itself. @@ -69,46 +76,48 @@ const char* PROFILER_FEATURES = "MOZ_PRO * to know are associated with different events */ // Values harvested from env vars, that control the profiler. static int sUnwindInterval; /* in milliseconds */ static int sUnwindStackScan; /* max # of dubious frames allowed */ static int sProfileEntries; /* how many entries do we store? */ std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = nullptr; -mozilla::Mutex* Sampler::sRegisteredThreadsMutex = nullptr; +mozilla::UniquePtr< ::Mutex> Sampler::sRegisteredThreadsMutex; TableTicker* Sampler::sActiveSampler; +#ifndef SPS_STANDALONE static mozilla::StaticAutoPtr<mozilla::ProfilerIOInterposeObserver> sInterposeObserver; +#endif // The name that identifies the gecko thread for calls to // profiler_register_thread. static const char * gGeckoThreadName = "GeckoMain"; void Sampler::Startup() { sRegisteredThreads = new std::vector<ThreadInfo*>(); - sRegisteredThreadsMutex = new mozilla::Mutex("sRegisteredThreads mutex"); + sRegisteredThreadsMutex = OS::CreateMutex("sRegisteredThreads mutex"); // We could create the sLUL object and read unwind info into it at // this point. That would match the lifetime implied by destruction // of it in Sampler::Shutdown just below. However, that gives a big // delay on startup, even if no profiling is actually to be done. // So, instead, sLUL is created on demand at the first call to // Sampler::Start. } void Sampler::Shutdown() { while (sRegisteredThreads->size() > 0) { delete sRegisteredThreads->back(); sRegisteredThreads->pop_back(); } - delete sRegisteredThreadsMutex; + sRegisteredThreadsMutex = nullptr; delete sRegisteredThreads; // UnregisterThread can be called after shutdown in XPCShell. Thus // we need to point to null to ignore such a call after shutdown. sRegisteredThreadsMutex = nullptr; sRegisteredThreads = nullptr; #if defined(USE_LUL_STACKWALK) @@ -127,17 +136,19 @@ ThreadInfo::ThreadInfo(const char* 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; @@ -240,17 +251,17 @@ void ProfilerMarker::StreamJSON(Spliceab enum class ProfilerVerbosity : int8_t { UNCHECKED, NOTVERBOSE, VERBOSE }; // Raced on, potentially static ProfilerVerbosity profiler_verbosity = ProfilerVerbosity::UNCHECKED; bool moz_profiler_verbose() { if (profiler_verbosity == ProfilerVerbosity::UNCHECKED) { - if (PR_GetEnv("MOZ_PROFILER_VERBOSE") != nullptr) + if (getenv("MOZ_PROFILER_VERBOSE") != nullptr) profiler_verbosity = ProfilerVerbosity::VERBOSE; else profiler_verbosity = ProfilerVerbosity::NOTVERBOSE; } return profiler_verbosity == ProfilerVerbosity::VERBOSE; } @@ -315,21 +326,21 @@ bool is_native_unwinding_avail() { // Read env vars at startup, so as to set: // sUnwindInterval, sProfileEntries, sUnwindStackScan. void read_profiler_env_vars() { /* Set defaults */ sUnwindInterval = 0; /* We'll have to look elsewhere */ sProfileEntries = 0; - const char* interval = PR_GetEnv(PROFILER_INTERVAL); - const char* entries = PR_GetEnv(PROFILER_ENTRIES); - const char* scanCount = PR_GetEnv(PROFILER_STACK); + const char* interval = getenv(PROFILER_INTERVAL); + const char* entries = getenv(PROFILER_ENTRIES); + const char* scanCount = getenv(PROFILER_STACK); - if (PR_GetEnv(PROFILER_HELP)) { + if (getenv(PROFILER_HELP)) { // Enable verbose output moz_profiler_set_verbosity(ProfilerVerbosity::VERBOSE); profiler_usage(); // Now force the next enquiry of moz_profiler_verbose to re-query // env var MOZ_PROFILER_VERBOSE. moz_profiler_set_verbosity(ProfilerVerbosity::UNCHECKED); } @@ -408,16 +419,17 @@ void set_tls_stack_top(void* stackTop) bool is_main_thread_name(const char* aName) { if (!aName) { return false; } return strcmp(aName, gGeckoThreadName) == 0; } +#ifndef SPS_STANDALONE #ifdef HAVE_VA_COPY #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar) #elif defined(HAVE_VA_LIST_AS_ARRAY) #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0] #else #define VARARGS_ASSIGN(foo, bar) (foo) = (bar) #endif @@ -446,27 +458,32 @@ mozilla_sampler_log(const char *fmt, va_ // EVENT_BACKTRACE could be used to get a source // for all log events. This could be a runtime // flag later. profiler_tracing("log", heapBuf, TRACING_EVENT); delete[] heapBuf; } } } +#endif //////////////////////////////////////////////////////////////////////// // BEGIN externally visible functions void mozilla_sampler_init(void* stackTop) { sInitCount++; if (stack_key_initialized) return; +#ifdef SPS_STANDALONE + mozilla::TimeStamp::Startup(); +#endif + LOG("BEGIN mozilla_sampler_init"); if (!tlsPseudoStack.init() || !tlsTicker.init() || !tlsStackTop.init()) { LOG("Failed to init."); return; } bool ignore; sStartTime = mozilla::TimeStamp::ProcessCreation(ignore); @@ -484,22 +501,24 @@ void mozilla_sampler_init(void* stackTop // Read interval settings from MOZ_PROFILER_INTERVAL and stack-scan // threshhold from MOZ_PROFILER_STACK_SCAN. read_profiler_env_vars(); // platform specific initialization OS::Startup(); +#ifndef SPS_STANDALONE set_stderr_callback(mozilla_sampler_log); +#endif // We can't open pref so we use an environment variable // to know if we should trigger the profiler on startup // NOTE: Default - const char *val = PR_GetEnv("MOZ_PROFILER_STARTUP"); + const char *val = getenv("MOZ_PROFILER_STARTUP"); if (!val || !*val) { return; } const char* features[] = {"js" , "leaf" , "threads" #if defined(XP_WIN) || defined(XP_MACOSX) \ @@ -525,33 +544,39 @@ void mozilla_sampler_shutdown() sInitCount--; if (sInitCount > 0) return; // Save the profile on shutdown if requested. TableTicker *t = tlsTicker.get(); if (t) { - const char *val = PR_GetEnv("MOZ_PROFILER_SHUTDOWN"); + const char *val = getenv("MOZ_PROFILER_SHUTDOWN"); if (val) { std::ofstream stream; stream.open(val); if (stream.is_open()) { t->ToStreamAsJSON(stream); stream.close(); } } } profiler_stop(); +#ifndef SPS_STANDALONE set_stderr_callback(nullptr); +#endif Sampler::Shutdown(); +#ifdef SPS_STANDALONE + mozilla::TimeStamp::Shutdown(); +#endif + PseudoStack *stack = tlsPseudoStack.get(); stack->deref(); tlsPseudoStack.set(nullptr); } void mozilla_sampler_save() { TableTicker *t = tlsTicker.get(); @@ -570,17 +595,18 @@ mozilla::UniquePtr<char[]> mozilla_sampl TableTicker *t = tlsTicker.get(); if (!t) { return nullptr; } return t->ToJSON(aSinceTime); } -JSObject* mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime) +#ifndef SPS_STANDALONE +JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, double aSinceTime) { TableTicker *t = tlsTicker.get(); if (!t) { return nullptr; } return t->ToJSObject(aCx, aSinceTime); } @@ -590,16 +616,17 @@ void mozilla_sampler_get_profile_data_as { TableTicker *t = tlsTicker.get(); if (NS_WARN_IF(!t)) { return; } t->ToJSObjectAsync(aSinceTime, aPromise); } +#endif void mozilla_sampler_save_profile_to_file(const char* aFilename) { TableTicker *t = tlsTicker.get(); if (!t) { return; } @@ -709,59 +736,64 @@ void mozilla_sampler_start(int aProfileE t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL, aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount); tlsTicker.set(t); t->Start(); if (t->ProfileJS() || t->InPrivacyMode()) { - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); + ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); std::vector<ThreadInfo*> threads = t->GetRegisteredThreads(); for (uint32_t i = 0; i < threads.size(); i++) { ThreadInfo* info = threads[i]; if (info->IsPendingDelete()) { continue; } ThreadProfile* thread_profile = info->Profile(); if (!thread_profile) { continue; } thread_profile->GetPseudoStack()->reinitializeOnResume(); +#ifndef SPS_STANDALONE if (t->ProfileJS()) { thread_profile->GetPseudoStack()->enableJSSampling(); } if (t->InPrivacyMode()) { thread_profile->GetPseudoStack()->mPrivacyMode = true; } +#endif } } #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) if (t->ProfileJava()) { int javaInterval = aInterval; // Java sampling doesn't accuratly keep up with 1ms sampling if (javaInterval < 10) { aInterval = 10; } mozilla::widget::GeckoJavaSampler::StartJavaProfiling(javaInterval, 1000); } #endif +#ifndef SPS_STANDALONE if (t->AddMainThreadIO()) { if (!sInterposeObserver) { // Lazily create IO interposer observer sInterposeObserver = new mozilla::ProfilerIOInterposeObserver(); } mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll, sInterposeObserver); } +#endif sIsProfiling = true; +#ifndef SPS_STANDALONE sIsGPUProfiling = t->ProfileGPU(); sIsLayersDump = t->LayersDump(); sIsDisplayListDump = t->DisplayListDump(); sIsRestyleProfiling = t->ProfileRestyle(); if (Sampler::CanNotifyObservers()) { nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) { @@ -778,16 +810,17 @@ void mozilla_sampler_start(int aProfileE nsCOMPtr<nsIProfilerStartParams> params = new nsProfilerStartParams(aProfileEntries, aInterval, featuresArray, threadNameFiltersArray); os->NotifyObservers(params, "profiler-started", nullptr); } } +#endif LOG("END mozilla_sampler_start"); } void mozilla_sampler_stop() { LOG("BEGIN mozilla_sampler_stop"); @@ -801,37 +834,41 @@ void mozilla_sampler_stop() } bool disableJS = t->ProfileJS(); t->Stop(); delete t; tlsTicker.set(nullptr); +#ifndef SPS_STANDALONE if (disableJS) { PseudoStack *stack = tlsPseudoStack.get(); ASSERT(stack != nullptr); stack->disableJSSampling(); } mozilla::IOInterposer::Unregister(mozilla::IOInterposeObserver::OpAll, sInterposeObserver); sInterposeObserver = nullptr; +#endif sIsProfiling = false; +#ifndef SPS_STANDALONE sIsGPUProfiling = false; sIsLayersDump = false; sIsDisplayListDump = false; sIsRestyleProfiling = false; if (Sampler::CanNotifyObservers()) { nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-stopped", nullptr); } +#endif LOG("END mozilla_sampler_stop"); } bool mozilla_sampler_is_paused() { if (Sampler::GetActiveSampler()) { return Sampler::GetActiveSampler()->IsPaused(); } else { @@ -889,26 +926,30 @@ void mozilla_sampler_responsiveness(cons void mozilla_sampler_frame_number(int frameNumber) { sFrameNumber = frameNumber; } void mozilla_sampler_lock() { profiler_stop(); +#ifndef SPS_STANDALONE nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-locked", nullptr); +#endif } void mozilla_sampler_unlock() { +#ifndef SPS_STANDALONE nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-unlocked", nullptr); +#endif } bool mozilla_sampler_register_thread(const char* aName, void* stackTop) { if (sInitCount == 0) { return false; } @@ -1021,17 +1062,17 @@ void mozilla_sampler_tracing(const char* { mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData, aCause)); } void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload) { // Note that aPayload may be allocated by the caller, so we need to make sure // that we free it at some point. - nsAutoPtr<ProfilerMarkerPayload> payload(aPayload); + mozilla::UniquePtr<ProfilerMarkerPayload> payload(aPayload); if (!stack_key_initialized) return; // Don't insert a marker if we're not profiling to avoid // the heap copy (malloc). if (!profiler_is_active()) { return; @@ -1045,15 +1086,76 @@ void mozilla_sampler_add_marker(const ch PseudoStack *stack = tlsPseudoStack.get(); if (!stack) { return; } mozilla::TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull()) ? aPayload->GetStartTime() : mozilla::TimeStamp::Now(); mozilla::TimeDuration delta = origin - sStartTime; - stack->addMarker(aMarker, payload.forget(), delta.ToMilliseconds()); + stack->addMarker(aMarker, payload.release(), delta.ToMilliseconds()); +} + +#ifndef SPS_STANDALONE +#include "mozilla/Mutex.h" + +class GeckoMutex : public ::Mutex { + public: + explicit GeckoMutex(const char* aDesc) : + mMutex(aDesc) + {} + + virtual ~GeckoMutex() {} + + virtual int Lock() { + mMutex.Lock(); + return 0; + } + + virtual int Unlock() { + mMutex.Unlock(); + return 0; + } + + private: + mozilla::Mutex mMutex; +}; + +mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) { + return mozilla::MakeUnique<GeckoMutex>(aDesc); } +#else +// Otherwise use c++11 Mutex +#include <mutex> + +class OSXMutex : public ::Mutex { + public: + OSXMutex(const char* aDesc) : + mMutex() + {} + + virtual ~OSXMutex() {} + + virtual int Lock() { + mMutex.lock(); + return 0; + } + + virtual int Unlock() { + mMutex.unlock(); + return 0; + } + + private: + std::mutex mMutex; +}; + +mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) { + return mozilla::MakeUnique<GeckoMutex>(aDesc); +} + +#endif + // END externally visible functions ////////////////////////////////////////////////////////////////////////
--- a/tools/profiler/platform.h +++ b/tools/profiler/platform.h @@ -24,34 +24,42 @@ // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. #ifndef TOOLS_PLATFORM_H_ #define TOOLS_PLATFORM_H_ +#ifdef SPS_STANDALONE +#define MOZ_COUNT_CTOR(name) +#define MOZ_COUNT_DTOR(name) +#endif + #ifdef ANDROID #include <android/log.h> #else #define __android_log_print(a, ...) #endif #ifdef XP_UNIX #include <pthread.h> #endif #include <stdint.h> #include <math.h> +#ifndef SPS_STANDALONE #include "MainThreadUtils.h" -#include "mozilla/unused.h" +#include "mozilla/Mutex.h" +#include "ThreadResponsiveness.h" +#endif #include "mozilla/TimeStamp.h" -#include "mozilla/Mutex.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/unused.h" #include "PlatformMacros.h" -#include "ThreadResponsiveness.h" #include "v8-support.h" #include <vector> #ifdef XP_WIN #include <windows.h> #endif #define ASSERT(a) MOZ_ASSERT(a) @@ -106,20 +114,32 @@ class Mutex { // locked and owned by the calling thread, and immediately. If the mutex // is already locked by another thread, suspends the calling thread until // the mutex is unlocked. virtual int Lock() = 0; // Unlocks the given mutex. The mutex is assumed to be locked and owned by // the calling thread on entrance. virtual int Unlock() = 0; +}; - // Tries to lock the given mutex. Returns whether the mutex was - // successfully locked. - virtual bool TryLock() = 0; +class MutexAutoLock { + public: + explicit MutexAutoLock(::Mutex& aMutex) + : mMutex(&aMutex) + { + mMutex->Lock(); + } + + ~MutexAutoLock() { + mMutex->Unlock(); + } + + private: + Mutex* mMutex; }; // ---------------------------------------------------------------------------- // OS // // This class has static methods for the different platform specific // functions. Add methods here to cope with differences between the // supported platforms. @@ -131,16 +151,18 @@ class OS { static void Sleep(const int milliseconds); // Sleep for a number of microseconds. static void SleepMicro(const int microseconds); // Called on startup to initialize platform specific things static void Startup(); + static mozilla::UniquePtr< ::Mutex> CreateMutex(const char* aDesc); + private: static const int msPerSecond = 1000; }; @@ -346,17 +368,17 @@ class Sampler { static void Startup(); // Should only be called on shutdown static void Shutdown(); static TableTicker* GetActiveSampler() { return sActiveSampler; } static void SetActiveSampler(TableTicker* sampler) { sActiveSampler = sampler; } - static mozilla::Mutex* sRegisteredThreadsMutex; + static mozilla::UniquePtr<Mutex> sRegisteredThreadsMutex; static bool CanNotifyObservers() { #ifdef MOZ_WIDGET_GONK // We use profile.sh on b2g to manually select threads and options per process. return false; #elif defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) // Android ANR reporter uses the profiler off the main thread return NS_IsMainThread(); @@ -409,29 +431,33 @@ class ThreadInfo { 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();
--- a/tools/profiler/shared-libraries.h +++ b/tools/profiler/shared-libraries.h @@ -11,17 +11,19 @@ #error This header does not have a useful implementation on your platform! #endif #include <algorithm> #include <vector> #include <string> #include <stdlib.h> #include <stdint.h> +#ifndef SPS_STANDALONE #include <nsID.h> +#endif class SharedLibrary { public: SharedLibrary(uintptr_t aStart, uintptr_t aEnd, uintptr_t aOffset, const std::string& aBreakpadId,
--- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp +++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp @@ -7,38 +7,38 @@ #include "ProfileEntry.h" // Make sure we can initialize our ThreadProfile TEST(ThreadProfile, Initialization) { PseudoStack* stack = PseudoStack::create(); Thread::tid_t tid = 1000; ThreadInfo info("testThread", tid, true, stack, nullptr); - nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(10); + mozilla::RefPtr<ProfileBuffer> pb = new ProfileBuffer(10); ThreadProfile tp(&info, pb); } // Make sure we can record one tag and read it TEST(ThreadProfile, InsertOneTag) { PseudoStack* stack = PseudoStack::create(); Thread::tid_t tid = 1000; ThreadInfo info("testThread", tid, true, stack, nullptr); - nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(10); + mozilla::RefPtr<ProfileBuffer> pb = new ProfileBuffer(10); pb->addTag(ProfileEntry('t', 123.1)); ASSERT_TRUE(pb->mEntries != nullptr); ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagName == 't'); ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagDouble == 123.1); } // See if we can insert some tags TEST(ThreadProfile, InsertTagsNoWrap) { PseudoStack* stack = PseudoStack::create(); Thread::tid_t tid = 1000; ThreadInfo info("testThread", tid, true, stack, nullptr); - nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(100); + mozilla::RefPtr<ProfileBuffer> pb = new ProfileBuffer(100); int test_size = 50; for (int i = 0; i < test_size; i++) { pb->addTag(ProfileEntry('t', i)); } ASSERT_TRUE(pb->mEntries != nullptr); int readPos = pb->mReadPos; while (readPos != pb->mWritePos) { ASSERT_TRUE(pb->mEntries[readPos].mTagName == 't'); @@ -50,17 +50,17 @@ TEST(ThreadProfile, InsertTagsNoWrap) { // See if wrapping works as it should in the basic case TEST(ThreadProfile, InsertTagsWrap) { PseudoStack* stack = PseudoStack::create(); Thread::tid_t tid = 1000; // we can fit only 24 tags in this buffer because of the empty slot int tags = 24; int buffer_size = tags + 1; ThreadInfo info("testThread", tid, true, stack, nullptr); - nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(buffer_size); + mozilla::RefPtr<ProfileBuffer> pb = new ProfileBuffer(buffer_size); int test_size = 43; for (int i = 0; i < test_size; i++) { pb->addTag(ProfileEntry('t', i)); } ASSERT_TRUE(pb->mEntries != nullptr); int readPos = pb->mReadPos; int ctr = 0; while (readPos != pb->mWritePos) {