js/src/builtin/Profilers.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /* Profiling-related API */
     9 #include "builtin/Profilers.h"
    11 #include <stdarg.h>
    13 #ifdef MOZ_CALLGRIND
    14 # include <valgrind/callgrind.h>
    15 #endif
    17 #ifdef __APPLE__
    18 #ifdef MOZ_INSTRUMENTS
    19 # include "devtools/Instruments.h"
    20 #endif
    21 #ifdef MOZ_SHARK
    22 # include "devtools/sharkctl.h"
    23 #endif
    24 #endif
    26 #ifdef XP_WIN
    27 # include <process.h>
    28 # define getpid _getpid
    29 #endif
    31 #include "vm/Probes.h"
    33 #include "jscntxtinlines.h"
    35 using namespace js;
    37 using mozilla::ArrayLength;
    39 /* Thread-unsafe error management */
    41 static char gLastError[2000];
    43 static void
    44 #ifdef __GNUC__
    45 __attribute__((unused,format(printf,1,2)))
    46 #endif
    47 UnsafeError(const char *format, ...)
    48 {
    49     va_list args;
    50     va_start(args, format);
    51     (void) vsnprintf(gLastError, sizeof(gLastError), format, args);
    52     va_end(args);
    54     gLastError[sizeof(gLastError) - 1] = '\0';
    55 }
    57 JS_PUBLIC_API(const char *)
    58 JS_UnsafeGetLastProfilingError()
    59 {
    60     return gLastError;
    61 }
    63 #ifdef __APPLE__
    64 static bool
    65 StartOSXProfiling(const char *profileName, pid_t pid)
    66 {
    67     bool ok = true;
    68     const char* profiler = nullptr;
    69 #ifdef MOZ_SHARK
    70     ok = Shark::Start();
    71     profiler = "Shark";
    72 #endif
    73 #ifdef MOZ_INSTRUMENTS
    74     ok = Instruments::Start(pid);
    75     profiler = "Instruments";
    76 #endif
    77     if (!ok) {
    78         if (profileName)
    79             UnsafeError("Failed to start %s for %s", profiler, profileName);
    80         else
    81             UnsafeError("Failed to start %s", profiler);
    82         return false;
    83     }
    84     return true;
    85 }
    86 #endif
    88 JS_PUBLIC_API(bool)
    89 JS_StartProfiling(const char *profileName, pid_t pid)
    90 {
    91     bool ok = true;
    92 #ifdef __APPLE__
    93     ok = StartOSXProfiling(profileName, pid);
    94 #endif
    95 #ifdef __linux__
    96     if (!js_StartPerf())
    97         ok = false;
    98 #endif
    99     return ok;
   100 }
   102 JS_PUBLIC_API(bool)
   103 JS_StopProfiling(const char *profileName)
   104 {
   105     bool ok = true;
   106 #ifdef __APPLE__
   107 #ifdef MOZ_SHARK
   108     Shark::Stop();
   109 #endif
   110 #ifdef MOZ_INSTRUMENTS
   111     Instruments::Stop(profileName);
   112 #endif
   113 #endif
   114 #ifdef __linux__
   115     if (!js_StopPerf())
   116         ok = false;
   117 #endif
   118     return ok;
   119 }
   121 /*
   122  * Start or stop whatever platform- and configuration-specific profiling
   123  * backends are available.
   124  */
   125 static bool
   126 ControlProfilers(bool toState)
   127 {
   128     bool ok = true;
   130     if (! probes::ProfilingActive && toState) {
   131 #ifdef __APPLE__
   132 #if defined(MOZ_SHARK) || defined(MOZ_INSTRUMENTS)
   133         const char* profiler;
   134 #ifdef MOZ_SHARK
   135         ok = Shark::Start();
   136         profiler = "Shark";
   137 #endif
   138 #ifdef MOZ_INSTRUMENTS
   139         ok = Instruments::Resume();
   140         profiler = "Instruments";
   141 #endif
   142         if (!ok) {
   143             UnsafeError("Failed to start %s", profiler);
   144         }
   145 #endif
   146 #endif
   147 #ifdef MOZ_CALLGRIND
   148         if (! js_StartCallgrind()) {
   149             UnsafeError("Failed to start Callgrind");
   150             ok = false;
   151         }
   152 #endif
   153     } else if (probes::ProfilingActive && ! toState) {
   154 #ifdef __APPLE__
   155 #ifdef MOZ_SHARK
   156         Shark::Stop();
   157 #endif
   158 #ifdef MOZ_INSTRUMENTS
   159         Instruments::Pause();
   160 #endif
   161 #endif
   162 #ifdef MOZ_CALLGRIND
   163         if (! js_StopCallgrind()) {
   164             UnsafeError("failed to stop Callgrind");
   165             ok = false;
   166         }
   167 #endif
   168     }
   170     probes::ProfilingActive = toState;
   172     return ok;
   173 }
   175 /*
   176  * Pause/resume whatever profiling mechanism is currently compiled
   177  * in, if applicable. This will not affect things like dtrace.
   178  *
   179  * Do not mix calls to these APIs with calls to the individual
   180  * profilers' pause/resume functions, because only overall state is
   181  * tracked, not the state of each profiler.
   182  */
   183 JS_PUBLIC_API(bool)
   184 JS_PauseProfilers(const char *profileName)
   185 {
   186     return ControlProfilers(false);
   187 }
   189 JS_PUBLIC_API(bool)
   190 JS_ResumeProfilers(const char *profileName)
   191 {
   192     return ControlProfilers(true);
   193 }
   195 JS_PUBLIC_API(bool)
   196 JS_DumpProfile(const char *outfile, const char *profileName)
   197 {
   198     bool ok = true;
   199 #ifdef MOZ_CALLGRIND
   200     js_DumpCallgrind(outfile);
   201 #endif
   202     return ok;
   203 }
   205 #ifdef MOZ_PROFILING
   207 struct RequiredStringArg {
   208     JSContext *mCx;
   209     char *mBytes;
   210     RequiredStringArg(JSContext *cx, const CallArgs &args, size_t argi, const char *caller)
   211         : mCx(cx), mBytes(nullptr)
   212     {
   213         if (args.length() <= argi) {
   214             JS_ReportError(cx, "%s: not enough arguments", caller);
   215         } else if (!args[argi].isString()) {
   216             JS_ReportError(cx, "%s: invalid arguments (string expected)", caller);
   217         } else {
   218             mBytes = JS_EncodeString(cx, args[argi].toString());
   219         }
   220     }
   221     operator void*() {
   222         return (void*) mBytes;
   223     }
   224     ~RequiredStringArg() {
   225         js_free(mBytes);
   226     }
   227 };
   229 static bool
   230 StartProfiling(JSContext *cx, unsigned argc, jsval *vp)
   231 {
   232     CallArgs args = CallArgsFromVp(argc, vp);
   233     if (args.length() == 0) {
   234         args.rval().setBoolean(JS_StartProfiling(nullptr, getpid()));
   235         return true;
   236     }
   238     RequiredStringArg profileName(cx, args, 0, "startProfiling");
   239     if (!profileName)
   240         return false;
   242     if (args.length() == 1) {
   243         args.rval().setBoolean(JS_StartProfiling(profileName.mBytes, getpid()));
   244         return true;
   245     }
   247     if (!args[1].isInt32()) {
   248         JS_ReportError(cx, "startProfiling: invalid arguments (int expected)");
   249         return false;
   250     }
   251     pid_t pid = static_cast<pid_t>(args[1].toInt32());
   252     args.rval().setBoolean(JS_StartProfiling(profileName.mBytes, pid));
   253     return true;
   254 }
   256 static bool
   257 StopProfiling(JSContext *cx, unsigned argc, jsval *vp)
   258 {
   259     CallArgs args = CallArgsFromVp(argc, vp);
   260     if (args.length() == 0) {
   261         args.rval().setBoolean(JS_StopProfiling(nullptr));
   262         return true;
   263     }
   265     RequiredStringArg profileName(cx, args, 0, "stopProfiling");
   266     if (!profileName)
   267         return false;
   268     args.rval().setBoolean(JS_StopProfiling(profileName.mBytes));
   269     return true;
   270 }
   272 static bool
   273 PauseProfilers(JSContext *cx, unsigned argc, jsval *vp)
   274 {
   275     CallArgs args = CallArgsFromVp(argc, vp);
   276     if (args.length() == 0) {
   277         args.rval().setBoolean(JS_PauseProfilers(nullptr));
   278         return true;
   279     }
   281     RequiredStringArg profileName(cx, args, 0, "pauseProfiling");
   282     if (!profileName)
   283         return false;
   284     args.rval().setBoolean(JS_PauseProfilers(profileName.mBytes));
   285     return true;
   286 }
   288 static bool
   289 ResumeProfilers(JSContext *cx, unsigned argc, jsval *vp)
   290 {
   291     CallArgs args = CallArgsFromVp(argc, vp);
   292     if (args.length() == 0) {
   293         args.rval().setBoolean(JS_ResumeProfilers(nullptr));
   294         return true;
   295     }
   297     RequiredStringArg profileName(cx, args, 0, "resumeProfiling");
   298     if (!profileName)
   299         return false;
   300     args.rval().setBoolean(JS_ResumeProfilers(profileName.mBytes));
   301     return true;
   302 }
   304 /* Usage: DumpProfile([filename[, profileName]]) */
   305 static bool
   306 DumpProfile(JSContext *cx, unsigned argc, jsval *vp)
   307 {
   308     bool ret;
   309     CallArgs args = CallArgsFromVp(argc, vp);
   310     if (args.length() == 0) {
   311         ret = JS_DumpProfile(nullptr, nullptr);
   312     } else {
   313         RequiredStringArg filename(cx, args, 0, "dumpProfile");
   314         if (!filename)
   315             return false;
   317         if (args.length() == 1) {
   318             ret = JS_DumpProfile(filename.mBytes, nullptr);
   319         } else {
   320             RequiredStringArg profileName(cx, args, 1, "dumpProfile");
   321             if (!profileName)
   322                 return false;
   324             ret = JS_DumpProfile(filename.mBytes, profileName.mBytes);
   325         }
   326     }
   328     args.rval().setBoolean(ret);
   329     return true;
   330 }
   332 #if defined(MOZ_SHARK) || defined(MOZ_INSTRUMENTS)
   334 static bool
   335 IgnoreAndReturnTrue(JSContext *cx, unsigned argc, jsval *vp)
   336 {
   337     CallArgs args = CallArgsFromVp(argc, vp);
   338     args.rval().setBoolean(true);
   339     return true;
   340 }
   342 #endif
   344 #ifdef MOZ_CALLGRIND
   345 static bool
   346 StartCallgrind(JSContext *cx, unsigned argc, jsval *vp)
   347 {
   348     CallArgs args = CallArgsFromVp(argc, vp);
   349     args.rval().setBoolean(js_StartCallgrind());
   350     return true;
   351 }
   353 static bool
   354 StopCallgrind(JSContext *cx, unsigned argc, jsval *vp)
   355 {
   356     CallArgs args = CallArgsFromVp(argc, vp);
   357     args.rval().setBoolean(js_StopCallgrind());
   358     return true;
   359 }
   361 static bool
   362 DumpCallgrind(JSContext *cx, unsigned argc, jsval *vp)
   363 {
   364     CallArgs args = CallArgsFromVp(argc, vp);
   365     if (args.length() == 0) {
   366         args.rval().setBoolean(js_DumpCallgrind(nullptr));
   367         return true;
   368     }
   370     RequiredStringArg outFile(cx, args, 0, "dumpCallgrind");
   371     if (!outFile)
   372         return false;
   374     args.rval().setBoolean(js_DumpCallgrind(outFile.mBytes));
   375     return true;
   376 }
   377 #endif
   379 static const JSFunctionSpec profiling_functions[] = {
   380     JS_FN("startProfiling",  StartProfiling,      1,0),
   381     JS_FN("stopProfiling",   StopProfiling,       1,0),
   382     JS_FN("pauseProfilers",  PauseProfilers,      1,0),
   383     JS_FN("resumeProfilers", ResumeProfilers,     1,0),
   384     JS_FN("dumpProfile",     DumpProfile,         2,0),
   385 #if defined(MOZ_SHARK) || defined(MOZ_INSTRUMENTS)
   386     /* Keep users of the old shark API happy. */
   387     JS_FN("connectShark",    IgnoreAndReturnTrue, 0,0),
   388     JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
   389     JS_FN("startShark",      StartProfiling,      0,0),
   390     JS_FN("stopShark",       StopProfiling,       0,0),
   391 #endif
   392 #ifdef MOZ_CALLGRIND
   393     JS_FN("startCallgrind", StartCallgrind,       0,0),
   394     JS_FN("stopCallgrind",  StopCallgrind,        0,0),
   395     JS_FN("dumpCallgrind",  DumpCallgrind,        1,0),
   396 #endif
   397     JS_FS_END
   398 };
   400 #endif
   402 JS_PUBLIC_API(bool)
   403 JS_DefineProfilingFunctions(JSContext *cx, JSObject *objArg)
   404 {
   405     RootedObject obj(cx, objArg);
   407     assertSameCompartment(cx, obj);
   408 #ifdef MOZ_PROFILING
   409     return JS_DefineFunctions(cx, obj, profiling_functions);
   410 #else
   411     return true;
   412 #endif
   413 }
   415 #ifdef MOZ_CALLGRIND
   417 JS_FRIEND_API(bool)
   418 js_StartCallgrind()
   419 {
   420     JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_START_INSTRUMENTATION);
   421     JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_ZERO_STATS);
   422     return true;
   423 }
   425 JS_FRIEND_API(bool)
   426 js_StopCallgrind()
   427 {
   428     JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_STOP_INSTRUMENTATION);
   429     return true;
   430 }
   432 JS_FRIEND_API(bool)
   433 js_DumpCallgrind(const char *outfile)
   434 {
   435     if (outfile) {
   436         JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS_AT(outfile));
   437     } else {
   438         JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS);
   439     }
   441     return true;
   442 }
   444 #endif /* MOZ_CALLGRIND */
   446 #ifdef __linux__
   448 /*
   449  * Code for starting and stopping |perf|, the Linux profiler.
   450  *
   451  * Output from profiling is written to mozperf.data in your cwd.
   452  *
   453  * To enable, set MOZ_PROFILE_WITH_PERF=1 in your environment.
   454  *
   455  * To pass additional parameters to |perf record|, provide them in the
   456  * MOZ_PROFILE_PERF_FLAGS environment variable.  If this variable does not
   457  * exist, we default it to "--call-graph".  (If you don't want --call-graph but
   458  * don't want to pass any other args, define MOZ_PROFILE_PERF_FLAGS to the empty
   459  * string.)
   460  *
   461  * If you include --pid or --output in MOZ_PROFILE_PERF_FLAGS, you're just
   462  * asking for trouble.
   463  *
   464  * Our split-on-spaces logic is lame, so don't expect MOZ_PROFILE_PERF_FLAGS to
   465  * work if you pass an argument which includes a space (e.g.
   466  * MOZ_PROFILE_PERF_FLAGS="-e 'foo bar'").
   467  */
   469 #include <signal.h>
   470 #include <sys/wait.h>
   471 #include <unistd.h>
   473 static bool perfInitialized = false;
   474 static pid_t perfPid = 0;
   476 bool js_StartPerf()
   477 {
   478     const char *outfile = "mozperf.data";
   480     if (perfPid != 0) {
   481         UnsafeError("js_StartPerf: called while perf was already running!\n");
   482         return false;
   483     }
   485     // Bail if MOZ_PROFILE_WITH_PERF is empty or undefined.
   486     if (!getenv("MOZ_PROFILE_WITH_PERF") ||
   487         !strlen(getenv("MOZ_PROFILE_WITH_PERF"))) {
   488         return true;
   489     }
   491     /*
   492      * Delete mozperf.data the first time through -- we're going to append to it
   493      * later on, so we want it to be clean when we start out.
   494      */
   495     if (!perfInitialized) {
   496         perfInitialized = true;
   497         unlink(outfile);
   498         char cwd[4096];
   499         printf("Writing perf profiling data to %s/%s\n",
   500                getcwd(cwd, sizeof(cwd)), outfile);
   501     }
   503     pid_t mainPid = getpid();
   505     pid_t childPid = fork();
   506     if (childPid == 0) {
   507         /* perf record --append --pid $mainPID --output=$outfile $MOZ_PROFILE_PERF_FLAGS */
   509         char mainPidStr[16];
   510         snprintf(mainPidStr, sizeof(mainPidStr), "%d", mainPid);
   511         const char *defaultArgs[] = {"perf", "record", "--append",
   512                                      "--pid", mainPidStr, "--output", outfile};
   514         Vector<const char*, 0, SystemAllocPolicy> args;
   515         args.append(defaultArgs, ArrayLength(defaultArgs));
   517         const char *flags = getenv("MOZ_PROFILE_PERF_FLAGS");
   518         if (!flags) {
   519             flags = "--call-graph";
   520         }
   522         char *flags2 = (char *)js_malloc(strlen(flags) + 1);
   523         if (!flags2)
   524             return false;
   525         strcpy(flags2, flags);
   527         // Split |flags2| on spaces.  (Don't bother to free it -- we're going to
   528         // exec anyway.)
   529         char *toksave;
   530         char *tok = strtok_r(flags2, " ", &toksave);
   531         while (tok) {
   532             args.append(tok);
   533             tok = strtok_r(nullptr, " ", &toksave);
   534         }
   536         args.append((char*) nullptr);
   538         execvp("perf", const_cast<char**>(args.begin()));
   540         /* Reached only if execlp fails. */
   541         fprintf(stderr, "Unable to start perf.\n");
   542         exit(1);
   543     }
   544     else if (childPid > 0) {
   545         perfPid = childPid;
   547         /* Give perf a chance to warm up. */
   548         usleep(500 * 1000);
   549         return true;
   550     }
   551     else {
   552         UnsafeError("js_StartPerf: fork() failed\n");
   553         return false;
   554     }
   555 }
   557 bool js_StopPerf()
   558 {
   559     if (perfPid == 0) {
   560         UnsafeError("js_StopPerf: perf is not running.\n");
   561         return true;
   562     }
   564     if (kill(perfPid, SIGINT)) {
   565         UnsafeError("js_StopPerf: kill failed\n");
   567         // Try to reap the process anyway.
   568         waitpid(perfPid, nullptr, WNOHANG);
   569     }
   570     else {
   571         waitpid(perfPid, nullptr, 0);
   572     }
   574     perfPid = 0;
   575     return true;
   576 }
   578 #endif /* __linux__ */

mercurial