Bug 976260 - Register javascript performance events with the profiler. r=jandem

This commit is contained in:
Kannan Vijayan 2014-02-28 13:22:03 -05:00
parent d0b2a48907
commit 300f083ef0
10 changed files with 119 additions and 11 deletions

View File

@ -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);

View File

@ -975,14 +975,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 = "<unknown>";
unsigned len = strlen(filename) + 200;
char *buf = js_pod_malloc<char>(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;

View File

@ -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 = "<unknown>";
unsigned len = strlen(filename) + 50;
char *buf = js_pod_malloc<char>(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.

View File

@ -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:
// "<filename>:<lineno>"
// Get the script filename, if any, and its length.
const char *filename = script->filename();
if (filename == nullptr)
filename = "<unknown>";
size_t len = strlen(filename) + 20;
char *buf = js_pod_malloc<char>(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<types::RecompileInfo> scripts(cx);
switch (mode) {

View File

@ -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;

View File

@ -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
};

View File

@ -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)
{

View File

@ -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();

View File

@ -143,3 +143,8 @@ IOMarkerPayload::preparePayloadImp<JSCustomObjectBuilder>(JSCustomObjectBuilder&
template JSObjectBuilder::Object
IOMarkerPayload::preparePayloadImp<JSObjectBuilder>(JSObjectBuilder& b);
void
ProfilerJSEventMarker(const char *event)
{
PROFILER_MARKER(event);
}

View File

@ -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;