Bug 673631 - Clean up probes and register (almost) all profiler control entry points in JS_DefineProfilingFunctions (r=luke,waldo,jst)

Previously, JS_DefineProfilingFunctions only defined a very basic set of
functions (startProfiling and stopProfiling), and various scattered places
added more specific ones (start/stop vtune, dumpProfile, etc.) This patch makes
jsdbgapi do all of it, so that all users get the same set.

Also rename JS_DumpProfile -> JS_DumpBytecode to avoid name conflict. The
bytecode dumps are how the counters ("profiles") are displayed, so the
DumpProfile name was bogus anyway.
This commit is contained in:
Steve Fink
2011-07-26 15:56:09 -07:00
parent 6d26544b63
commit 657c83e57a
11 changed files with 712 additions and 525 deletions

View File

@@ -2963,25 +2963,6 @@ static JSFunctionSpec JProfFunctions[] = {
#endif /* defined(MOZ_JPROF) */ #endif /* defined(MOZ_JPROF) */
#ifdef MOZ_CALLGRIND
static JSFunctionSpec CallgrindFunctions[] = {
{"startCallgrind", js_StartCallgrind, 0, 0},
{"stopCallgrind", js_StopCallgrind, 0, 0},
{"dumpCallgrind", js_DumpCallgrind, 1, 0},
{nsnull, nsnull, 0, 0}
};
#endif
#ifdef MOZ_VTUNE
static JSFunctionSpec VtuneFunctions[] = {
{"startVtune", js_StartVtune, 1, 0},
{"stopVtune", js_StopVtune, 0, 0},
{"pauseVtune", js_PauseVtune, 0, 0},
{"resumeVtune", js_ResumeVtune, 0, 0},
{nsnull, nsnull, 0, 0}
};
#endif
#ifdef MOZ_TRACEVIS #ifdef MOZ_TRACEVIS
static JSFunctionSpec EthogramFunctions[] = { static JSFunctionSpec EthogramFunctions[] = {
{"initEthogram", js_InitEthogram, 0, 0}, {"initEthogram", js_InitEthogram, 0, 0},
@@ -3017,16 +2998,6 @@ nsJSContext::InitClasses(void *aGlobalObj)
::JS_DefineFunctions(mContext, globalObj, JProfFunctions); ::JS_DefineFunctions(mContext, globalObj, JProfFunctions);
#endif #endif
#ifdef MOZ_CALLGRIND
// Attempt to initialize Callgrind functions
::JS_DefineFunctions(mContext, globalObj, CallgrindFunctions);
#endif
#ifdef MOZ_VTUNE
// Attempt to initialize Vtune functions
::JS_DefineFunctions(mContext, globalObj, VtuneFunctions);
#endif
#ifdef MOZ_TRACEVIS #ifdef MOZ_TRACEVIS
// Attempt to initialize Ethogram functions // Attempt to initialize Ethogram functions
::JS_DefineFunctions(mContext, globalObj, EthogramFunctions); ::JS_DefineFunctions(mContext, globalObj, EthogramFunctions);

View File

@@ -558,11 +558,6 @@ JSFunctionSpec gGlobalFunctions[] =
{"clear", Clear, 1,0}, {"clear", Clear, 1,0},
#ifdef DEBUG #ifdef DEBUG
{"dumpHeap", DumpHeap, 5,0}, {"dumpHeap", DumpHeap, 5,0},
#endif
#ifdef MOZ_CALLGRIND
{"startCallgrind", js_StartCallgrind, 0,0},
{"stopCallgrind", js_StopCallgrind, 0,0},
{"dumpCallgrind", js_DumpCallgrind, 1,0},
#endif #endif
{nsnull,nsnull,0,0} {nsnull,nsnull,0,0}
}; };

View File

@@ -43,6 +43,7 @@
* JS debugging API. * JS debugging API.
*/ */
#include <string.h> #include <string.h>
#include <stdarg.h>
#include "jsprvtd.h" #include "jsprvtd.h"
#include "jstypes.h" #include "jstypes.h"
#include "jsstdint.h" #include "jsstdint.h"
@@ -83,6 +84,10 @@
#include "methodjit/MethodJIT.h" #include "methodjit/MethodJIT.h"
#include "methodjit/Retcon.h" #include "methodjit/Retcon.h"
#ifdef __APPLE__
#include "sharkctl.h"
#endif
using namespace js; using namespace js;
using namespace js::gc; using namespace js::gc;
@@ -1213,32 +1218,251 @@ JS_ClearContextDebugHooks(JSContext *cx)
return JS_SetContextDebugHooks(cx, &js_NullDebugHooks); return JS_SetContextDebugHooks(cx, &js_NullDebugHooks);
} }
JS_PUBLIC_API(JSBool) /************************************************************************/
JS_StartProfiling()
/* Profiling-related API */
/* Thread-unsafe error management */
static char gLastError[2000];
static void
#ifdef _GNU_SOURCE
__attribute__((unused,format(printf,1,2)))
#endif
UnsafeError(const char *format, ...)
{ {
return Probes::startProfiling(); va_list args;
va_start(args, format);
(void) vsnprintf(gLastError, sizeof(gLastError), format, args);
va_end(args);
gLastError[sizeof(gLastError) - 1] = '\0';
} }
JS_PUBLIC_API(void) JS_PUBLIC_API(const char *)
JS_StopProfiling() JS_UnsafeGetLastProfilingError()
{ {
Probes::stopProfiling(); return gLastError;
}
JS_PUBLIC_API(JSBool)
JS_StartProfiling(const char *profileName)
{
JSBool ok = JS_TRUE;
#if defined(MOZ_SHARK) && defined(__APPLE__)
if (!Shark::Start()) {
UnsafeError("Failed to start Shark for %s", profileName);
ok = JS_FALSE;
}
#endif
#ifdef MOZ_VTUNE
if (!js_StartVtune(profileName))
ok = JS_FALSE;
#endif
return ok;
}
JS_PUBLIC_API(JSBool)
JS_StopProfiling(const char *profileName)
{
JSBool ok = JS_TRUE;
#if defined(MOZ_SHARK) && defined(__APPLE__)
Shark::Stop();
#endif
#ifdef MOZ_VTUNE
if (!js_StopVtune())
ok = JS_FALSE;
#endif
return ok;
}
/*
* Start or stop whatever platform- and configuration-specific profiling
* backends are available.
*/
static JSBool
ControlProfilers(bool toState)
{
JSBool ok = JS_TRUE;
if (! Probes::ProfilingActive && toState) {
#if defined(MOZ_SHARK) && defined(__APPLE__)
if (!Shark::Start()) {
UnsafeError("Failed to start Shark");
ok = JS_FALSE;
}
#endif
#ifdef MOZ_CALLGRIND
if (! js_StartCallgrind()) {
UnsafeError("Failed to start Callgrind");
ok = JS_FALSE;
}
#endif
#ifdef MOZ_VTUNE
if (! js_ResumeVtune())
ok = JS_FALSE;
#endif
} else if (Probes::ProfilingActive && ! toState) {
#if defined(MOZ_SHARK) && defined(__APPLE__)
Shark::Stop();
#endif
#ifdef MOZ_CALLGRIND
if (! js_StopCallgrind()) {
UnsafeError("failed to stop Callgrind");
ok = JS_FALSE;
}
#endif
#ifdef MOZ_VTUNE
if (! js_PauseVtune())
ok = JS_FALSE;
#endif
}
Probes::ProfilingActive = toState;
return ok;
}
/*
* Pause/resume whatever profiling mechanism is currently compiled
* in, if applicable. This will not affect things like dtrace.
*
* Do not mix calls to these APIs with calls to the individual
* profilers' pause/resume functions, because only overall state is
* tracked, not the state of each profiler.
*/
JS_PUBLIC_API(JSBool)
JS_PauseProfilers(const char *profileName)
{
return ControlProfilers(false);
}
JS_PUBLIC_API(JSBool)
JS_ResumeProfilers(const char *profileName)
{
return ControlProfilers(true);
}
JS_PUBLIC_API(JSBool)
JS_DumpProfile(const char *outfile, const char *profileName)
{
JSBool ok = JS_TRUE;
#ifdef MOZ_CALLGRIND
js_DumpCallgrind(outfile);
#endif
return ok;
} }
#ifdef MOZ_PROFILING #ifdef MOZ_PROFILING
struct RequiredStringArg {
JSContext *mCx;
char *mBytes;
RequiredStringArg(JSContext *cx, uintN argc, jsval *vp, size_t argi, const char *caller)
: mCx(cx), mBytes(NULL)
{
if (argc <= argi) {
JS_ReportError(cx, "%s: not enough arguments", caller);
} else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) {
JS_ReportError(cx, "%s: invalid arguments (string expected)", caller);
} else {
mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi]));
}
}
operator void*() {
return (void*) mBytes;
}
~RequiredStringArg() {
if (mBytes)
mCx->free_(mBytes);
}
};
static JSBool static JSBool
StartProfiling(JSContext *cx, uintN argc, jsval *vp) StartProfiling(JSContext *cx, uintN argc, jsval *vp)
{ {
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling())); if (argc == 0) {
return true; JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL)));
return JS_TRUE;
}
RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling");
if (!profileName)
return JS_FALSE;
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes)));
return JS_TRUE;
} }
static JSBool static JSBool
StopProfiling(JSContext *cx, uintN argc, jsval *vp) StopProfiling(JSContext *cx, uintN argc, jsval *vp)
{ {
JS_StopProfiling(); if (argc == 0) {
JS_SET_RVAL(cx, vp, JSVAL_VOID); JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL)));
return JS_TRUE;
}
RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling");
if (!profileName)
return JS_FALSE;
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes)));
return JS_TRUE;
}
static JSBool
PauseProfilers(JSContext *cx, uintN argc, jsval *vp)
{
if (argc == 0) {
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL)));
return JS_TRUE;
}
RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling");
if (!profileName)
return JS_FALSE;
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes)));
return JS_TRUE;
}
static JSBool
ResumeProfilers(JSContext *cx, uintN argc, jsval *vp)
{
if (argc == 0) {
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL)));
return JS_TRUE;
}
RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling");
if (!profileName)
return JS_FALSE;
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes)));
return JS_TRUE;
}
/* Usage: DumpProfile([filename[, profileName]]) */
static JSBool
DumpProfile(JSContext *cx, uintN argc, jsval *vp)
{
bool ret;
if (argc == 0) {
ret = JS_DumpProfile(NULL, NULL);
} else {
RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile");
if (!filename)
return JS_FALSE;
if (argc == 1) {
ret = JS_DumpProfile(filename.mBytes, NULL);
} else {
RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile");
if (!profileName)
return JS_FALSE;
ret = JS_DumpProfile(filename.mBytes, profileName.mBytes);
}
}
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret));
return true; return true;
} }
@@ -1253,15 +1477,94 @@ IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp)
#endif #endif
#ifdef MOZ_CALLGRIND
static JSBool
StartCallgrind(JSContext *cx, uintN argc, jsval *vp)
{
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind()));
return JS_TRUE;
}
static JSBool
StopCallgrind(JSContext *cx, uintN argc, jsval *vp)
{
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind()));
return JS_TRUE;
}
static JSBool
DumpCallgrind(JSContext *cx, uintN argc, jsval *vp)
{
if (argc == 0) {
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL)));
return JS_TRUE;
}
RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind");
if (!outFile)
return JS_FALSE;
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes)));
return JS_TRUE;
}
#endif
#ifdef MOZ_VTUNE
static JSBool
StartVtune(JSContext *cx, uintN argc, jsval *vp)
{
RequiredStringArg profileName(cx, argc, vp, 0, "startVtune");
if (!profileName)
return JS_FALSE;
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartVtune(profileName.mBytes)));
return JS_TRUE;
}
static JSBool
StopVtune(JSContext *cx, uintN argc, jsval *vp)
{
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopVtune()));
return JS_TRUE;
}
static JSBool
PauseVtune(JSContext *cx, uintN argc, jsval *vp)
{
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_PauseVtune()));
return JS_TRUE;
}
static JSBool
ResumeVtune(JSContext *cx, uintN argc, jsval *vp)
{
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_ResumeVtune()));
return JS_TRUE;
}
#endif
static JSFunctionSpec profiling_functions[] = { static JSFunctionSpec profiling_functions[] = {
JS_FN("startProfiling", StartProfiling, 0,0), JS_FN("startProfiling", StartProfiling, 1,0),
JS_FN("stopProfiling", StopProfiling, 0,0), JS_FN("stopProfiling", StopProfiling, 1,0),
JS_FN("pauseProfilers", PauseProfilers, 1,0),
JS_FN("resumeProfilers", ResumeProfilers, 1,0),
JS_FN("dumpProfile", DumpProfile, 2,0),
#ifdef MOZ_SHARK #ifdef MOZ_SHARK
/* Keep users of the old shark API happy. */ /* Keep users of the old shark API happy. */
JS_FN("connectShark", IgnoreAndReturnTrue, 0,0), JS_FN("connectShark", IgnoreAndReturnTrue, 0,0),
JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0), JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
JS_FN("startShark", StartProfiling, 0,0), JS_FN("startShark", StartProfiling, 0,0),
JS_FN("stopShark", StopProfiling, 0,0), JS_FN("stopShark", StopProfiling, 0,0),
#endif
#ifdef MOZ_CALLGRIND
JS_FN("startCallgrind", StartCallgrind, 0,0),
JS_FN("stopCallgrind", StopCallgrind, 0,0),
JS_FN("dumpCallgrind", DumpCallgrind, 1,0),
#endif
#ifdef MOZ_VTUNE
JS_FN("startVtune", js_StartVtune, 1,0),
JS_FN("stopVtune", js_StopVtune, 0,0),
JS_FN("pauseVtune", js_PauseVtune, 0,0),
JS_FN("resumeVtune", js_ResumeVtune, 0,0),
#endif #endif
JS_FS_END JS_FS_END
}; };
@@ -1284,40 +1587,30 @@ JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj)
#include <valgrind/callgrind.h> #include <valgrind/callgrind.h>
JS_FRIEND_API(JSBool) JS_FRIEND_API(JSBool)
js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp) js_StartCallgrind()
{ {
CALLGRIND_START_INSTRUMENTATION; CALLGRIND_START_INSTRUMENTATION;
CALLGRIND_ZERO_STATS; CALLGRIND_ZERO_STATS;
JS_SET_RVAL(cx, vp, JSVAL_VOID); return true;
return JS_TRUE;
} }
JS_FRIEND_API(JSBool) JS_FRIEND_API(JSBool)
js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp) js_StopCallgrind()
{ {
CALLGRIND_STOP_INSTRUMENTATION; CALLGRIND_STOP_INSTRUMENTATION;
JS_SET_RVAL(cx, vp, JSVAL_VOID); return true;
return JS_TRUE;
} }
JS_FRIEND_API(JSBool) JS_FRIEND_API(JSBool)
js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp) js_DumpCallgrind(const char *outfile)
{ {
JSString *str; if (outfile) {
CALLGRIND_DUMP_STATS_AT(outfile);
jsval *argv = JS_ARGV(cx, vp); } else {
if (argc > 0 && JSVAL_IS_STRING(argv[0])) { CALLGRIND_DUMP_STATS;
str = JSVAL_TO_STRING(argv[0]);
JSAutoByteString bytes(cx, str);
if (!!bytes) {
CALLGRIND_DUMP_STATS_AT(bytes.ptr());
return JS_TRUE;
}
} }
CALLGRIND_DUMP_STATS;
JS_SET_RVAL(cx, vp, JSVAL_VOID); return true;
return JS_TRUE;
} }
#endif /* MOZ_CALLGRIND */ #endif /* MOZ_CALLGRIND */
@@ -1352,8 +1645,8 @@ static const char *vtuneErrorMessages[] = {
}; };
JS_FRIEND_API(JSBool) bool
js_StartVtune(JSContext *cx, uintN argc, jsval *vp) js_StartVtune(const char *profileName)
{ {
VTUNE_EVENT events[] = { VTUNE_EVENT events[] = {
{ 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" }, { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
@@ -1380,62 +1673,54 @@ js_StartVtune(JSContext *cx, uintN argc, jsval *vp)
default_filename, default_filename,
}; };
jsval *argv = JS_ARGV(cx, vp); if (profileName) {
if (argc > 0 && JSVAL_IS_STRING(argv[0])) { char filename[strlen(profileName) + strlen("-vtune.tb5") + 1];
str = JSVAL_TO_STRING(argv[0]); snprintf(filename, sizeof(filename), "%s-vtune.tb5", profileName);
params.tb5Filename = DeflateString(cx, str->chars(), str->length()); params.tb5Filename = filename;
} }
status = VTStartSampling(&params); status = VTStartSampling(&params);
if (params.tb5Filename != default_filename) if (params.tb5Filename != default_filename)
cx->free_(params.tb5Filename); Foreground::free_(params.tb5Filename);
if (status != 0) { if (status != 0) {
if (status == VTAPI_MULTIPLE_RUNS) if (status == VTAPI_MULTIPLE_RUNS)
VTStopSampling(0); VTStopSampling(0);
if (status < sizeof(vtuneErrorMessages)) if (status < sizeof(vtuneErrorMessages))
JS_ReportError(cx, "Vtune setup error: %s", UnsafeError("Vtune setup error: %s", vtuneErrorMessages[status]);
vtuneErrorMessages[status]);
else else
JS_ReportError(cx, "Vtune setup error: %d", UnsafeError("Vtune setup error: %d", status);
status);
return false; return false;
} }
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return true; return true;
} }
JS_FRIEND_API(JSBool) bool
js_StopVtune(JSContext *cx, uintN argc, jsval *vp) js_StopVtune()
{ {
U32 status = VTStopSampling(1); U32 status = VTStopSampling(1);
if (status) { if (status) {
if (status < sizeof(vtuneErrorMessages)) if (status < sizeof(vtuneErrorMessages))
JS_ReportError(cx, "Vtune shutdown error: %s", UnsafeError("Vtune shutdown error: %s", vtuneErrorMessages[status]);
vtuneErrorMessages[status]);
else else
JS_ReportError(cx, "Vtune shutdown error: %d", UnsafeError("Vtune shutdown error: %d", status);
status);
return false; return false;
} }
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return true; return true;
} }
JS_FRIEND_API(JSBool) bool
js_PauseVtune(JSContext *cx, uintN argc, jsval *vp) js_PauseVtune()
{ {
VTPause(); VTPause();
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return true; return true;
} }
JS_FRIEND_API(JSBool) bool
js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp) js_ResumeVtune()
{ {
VTResume(); VTResume();
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return true; return true;
} }
@@ -1906,32 +2191,29 @@ JS_GetFunctionCallback(JSContext *cx)
#endif /* MOZ_TRACE_JSCALLS */ #endif /* MOZ_TRACE_JSCALLS */
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
JS_DumpProfile(JSContext *cx, JSScript *script) JS_DumpBytecode(JSContext *cx, JSScript *script)
{ {
JS_ASSERT(!cx->runtime->gcRunning); JS_ASSERT(!cx->runtime->gcRunning);
#if defined(DEBUG) #if defined(DEBUG)
if (script->pcCounters) { AutoArenaAllocator mark(&cx->tempPool);
// Display hit counts for every JS code line Sprinter sprinter;
AutoArenaAllocator mark(&cx->tempPool); INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
Sprinter sprinter;
INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
fprintf(stdout, "--- PC COUNTS %s:%d ---\n", script->filename, script->lineno); fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno);
js_Disassemble(cx, script, true, &sprinter); js_Disassemble(cx, script, true, &sprinter);
fprintf(stdout, "%s\n", sprinter.base); fprintf(stdout, "%s\n", sprinter.base);
fprintf(stdout, "--- END PC COUNTS %s:%d ---\n", script->filename, script->lineno); fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno);
}
#endif #endif
} }
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
JS_DumpAllProfiles(JSContext *cx) JS_DumpCompartmentBytecode(JSContext *cx)
{ {
for (JSScript *script = (JSScript *) JS_LIST_HEAD(&cx->compartment->scripts); for (JSScript *script = (JSScript *) JS_LIST_HEAD(&cx->compartment->scripts);
script != (JSScript *) &cx->compartment->scripts; script != (JSScript *) &cx->compartment->scripts;
script = (JSScript *) JS_NEXT_LINK((JSCList *)script)) script = (JSScript *) JS_NEXT_LINK((JSCList *)script))
{ {
JS_DumpProfile(cx, script); JS_DumpBytecode(cx, script);
} }
} }

View File

@@ -497,12 +497,51 @@ JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks);
extern JS_PUBLIC_API(JSDebugHooks *) extern JS_PUBLIC_API(JSDebugHooks *)
JS_ClearContextDebugHooks(JSContext *cx); JS_ClearContextDebugHooks(JSContext *cx);
/**
* Start any profilers that are available and have been configured on for this
* platform. This is NOT thread safe.
*
* The profileName is used by some profilers to describe the current profiling
* run. It may be used for part of the filename of the output, but the
* specifics depend on the profiler. Many profilers will ignore it. Passing in
* NULL is legal; some profilers may use it to output to stdout or similar.
*
* Returns true if no profilers fail to start.
*/
extern JS_PUBLIC_API(JSBool) extern JS_PUBLIC_API(JSBool)
JS_StartProfiling(); JS_StartProfiling(const char *profileName);
extern JS_PUBLIC_API(void) /**
JS_StopProfiling(); * Stop any profilers that were previously started with JS_StartProfiling.
* Returns true if no profilers fail to stop.
*/
extern JS_PUBLIC_API(JSBool)
JS_StopProfiling(const char *profileName);
/**
* Write the current profile data to the given file, if applicable to whatever
* profiler is being used.
*/
extern JS_PUBLIC_API(JSBool)
JS_DumpProfile(const char *outfile, const char *profileName);
/**
* Pause currently active profilers (only supported by some profilers). Returns
* whether any profilers failed to pause. (Profilers that do not support
* pause/resume do not count.)
*/
extern JS_PUBLIC_API(JSBool)
JS_PauseProfilers(const char *profileName);
/**
* Resume suspended profilers
*/
extern JS_PUBLIC_API(JSBool)
JS_ResumeProfilers(const char *profileName);
/**
* Add various profiling-related functions as properties of the given object.
*/
extern JS_PUBLIC_API(JSBool) extern JS_PUBLIC_API(JSBool)
JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj); JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj);
@@ -510,32 +549,40 @@ JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj);
extern JS_PUBLIC_API(JSBool) extern JS_PUBLIC_API(JSBool)
JS_DefineDebuggerObject(JSContext *cx, JSObject *obj); JS_DefineDebuggerObject(JSContext *cx, JSObject *obj);
/**
* The profiling API calls are not able to report errors, so they use a
* thread-unsafe global memory buffer to hold the last error encountered. This
* should only be called after something returns false.
*/
JS_PUBLIC_API(const char *)
JS_UnsafeGetLastProfilingError();
#ifdef MOZ_CALLGRIND #ifdef MOZ_CALLGRIND
extern JS_FRIEND_API(JSBool) extern JS_FRIEND_API(JSBool)
js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp); js_StopCallgrind();
extern JS_FRIEND_API(JSBool) extern JS_FRIEND_API(JSBool)
js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp); js_StartCallgrind();
extern JS_FRIEND_API(JSBool) extern JS_FRIEND_API(JSBool)
js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp); js_DumpCallgrind(const char *outfile);
#endif /* MOZ_CALLGRIND */ #endif /* MOZ_CALLGRIND */
#ifdef MOZ_VTUNE #ifdef MOZ_VTUNE
extern JS_FRIEND_API(JSBool) extern JS_FRIEND_API(bool)
js_StartVtune(JSContext *cx, uintN argc, jsval *vp); js_StartVtune(const char *profileName);
extern JS_FRIEND_API(JSBool) extern JS_FRIEND_API(bool)
js_StopVtune(JSContext *cx, uintN argc, jsval *vp); js_StopVtune();
extern JS_FRIEND_API(JSBool) extern JS_FRIEND_API(bool)
js_PauseVtune(JSContext *cx, uintN argc, jsval *vp); js_PauseVtune();
extern JS_FRIEND_API(JSBool) extern JS_FRIEND_API(bool)
js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp); js_ResumeVtune();
#endif /* MOZ_VTUNE */ #endif /* MOZ_VTUNE */
@@ -569,10 +616,10 @@ JS_GetFunctionCallback(JSContext *cx);
#endif /* MOZ_TRACE_JSCALLS */ #endif /* MOZ_TRACE_JSCALLS */
extern JS_PUBLIC_API(void) extern JS_PUBLIC_API(void)
JS_DumpProfile(JSContext *cx, JSScript *script); JS_DumpBytecode(JSContext *cx, JSScript *script);
extern JS_PUBLIC_API(void) extern JS_PUBLIC_API(void)
JS_DumpAllProfiles(JSContext *cx); JS_DumpCompartmentBytecode(JSContext *cx);
JS_END_EXTERN_C JS_END_EXTERN_C

View File

@@ -2202,7 +2202,6 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind)
(compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT))
{ {
compartment->freeLists.checkEmpty(); compartment->freeLists.checkEmpty();
Probes::GCEndSweepPhase(compartment);
if (callback) if (callback)
JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY)); JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY));
if (compartment->principals) if (compartment->principals)
@@ -2338,6 +2337,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
* unreachable compartments. * unreachable compartments.
*/ */
if (comp) { if (comp) {
Probes::GCStartSweepPhase(comp);
comp->sweep(cx, 0); comp->sweep(cx, 0);
comp->finalizeObjectArenaLists(cx); comp->finalizeObjectArenaLists(cx);
GCTIMESTAMP(sweepObjectEnd); GCTIMESTAMP(sweepObjectEnd);
@@ -2345,6 +2345,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
GCTIMESTAMP(sweepStringEnd); GCTIMESTAMP(sweepStringEnd);
comp->finalizeShapeArenaLists(cx); comp->finalizeShapeArenaLists(cx);
GCTIMESTAMP(sweepShapeEnd); GCTIMESTAMP(sweepShapeEnd);
Probes::GCEndSweepPhase(comp);
} else { } else {
/* /*
* Some sweeping is not compartment-specific. Start a NULL-compartment * Some sweeping is not compartment-specific. Start a NULL-compartment

View File

@@ -53,10 +53,6 @@
#include "jsstaticcheck.h" #include "jsstaticcheck.h"
#include "jsstr.h" #include "jsstr.h"
#ifdef __APPLE__
#include "sharkctl.h"
#endif
#include "jsprobes.h" #include "jsprobes.h"
#include <sys/types.h> #include <sys/types.h>
@@ -69,48 +65,65 @@ const char Probes::anonymousName[] = "(anonymous)";
bool Probes::ProfilingActive = true; bool Probes::ProfilingActive = true;
bool #ifdef INCLUDE_MOZILLA_DTRACE
Probes::controlProfilers(JSContext *cx, bool toState) static const char *
ScriptFilename(const JSScript *script)
{ {
JSBool ok = JS_TRUE; if (!script)
#if defined(MOZ_CALLGRIND) || defined(MOZ_VTUNE) return Probes::nullName;
jsval dummy; if (!script->filename)
#endif return Probes::anonymousName;
return script->filename;
}
if (! ProfilingActive && toState) { static const char *
#if defined(MOZ_SHARK) && defined(__APPLE__) FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString* bytes)
if (!Shark::Start()) {
ok = JS_FALSE; if (!fun)
#endif return Probes::nullName;
#ifdef MOZ_CALLGRIND JSAtom *atom = const_cast<JSAtom*>(fun->atom);
if (! js_StartCallgrind(cx, 0, &dummy)) if (!atom)
ok = JS_FALSE; return Probes::anonymousName;
#endif return bytes->encode(cx, atom) ? bytes->ptr() : Probes::nullName;
#ifdef MOZ_VTUNE }
if (! js_ResumeVtune(cx, 0, &dummy))
ok = JS_FALSE;
#endif
} else if (ProfilingActive && ! toState) {
#if defined(MOZ_SHARK) && defined(__APPLE__)
Shark::Stop();
#endif
#ifdef MOZ_CALLGRIND
if (! js_StopCallgrind(cx, 0, &dummy))
ok = JS_FALSE;
#endif
#ifdef MOZ_VTUNE
if (! js_PauseVtune(cx, 0, &dummy))
ok = JS_FALSE;
#endif
}
ProfilingActive = toState; static const char *
FunctionClassname(const JSFunction *fun)
{
if (!fun || FUN_INTERPRETED(fun))
return Probes::nullName;
if (!(fun->flags & JSFUN_TRCINFO) && FUN_CLASP(fun))
return (char *)FUN_CLASP(fun)->name;
return Probes::nullName;
}
return ok; /*
* These functions call the DTrace macros for the JavaScript USDT probes.
* Originally this code was inlined in the JavaScript code; however since
* a number of operations are called, these have been placed into functions
* to reduce any negative compiler optimization effect that the addition of
* a number of usually unused lines of code would cause.
*/
void
Probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
{
JSAutoByteString funNameBytes;
JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun),
FunctionName(cx, fun, &funNameBytes));
} }
void void
Probes::current_location(JSContext *cx, int* lineno, char const **filename) Probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
{
JSAutoByteString funNameBytes;
JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun),
FunctionName(cx, fun, &funNameBytes));
}
#endif
#ifdef MOZ_ETW
static void
current_location(JSContext *cx, int* lineno, char const **filename)
{ {
JSScript *script = js_GetCurrentScript(cx); JSScript *script = js_GetCurrentScript(cx);
if (! script) { if (! script) {
@@ -122,75 +135,6 @@ Probes::current_location(JSContext *cx, int* lineno, char const **filename)
*filename = ScriptFilename(script); *filename = ScriptFilename(script);
} }
const char *
Probes::FunctionClassname(const JSFunction *fun)
{
return (fun && !FUN_INTERPRETED(fun) && !(fun->flags & JSFUN_TRCINFO) && FUN_CLASP(fun))
? (char *)FUN_CLASP(fun)->name
: nullName;
}
const char *
Probes::ScriptFilename(JSScript *script)
{
return (script && script->filename) ? (char *)script->filename : nullName;
}
int
Probes::FunctionLineNumber(JSContext *cx, const JSFunction *fun)
{
if (fun && FUN_INTERPRETED(fun))
return (int) JS_GetScriptBaseLineNumber(cx, FUN_SCRIPT(fun));
return 0;
}
#ifdef INCLUDE_MOZILLA_DTRACE
/*
* These functions call the DTrace macros for the JavaScript USDT probes.
* Originally this code was inlined in the JavaScript code; however since
* a number of operations are called, these have been placed into functions
* to reduce any negative compiler optimization effect that the addition of
* a number of usually unused lines of code would cause.
*/
void
Probes::enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script)
{
JSAutoByteString funNameBytes;
JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun),
FunctionName(cx, fun, &funNameBytes));
}
void
Probes::handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script)
{
JSAutoByteString funNameBytes;
JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun),
FunctionName(cx, fun, &funNameBytes));
}
#endif
bool
Probes::startProfiling()
{
#if defined(MOZ_SHARK) && defined(__APPLE__)
if (Shark::Start())
return true;
#endif
return false;
}
void
Probes::stopProfiling()
{
#if defined(MOZ_SHARK) && defined(__APPLE__)
Shark::Stop();
#endif
}
#ifdef MOZ_ETW
/* /*
* ETW (Event Tracing for Windows) * ETW (Event Tracing for Windows)
* *

View File

@@ -14,10 +14,14 @@
* for the specific language governing rights and limitations under the * for the specific language governing rights and limitations under the
* License. * License.
* *
* Copyright (C) 2007 Sun Microsystems, Inc. All Rights Reserved. * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* June 12, 2009.
*
* The Initial Developer of the Original Code is
* the Mozilla Corporation.
* *
* Contributor(s): * Contributor(s):
* Brendan Eich <brendan@mozilla.org> * Steve Fink <sfink@mozilla.org>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"), * either of the GNU General Public License Version 2 or later (the "GPL"),
@@ -44,168 +48,204 @@
namespace js { namespace js {
class Probes { namespace Probes {
static bool ProfilingActive;
static bool controlProfilers(JSContext *cx, bool toState);
static const char nullName[]; /*
static const char anonymousName[]; * Static probes
*
* The probe points defined in this file are scattered around the SpiderMonkey
* source tree. The presence of Probes::someEvent() means that someEvent is
* about to happen or has happened. To the extent possible, probes should be
* inserted in all paths associated with a given event, regardless of the
* active runmode (interpreter/traceJIT/methodJIT/ionJIT).
*
* When a probe fires, it is handled by any probe handling backends that have
* been compiled in. By default, most probes do nothing or at least do nothing
* expensive, so the presence of the probe should have negligible effect on
* running time. (Probes in slow paths may do something by default, as long as
* there is no noticeable slowdown.)
*
* For some probes, the mere existence of the probe is too expensive even if it
* does nothing when called. For example, just having consistent information
* available for a function call entry/exit probe causes the JITs to
* de-optimize function calls. In those cases, the JITs may query at compile
* time whether a probe is desired, and omit the probe invocation if not. If a
* probe is runtime-disabled at compilation time, it is not guaranteed to fire
* within a compiled function if it is later enabled.
*
* Not all backends handle all of the probes listed here.
*/
static const char *FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString* bytes) /*
{ * Internal use only: remember whether "profiling", whatever that means, is
if (!fun) * currently active. Used for state management.
return nullName; */
JSAtom *atom = const_cast<JSAtom*>(fun->atom); extern bool ProfilingActive;
if (!atom)
return anonymousName;
return bytes->encode(cx, atom) ? bytes->ptr() : nullName;
}
static const char *ScriptFilename(const JSScript *script) { extern const char nullName[];
if (! script) extern const char anonymousName[];
return "(null)";
if (! script->filename)
return "(anonymous)";
return script->filename;
}
static const char *ObjectClassname(JSObject *obj) { /* JSRuntime created, with currently valid fields */
if (! obj) bool createRuntime(JSRuntime *rt);
return "(null object)";
Class *clasp = obj->getClass();
if (! clasp)
return "(null)";
const char *class_name = clasp->name;
if (! class_name)
return "(null class name)";
return class_name;
}
static void current_location(JSContext *cx, int* lineno, char const **filename); /* JSRuntime about to be destroyed */
bool destroyRuntime(JSRuntime *rt);
static const char *FunctionClassname(const JSFunction *fun); /* Total JS engine shutdown */
static const char *ScriptFilename(JSScript *script); bool shutdown();
static int FunctionLineNumber(JSContext *cx, const JSFunction *fun);
static void enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script); /*
static void handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script); * Test whether we are tracking JS function call enter/exit. The JITs use this
static void finalizeObjectImpl(JSObject *obj); * to decide whether they can optimize in a way that would prevent probes from
public: * firing.
static bool createRuntime(JSRuntime *rt); */
static bool destroyRuntime(JSRuntime *rt); bool callTrackingActive(JSContext *);
static bool shutdown();
/* /* Entering a JS function */
* Pause/resume whatever profiling mechanism is currently compiled bool enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1);
* in, if applicable. This will not affect things like dtrace.
*
* Do not mix calls to these APIs with calls to the individual
* profilers' pase/resume functions, because only overall state is
* tracked, not the state of each profiler.
*
* Return the previous state.
*/
static bool pauseProfilers(JSContext *cx) {
bool prevState = ProfilingActive;
controlProfilers(cx, false);
return prevState;
}
static bool resumeProfilers(JSContext *cx) {
bool prevState = ProfilingActive;
controlProfilers(cx, true);
return prevState;
}
static bool callTrackingActive(JSContext *); /* About to leave a JS function */
bool exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0);
static bool enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1); /* Executing a script */
static bool exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0); bool startExecution(JSContext *cx, JSScript *script);
static bool startExecution(JSContext *cx, JSScript *script); /* Script has completed execution */
static bool stopExecution(JSContext *cx, JSScript *script); bool stopExecution(JSContext *cx, JSScript *script);
static bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); /* Heap has been resized */
bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
/* |obj| must exist (its class and size are computed) */ /*
static bool createObject(JSContext *cx, JSObject *obj); * Object has been created. |obj| must exist (its class and size are read)
*/
bool createObject(JSContext *cx, JSObject *obj);
static bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); /* Object has been resized */
bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize);
/* |obj| must still exist (its class is accessed) */ /*
static bool finalizeObject(JSObject *obj); * Object is about to be finalized. |obj| must still exist (its class is
* read)
*/
bool finalizeObject(JSObject *obj);
/* /*
* |string| does not need to contain any content yet; only its * String has been created.
* pointer value is used. |length| is the length of the string and *
* does not imply anything about the amount of storage consumed to * |string|'s content is not (yet) valid. |length| is the length of the string
* store the string. (It may be a short string, an external * and does not imply anything about the amount of storage consumed to store
* string, or a rope, and the encoding is not taken into * the string. (It may be a short string, an external string, or a rope, and
* consideration.) * the encoding is not taken into consideration.)
*/ */
static bool createString(JSContext *cx, JSString *string, size_t length); bool createString(JSContext *cx, JSString *string, size_t length);
/* /*
* |string| must still have a valid length. * String is about to be finalized
*/ *
static bool finalizeString(JSString *string); * |string| must still have a valid length.
*/
bool finalizeString(JSString *string);
static bool compileScriptBegin(JSContext *cx, const char *filename, int lineno); /* Script is about to be compiled */
static bool compileScriptEnd(JSContext *cx, JSScript *script, const char *filename, int lineno); bool compileScriptBegin(JSContext *cx, const char *filename, int lineno);
static bool calloutBegin(JSContext *cx, JSFunction *fun); /* Script has just finished compilation */
static bool calloutEnd(JSContext *cx, JSFunction *fun); bool compileScriptEnd(JSContext *cx, JSScript *script, const char *filename, int lineno);
static bool acquireMemory(JSContext *cx, void *address, size_t nbytes); /* About to make a call from JS into native code */
static bool releaseMemory(JSContext *cx, void *address, size_t nbytes); bool calloutBegin(JSContext *cx, JSFunction *fun);
static bool GCStart(JSCompartment *compartment); /* Native code called by JS has terminated */
static bool GCEnd(JSCompartment *compartment); bool calloutEnd(JSContext *cx, JSFunction *fun);
static bool GCStartMarkPhase(JSCompartment *compartment);
static bool GCEndMarkPhase(JSCompartment *compartment); /* Unimplemented */
static bool GCStartSweepPhase(JSCompartment *compartment); bool acquireMemory(JSContext *cx, void *address, size_t nbytes);
static bool GCEndSweepPhase(JSCompartment *compartment); bool releaseMemory(JSContext *cx, void *address, size_t nbytes);
static bool CustomMark(JSString *string); /*
static bool CustomMark(const char *string); * Garbage collection probes
static bool CustomMark(int marker); *
* GC timing is tricky and at the time of this writing is changing frequently.
* GCStart(NULL)/GCEnd(NULL) are intended to bracket the entire garbage
* collection (either global or single-compartment), but a separate thread may
* continue doing work after GCEnd.
*
* Multiple compartments' GC will be interleaved during a global collection
* (eg, compartment 1 starts, compartment 2 starts, compartment 1 ends, ...)
*/
bool GCStart(JSCompartment *compartment);
bool GCEnd(JSCompartment *compartment);
static bool startProfiling(); bool GCStartMarkPhase(JSCompartment *compartment);
static void stopProfiling(); bool GCEndMarkPhase(JSCompartment *compartment);
bool GCStartSweepPhase(JSCompartment *compartment);
bool GCEndSweepPhase(JSCompartment *compartment);
/*
* Various APIs for inserting custom probe points. These might be used to mark
* when something starts and stops, or for various other purposes the user has
* in mind. These are useful to export to JS so that JS code can mark
* application-meaningful events and phases of execution.
*
* Not all backends support these.
*/
bool CustomMark(JSString *string);
bool CustomMark(const char *string);
bool CustomMark(int marker);
/*
* Internal: DTrace-specific functions to be called during Probes::enterJSFun
* and Probes::exitJSFun. These will not be inlined, but the argument
* marshalling required for these probe points is expensive enough that it
* shouldn't really matter.
*/
void DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script);
void DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script);
/*
* Internal: ETW-specific probe functions
*/
#ifdef MOZ_ETW #ifdef MOZ_ETW
// ETW Handlers // ETW Handlers
static bool ETWCreateRuntime(JSRuntime *rt); bool ETWCreateRuntime(JSRuntime *rt);
static bool ETWDestroyRuntime(JSRuntime *rt); bool ETWDestroyRuntime(JSRuntime *rt);
static bool ETWShutdown(); bool ETWShutdown();
static bool ETWCallTrackingActive(JSContext *cx); bool ETWCallTrackingActive(JSContext *cx);
static bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter);
static bool ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); bool ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter);
static bool ETWCreateObject(JSContext *cx, JSObject *obj); bool ETWCreateObject(JSContext *cx, JSObject *obj);
static bool ETWFinalizeObject(JSObject *obj); bool ETWFinalizeObject(JSObject *obj);
static bool ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); bool ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize);
static bool ETWCreateString(JSContext *cx, JSString *string, size_t length); bool ETWCreateString(JSContext *cx, JSString *string, size_t length);
static bool ETWFinalizeString(JSString *string); bool ETWFinalizeString(JSString *string);
static bool ETWCompileScriptBegin(const char *filename, int lineno); bool ETWCompileScriptBegin(const char *filename, int lineno);
static bool ETWCompileScriptEnd(const char *filename, int lineno); bool ETWCompileScriptEnd(const char *filename, int lineno);
static bool ETWCalloutBegin(JSContext *cx, JSFunction *fun); bool ETWCalloutBegin(JSContext *cx, JSFunction *fun);
static bool ETWCalloutEnd(JSContext *cx, JSFunction *fun); bool ETWCalloutEnd(JSContext *cx, JSFunction *fun);
static bool ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes); bool ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes);
static bool ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes); bool ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes);
static bool ETWGCStart(JSCompartment *compartment); bool ETWGCStart(JSCompartment *compartment);
static bool ETWGCEnd(JSCompartment *compartment); bool ETWGCEnd(JSCompartment *compartment);
static bool ETWGCStartMarkPhase(JSCompartment *compartment); bool ETWGCStartMarkPhase(JSCompartment *compartment);
static bool ETWGCEndMarkPhase(JSCompartment *compartment); bool ETWGCEndMarkPhase(JSCompartment *compartment);
static bool ETWGCStartSweepPhase(JSCompartment *compartment); bool ETWGCStartSweepPhase(JSCompartment *compartment);
static bool ETWGCEndSweepPhase(JSCompartment *compartment); bool ETWGCEndSweepPhase(JSCompartment *compartment);
static bool ETWCustomMark(JSString *string); bool ETWCustomMark(JSString *string);
static bool ETWCustomMark(const char *string); bool ETWCustomMark(const char *string);
static bool ETWCustomMark(int marker); bool ETWCustomMark(int marker);
static bool ETWStartExecution(JSContext *cx, JSScript *script); bool ETWStartExecution(JSContext *cx, JSScript *script);
static bool ETWStopExecution(JSContext *cx, JSScript *script); bool ETWStopExecution(JSContext *cx, JSScript *script);
static bool ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); bool ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
#endif #endif
};
} /* namespace Probes */
/*
* Probe handlers are implemented inline for minimal performance impact,
* especially important when no backends are enabled.
*/
inline bool inline bool
Probes::createRuntime(JSRuntime *rt) Probes::createRuntime(JSRuntime *rt)
@@ -258,30 +298,13 @@ Probes::callTrackingActive(JSContext *cx)
return false; return false;
} }
extern inline JS_FRIEND_API(JSBool)
js_PauseProfilers(JSContext *cx, uintN argc, jsval *vp)
{
Probes::pauseProfilers(cx);
return JS_TRUE;
}
extern inline JS_FRIEND_API(JSBool)
js_ResumeProfilers(JSContext *cx, uintN argc, jsval *vp)
{
Probes::resumeProfilers(cx);
return JS_TRUE;
}
extern JS_FRIEND_API(JSBool)
js_ResumeProfilers(JSContext *cx, uintN argc, jsval *vp);
inline bool inline bool
Probes::enterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter) Probes::enterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
{ {
bool ok = true; bool ok = true;
#ifdef INCLUDE_MOZILLA_DTRACE #ifdef INCLUDE_MOZILLA_DTRACE
if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
enterJSFunImpl(cx, fun, script); DTraceEnterJSFun(cx, fun, script);
#endif #endif
#ifdef MOZ_TRACE_JSCALLS #ifdef MOZ_TRACE_JSCALLS
cx->doFunctionCallback(fun, script, counter); cx->doFunctionCallback(fun, script, counter);
@@ -301,7 +324,7 @@ Probes::exitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
#ifdef INCLUDE_MOZILLA_DTRACE #ifdef INCLUDE_MOZILLA_DTRACE
if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
handleFunctionReturn(cx, fun, script); DTraceExitJSFun(cx, fun, script);
#endif #endif
#ifdef MOZ_TRACE_JSCALLS #ifdef MOZ_TRACE_JSCALLS
if (counter > 0) if (counter > 0)
@@ -329,6 +352,20 @@ Probes::resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize)
return ok; return ok;
} }
#ifdef INCLUDE_MOZILLA_DTRACE
static const char *ObjectClassname(JSObject *obj) {
if (! obj)
return "(null object)";
Class *clasp = obj->getClass();
if (! clasp)
return "(null)";
const char *class_name = clasp->name;
if (! class_name)
return "(null class name)";
return class_name;
}
#endif
inline bool inline bool
Probes::createObject(JSContext *cx, JSObject *obj) Probes::createObject(JSContext *cx, JSObject *obj)
{ {
@@ -657,5 +694,10 @@ struct AutoFunctionCallProbe {
}; };
} /* namespace js */ } /* namespace js */
/*
* Internal functions for controlling various profilers. The profiler-specific
* implementations of these are mostly in jsdbgapi.cpp.
*/
#endif /* _JSPROBES_H */ #endif /* _JSPROBES_H */

View File

@@ -3960,19 +3960,6 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("evalInFrame", EvalInFrame, 2,0), JS_FN("evalInFrame", EvalInFrame, 2,0),
JS_FN("shapeOf", ShapeOf, 1,0), JS_FN("shapeOf", ShapeOf, 1,0),
JS_FN("resolver", Resolver, 1,0), JS_FN("resolver", Resolver, 1,0),
JS_FN("pauseProfilers", js_PauseProfilers, 0,0),
JS_FN("resumeProfilers", js_ResumeProfilers, 0,0),
#ifdef MOZ_CALLGRIND
JS_FN("startCallgrind", js_StartCallgrind, 0,0),
JS_FN("stopCallgrind", js_StopCallgrind, 0,0),
JS_FN("dumpCallgrind", js_DumpCallgrind, 1,0),
#endif
#ifdef MOZ_VTUNE
JS_FN("startVtune", js_StartVtune, 1,0),
JS_FN("stopVtune", js_StopVtune, 0,0),
JS_FN("pauseVtune", js_PauseVtune, 0,0),
JS_FN("resumeVtune", js_ResumeVtune, 0,0),
#endif
#ifdef MOZ_TRACEVIS #ifdef MOZ_TRACEVIS
JS_FN("startTraceVis", StartTraceVisNative, 1,0), JS_FN("startTraceVis", StartTraceVisNative, 1,0),
JS_FN("stopTraceVis", StopTraceVisNative, 0,0), JS_FN("stopTraceVis", StopTraceVisNative, 0,0),
@@ -4099,19 +4086,6 @@ static const char *const shell_help_messages[] = {
"shapeOf(obj) Get the shape of obj (an implementation detail)", "shapeOf(obj) Get the shape of obj (an implementation detail)",
"resolver(src[, proto]) Create object with resolve hook that copies properties\n" "resolver(src[, proto]) Create object with resolve hook that copies properties\n"
" from src. If proto is omitted, use Object.prototype.", " from src. If proto is omitted, use Object.prototype.",
"pauseProfilers() Pause all profilers that can be paused",
"resumeProfilers() Resume profilers if they are paused",
#ifdef MOZ_CALLGRIND
"startCallgrind() Start callgrind instrumentation",
"stopCallgrind() Stop callgrind instrumentation",
"dumpCallgrind([name]) Dump callgrind counters",
#endif
#ifdef MOZ_VTUNE
"startVtune([filename]) Start vtune instrumentation",
"stopVtune() Stop vtune instrumentation",
"pauseVtune() Pause vtune collection",
"resumeVtune() Resume vtune collection",
#endif
#ifdef MOZ_TRACEVIS #ifdef MOZ_TRACEVIS
"startTraceVis(filename) Start TraceVis recording (stops any current recording)", "startTraceVis(filename) Start TraceVis recording (stops any current recording)",
"stopTraceVis() Stop TraceVis recording", "stopTraceVis() Stop TraceVis recording",
@@ -4153,20 +4127,50 @@ static const char *const shell_help_messages[] = {
/* Keep these last: see the static assertion below. */ /* Keep these last: see the static assertion below. */
#ifdef MOZ_PROFILING #ifdef MOZ_PROFILING
"startProfiling() Start a profiling session.\n" "startProfiling([profileName])\n"
" Start a profiling session\n"
" Profiler must be running with programatic sampling", " Profiler must be running with programatic sampling",
"stopProfiling() Stop a running profiling session\n" "stopProfiling([profileName])\n"
" Stop a running profiling session",
"pauseProfilers([profileName])\n"
" Pause a running profiling session",
"resumeProfilers([profileName])\n"
" Resume a paused profiling session",
"dumpProfile([outfile[, profileName]])\n"
" Dump out current profile info (only valid for callgrind)",
# ifdef MOZ_CALLGRIND
"startCallgrind() Start Callgrind instrumentation",
"stopCallgrind() Stop Callgrind instrumentation",
"dumpCallgrind([outfile]) Dump current Callgrind counters to file or stdout",
# endif
# ifdef MOZ_VTUNE
"startVtune() Start Vtune instrumentation",
"stopVtune() Stop Vtune instrumentation",
"pauseVtune() Pause Vtune collection",
"resumeVtune() Resume Vtune collection",
# endif
#endif #endif
}; };
#ifdef MOZ_PROFILING #ifdef MOZ_PROFILING
#define PROFILING_FUNCTION_COUNT 2 # define PROFILING_FUNCTION_COUNT 5
# ifdef MOZ_CALLGRIND
# define CALLGRIND_FUNCTION_COUNT 3
# else
# define CALLGRIND_FUNCTION_COUNT 0
# endif
# ifdef MOZ_VTUNE
# define VTUNE_FUNCTION_COUNT 4
# else
# define VTUNE_FUNCTION_COUNT 0
# endif
# define EXTERNAL_FUNCTION_COUNT (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
#else #else
#define PROFILING_FUNCTION_COUNT 0 # define EXTERNAL_FUNCTION_COUNT 0
#endif #endif
/* Help messages must match shell functions. */ /* Help messages must match shell functions. */
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - PROFILING_FUNCTION_COUNT == JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - EXTERNAL_FUNCTION_COUNT ==
JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */); JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */);
#ifdef DEBUG #ifdef DEBUG
@@ -4177,7 +4181,7 @@ CheckHelpMessages()
const char *lp; const char *lp;
/* Messages begin with "function_name(" prefix and don't end with \n. */ /* Messages begin with "function_name(" prefix and don't end with \n. */
for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages) - PROFILING_FUNCTION_COUNT; ++m) { for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages) - EXTERNAL_FUNCTION_COUNT; ++m) {
lp = strchr(*m, '('); lp = strchr(*m, '(');
JS_ASSERT(lp); JS_ASSERT(lp);
JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name, JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
@@ -4190,6 +4194,9 @@ CheckHelpMessages()
#endif #endif
#undef PROFILING_FUNCTION_COUNT #undef PROFILING_FUNCTION_COUNT
#undef CALLGRIND_FUNCTION_COUNT
#undef VTUNE_FUNCTION_COUNT
#undef EXTERNAL_FUNCTION_COUNT
static JSBool static JSBool
Help(JSContext *cx, uintN argc, jsval *vp) Help(JSContext *cx, uintN argc, jsval *vp)
@@ -5221,7 +5228,7 @@ Shell(JSContext *cx, OptionParser *op, char **envp)
#endif /* JSDEBUGGER */ #endif /* JSDEBUGGER */
if (enableDisassemblyDumps) if (enableDisassemblyDumps)
JS_DumpAllProfiles(cx); JS_DumpCompartmentBytecode(cx);
return result; return result;
} }

View File

@@ -291,17 +291,6 @@ static JSFunctionSpec gGlobalFun[] = {
{"atob", Atob, 1,0}, {"atob", Atob, 1,0},
{"btoa", Btoa, 1,0}, {"btoa", Btoa, 1,0},
{"File", File, 1,JSFUN_CONSTRUCTOR}, {"File", File, 1,JSFUN_CONSTRUCTOR},
#ifdef MOZ_CALLGRIND
{"startCallgrind", js_StartCallgrind, 0,0},
{"stopCallgrind", js_StopCallgrind, 0,0},
{"dumpCallgrind", js_DumpCallgrind, 1,0},
#endif
#ifdef MOZ_VTUNE
{"startVtune", js_StartVtune, 1,0},
{"stopVtune", js_StopVtune, 0,0},
{"pauseVtune", js_PauseVtune, 0,0},
{"resumeVtune", js_ResumeVtune, 0,0},
#endif
#ifdef MOZ_TRACEVIS #ifdef MOZ_TRACEVIS
{"initEthogram", js_InitEthogram, 0,0}, {"initEthogram", js_InitEthogram, 0,0},
{"shutdownEthogram", js_ShutdownEthogram, 0,0}, {"shutdownEthogram", js_ShutdownEthogram, 0,0},

View File

@@ -847,11 +847,6 @@ static JSFunctionSpec glob_functions[] = {
#endif #endif
{"sendCommand", SendCommand, 1,0}, {"sendCommand", SendCommand, 1,0},
{"getChildGlobalObject", GetChildGlobalObject, 0,0}, {"getChildGlobalObject", GetChildGlobalObject, 0,0},
#ifdef MOZ_CALLGRIND
{"startCallgrind", js_StartCallgrind, 0,0},
{"stopCallgrind", js_StopCallgrind, 0,0},
{"dumpCallgrind", js_DumpCallgrind, 1,0},
#endif
{nsnull,nsnull,0,0} {nsnull,nsnull,0,0}
}; };

View File

@@ -56,6 +56,7 @@
#include "nsIFile.h" #include "nsIFile.h"
#include "nsIProperties.h" #include "nsIProperties.h"
#include "nsXULAppAPI.h" #include "nsXULAppAPI.h"
#include "jsdbgapi.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
@@ -95,17 +96,6 @@ void passed(const char* test)
// Code profiling // Code profiling
// //
static const char* gCurrentProfile; static const char* gCurrentProfile;
static PRBool gProfilerTriedInit = PR_FALSE;
static PRBool gProfilerInited = PR_FALSE;
// Platform profilers must implement these functions.
// Init and deinit are guaranteed to only be called once, and
// StartProfile/StopProfile may assume that they are only called
// when the profiler has successfully been initialized.
static PRBool _PlatformInitProfiler();
static PRBool _PlatformStartProfile(const char* profileName);
static PRBool _PlatformStopProfile(const char* profileName);
static PRBool _PlatformDeinitProfiler();
/** /**
* If the build has been configured properly, start the best code profiler * If the build has been configured properly, start the best code profiler
@@ -124,19 +114,12 @@ static PRBool _PlatformDeinitProfiler();
inline PRBool inline PRBool
StartProfiling(const char* profileName) StartProfiling(const char* profileName)
{ {
if (!gProfilerTriedInit) {
gProfilerTriedInit = PR_TRUE;
gProfilerInited = _PlatformInitProfiler();
}
if (!gProfilerInited)
return PR_FALSE;
NS_ASSERTION(profileName, "need a name for this profile"); NS_ASSERTION(profileName, "need a name for this profile");
NS_PRECONDITION(!gCurrentProfile, "started a new profile before stopping another"); NS_PRECONDITION(!gCurrentProfile, "started a new profile before stopping another");
PRBool rv = _PlatformStartProfile(profileName); JSBool ok = JS_StartProfiling(profileName);
gCurrentProfile = profileName; gCurrentProfile = profileName;
return rv; return ok ? PR_TRUE : PR_FALSE;
} }
/** /**
@@ -153,78 +136,13 @@ StartProfiling(const char* profileName)
inline PRBool inline PRBool
StopProfiling() StopProfiling()
{ {
NS_ASSERTION(gProfilerTriedInit, "tried to stop profile before starting one");
if (!gProfilerInited)
return PR_FALSE;
NS_PRECONDITION(gCurrentProfile, "tried to stop profile before starting one"); NS_PRECONDITION(gCurrentProfile, "tried to stop profile before starting one");
const char* profileName = gCurrentProfile; const char* profileName = gCurrentProfile;
gCurrentProfile = 0; gCurrentProfile = 0;
return _PlatformStopProfile(profileName); return JS_StopProfiling(profileName) ? PR_TRUE : PR_FALSE;
} }
//--------------------------------------------------
// Shark impl
#if defined(MOZ_SHARK)
#include "jsdbgapi.h"
static PRBool
_PlatformInitProfiler()
{
return PR_TRUE;
}
static PRBool
_PlatformStartProfile(const char* profileName)
{
return JS_StartProfiling() ? PR_TRUE : PR_FALSE;
}
static PRBool
_PlatformStopProfile(const char* profileName)
{
JS_StopProfiling();
return PR_TRUE;
}
static PRBool
_PlatformDeinitProfiler()
{
return PR_TRUE;
}
//--------------------------------------------------
// Default, no-profiler impl
#else
static PRBool
_PlatformInitProfiler()
{
NS_WARNING("Profiling is not available/configured for your platform.");
return PR_FALSE;
}
static PRBool
_PlatformStartProfile(const char* profileName)
{
NS_WARNING("Profiling is not available/configured for your platform.");
return PR_FALSE;
}
static PRBool
_PlatformStopProfile(const char* profileName)
{
NS_WARNING("Profiling is not available/configured for your platform.");
return PR_FALSE;
}
static PRBool
_PlatformDeinitProfiler()
{
NS_WARNING("Profiling is not available/configured for your platform.");
return PR_FALSE;
}
#endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
class ScopedLogging class ScopedLogging
@@ -264,10 +182,6 @@ class ScopedXPCOM : public nsIDirectoryServiceProvider2
~ScopedXPCOM() ~ScopedXPCOM()
{ {
if (gProfilerInited)
if (!_PlatformDeinitProfiler())
NS_WARNING("Problem shutting down profiler");
// If we created a profile directory, we need to remove it. // If we created a profile directory, we need to remove it.
if (mProfD) { if (mProfD) {
if (NS_FAILED(mProfD->Remove(PR_TRUE))) if (NS_FAILED(mProfD->Remove(PR_TRUE)))