Bug 790117: Move external profiler control functions to their own source file in js/src/builtin; drop VTune. r=sfink

The VTune support needed to be updated for changes to the VTune control API; that work is bug 675098.
This commit is contained in:
Jim Blandy 2012-09-21 16:36:13 -07:00
Родитель 6415639ea2
Коммит f4c7bbceb8
5 изменённых файлов: 593 добавлений и 740 удалений

Просмотреть файл

@ -131,6 +131,7 @@ CPPSRCS = \
SPSProfiler.cpp \
TokenStream.cpp \
TestingFunctions.cpp \
Profilers.cpp \
LifoAlloc.cpp \
Eval.cpp \
MapObject.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__ */

Просмотреть файл

@ -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___ */

Просмотреть файл

@ -9,7 +9,6 @@
* JS debugging API.
*/
#include <string.h>
#include <stdarg.h>
#include "jsprvtd.h"
#include "jstypes.h"
#include "jsutil.h"
@ -48,10 +47,6 @@
#include "jsautooplen.h"
#include "mozilla/Util.h"
#ifdef __APPLE__
#include "devtools/sharkctl.h"
#endif
using namespace js;
using namespace js::gc;
using namespace mozilla;
@ -1064,652 +1059,6 @@ JS_GetGlobalDebugHooks(JSRuntime *rt)
/************************************************************************/
/* 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(&params);
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)
{

Просмотреть файл

@ -394,48 +394,6 @@ 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.
*/
@ -446,53 +404,6 @@ JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj);
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);