зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
6415639ea2
Коммит
f4c7bbceb8
|
@ -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(¶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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче