From 88eff64bd1391ffa2f257987a52021cd76977efe Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Fri, 28 Feb 2014 14:02:46 -0500 Subject: [PATCH] Bug 976260 - Register javascript performance events with the profiler. r=jandem --- js/public/ProfilingStack.h | 3 +++ js/src/jit/BaselineBailouts.cpp | 37 +++++++++++++++++++++++------- js/src/jit/CodeGenerator.cpp | 13 +++++++++++ js/src/jit/Ion.cpp | 22 ++++++++++++++++++ js/src/jit/IonTypes.h | 2 -- js/src/shell/js.cpp | 21 +++++++++++++++++ js/src/vm/SPSProfiler.cpp | 16 ++++++++++++- js/src/vm/SPSProfiler.h | 8 +++++++ tools/profiler/ProfilerMarkers.cpp | 5 ++++ tools/profiler/PseudoStack.h | 4 ++++ 10 files changed, 120 insertions(+), 11 deletions(-) diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h index 34f8ed10107a..fcf9d49dc22d 100644 --- a/js/public/ProfilingStack.h +++ b/js/public/ProfilingStack.h @@ -91,6 +91,9 @@ SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, JS_FRIEND_API(void) EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled); +JS_FRIEND_API(void) +RegisterRuntimeProfilingEventMarker(JSRuntime *rt, void (*fn)(const char *)); + JS_FRIEND_API(jsbytecode*) ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip); diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index 55afa172894c..d74a0d2860c1 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -4,6 +4,7 @@ * 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 "jsprf.h" #include "jit/arm/Simulator-arm.h" #include "jit/BaselineIC.h" #include "jit/BaselineJIT.h" @@ -975,14 +976,34 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, IonSpew(IonSpew_BaselineBailouts, " Set resumeAddr=%p", opReturnAddr); } - if (cx->runtime()->spsProfiler.enabled() && blFrame->hasPushedSPSFrame()) { - // Set PC index to 0 for the innermost frame to match what the - // interpreter and Baseline do: they update the SPS pc for - // JSOP_CALL ops but set it to 0 when running other ops. Ion code - // can set the pc to NullPCIndex and this will confuse SPS when - // Baseline calls into the VM at non-CALL ops and re-enters JS. - IonSpew(IonSpew_BaselineBailouts, " Setting PCidx for last frame to 0"); - cx->runtime()->spsProfiler.updatePC(script, script->code()); + if (cx->runtime()->spsProfiler.enabled()) { + if (blFrame->hasPushedSPSFrame()) { + // Set PC index to 0 for the innermost frame to match what the + // interpreter and Baseline do: they update the SPS pc for + // JSOP_CALL ops but set it to 0 when running other ops. Ion code + // can set the pc to NullPCIndex and this will confuse SPS when + // Baseline calls into the VM at non-CALL ops and re-enters JS. + IonSpew(IonSpew_BaselineBailouts, " Setting PCidx for last frame to 0"); + cx->runtime()->spsProfiler.updatePC(script, script->code()); + } + + // Register bailout with profiler. + const char *filename = script->filename(); + if (filename == nullptr) + filename = ""; + unsigned len = strlen(filename) + 200; + char *buf = js_pod_malloc(len); + if (buf == nullptr) + return false; + JS_snprintf(buf, len, "%s %s %s on line %d of %s:%d", + BailoutKindString(bailoutKind), + resumeAfter ? "after" : "at", + js_CodeName[op], + int(PCToLineNumber(script, pc)), + filename, + int(script->lineno())); + cx->runtime()->spsProfiler.markEvent(buf); + js_free(buf); } return true; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 19e18f3cf43b..6ca497f4db7c 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -6226,6 +6226,19 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) SetIonScript(script, executionMode, ionScript); + if (cx->runtime()->spsProfiler.enabled()) { + const char *filename = script->filename(); + if (filename == nullptr) + filename = ""; + unsigned len = strlen(filename) + 50; + char *buf = js_pod_malloc(len); + if (!buf) + return false; + JS_snprintf(buf, len, "Ion compiled %s:%d", filename, (int) script->lineno()); + cx->runtime()->spsProfiler.markEvent(buf); + js_free(buf); + } + // In parallel execution mode, when we first compile a script, we // don't know that its potential callees are compiled, so set a // flag warning that the callees may not be fully compiled. diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index c271f92d546e..3c739c091f2c 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -15,6 +15,7 @@ #include "TraceLogging.h" #endif +#include "jsprf.h" #include "gc/Marking.h" #include "jit/AliasAnalysis.h" #include "jit/AsmJSModule.h" @@ -2621,6 +2622,27 @@ jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetU { JS_ASSERT(script->hasIonScript()); + if (cx->runtime()->spsProfiler.enabled()) { + // Register invalidation with profiler. + // Format of event payload string: + // ":" + + // Get the script filename, if any, and its length. + const char *filename = script->filename(); + if (filename == nullptr) + filename = ""; + + size_t len = strlen(filename) + 20; + char *buf = js_pod_malloc(len); + if (!buf) + return false; + + // Construct the descriptive string. + JS_snprintf(buf, len, "Invalidate %s:%llu", filename, script->lineno()); + cx->runtime()->spsProfiler.markEvent(buf); + js_free(buf); + } + Vector scripts(cx); switch (mode) { diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index c325e9bede80..0a7d9276ef7b 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -52,7 +52,6 @@ enum BailoutKind static const uint32_t BAILOUT_KIND_BITS = 3; static const uint32_t BAILOUT_RESUME_BITS = 1; -#ifdef DEBUG inline const char * BailoutKindString(BailoutKind kind) { @@ -71,7 +70,6 @@ BailoutKindString(BailoutKind kind) MOZ_ASSUME_UNREACHABLE("Invalid BailoutKind"); } } -#endif static const uint32_t ELEMENT_TYPE_BITS = 4; static const uint32_t ELEMENT_TYPE_SHIFT = 0; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index e7d7bd49cd44..be3150fabd6f 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -4275,6 +4275,22 @@ SetCachingEnabled(JSContext *cx, unsigned argc, Value *vp) return true; } +static void +PrintProfilerEvents_Callback(const char *msg) +{ + fprintf(stderr, "PROFILER EVENT: %s\n", msg); +} + +static bool +PrintProfilerEvents(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (cx->runtime()->spsProfiler.enabled()) + js::RegisterRuntimeProfilingEventMarker(cx->runtime(), &PrintProfilerEvents_Callback); + args.rval().setUndefined(); + return true; +} + static const JSFunctionSpecWithHelp shell_functions[] = { JS_FN_HELP("version", Version, 0, 0, "version([number])", @@ -4615,6 +4631,11 @@ static const JSFunctionSpecWithHelp shell_functions[] = { " and read by the \"evaluate\" function by using it in place of the source, and\n" " by setting \"saveBytecode\" and \"loadBytecode\" options."), + JS_FN_HELP("printProfilerEvents", PrintProfilerEvents, 0, 0, +"printProfilerEvents()", +" Register a callback with the profiler that prints javascript profiler events\n" +" to stderr. Callback is only registered if profiling is enabled."), + JS_FS_HELP_END }; diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index e9d680aa2b22..65c0a7559e5c 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -26,7 +26,8 @@ SPSProfiler::SPSProfiler(JSRuntime *rt) max_(0), slowAssertions(false), enabled_(false), - lock_(nullptr) + lock_(nullptr), + eventMarker_(nullptr) { JS_ASSERT(rt != nullptr); } @@ -66,6 +67,12 @@ SPSProfiler::setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max max_ = max; } +void +SPSProfiler::setEventMarker(void (*fn)(const char *)) +{ + eventMarker_ = fn; +} + void SPSProfiler::enable(bool enabled) { @@ -331,6 +338,13 @@ js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled) rt->spsProfiler.enable(enabled); } +JS_FRIEND_API(void) +js::RegisterRuntimeProfilingEventMarker(JSRuntime *rt, void (*fn)(const char *)) +{ + JS_ASSERT(rt->spsProfiler.enabled()); + rt->spsProfiler.setEventMarker(fn); +} + JS_FRIEND_API(jsbytecode*) js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip) { diff --git a/js/src/vm/SPSProfiler.h b/js/src/vm/SPSProfiler.h index 38862186c8f8..494ccdf6bc1a 100644 --- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -125,6 +125,7 @@ class SPSProfiler bool slowAssertions; uint32_t enabled_; PRLock *lock_; + void (*eventMarker_)(const char *); const char *allocProfileString(JSScript *script, JSFunction *function); void push(const char *string, void *sp, JSScript *script, jsbytecode *pc); @@ -191,9 +192,16 @@ class SPSProfiler jsbytecode *ipToPC(JSScript *script, size_t ip) { return nullptr; } void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max); + void setEventMarker(void (*fn)(const char *)); const char *profileString(JSScript *script, JSFunction *maybeFun); void onScriptFinalized(JSScript *script); + void markEvent(const char *event) { + JS_ASSERT(enabled()); + if (eventMarker_) + eventMarker_(event); + } + /* meant to be used for testing, not recommended to call in normal code */ size_t stringsCount(); void stringsReset(); diff --git a/tools/profiler/ProfilerMarkers.cpp b/tools/profiler/ProfilerMarkers.cpp index 03fbbb870ffb..4e1c435613d4 100644 --- a/tools/profiler/ProfilerMarkers.cpp +++ b/tools/profiler/ProfilerMarkers.cpp @@ -143,3 +143,8 @@ IOMarkerPayload::preparePayloadImp(JSCustomObjectBuilder& template JSObjectBuilder::Object IOMarkerPayload::preparePayloadImp(JSObjectBuilder& b); +void +ProfilerJSEventMarker(const char *event) +{ + PROFILER_MARKER(event); +} diff --git a/tools/profiler/PseudoStack.h b/tools/profiler/PseudoStack.h index 99081c8cd387..78b63aa5f6d5 100644 --- a/tools/profiler/PseudoStack.h +++ b/tools/profiler/PseudoStack.h @@ -292,6 +292,9 @@ private: volatile bool mSignalLock; }; +// Stub eventMarker function for js-engine event generation. +void ProfilerJSEventMarker(const char *event); + // the PseudoStack members are read by signal // handlers, so the mutation of them needs to be signal-safe. struct PseudoStack @@ -398,6 +401,7 @@ public: void enableJSSampling() { if (mRuntime) { js::EnableRuntimeProfilingStack(mRuntime, true); + js::RegisterRuntimeProfilingEventMarker(mRuntime, &ProfilerJSEventMarker); mStartJSSampling = false; } else { mStartJSSampling = true;