author | Jim Blandy <jimb@mozilla.com> |
Fri, 21 Sep 2012 16:36:13 -0700 | |
changeset 107784 | 6fd36867bac751417fce03468cbe2c771810f24c |
parent 107783 | 87bcca77b8579875a593fddaa71b8ef91aee8be5 |
child 107785 | c914225fc39adfa6e9865fea3002a67eca84e211 |
push id | 23509 |
push user | ryanvm@gmail.com |
push date | Sat, 22 Sep 2012 12:28:38 +0000 |
treeherder | mozilla-central@b461a7cd250e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | sfink |
bugs | 790117, 675098 |
milestone | 18.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/src/Makefile.in +++ b/js/src/Makefile.in @@ -126,16 +126,17 @@ CPPSRCS = \ NameFunctions.cpp \ ParallelArray.cpp \ ParseMaps.cpp \ ParseNode.cpp \ Parser.cpp \ SPSProfiler.cpp \ TokenStream.cpp \ TestingFunctions.cpp \ + Profilers.cpp \ LifoAlloc.cpp \ Eval.cpp \ MapObject.cpp \ RegExpObject.cpp \ RegExpStatics.cpp \ RegExp.cpp \ Marking.cpp \ Memory.cpp \
new file mode 100644 --- /dev/null +++ b/js/src/builtin/Profilers.cpp @@ -0,0 +1,502 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* Profiling-related API */ + +#include <stdarg.h> + +#include "Profilers.h" +#include "jsapi.h" +#include "jscntxt.h" +#include "jsprobes.h" + +#include "jscntxtinlines.h" +#include "vm/Stack-inl.h" + +#ifdef MOZ_CALLGRIND +#include <valgrind/callgrind.h> +#endif + +#ifdef __APPLE__ +#include "devtools/sharkctl.h" +#endif + +using namespace js; + +/* Thread-unsafe error management */ + +static char gLastError[2000]; + +static void +#ifdef __GNUC__ +__attribute__((unused,format(printf,1,2))) +#endif +UnsafeError(const char *format, ...) +{ + 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(const char *) +JS_UnsafeGetLastProfilingError() +{ + 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 __linux__ + if (!js_StartPerf()) + 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 __linux__ + if (!js_StopPerf()) + 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 + } 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 + } + + 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 + +struct RequiredStringArg { + JSContext *mCx; + char *mBytes; + RequiredStringArg(JSContext *cx, unsigned 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) + js_free(mBytes); + } +}; + +static JSBool +StartProfiling(JSContext *cx, unsigned argc, jsval *vp) +{ + if (argc == 0) { + 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 +StopProfiling(JSContext *cx, unsigned argc, jsval *vp) +{ + if (argc == 0) { + 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, unsigned 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, unsigned 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, unsigned 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; +} + +#ifdef MOZ_SHARK + +static JSBool +IgnoreAndReturnTrue(JSContext *cx, unsigned argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, JSVAL_TRUE); + return true; +} + +#endif + +#ifdef MOZ_CALLGRIND +static JSBool +StartCallgrind(JSContext *cx, unsigned argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind())); + return JS_TRUE; +} + +static JSBool +StopCallgrind(JSContext *cx, unsigned argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind())); + return JS_TRUE; +} + +static JSBool +DumpCallgrind(JSContext *cx, unsigned 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 + +static JSFunctionSpec profiling_functions[] = { + JS_FN("startProfiling", StartProfiling, 1,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 + /* Keep users of the old shark API happy. */ + JS_FN("connectShark", IgnoreAndReturnTrue, 0,0), + JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0), + JS_FN("startShark", StartProfiling, 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 + JS_FS_END +}; + +#endif + +JS_PUBLIC_API(JSBool) +JS_DefineProfilingFunctions(JSContext *cx, JSObject *objArg) +{ + RootedObject obj(cx, objArg); + + assertSameCompartment(cx, obj); +#ifdef MOZ_PROFILING + return JS_DefineFunctions(cx, obj, profiling_functions); +#else + return true; +#endif +} + +#ifdef MOZ_CALLGRIND + +JS_FRIEND_API(JSBool) +js_StartCallgrind() +{ + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_START_INSTRUMENTATION); + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_ZERO_STATS); + return true; +} + +JS_FRIEND_API(JSBool) +js_StopCallgrind() +{ + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_STOP_INSTRUMENTATION); + return true; +} + +JS_FRIEND_API(JSBool) +js_DumpCallgrind(const char *outfile) +{ + if (outfile) { + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS_AT(outfile)); + } else { + JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS); + } + + return true; +} + +#endif /* MOZ_CALLGRIND */ + +#ifdef __linux__ + +/* + * Code for starting and stopping |perf|, the Linux profiler. + * + * Output from profiling is written to mozperf.data in your cwd. + * + * To enable, set MOZ_PROFILE_WITH_PERF=1 in your environment. + * + * To pass additional parameters to |perf record|, provide them in the + * MOZ_PROFILE_PERF_FLAGS environment variable. If this variable does not + * exist, we default it to "--call-graph". (If you don't want --call-graph but + * don't want to pass any other args, define MOZ_PROFILE_PERF_FLAGS to the empty + * string.) + * + * If you include --pid or --output in MOZ_PROFILE_PERF_FLAGS, you're just + * asking for trouble. + * + * Our split-on-spaces logic is lame, so don't expect MOZ_PROFILE_PERF_FLAGS to + * work if you pass an argument which includes a space (e.g. + * MOZ_PROFILE_PERF_FLAGS="-e 'foo bar'"). + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <signal.h> + +static bool perfInitialized = false; +static pid_t perfPid = 0; + +JSBool js_StartPerf() +{ + const char *outfile = "mozperf.data"; + + if (perfPid != 0) { + UnsafeError("js_StartPerf: called while perf was already running!\n"); + return false; + } + + // Bail if MOZ_PROFILE_WITH_PERF is empty or undefined. + if (!getenv("MOZ_PROFILE_WITH_PERF") || + !strlen(getenv("MOZ_PROFILE_WITH_PERF"))) { + return true; + } + + /* + * Delete mozperf.data the first time through -- we're going to append to it + * later on, so we want it to be clean when we start out. + */ + if (!perfInitialized) { + perfInitialized = true; + unlink(outfile); + char cwd[4096]; + printf("Writing perf profiling data to %s/%s\n", + getcwd(cwd, sizeof(cwd)), outfile); + } + + pid_t mainPid = getpid(); + + pid_t childPid = fork(); + if (childPid == 0) { + /* perf record --append --pid $mainPID --output=$outfile $MOZ_PROFILE_PERF_FLAGS */ + + char mainPidStr[16]; + snprintf(mainPidStr, sizeof(mainPidStr), "%d", mainPid); + const char *defaultArgs[] = {"perf", "record", "--append", + "--pid", mainPidStr, "--output", outfile}; + + Vector<const char*, 0, SystemAllocPolicy> args; + args.append(defaultArgs, ArrayLength(defaultArgs)); + + const char *flags = getenv("MOZ_PROFILE_PERF_FLAGS"); + if (!flags) { + flags = "--call-graph"; + } + + // Split |flags| on spaces. (Don't bother to free it -- we're going to + // exec anyway.) + char *toksave; + char *tok = strtok_r(strdup(flags), " ", &toksave); + while (tok) { + args.append(tok); + tok = strtok_r(NULL, " ", &toksave); + } + + args.append((char*) NULL); + + execvp("perf", const_cast<char**>(args.begin())); + + /* Reached only if execlp fails. */ + fprintf(stderr, "Unable to start perf.\n"); + exit(1); + } + else if (childPid > 0) { + perfPid = childPid; + + /* Give perf a chance to warm up. */ + usleep(500 * 1000); + return true; + } + else { + UnsafeError("js_StartPerf: fork() failed\n"); + return false; + } +} + +JSBool js_StopPerf() +{ + if (perfPid == 0) { + UnsafeError("js_StopPerf: perf is not running.\n"); + return true; + } + + if (kill(perfPid, SIGINT)) { + UnsafeError("js_StopPerf: kill failed\n"); + + // Try to reap the process anyway. + waitpid(perfPid, NULL, WNOHANG); + } + else { + waitpid(perfPid, NULL, 0); + } + + perfPid = 0; + return true; +} + +#endif /* __linux__ */
new file mode 100644 --- /dev/null +++ b/js/src/builtin/Profilers.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * 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/. */ + +/* + * Functions for controlling profilers from within JS: Valgrind, Perf, + * Shark, etc. + */ +#ifndef Profilers_h___ +#define Profilers_h___ + +#include "jsapi.h" + +/** + * 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) +JS_StartProfiling(const char *profileName); + +/** + * 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); + +/** + * 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 + +extern JS_FRIEND_API(JSBool) +js_StopCallgrind(); + +extern JS_FRIEND_API(JSBool) +js_StartCallgrind(); + +extern JS_FRIEND_API(JSBool) +js_DumpCallgrind(const char *outfile); + +#endif /* MOZ_CALLGRIND */ + +#ifdef __linux__ + +extern JS_FRIEND_API(JSBool) +js_StartPerf(); + +extern JS_FRIEND_API(JSBool) +js_StopPerf(); + +#endif /* __linux__ */ + +#endif /* Profilers_h___ */
--- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -4,17 +4,16 @@ * 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/. */ /* * JS debugging API. */ #include <string.h> -#include <stdarg.h> #include "jsprvtd.h" #include "jstypes.h" #include "jsutil.h" #include "jsclist.h" #include "jsapi.h" #include "jscntxt.h" #include "jsversion.h" #include "jsdbgapi.h" @@ -43,20 +42,16 @@ #include "jsscopeinlines.h" #include "jsscriptinlines.h" #include "vm/Stack-inl.h" #include "jsautooplen.h" #include "mozilla/Util.h" -#ifdef __APPLE__ -#include "devtools/sharkctl.h" -#endif - using namespace js; using namespace js::gc; using namespace mozilla; JS_PUBLIC_API(JSBool) JS_GetDebugMode(JSContext *cx) { return cx->compartment->debugMode(); @@ -1059,662 +1054,16 @@ js_RevertVersion(JSContext *cx) JS_PUBLIC_API(const JSDebugHooks *) JS_GetGlobalDebugHooks(JSRuntime *rt) { return &rt->debugHooks; } /************************************************************************/ -/* Profiling-related API */ - -/* Thread-unsafe error management */ - -static char gLastError[2000]; - -static void -#ifdef __GNUC__ -__attribute__((unused,format(printf,1,2))) -#endif -UnsafeError(const char *format, ...) -{ - 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(const char *) -JS_UnsafeGetLastProfilingError() -{ - 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 -#if 0 //def MOZ_VTUNE - if (!js_StartVtune(profileName)) - ok = JS_FALSE; -#endif -#ifdef __linux__ - if (!js_StartPerf()) - 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 -#if 0 //def MOZ_VTUNE - if (!js_StopVtune()) - ok = JS_FALSE; -#endif -#ifdef __linux__ - if (!js_StopPerf()) - 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 -#if 0 //def 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 -#if 0 //def 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 - -struct RequiredStringArg { - JSContext *mCx; - char *mBytes; - RequiredStringArg(JSContext *cx, unsigned 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) - js_free(mBytes); - } -}; - -static JSBool -StartProfiling(JSContext *cx, unsigned argc, jsval *vp) -{ - if (argc == 0) { - 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 -StopProfiling(JSContext *cx, unsigned argc, jsval *vp) -{ - if (argc == 0) { - 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, unsigned 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, unsigned 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, unsigned 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; -} - -#ifdef MOZ_SHARK - -static JSBool -IgnoreAndReturnTrue(JSContext *cx, unsigned argc, jsval *vp) -{ - JS_SET_RVAL(cx, vp, JSVAL_TRUE); - return true; -} - -#endif - -#ifdef MOZ_CALLGRIND -static JSBool -StartCallgrind(JSContext *cx, unsigned argc, jsval *vp) -{ - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind())); - return JS_TRUE; -} - -static JSBool -StopCallgrind(JSContext *cx, unsigned argc, jsval *vp) -{ - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind())); - return JS_TRUE; -} - -static JSBool -DumpCallgrind(JSContext *cx, unsigned 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, unsigned 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, unsigned argc, jsval *vp) -{ - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopVtune())); - return JS_TRUE; -} - -static JSBool -PauseVtune(JSContext *cx, unsigned argc, jsval *vp) -{ - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_PauseVtune())); - return JS_TRUE; -} - -static JSBool -ResumeVtune(JSContext *cx, unsigned argc, jsval *vp) -{ - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_ResumeVtune())); - return JS_TRUE; -} -#endif - -static JSFunctionSpec profiling_functions[] = { - JS_FN("startProfiling", StartProfiling, 1,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 - /* Keep users of the old shark API happy. */ - JS_FN("connectShark", IgnoreAndReturnTrue, 0,0), - JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0), - JS_FN("startShark", StartProfiling, 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 -#if 0 //ef 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 - JS_FS_END -}; - -#endif - -JS_PUBLIC_API(JSBool) -JS_DefineProfilingFunctions(JSContext *cx, JSObject *objArg) -{ - RootedObject obj(cx, objArg); - - assertSameCompartment(cx, obj); -#ifdef MOZ_PROFILING - return JS_DefineFunctions(cx, obj, profiling_functions); -#else - return true; -#endif -} - -#ifdef MOZ_CALLGRIND - -#include <valgrind/callgrind.h> - -JS_FRIEND_API(JSBool) -js_StartCallgrind() -{ - JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_START_INSTRUMENTATION); - JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_ZERO_STATS); - return true; -} - -JS_FRIEND_API(JSBool) -js_StopCallgrind() -{ - JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_STOP_INSTRUMENTATION); - return true; -} - -JS_FRIEND_API(JSBool) -js_DumpCallgrind(const char *outfile) -{ - if (outfile) { - JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS_AT(outfile)); - } else { - JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS); - } - - return true; -} - -#endif /* MOZ_CALLGRIND */ - -#if 0 //def MOZ_VTUNE -#include <VTuneApi.h> - -static const char *vtuneErrorMessages[] = { - "unknown, error #0", - "invalid 'max samples' field", - "invalid 'samples per buffer' field", - "invalid 'sample interval' field", - "invalid path", - "sample file in use", - "invalid 'number of events' field", - "unknown, error #7", - "internal error", - "bad event name", - "VTStopSampling called without calling VTStartSampling", - "no events selected for event-based sampling", - "events selected cannot be run together", - "no sampling parameters", - "sample database already exists", - "sampling already started", - "time-based sampling not supported", - "invalid 'sampling parameters size' field", - "invalid 'event size' field", - "sampling file already bound", - "invalid event path", - "invalid license", - "invalid 'global options' field", - -}; - -bool -js_StartVtune(const char *profileName) -{ - VTUNE_EVENT events[] = { - { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" }, - { 1000000, 0, 0, 0, "INST_RETIRED.ANY" }, - }; - - U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT); - char *default_filename = "mozilla-vtune.tb5"; - JSString *str; - U32 status; - - VTUNE_SAMPLING_PARAMS params = { - sizeof(VTUNE_SAMPLING_PARAMS), - sizeof(VTUNE_EVENT), - 0, 0, /* Reserved fields */ - 1, /* Initialize in "paused" state */ - 0, /* Max samples, or 0 for "continuous" */ - 4096, /* Samples per buffer */ - 0.1, /* Sampling interval in ms */ - 1, /* 1 for event-based sampling, 0 for time-based */ - - n_events, - events, - default_filename, - }; - - if (profileName) { - char filename[strlen(profileName) + strlen("-vtune.tb5") + 1]; - snprintf(filename, sizeof(filename), "%s-vtune.tb5", profileName); - params.tb5Filename = filename; - } - - status = VTStartSampling(¶ms); - - if (params.tb5Filename != default_filename) - js_free(params.tb5Filename); - - if (status != 0) { - if (status == VTAPI_MULTIPLE_RUNS) - VTStopSampling(0); - if (status < sizeof(vtuneErrorMessages)) - UnsafeError("Vtune setup error: %s", vtuneErrorMessages[status]); - else - UnsafeError("Vtune setup error: %d", status); - return false; - } - return true; -} - -bool -js_StopVtune() -{ - U32 status = VTStopSampling(1); - if (status) { - if (status < sizeof(vtuneErrorMessages)) - UnsafeError("Vtune shutdown error: %s", vtuneErrorMessages[status]); - else - UnsafeError("Vtune shutdown error: %d", status); - return false; - } - return true; -} - -bool -js_PauseVtune() -{ - VTPause(); - return true; -} - -bool -js_ResumeVtune() -{ - VTResume(); - return true; -} - -#endif /* MOZ_VTUNE */ - -#ifdef __linux__ - -/* - * Code for starting and stopping |perf|, the Linux profiler. - * - * Output from profiling is written to mozperf.data in your cwd. - * - * To enable, set MOZ_PROFILE_WITH_PERF=1 in your environment. - * - * To pass additional parameters to |perf record|, provide them in the - * MOZ_PROFILE_PERF_FLAGS environment variable. If this variable does not - * exist, we default it to "--call-graph". (If you don't want --call-graph but - * don't want to pass any other args, define MOZ_PROFILE_PERF_FLAGS to the empty - * string.) - * - * If you include --pid or --output in MOZ_PROFILE_PERF_FLAGS, you're just - * asking for trouble. - * - * Our split-on-spaces logic is lame, so don't expect MOZ_PROFILE_PERF_FLAGS to - * work if you pass an argument which includes a space (e.g. - * MOZ_PROFILE_PERF_FLAGS="-e 'foo bar'"). - */ - -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> -#include <signal.h> - -static bool perfInitialized = false; -static pid_t perfPid = 0; - -JSBool js_StartPerf() -{ - const char *outfile = "mozperf.data"; - - if (perfPid != 0) { - UnsafeError("js_StartPerf: called while perf was already running!\n"); - return false; - } - - // Bail if MOZ_PROFILE_WITH_PERF is empty or undefined. - if (!getenv("MOZ_PROFILE_WITH_PERF") || - !strlen(getenv("MOZ_PROFILE_WITH_PERF"))) { - return true; - } - - /* - * Delete mozperf.data the first time through -- we're going to append to it - * later on, so we want it to be clean when we start out. - */ - if (!perfInitialized) { - perfInitialized = true; - unlink(outfile); - char cwd[4096]; - printf("Writing perf profiling data to %s/%s\n", - getcwd(cwd, sizeof(cwd)), outfile); - } - - pid_t mainPid = getpid(); - - pid_t childPid = fork(); - if (childPid == 0) { - /* perf record --append --pid $mainPID --output=$outfile $MOZ_PROFILE_PERF_FLAGS */ - - char mainPidStr[16]; - snprintf(mainPidStr, sizeof(mainPidStr), "%d", mainPid); - const char *defaultArgs[] = {"perf", "record", "--append", - "--pid", mainPidStr, "--output", outfile}; - - Vector<const char*, 0, SystemAllocPolicy> args; - args.append(defaultArgs, ArrayLength(defaultArgs)); - - const char *flags = getenv("MOZ_PROFILE_PERF_FLAGS"); - if (!flags) { - flags = "--call-graph"; - } - - // Split |flags| on spaces. (Don't bother to free it -- we're going to - // exec anyway.) - char *toksave; - char *tok = strtok_r(strdup(flags), " ", &toksave); - while (tok) { - args.append(tok); - tok = strtok_r(NULL, " ", &toksave); - } - - args.append((char*) NULL); - - execvp("perf", const_cast<char**>(args.begin())); - - /* Reached only if execlp fails. */ - fprintf(stderr, "Unable to start perf.\n"); - exit(1); - } - else if (childPid > 0) { - perfPid = childPid; - - /* Give perf a chance to warm up. */ - usleep(500 * 1000); - return true; - } - else { - UnsafeError("js_StartPerf: fork() failed\n"); - return false; - } -} - -JSBool js_StopPerf() -{ - if (perfPid == 0) { - UnsafeError("js_StopPerf: perf is not running.\n"); - return true; - } - - if (kill(perfPid, SIGINT)) { - UnsafeError("js_StopPerf: kill failed\n"); - - // Try to reap the process anyway. - waitpid(perfPid, NULL, WNOHANG); - } - else { - waitpid(perfPid, NULL, 0); - } - - perfPid = 0; - return true; -} - -#endif /* __linux__ */ - JS_PUBLIC_API(void) JS_DumpBytecode(JSContext *cx, JSScript *scriptArg) { #if defined(DEBUG) Rooted<JSScript*> script(cx, scriptArg); Sprinter sprinter(cx); if (!sprinter.init())
--- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -390,114 +390,25 @@ JS_GetScriptTotalSize(JSContext *cx, JSS extern JS_FRIEND_API(void) js_RevertVersion(JSContext *cx); extern JS_PUBLIC_API(const JSDebugHooks *) JS_GetGlobalDebugHooks(JSRuntime *rt); /** - * 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) -JS_StartProfiling(const char *profileName); - -/** - * 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) JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj); /* Defined in vm/Debugger.cpp. */ extern JS_PUBLIC_API(JSBool) 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 - -extern JS_FRIEND_API(JSBool) -js_StopCallgrind(); - -extern JS_FRIEND_API(JSBool) -js_StartCallgrind(); - -extern JS_FRIEND_API(JSBool) -js_DumpCallgrind(const char *outfile); - -#endif /* MOZ_CALLGRIND */ - -#ifdef MOZ_VTUNE - -extern JS_FRIEND_API(bool) -js_StartVtune(const char *profileName); - -extern JS_FRIEND_API(bool) -js_StopVtune(); - -extern JS_FRIEND_API(bool) -js_PauseVtune(); - -extern JS_FRIEND_API(bool) -js_ResumeVtune(); - -#endif /* MOZ_VTUNE */ - -#ifdef __linux__ - -extern JS_FRIEND_API(JSBool) -js_StartPerf(); - -extern JS_FRIEND_API(JSBool) -js_StopPerf(); - -#endif /* __linux__ */ - extern JS_PUBLIC_API(void) JS_DumpBytecode(JSContext *cx, JSScript *script); extern JS_PUBLIC_API(void) JS_DumpCompartmentBytecode(JSContext *cx); extern JS_PUBLIC_API(void) JS_DumpPCCounts(JSContext *cx, JSScript *script);