Bug 1410528 - Add a --dll flag to the JS shell for loading shared libs, r=ted,jonco
☠☠ backed out by 010374bce606 ☠ ☠
authorSteve Fink <sfink@mozilla.com>
Mon, 13 Nov 2017 10:00:16 -0800
changeset 392281 ec0702d55b012ccb507686d5a644e7c568524f0e
parent 392280 abe5e28c49908b8a94196b73a787656a546bac9c
child 392282 5e1dc7fec0ff1181361043f64668298fdbab2bdc
push id32916
push useraciure@mozilla.com
push dateFri, 17 Nov 2017 09:59:52 +0000
treeherdermozilla-central@a77c628829b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted, jonco
bugs1410528
milestone59.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
Bug 1410528 - Add a --dll flag to the JS shell for loading shared libs, r=ted,jonco
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/shell/js.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -11,16 +11,19 @@
 #include "jsapi.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Sprintf.h"
 
 #include <ctype.h>
+#ifdef __linux__
+# include <dlfcn.h>
+#endif
 #include <stdarg.h>
 #include <string.h>
 #include <sys/stat.h>
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
@@ -7752,8 +7755,22 @@ js::SetStackFormat(JSContext* cx, js::St
     cx->runtime()->setStackFormat(format);
 }
 
 JS_PUBLIC_API(js::StackFormat)
 js::GetStackFormat(JSContext* cx)
 {
     return cx->runtime()->stackFormat();
 }
+
+namespace js {
+
+JS_PUBLIC_API(void)
+NoteIntentionalCrash()
+{
+#ifdef __linux__
+    static bool* addr = reinterpret_cast<bool*>(dlsym(RTLD_DEFAULT, "gBreakpadInjectorEnabled"));
+    if (addr)
+        *addr = false;
+#endif
+}
+
+} // namespace js
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -7164,16 +7164,23 @@ typedef bool
 extern JS_PUBLIC_API(bool)
 SetStopwatchCommitCallback(JSContext*, StopwatchCommitCallback, void*);
 
 typedef bool
 (*GetGroupsCallback)(JSContext*, PerformanceGroupVector&, void*);
 extern JS_PUBLIC_API(bool)
 SetGetPerformanceGroupsCallback(JSContext*, GetGroupsCallback, void*);
 
+/**
+ * Hint that we expect a crash. Currently, the only thing that cares is the
+ * breakpad injector, which (if loaded) will suppress minidump generation.
+ */
+extern JS_PUBLIC_API(void)
+NoteIntentionalCrash();
+
 } /* namespace js */
 
 namespace js {
 
 enum class CompletionKind {
     Normal,
     Return,
     Throw
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1656,16 +1656,17 @@ CompartmentChecker::check(AbstractFrameP
         check(frame.environmentChain());
 }
 #endif
 
 void
 AutoEnterOOMUnsafeRegion::crash(const char* reason)
 {
     char msgbuf[1024];
+    js::NoteIntentionalCrash();
     SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason);
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
 AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback
 AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback = nullptr;
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -14,16 +14,19 @@
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TimeStamp.h"
 
 #include <chrono>
+#ifdef __linux__
+# include <dlfcn.h>
+#endif
 #ifdef XP_WIN
 # include <direct.h>
 # include <process.h>
 #endif
 #include <errno.h>
 #include <fcntl.h>
 #if defined(XP_WIN)
 # include <io.h>     /* for isatty() */
@@ -57,16 +60,20 @@
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
+#ifndef __linux__
+# include "prerror.h"
+# include "prlink.h"
+#endif
 #include "shellmoduleloader.out.h"
 
 #include "builtin/ModuleObject.h"
 #include "builtin/RegExp.h"
 #include "builtin/TestingFunctions.h"
 #include "frontend/Parser.h"
 #include "gc/GCInternals.h"
 #include "jit/arm/Simulator-arm.h"
@@ -124,16 +131,33 @@ using mozilla::MakeScopeExit;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::NumberEqualsInt32;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 
+// Avoid an unnecessary NSPR dependency on Linux just for the shell.
+#ifdef __linux__
+typedef void PRLibrary;
+
+static PRLibrary*
+PR_LoadLibrary(const char* path)
+{
+    return dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
+}
+
+static void
+PR_UnloadLibrary(PRLibrary* dll)
+{
+    dlclose(dll);
+}
+#endif
+
 enum JSShellExitCode {
     EXITCODE_RUNTIME_ERROR      = 3,
     EXITCODE_FILE_NOT_FOUND     = 4,
     EXITCODE_OUT_OF_MEMORY      = 5,
     EXITCODE_TIMEOUT            = 6
 };
 
 /*
@@ -3192,16 +3216,24 @@ Crash(JSContext* cx, unsigned argc, Valu
     if (args.length() == 0)
         MOZ_CRASH("forced crash");
     RootedString message(cx, JS::ToString(cx, args[0]));
     if (!message)
         return false;
     char* utf8chars = JS_EncodeStringToUTF8(cx, message);
     if (!utf8chars)
         return false;
+    if (args.get(1).isObject()) {
+        RootedValue v(cx);
+        RootedObject opts(cx, &args[1].toObject());
+        if (!JS_GetProperty(cx, opts, "suppress_minidump", &v))
+            return false;
+        if (v.isBoolean() && v.toBoolean())
+            js::NoteIntentionalCrash();
+    }
 #ifndef DEBUG
     MOZ_ReportCrash(utf8chars, __FILE__, __LINE__);
 #endif
     MOZ_CRASH_UNSAFE_OOL(utf8chars);
 }
 
 static bool
 GetSLX(JSContext* cx, unsigned argc, Value* vp)
@@ -7075,18 +7107,22 @@ TestAssertRecoveredOnBailout,
 "getMarks()",
 "  Return an array of strings representing the current state of the mark\n"
 "  bits ('gray' or 'black', or 'dead' if the object has been collected)\n"
 "  for the objects registered via addMarkObservers. Note that some of the\n"
 "  objects tested may be from different compartments than the one in which\n"
 "  this function runs."),
 
     JS_FN_HELP("crash", Crash, 0, 0,
-"crash([message])",
-"  Crashes the process with a MOZ_CRASH."),
+"crash([message, [{disable_minidump:true}]])",
+"  Crashes the process with a MOZ_CRASH, optionally providing a message.\n"
+"  An options object may be passed as the second argument. If the key\n"
+"  'suppress_minidump' is set to true, then a minidump will not be\n"
+"  generated by the crash (which only has an effect if the breakpad\n"
+"  dumping library is loaded.)"),
 
     JS_FN_HELP("setARMHwCapFlags", SetARMHwCapFlags, 1, 0,
 "setARMHwCapFlags(\"flag1,flag2 flag3\")",
 "  On non-ARM, no-op. On ARM, set the hardware capabilities. The list of \n"
 "  flags is available by calling this function with \"help\" as the flag's name"),
 
     JS_FN_HELP("wasmLoop", WasmLoop, 2, 0,
 "wasmLoop(filename, imports)",
@@ -8627,16 +8663,44 @@ PreInit()
         // instead of hanging automation.
         UINT newMode = SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX;
         UINT prevMode = SetErrorMode(newMode);
         SetErrorMode(prevMode | newMode);
     }
 #endif
 }
 
+class AutoLibraryLoader {
+    Vector<PRLibrary*, 4, SystemAllocPolicy> libraries;
+
+  public:
+
+    ~AutoLibraryLoader() {
+        for (auto dll : libraries) {
+            PR_UnloadLibrary(dll);
+        }
+    }
+
+    PRLibrary* load(const char* path) {
+        PRLibrary* dll = PR_LoadLibrary(path);
+        if (!dll) {
+#ifdef __linux__
+            fprintf(stderr, "LoadLibrary '%s' failed: %s\n", path, dlerror());
+#else
+            fprintf(stderr, "LoadLibrary '%s' failed with code %d\n", path, PR_GetError());
+#endif
+            MOZ_CRASH("Failed to load library");
+        }
+
+        MOZ_ALWAYS_TRUE(libraries.append(dll));
+        return dll;
+    }
+};
+
+
 int
 main(int argc, char** argv, char** envp)
 {
     PreInit();
 
     sArgc = argc;
     sArgv = argv;
 
@@ -8829,16 +8893,17 @@ main(int argc, char** argv, char** envp)
         || !op.addIntOption('\0', "nursery-size", "SIZE-MB", "Set the maximum nursery size in MB", 16)
 #ifdef JS_GC_ZEAL
         || !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]", gc::ZealModeHelpText)
 #else
         || !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]", "option ignored in non-gc-zeal builds")
 #endif
         || !op.addStringOption('\0', "module-load-path", "DIR", "Set directory to load modules from")
         || !op.addBoolOption('\0', "no-async-stacks", "Disable async stacks")
+        || !op.addMultiStringOption('\0', "dll", "LIBRARY", "Dynamically load LIBRARY")
     )
     {
         return EXIT_FAILURE;
     }
 
     op.setArgTerminatesOptions("script", true);
     op.setArgCapturesRest("scriptArgs");
 
@@ -8878,16 +8943,24 @@ main(int argc, char** argv, char** envp)
         js::jit::CPUInfo::SetAVXEnabled();
         PropagateFlagToNestedShells("--enable-avx");
     }
 #endif
 
     if (op.getBoolOption("no-threads"))
         js::DisableExtraThreads();
 
+    AutoLibraryLoader loader;
+    MultiStringRange dllPaths = op.getMultiStringOption("dll");
+    while (!dllPaths.empty()) {
+        char* path = dllPaths.front();
+        loader.load(path);
+        dllPaths.popFront();
+    }
+
     if (!InitSharedArrayBufferMailbox())
         return 1;
 
     // The fake CPU count must be set before initializing the Runtime,
     // which spins up the thread pool.
     int32_t cpuCount = op.getIntOption("cpu-count"); // What we're really setting
     if (cpuCount < 0)
         cpuCount = op.getIntOption("thread-count");  // Legacy name