From 8bc06cf8cda587d393e111bf4d032cdd1da5dada Mon Sep 17 00:00:00 2001 From: Greg Tatum Date: Wed, 19 Jun 2019 21:30:48 +0000 Subject: [PATCH] Bug 1545582 - Integrate JavaScript memory allocation tracking to the profiler; r=canaltinova Differential Revision: https://phabricator.services.mozilla.com/D34543 --HG-- extra : moz-landing-system : lando --- mozglue/baseprofiler/core/platform.h | 7 +- .../extensions/schemas/geckoProfiler.json | 3 +- tools/profiler/core/ProfilerMarkerPayload.cpp | 25 ++++++ tools/profiler/core/RegisteredThread.h | 16 +++- tools/profiler/core/platform.cpp | 23 ++++-- tools/profiler/core/platform.h | 7 +- tools/profiler/public/GeckoProfiler.h | 82 ++++++++++--------- tools/profiler/public/ProfilerMarkerPayload.h | 41 ++++++++++ 8 files changed, 152 insertions(+), 52 deletions(-) diff --git a/mozglue/baseprofiler/core/platform.h b/mozglue/baseprofiler/core/platform.h index b4f6234d692e..054367d6e55c 100644 --- a/mozglue/baseprofiler/core/platform.h +++ b/mozglue/baseprofiler/core/platform.h @@ -116,11 +116,12 @@ void profiler_get_profile_json_into_lazily_allocated_buffer( const std::function& aAllocator, double aSinceTime, bool aIsShuttingDown); -// Flags to conveniently track various JS features. -enum class JSSamplingFlags { +// Flags to conveniently track various JS instrumentations. +enum class JSInstrumentationFlags { StackSampling = 0x1, TrackOptimizations = 0x2, - TraceLogging = 0x4 + TraceLogging = 0x4, + Allocations = 0x8 }; // Record an exit profile from a child process. diff --git a/toolkit/components/extensions/schemas/geckoProfiler.json b/toolkit/components/extensions/schemas/geckoProfiler.json index 9f2702a70d9d..4e3460ce3cb0 100644 --- a/toolkit/components/extensions/schemas/geckoProfiler.json +++ b/toolkit/components/extensions/schemas/geckoProfiler.json @@ -36,7 +36,8 @@ "tasktracer", "threads", "trackopts", - "jstracer" + "jstracer", + "jsallocations" ] }, { diff --git a/tools/profiler/core/ProfilerMarkerPayload.cpp b/tools/profiler/core/ProfilerMarkerPayload.cpp index 7bdf7a01cbb3..7a61bf9c12ce 100644 --- a/tools/profiler/core/ProfilerMarkerPayload.cpp +++ b/tools/profiler/core/ProfilerMarkerPayload.cpp @@ -296,3 +296,28 @@ void LongTaskMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, aUniqueStacks); aWriter.StringProperty("category", "LongTask"); } + +void JsAllocationMarkerPayload::StreamPayload( + SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) { + StreamCommonProps("JS allocation", aWriter, aProcessStartTime, aUniqueStacks); + + if (mClassName) { + aWriter.StringProperty("className", mClassName.get()); + } + if (mScriptFilename) { + aWriter.StringProperty("scriptFilename", mScriptFilename.get()); + } + if (mTypeName) { + aWriter.StringProperty("typeName", + NS_ConvertUTF16toUTF8(mTypeName.get()).get()); + } + if (mDescriptiveTypeName) { + aWriter.StringProperty( + "descriptiveTypeName", + NS_ConvertUTF16toUTF8(mDescriptiveTypeName.get()).get()); + } + aWriter.StringProperty("coarseType", mCoarseType); + aWriter.IntProperty("size", mSize); + aWriter.BoolProperty("inNursery", mInNursery); +} diff --git a/tools/profiler/core/RegisteredThread.h b/tools/profiler/core/RegisteredThread.h index 7ce4bfa80fdb..66dc9b2e99f8 100644 --- a/tools/profiler/core/RegisteredThread.h +++ b/tools/profiler/core/RegisteredThread.h @@ -250,6 +250,11 @@ class RegisteredThread final { if (JSTracerEnabled()) { JS::StartTraceLogger(mContext); } + if (JSAllocationsEnabled()) { + // TODO - This probability should not be hardcoded. See Bug 1547284. + JS::EnableRecordingAllocations( + mContext, profiler_add_js_allocation_marker, 0.01); + } js::RegisterContextProfilingEventMarker(mContext, profiler_add_js_marker); @@ -259,6 +264,9 @@ class RegisteredThread final { if (JSTracerEnabled()) { JS::StopTraceLogger(mContext); } + if (JSAllocationsEnabled()) { + JS::DisableRecordingAllocations(mContext); + } } } } @@ -326,11 +334,15 @@ class RegisteredThread final { uint32_t mJSFlags; bool TrackOptimizationsEnabled() { - return mJSFlags & uint32_t(JSSamplingFlags::TrackOptimizations); + return mJSFlags & uint32_t(JSInstrumentationFlags::TrackOptimizations); } bool JSTracerEnabled() { - return mJSFlags & uint32_t(JSSamplingFlags::TraceLogging); + return mJSFlags & uint32_t(JSInstrumentationFlags::TraceLogging); + } + + bool JSAllocationsEnabled() { + return mJSFlags & uint32_t(JSInstrumentationFlags::Allocations); } }; diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index 2eb0c40aeca6..cf9529e07643 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -43,6 +43,7 @@ #include "VTuneProfiler.h" #include "js/TraceLoggerAPI.h" +#include "js/ProfilingFrameIterator.h" #include "memory_hooks.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Atomics.h" @@ -654,12 +655,17 @@ class ActivePS { static uint32_t JSFlags(PSLockRef aLock) { uint32_t Flags = 0; - Flags |= FeatureJS(aLock) ? uint32_t(JSSamplingFlags::StackSampling) : 0; - Flags |= FeatureTrackOptimizations(aLock) - ? uint32_t(JSSamplingFlags::TrackOptimizations) - : 0; Flags |= - FeatureJSTracer(aLock) ? uint32_t(JSSamplingFlags::TraceLogging) : 0; + FeatureJS(aLock) ? uint32_t(JSInstrumentationFlags::StackSampling) : 0; + Flags |= FeatureTrackOptimizations(aLock) + ? uint32_t(JSInstrumentationFlags::TrackOptimizations) + : 0; + Flags |= FeatureJSTracer(aLock) + ? uint32_t(JSInstrumentationFlags::TraceLogging) + : 0; + Flags |= FeatureJSAllocations(aLock) + ? uint32_t(JSInstrumentationFlags::Allocations) + : 0; return Flags; } @@ -4006,6 +4012,13 @@ void profiler_add_js_marker(const char* aMarkerName) { profiler_add_marker(aMarkerName, JS::ProfilingCategoryPair::JS, nullptr); } +void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info) { + profiler_add_marker( + "JS allocation", JS::ProfilingCategoryPair::JS, + MakeUnique(TimeStamp::Now(), std::move(info), + profiler_get_backtrace())); +} + void profiler_add_network_marker( nsIURI* aURI, int32_t aPriority, uint64_t aChannelId, NetworkLoadType aType, mozilla::TimeStamp aStart, mozilla::TimeStamp aEnd, int64_t aCount, diff --git a/tools/profiler/core/platform.h b/tools/profiler/core/platform.h index 4b5c55ab517f..afa940cd2bec 100644 --- a/tools/profiler/core/platform.h +++ b/tools/profiler/core/platform.h @@ -88,11 +88,12 @@ void profiler_get_profile_json_into_lazily_allocated_buffer( const std::function& aAllocator, double aSinceTime, bool aIsShuttingDown); -// Flags to conveniently track various JS features. -enum class JSSamplingFlags { +// Flags to conveniently track various JS instrumentations. +enum class JSInstrumentationFlags { StackSampling = 0x1, TrackOptimizations = 0x2, - TraceLogging = 0x4 + TraceLogging = 0x4, + Allocations = 0x8 }; // Record an exit profile from a child process. diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h index a3259b63d44d..b2b62323822a 100644 --- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -69,6 +69,8 @@ #else // !MOZ_GECKO_PROFILER +# include "js/AllocationRecording.h" +# include "js/ProfilingFrameIterator.h" # include "js/ProfilingStack.h" # include "js/RootingAPI.h" # include "js/TypeDecls.h" @@ -118,44 +120,47 @@ class Vector; // values are used internally only and so can be changed without consequence. // Any changes to this list should also be applied to the feature list in // toolkit/components/extensions/schemas/geckoProfiler.json. -# define PROFILER_FOR_EACH_FEATURE(MACRO) \ - MACRO(0, "java", Java, "Profile Java code, Android only") \ - \ - MACRO(1, "js", JS, \ - "Get the JS engine to expose the JS stack to the profiler") \ - \ - /* The DevTools profiler doesn't want the native addresses. */ \ - MACRO(2, "leaf", Leaf, "Include the C++ leaf node if not stackwalking") \ - \ - MACRO(3, "mainthreadio", MainThreadIO, \ - "Add main thread I/O to the profile") \ - \ - MACRO(4, "memory", Memory, "Add memory measurements") \ - \ - MACRO(5, "privacy", Privacy, \ - "Do not include user-identifiable information") \ - \ - MACRO(6, "responsiveness", Responsiveness, \ - "Collect thread responsiveness information") \ - \ - MACRO(7, "screenshots", Screenshots, \ - "Take a snapshot of the window on every composition") \ - \ - MACRO(8, "seqstyle", SequentialStyle, \ - "Disable parallel traversal in styling") \ - \ - MACRO(9, "stackwalk", StackWalk, \ - "Walk the C++ stack, not available on all platforms") \ - \ - MACRO(10, "tasktracer", TaskTracer, \ - "Start profiling with feature TaskTracer") \ - \ - MACRO(11, "threads", Threads, "Profile the registered secondary threads") \ - \ - MACRO(12, "trackopts", TrackOptimizations, \ - "Have the JavaScript engine track JIT optimizations") \ - \ - MACRO(13, "jstracer", JSTracer, "Enable tracing of the JavaScript engine") +# define PROFILER_FOR_EACH_FEATURE(MACRO) \ + MACRO(0, "java", Java, "Profile Java code, Android only") \ + \ + MACRO(1, "js", JS, \ + "Get the JS engine to expose the JS stack to the profiler") \ + \ + /* The DevTools profiler doesn't want the native addresses. */ \ + MACRO(2, "leaf", Leaf, "Include the C++ leaf node if not stackwalking") \ + \ + MACRO(3, "mainthreadio", MainThreadIO, \ + "Add main thread I/O to the profile") \ + \ + MACRO(4, "memory", Memory, "Add memory measurements") \ + \ + MACRO(5, "privacy", Privacy, \ + "Do not include user-identifiable information") \ + \ + MACRO(6, "responsiveness", Responsiveness, \ + "Collect thread responsiveness information") \ + \ + MACRO(7, "screenshots", Screenshots, \ + "Take a snapshot of the window on every composition") \ + \ + MACRO(8, "seqstyle", SequentialStyle, \ + "Disable parallel traversal in styling") \ + \ + MACRO(9, "stackwalk", StackWalk, \ + "Walk the C++ stack, not available on all platforms") \ + \ + MACRO(10, "tasktracer", TaskTracer, \ + "Start profiling with feature TaskTracer") \ + \ + MACRO(11, "threads", Threads, "Profile the registered secondary threads") \ + \ + MACRO(12, "trackopts", TrackOptimizations, \ + "Have the JavaScript engine track JIT optimizations") \ + \ + MACRO(13, "jstracer", JSTracer, "Enable tracing of the JavaScript engine") \ + \ + MACRO(14, "jsallocations", JSAllocations, \ + "Have the JavaScript engine track allocations") struct ProfilerFeature { # define DECLARE(n_, str_, Name_, desc_) \ @@ -643,6 +648,7 @@ void profiler_add_marker(const char* aMarkerName, JS::ProfilingCategoryPair aCategoryPair, mozilla::UniquePtr aPayload); void profiler_add_js_marker(const char* aMarkerName); +void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info); // Insert a marker in the profile timeline for a specified thread. void profiler_add_marker_for_thread( diff --git a/tools/profiler/public/ProfilerMarkerPayload.h b/tools/profiler/public/ProfilerMarkerPayload.h index 6777a1cc86bf..1d1fe7c6c77e 100644 --- a/tools/profiler/public/ProfilerMarkerPayload.h +++ b/tools/profiler/public/ProfilerMarkerPayload.h @@ -16,9 +16,12 @@ #include "mozilla/net/TimingStruct.h" #include "nsString.h" +#include "nsCRTGlue.h" #include "GeckoProfiler.h" #include "js/Utility.h" +#include "js/AllocationRecording.h" +#include "js/ProfilingFrameIterator.h" #include "gfxASurface.h" #include "mozilla/ServoTraversalStatistics.h" @@ -413,4 +416,42 @@ class LogMarkerPayload : public ProfilerMarkerPayload { nsCString mText; }; +class JsAllocationMarkerPayload : public ProfilerMarkerPayload { + public: + JsAllocationMarkerPayload(const mozilla::TimeStamp& aStartTime, + const JS::RecordAllocationInfo& aInfo, + UniqueProfilerBacktrace aStack) + : ProfilerMarkerPayload(aStartTime, aStartTime, mozilla::Nothing(), + mozilla::Nothing(), std::move(aStack)), + // Copy the strings, and take ownership of them. + mTypeName(aInfo.typeName ? NS_xstrdup(aInfo.typeName) : nullptr), + mClassName(aInfo.className ? strdup(aInfo.className) : nullptr), + mDescriptiveTypeName(aInfo.descriptiveTypeName + ? NS_xstrdup(aInfo.descriptiveTypeName) + : nullptr), + mScriptFilename(aInfo.scriptFilename ? strdup(aInfo.scriptFilename) + : nullptr), + // The coarseType points to a string literal, so does not need to be + // duplicated. + mCoarseType(aInfo.coarseType), + mSize(aInfo.size), + mInNursery(aInfo.inNursery) {} + + DECL_STREAM_PAYLOAD + + private: + mozilla::UniqueFreePtr mTypeName; + mozilla::UniqueFreePtr mClassName; + mozilla::UniqueFreePtr mDescriptiveTypeName; + mozilla::UniqueFreePtr mScriptFilename; + // Points to a string literal, so does not need to be freed. + const char* mCoarseType; + + // The size in bytes of the allocation. + uint64_t mSize; + + // Whether or not the allocation is in the nursery or not. + bool mInNursery; +}; + #endif // ProfilerMarkerPayload_h