author | Csoregi Natalia <ncsoregi@mozilla.com> |
Wed, 29 Nov 2017 03:08:46 +0200 | |
changeset 394030 | 8cbc145b9f915eb5911141be9266c909f8ac26af |
parent 394029 | a7ed89e13a4cc4ce7b8ebe4dd99bce5160852003 |
child 394031 | 3c8385b19b24b182ddfdc35ffc746168a4cb9e83 |
push id | 32989 |
push user | dluca@mozilla.com |
push date | Wed, 29 Nov 2017 10:09:52 +0000 |
treeherder | mozilla-central@40b464eb6b31 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | backout |
bugs | 1420355 |
milestone | 59.0a1 |
backs out | a7ed89e13a4cc4ce7b8ebe4dd99bce5160852003 fd6702e6e0a0a24e2d5d6c98b5467768cff9dbc2 0479dda078a2ab5a9be9ebf5b82705a332ba75bd e69357ccca9e26deb32ef6c5a4e445673fe4ada9 3742a4b69ba20bdcc47ee8bfa7d56268be944ae1 451cd087922f1f1f0e66de9196b7b99e36c8072e d80b5c4e1dd09776fdc85df09693ca76a0dc408b |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -63,16 +63,19 @@ [xpcom] @RESPATH@/dependentlibs.list #ifdef MOZ_SHARED_MOZGLUE @BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@ #endif #ifndef MOZ_STATIC_JS @BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@ #endif +#ifdef MOZ_DMD +@BINPATH@/@DLL_PREFIX@dmd@DLL_SUFFIX@ +#endif #ifndef MOZ_SYSTEM_NSPR #ifndef MOZ_FOLD_LIBS @BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@ #endif #endif #ifdef XP_MACOSX
--- a/build/automation.py.in +++ b/build/automation.py.in @@ -178,22 +178,44 @@ class Automation(object): def kill(self): if Automation().IS_WIN32: import platform pid = "%i" % self.pid subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait() else: os.kill(self.pid, signal.SIGKILL) - def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False, lsanPath=None, ubsanPath=None): + def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False, dmdPath=None, lsanPath=None, ubsanPath=None): if xrePath == None: xrePath = self.DIST_BIN if env == None: env = dict(os.environ) + ldLibraryPath = os.path.abspath(os.path.join(SCRIPT_DIR, xrePath)) + dmdLibrary = None + preloadEnvVar = None + if self.UNIXISH or self.IS_MAC: + envVar = "LD_LIBRARY_PATH" + preloadEnvVar = "LD_PRELOAD" + if self.IS_MAC: + envVar = "DYLD_LIBRARY_PATH" + dmdLibrary = "libdmd.dylib" + else: # unixish + dmdLibrary = "libdmd.so" + if envVar in env: + ldLibraryPath = ldLibraryPath + ":" + env[envVar] + env[envVar] = ldLibraryPath + elif self.IS_WIN32: + env["PATH"] = env["PATH"] + ";" + str(ldLibraryPath) + dmdLibrary = "dmd.dll" + preloadEnvVar = "MOZ_REPLACE_MALLOC_LIB" + + if dmdPath and dmdLibrary and preloadEnvVar: + env[preloadEnvVar] = os.path.join(dmdPath, dmdLibrary) + if crashreporter and not debugger: env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' env['MOZ_CRASHREPORTER'] = '1' else: env['MOZ_CRASHREPORTER_DISABLE'] = '1' # Crash on non-local network connections by default. # MOZ_DISABLE_NONLOCAL_CONNECTIONS can be set to "0" to temporarily
--- a/build/mobile/remoteautomation.py +++ b/build/mobile/remoteautomation.py @@ -55,22 +55,25 @@ class RemoteAutomation(Automation): def setProduct(self, product): self._product = product def setRemoteLog(self, logfile): self._remoteLog = logfile # Set up what we need for the remote environment - def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False, lsanPath=None, ubsanPath=None): + def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False, dmdPath=None, lsanPath=None, ubsanPath=None): # Because we are running remote, we don't want to mimic the local env # so no copying of os.environ if env is None: env = {} + if dmdPath: + env['MOZ_REPLACE_MALLOC_LIB'] = os.path.join(dmdPath, 'libdmd.so') + # Except for the mochitest results table hiding option, which isn't # passed to runtestsremote.py as an actual option, but through the # MOZ_HIDE_RESULTS_TABLE environment variable. if 'MOZ_HIDE_RESULTS_TABLE' in os.environ: env['MOZ_HIDE_RESULTS_TABLE'] = os.environ['MOZ_HIDE_RESULTS_TABLE'] if crashreporter and not debugger: env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
--- a/build/moz.configure/memory.configure +++ b/build/moz.configure/memory.configure @@ -57,19 +57,8 @@ def replace_malloc(value, jemalloc, mile return True if milestone.is_nightly and jemalloc and build_project != 'js': return True set_config('MOZ_REPLACE_MALLOC', replace_malloc) set_define('MOZ_REPLACE_MALLOC', replace_malloc) add_old_configure_assignment('MOZ_REPLACE_MALLOC', replace_malloc) - - -@depends(replace_malloc, build_project) -def replace_malloc_static(replace_malloc, build_project): - # Default to statically linking replace-malloc libraries that can be - # statically linked, except when building with --enable-project=memory. - if replace_malloc and build_project != 'memory': - return True - - -set_config('MOZ_REPLACE_MALLOC_STATIC', replace_malloc_static)
--- a/memory/build/moz.build +++ b/memory/build/moz.build @@ -44,12 +44,9 @@ if CONFIG['OS_TARGET'] == 'Android' and ] if CONFIG['MOZ_BUILD_APP'] != 'memory': FINAL_LIBRARY = 'mozglue' if CONFIG['_MSC_VER']: CXXFLAGS += ['-wd4273'] # inconsistent dll linkage (bug 558163) -if CONFIG['MOZ_REPLACE_MALLOC_STATIC']: - DEFINES['MOZ_REPLACE_MALLOC_STATIC'] = True - DisableStlWrapping()
--- a/memory/build/mozjemalloc.cpp +++ b/memory/build/mozjemalloc.cpp @@ -4869,64 +4869,36 @@ replace_malloc_handle() #define REPLACE_MALLOC_GET_INIT_FUNC(handle) \ (replace_init_impl_t*)dlsym(handle, "replace_init") #endif static void replace_malloc_init_funcs(); -#ifdef MOZ_REPLACE_MALLOC_STATIC -extern "C" void -logalloc_init(malloc_table_t*, ReplaceMallocBridge**); - -extern "C" void -dmd_init(malloc_table_t*, ReplaceMallocBridge**); -#endif - -bool -Equals(malloc_table_t& aTable1, malloc_table_t& aTable2) -{ - return memcmp(&aTable1, &aTable2, sizeof(malloc_table_t)) == 0; -} - // Below is the malloc implementation overriding jemalloc and calling the // replacement functions if they exist. static bool gReplaceMallocInitialized = false; static ReplaceMallocBridge* gReplaceMallocBridge = nullptr; static void init() { -#ifdef MOZ_REPLACE_MALLOC_STATIC - malloc_table_t initialTable = gReplaceMallocTable; -#endif - #ifdef MOZ_DYNAMIC_REPLACE_INIT replace_malloc_handle_t handle = replace_malloc_handle(); if (handle) { replace_init = REPLACE_MALLOC_GET_INIT_FUNC(handle); } #endif // Set this *before* calling replace_init, otherwise if replace_init calls // malloc() we'll get an infinite loop. gReplaceMallocInitialized = true; if (replace_init) { replace_init(&gReplaceMallocTable, &gReplaceMallocBridge); } -#ifdef MOZ_REPLACE_MALLOC_STATIC - if (Equals(initialTable, gReplaceMallocTable)) { - logalloc_init(&gReplaceMallocTable, &gReplaceMallocBridge); - } -#ifdef MOZ_DMD - if (Equals(initialTable, gReplaceMallocTable)) { - dmd_init(&gReplaceMallocTable, &gReplaceMallocBridge); - } -#endif -#endif replace_malloc_init_funcs(); } #define MALLOC_DECL(name, return_type, ...) \ template<> \ inline return_type ReplaceMalloc::name( \ ARGS_HELPER(TYPED_ARGS, ##__VA_ARGS__)) \ { \
--- a/memory/build/replace_malloc.h +++ b/memory/build/replace_malloc.h @@ -71,36 +71,30 @@ #define REPLACE_MALLOC_IMPL #include "replace_malloc_bridge.h" // Implementing a replace-malloc library is incompatible with using mozalloc. #define MOZ_NO_MOZALLOC 1 -#include "mozilla/MacroArgs.h" #include "mozilla/Types.h" MOZ_BEGIN_EXTERN_C // MOZ_REPLACE_WEAK is only defined in mozjemalloc.cpp. Normally including // this header will add function definitions. #ifndef MOZ_REPLACE_WEAK #define MOZ_REPLACE_WEAK #endif -// When building a replace-malloc library for static linking, we want -// each to have a different name for their "public" functions. -// The build system defines MOZ_REPLACE_MALLOC_PREFIX in that case. -#ifdef MOZ_REPLACE_MALLOC_PREFIX -#define replace_init MOZ_CONCAT(MOZ_REPLACE_MALLOC_PREFIX, _init) -#define MOZ_REPLACE_PUBLIC -#else -#define MOZ_REPLACE_PUBLIC MOZ_EXPORT -#endif +// Replace-malloc library initialization function. See top of this file +MOZ_EXPORT void +replace_init(malloc_table_t*, struct ReplaceMallocBridge**) MOZ_REPLACE_WEAK; -// Replace-malloc library initialization function. See top of this file -MOZ_REPLACE_PUBLIC void -replace_init(malloc_table_t*, struct ReplaceMallocBridge**) MOZ_REPLACE_WEAK; +// Define the replace_* functions as not exported. +#define MALLOC_DECL(name, return_type, ...) \ + return_type replace_##name(__VA_ARGS__); +#include "malloc_decls.h" MOZ_END_EXTERN_C #endif // replace_malloc_h
--- a/memory/replace/dmd/DMD.cpp +++ b/memory/replace/dmd/DMD.cpp @@ -86,16 +86,19 @@ StatusMsg(const char* aFmt, ...) #ifndef DISALLOW_COPY_AND_ASSIGN #define DISALLOW_COPY_AND_ASSIGN(T) \ T(const T&); \ void operator=(const T&) #endif static malloc_table_t gMallocTable; +// Whether DMD finished initializing. +static bool gIsDMDInitialized = false; + // This provides infallible allocations (they abort on OOM). We use it for all // of DMD's own allocations, which fall into the following three cases. // // - Direct allocations (the easy case). // // - Indirect allocations in js::{Vector,HashSet,HashMap} -- this class serves // as their AllocPolicy. // @@ -1251,59 +1254,85 @@ FreeCallback(void* aPtr, Thread* aT, Dea GCStackTraces(); } } //--------------------------------------------------------------------------- // malloc/free interception //--------------------------------------------------------------------------- -static bool Init(malloc_table_t* aMallocTable); +static void Init(malloc_table_t* aMallocTable); } // namespace dmd } // namespace mozilla -static void* +void +replace_init(malloc_table_t* aMallocTable, ReplaceMallocBridge** aBridge) +{ + mozilla::dmd::Init(aMallocTable); +#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC_BASE +#define MALLOC_DECL(name, ...) aMallocTable->name = replace_ ## name; +#include "malloc_decls.h" + *aBridge = mozilla::dmd::gDMDBridge; +} + +void* replace_malloc(size_t aSize) { using namespace mozilla::dmd; + if (!gIsDMDInitialized) { + // DMD hasn't started up, either because it wasn't enabled by the user, or + // we're still in Init() and something has indirectly called malloc. Do a + // vanilla malloc. (In the latter case, if it fails we'll crash. But + // OOM is highly unlikely so early on.) + return gMallocTable.malloc(aSize); + } + Thread* t = Thread::Fetch(); if (t->InterceptsAreBlocked()) { // Intercepts are blocked, which means this must be a call to malloc // triggered indirectly by DMD (e.g. via MozStackWalk). Be infallible. return InfallibleAllocPolicy::malloc_(aSize); } // This must be a call to malloc from outside DMD. Intercept it. void* ptr = gMallocTable.malloc(aSize); AllocCallback(ptr, aSize, t); return ptr; } -static void* +void* replace_calloc(size_t aCount, size_t aSize) { using namespace mozilla::dmd; + if (!gIsDMDInitialized) { + return gMallocTable.calloc(aCount, aSize); + } + Thread* t = Thread::Fetch(); if (t->InterceptsAreBlocked()) { return InfallibleAllocPolicy::calloc_(aCount * aSize); } void* ptr = gMallocTable.calloc(aCount, aSize); AllocCallback(ptr, aCount * aSize, t); return ptr; } -static void* +void* replace_realloc(void* aOldPtr, size_t aSize) { using namespace mozilla::dmd; + if (!gIsDMDInitialized) { + return gMallocTable.realloc(aOldPtr, aSize); + } + Thread* t = Thread::Fetch(); if (t->InterceptsAreBlocked()) { return InfallibleAllocPolicy::realloc_(aOldPtr, aSize); } // If |aOldPtr| is nullptr, the call is equivalent to |malloc(aSize)|. if (!aOldPtr) { return replace_malloc(aSize); @@ -1326,61 +1355,59 @@ replace_realloc(void* aOldPtr, size_t aS // block will end up looking like it was allocated for the first time here, // which is untrue, and the slop bytes will be zero, which may be untrue. // But this case is rare and doing better isn't worth the effort. AllocCallback(aOldPtr, gMallocTable.malloc_usable_size(aOldPtr), t); } return ptr; } -static void* +void* replace_memalign(size_t aAlignment, size_t aSize) { using namespace mozilla::dmd; + if (!gIsDMDInitialized) { + return gMallocTable.memalign(aAlignment, aSize); + } + Thread* t = Thread::Fetch(); if (t->InterceptsAreBlocked()) { return InfallibleAllocPolicy::memalign_(aAlignment, aSize); } void* ptr = gMallocTable.memalign(aAlignment, aSize); AllocCallback(ptr, aSize, t); return ptr; } -static void +void replace_free(void* aPtr) { using namespace mozilla::dmd; + if (!gIsDMDInitialized) { + gMallocTable.free(aPtr); + return; + } + Thread* t = Thread::Fetch(); if (t->InterceptsAreBlocked()) { return InfallibleAllocPolicy::free_(aPtr); } // Do the actual free after updating the table. Otherwise, another thread // could call malloc and get the freed block and update the table, and then // our update here would remove the newly-malloc'd block. DeadBlock db; FreeCallback(aPtr, t, &db); MaybeAddToDeadBlockTable(db); gMallocTable.free(aPtr); } -void -replace_init(malloc_table_t* aMallocTable, ReplaceMallocBridge** aBridge) -{ - if (mozilla::dmd::Init(aMallocTable)) { -#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC_BASE -#define MALLOC_DECL(name, ...) aMallocTable->name = replace_ ## name; -#include "malloc_decls.h" - *aBridge = mozilla::dmd::gDMDBridge; - } -} - namespace mozilla { namespace dmd { //--------------------------------------------------------------------------- // Options (Part 2) //--------------------------------------------------------------------------- // Given an |aOptionName| like "foo", succeed if |aArg| has the form "foo=blah" @@ -1435,16 +1462,19 @@ Options::GetBool(const char* aArg, const Options::Options(const char* aDMDEnvVar) : mDMDEnvVar(aDMDEnvVar ? InfallibleAllocPolicy::strdup_(aDMDEnvVar) : nullptr) , mMode(Mode::DarkMatter) , mStacks(Stacks::Partial) , mShowDumpStats(false) { + // It's no longer necessary to set the DMD env var to "1" if you want default + // options (you can leave it undefined) but we still accept "1" for + // backwards compatibility. char* e = mDMDEnvVar; if (e && strcmp(e, "1") != 0) { bool isEnd = false; while (!isEnd) { // Consume leading whitespace. while (isspace(*e)) { e++; } @@ -1544,42 +1574,41 @@ postfork() gStateLock->Unlock(); } } // WARNING: this function runs *very* early -- before all static initializers // have run. For this reason, non-scalar globals such as gStateLock and // gStackTraceTable are allocated dynamically (so we can guarantee their // construction in this function) rather than statically. -static bool +static void Init(malloc_table_t* aMallocTable) { - // DMD is controlled by the |DMD| environment variable. - const char* e = getenv("DMD"); - - if (!e) { - return false; - } - // Initialize the function table first, because StatusMsg uses - // InfallibleAllocPolicy::malloc_, which uses it. gMallocTable = *aMallocTable; - - StatusMsg("$DMD = '%s'\n", e); - gDMDBridge = InfallibleAllocPolicy::new_<DMDBridge>(); #ifndef XP_WIN // Avoid deadlocks when forking by acquiring our state lock prior to forking // and releasing it after forking. See |LogAlloc|'s |replace_init| for // in-depth details. // // Note: This must run after attempting an allocation so as to give the // system malloc a chance to insert its own atfork handler. pthread_atfork(prefork, postfork, postfork); #endif + + // DMD is controlled by the |DMD| environment variable. + const char* e = getenv("DMD"); + + if (e) { + StatusMsg("$DMD = '%s'\n", e); + } else { + StatusMsg("$DMD is undefined\n"); + } + // Parse $DMD env var. gOptions = InfallibleAllocPolicy::new_<Options>(e); gStateLock = InfallibleAllocPolicy::new_<Mutex>(); gBernoulli = (FastBernoulliTrial*) InfallibleAllocPolicy::malloc_(sizeof(FastBernoulliTrial)); ResetBernoulli(); @@ -1598,17 +1627,17 @@ Init(malloc_table_t* aMallocTable) // Create this even if the mode isn't Cumulative (albeit with a small // size), in case the mode is changed later on (as is done by SmokeDMD.cpp, // for example). gDeadBlockTable = InfallibleAllocPolicy::new_<DeadBlockTable>(); size_t tableSize = gOptions->IsCumulativeMode() ? 8192 : 4; MOZ_ALWAYS_TRUE(gDeadBlockTable->init(tableSize)); } - return true; + gIsDMDInitialized = true; } //--------------------------------------------------------------------------- // Block reporting and unreporting //--------------------------------------------------------------------------- static void ReportHelper(const void* aPtr, bool aReportedOnAlloc)
--- a/memory/replace/dmd/moz.build +++ b/memory/replace/dmd/moz.build @@ -4,28 +4,24 @@ # 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/. EXPORTS += [ 'DMD.h', ] UNIFIED_SOURCES += [ + '../../../mfbt/HashFunctions.cpp', + '../../../mfbt/JSONWriter.cpp', + '../../../mfbt/Poison.cpp', + '../../../mozglue/misc/StackWalk.cpp', 'DMD.cpp', ] -if not CONFIG['MOZ_REPLACE_MALLOC_STATIC']: - UNIFIED_SOURCES += [ - '/mfbt/HashFunctions.cpp', - '/mfbt/JSONWriter.cpp', - '/mfbt/Poison.cpp', - '/mozglue/misc/StackWalk.cpp', - ] - -ReplaceMalloc('dmd') +SharedLibrary('dmd') DEFINES['MOZ_NO_MOZALLOC'] = True DEFINES['IMPL_MFBT'] = True DEFINES['XPCOM_GLUE'] = True if CONFIG['MOZ_OPTIMIZE']: DEFINES['MOZ_OPTIMIZE'] = True
--- a/memory/replace/dmd/test/script-diff-dark-matter-expected.txt +++ b/memory/replace/dmd/test/script-diff-dark-matter-expected.txt @@ -2,17 +2,17 @@ # dmd.py --filter-stacks-for-testing -o script-diff-dark-matter-actual.txt script-diff-dark-matter1.json script-diff-dark-matter2.json Invocation 1 { $DMD = '--mode=dark-matter' Mode = 'dark-matter' } Invocation 2 { - $DMD = '1' + $DMD is undefined Mode = 'dark-matter' } #----------------------------------------------------------------- Twice-reported { -1 blocks in heap block record 1 of 1 -1,088 bytes (-1,064 requested / -24 slop)
--- a/memory/replace/dmd/test/script-diff-dark-matter2.json +++ b/memory/replace/dmd/test/script-diff-dark-matter2.json @@ -1,12 +1,12 @@ { "version": 5, "invocation": { - "dmdEnvVar": "1", + "dmdEnvVar": null, "mode": "dark-matter" }, "blockList": [ {"req": 4096, "alloc": "A", "num": 4}, {"req": 8192, "alloc": "B"}, {"req": 8192, "alloc": "B"},
--- a/memory/replace/dmd/test/script-ignore-alloc-fns-expected.txt +++ b/memory/replace/dmd/test/script-ignore-alloc-fns-expected.txt @@ -1,13 +1,13 @@ #----------------------------------------------------------------- # dmd.py --filter-stacks-for-testing -o script-ignore-alloc-fns-actual.txt --ignore-alloc-fns script-ignore-alloc-fns.json Invocation { - $DMD = '1' + $DMD is undefined Mode = 'dark-matter' } #----------------------------------------------------------------- # no twice-reported heap blocks #-----------------------------------------------------------------
--- a/memory/replace/dmd/test/script-ignore-alloc-fns.json +++ b/memory/replace/dmd/test/script-ignore-alloc-fns.json @@ -1,12 +1,12 @@ { "version": 5, "invocation": { - "dmdEnvVar": "1", + "dmdEnvVar": null, "mode": "dark-matter" }, "blockList": [ {"req": 1048576, "alloc": "A"}, {"req": 65536, "alloc": "B"}, {"req": 8000, "slop": 192, "alloc": "C"}, {"req": 2500, "alloc": "D"} ],
--- a/memory/replace/dmd/test/test_dmd.js +++ b/memory/replace/dmd/test/test_dmd.js @@ -131,17 +131,17 @@ function run_test() { // These tests do complete end-to-end testing of DMD, i.e. both the C++ code // that generates the JSON output, and the script that post-processes that // output. // // Run these synchronously, because test() updates the complete*.json files // in-place (to fix stacks) when it runs dmd.py, and that's not safe to do // asynchronously. - gEnv.set('DMD', '1'); + gEnv.set(gEnv.get("DMD_PRELOAD_VAR"), gEnv.get("DMD_PRELOAD_VALUE")); runProcess(gDmdTestFile, []); function test2(aTestName, aMode) { let name = "complete-" + aTestName + "-" + aMode; jsonFile = FileUtils.getFile("CurWorkD", [name + ".json"]); test(name, [jsonFile.path]); jsonFile.remove(true);
--- a/memory/replace/logalloc/LogAlloc.cpp +++ b/memory/replace/logalloc/LogAlloc.cpp @@ -68,128 +68,65 @@ class LogAllocBridge : public ReplaceMal { virtual void InitDebugFd(mozilla::DebugFdRegistry& aRegistry) override { if (!sStdoutOrStderr) { aRegistry.RegisterHandle(sFd); } } }; -/* Do a simple, text-form, log of all calls to replace-malloc functions. - * Use locking to guarantee that an allocation that did happen is logged - * before any other allocation/free happens. - */ - -static void* -replace_malloc(size_t aSize) -{ - AutoLock lock(sLock); - void* ptr = sFuncs.malloc(aSize); - if (ptr) { - FdPrintf(sFd, "%zu %zu malloc(%zu)=%p\n", GetPid(), GetTid(), aSize, ptr); - } - return ptr; -} - -#ifndef LOGALLOC_MINIMAL -static int -replace_posix_memalign(void** aPtr, size_t aAlignment, size_t aSize) -{ - AutoLock lock(sLock); - int ret = sFuncs.posix_memalign(aPtr, aAlignment, aSize); - if (ret == 0) { - FdPrintf(sFd, "%zu %zu posix_memalign(%zu,%zu)=%p\n", GetPid(), GetTid(), - aAlignment, aSize, *aPtr); - } - return ret; -} - -static void* -replace_aligned_alloc(size_t aAlignment, size_t aSize) -{ - AutoLock lock(sLock); - void* ptr = sFuncs.aligned_alloc(aAlignment, aSize); - if (ptr) { - FdPrintf(sFd, "%zu %zu aligned_alloc(%zu,%zu)=%p\n", GetPid(), GetTid(), - aAlignment, aSize, ptr); - } - return ptr; -} -#endif - -static void* -replace_calloc(size_t aNum, size_t aSize) -{ - AutoLock lock(sLock); - void* ptr = sFuncs.calloc(aNum, aSize); - if (ptr) { - FdPrintf(sFd, "%zu %zu calloc(%zu,%zu)=%p\n", GetPid(), GetTid(), aNum, - aSize, ptr); - } - return ptr; -} - -static void* -replace_realloc(void* aPtr, size_t aSize) -{ - AutoLock lock(sLock); - void* new_ptr = sFuncs.realloc(aPtr, aSize); - if (new_ptr || !aSize) { - FdPrintf(sFd, "%zu %zu realloc(%p,%zu)=%p\n", GetPid(), GetTid(), aPtr, - aSize, new_ptr); - } - return new_ptr; -} - -static void -replace_free(void* aPtr) -{ - AutoLock lock(sLock); - if (aPtr) { - FdPrintf(sFd, "%zu %zu free(%p)\n", GetPid(), GetTid(), aPtr); - } - sFuncs.free(aPtr); -} - -static void* -replace_memalign(size_t aAlignment, size_t aSize) -{ - AutoLock lock(sLock); - void* ptr = sFuncs.memalign(aAlignment, aSize); - if (ptr) { - FdPrintf(sFd, "%zu %zu memalign(%zu,%zu)=%p\n", GetPid(), GetTid(), - aAlignment, aSize, ptr); - } - return ptr; -} - -#ifndef LOGALLOC_MINIMAL -static void* -replace_valloc(size_t aSize) -{ - AutoLock lock(sLock); - void* ptr = sFuncs.valloc(aSize); - if (ptr) { - FdPrintf(sFd, "%zu %zu valloc(%zu)=%p\n", GetPid(), GetTid(), aSize, ptr); - } - return ptr; -} -#endif - -static void -replace_jemalloc_stats(jemalloc_stats_t* aStats) -{ - AutoLock lock(sLock); - sFuncs.jemalloc_stats(aStats); - FdPrintf(sFd, "%zu %zu jemalloc_stats()\n", GetPid(), GetTid()); -} - void replace_init(malloc_table_t* aTable, ReplaceMallocBridge** aBridge) { + static LogAllocBridge bridge; + sFuncs = *aTable; +#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC_BASE +#define MALLOC_DECL(name, ...) aTable->name = replace_ ## name; +#include "malloc_decls.h" + aTable->jemalloc_stats = replace_jemalloc_stats; +#ifndef LOGALLOC_MINIMAL + aTable->posix_memalign = replace_posix_memalign; + aTable->aligned_alloc = replace_aligned_alloc; + aTable->valloc = replace_valloc; +#endif + *aBridge = &bridge; + +#ifndef _WIN32 + /* When another thread has acquired a lock before forking, the child + * process will inherit the lock state but the thread, being nonexistent + * in the child process, will never release it, leading to a dead-lock + * whenever the child process gets the lock. We thus need to ensure no + * other thread is holding the lock before forking, by acquiring it + * ourselves, and releasing it after forking, both in the parent and child + * processes. + * Windows doesn't have this problem since there is no fork(). + * The real allocator, however, might be doing the same thing (jemalloc + * does). But pthread_atfork `prepare` handlers (first argument) are + * processed in reverse order they were established. But replace_init + * runs before the real allocator has had any chance to initialize and + * call pthread_atfork itself. This leads to its prefork running before + * ours. This leads to a race condition that can lead to a deadlock like + * the following: + * - thread A forks. + * - libc calls real allocator's prefork, so thread A holds the real + * allocator lock. + * - thread B calls malloc, which calls our replace_malloc. + * - consequently, thread B holds our lock. + * - thread B then proceeds to call the real allocator's malloc, and + * waits for the real allocator's lock, which thread A holds. + * - libc calls our prefork, so thread A waits for our lock, which + * thread B holds. + * To avoid this race condition, the real allocator's prefork must be + * called after ours, which means it needs to be registered before ours. + * So trick the real allocator into initializing itself without more side + * effects by calling malloc with a size it can't possibly allocate. */ + sFuncs.malloc(-1); + pthread_atfork(prefork, postfork, postfork); +#endif + /* Initialize output file descriptor from the MALLOC_LOG environment * variable. Numbers up to 9999 are considered as a preopened file * descriptor number. Other values are considered as a file name. */ char* log = getenv("MALLOC_LOG"); if (log && *log) { int fd = 0; const char *fd_num = log; while (*fd_num) { @@ -226,60 +163,118 @@ replace_init(malloc_table_t* aTable, Rep if (fd == -1) { fd = open(log, O_WRONLY | O_CREAT | O_APPEND, 0644); } if (fd > 0) { sFd = fd; } #endif } +} - // Don't initialize if we weren't passed a valid MALLOC_LOG. - if (sFd == 0) { - return; +/* Do a simple, text-form, log of all calls to replace-malloc functions. + * Use locking to guarantee that an allocation that did happen is logged + * before any other allocation/free happens. + */ + +void* +replace_malloc(size_t aSize) +{ + AutoLock lock(sLock); + void* ptr = sFuncs.malloc(aSize); + if (ptr) { + FdPrintf(sFd, "%zu %zu malloc(%zu)=%p\n", GetPid(), GetTid(), aSize, ptr); } + return ptr; +} - static LogAllocBridge bridge; - sFuncs = *aTable; -#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC_BASE -#define MALLOC_DECL(name, ...) aTable->name = replace_ ## name; -#include "malloc_decls.h" - aTable->jemalloc_stats = replace_jemalloc_stats; #ifndef LOGALLOC_MINIMAL - aTable->posix_memalign = replace_posix_memalign; - aTable->aligned_alloc = replace_aligned_alloc; - aTable->valloc = replace_valloc; +int +replace_posix_memalign(void** aPtr, size_t aAlignment, size_t aSize) +{ + AutoLock lock(sLock); + int ret = sFuncs.posix_memalign(aPtr, aAlignment, aSize); + if (ret == 0) { + FdPrintf(sFd, "%zu %zu posix_memalign(%zu,%zu)=%p\n", GetPid(), GetTid(), + aAlignment, aSize, *aPtr); + } + return ret; +} + +void* +replace_aligned_alloc(size_t aAlignment, size_t aSize) +{ + AutoLock lock(sLock); + void* ptr = sFuncs.aligned_alloc(aAlignment, aSize); + if (ptr) { + FdPrintf(sFd, "%zu %zu aligned_alloc(%zu,%zu)=%p\n", GetPid(), GetTid(), + aAlignment, aSize, ptr); + } + return ptr; +} #endif - *aBridge = &bridge; -#ifndef _WIN32 - /* When another thread has acquired a lock before forking, the child - * process will inherit the lock state but the thread, being nonexistent - * in the child process, will never release it, leading to a dead-lock - * whenever the child process gets the lock. We thus need to ensure no - * other thread is holding the lock before forking, by acquiring it - * ourselves, and releasing it after forking, both in the parent and child - * processes. - * Windows doesn't have this problem since there is no fork(). - * The real allocator, however, might be doing the same thing (jemalloc - * does). But pthread_atfork `prepare` handlers (first argument) are - * processed in reverse order they were established. But replace_init - * runs before the real allocator has had any chance to initialize and - * call pthread_atfork itself. This leads to its prefork running before - * ours. This leads to a race condition that can lead to a deadlock like - * the following: - * - thread A forks. - * - libc calls real allocator's prefork, so thread A holds the real - * allocator lock. - * - thread B calls malloc, which calls our replace_malloc. - * - consequently, thread B holds our lock. - * - thread B then proceeds to call the real allocator's malloc, and - * waits for the real allocator's lock, which thread A holds. - * - libc calls our prefork, so thread A waits for our lock, which - * thread B holds. - * To avoid this race condition, the real allocator's prefork must be - * called after ours, which means it needs to be registered before ours. - * So trick the real allocator into initializing itself without more side - * effects by calling malloc with a size it can't possibly allocate. */ - sFuncs.malloc(-1); - pthread_atfork(prefork, postfork, postfork); +void* +replace_calloc(size_t aNum, size_t aSize) +{ + AutoLock lock(sLock); + void* ptr = sFuncs.calloc(aNum, aSize); + if (ptr) { + FdPrintf(sFd, "%zu %zu calloc(%zu,%zu)=%p\n", GetPid(), GetTid(), aNum, + aSize, ptr); + } + return ptr; +} + +void* +replace_realloc(void* aPtr, size_t aSize) +{ + AutoLock lock(sLock); + void* new_ptr = sFuncs.realloc(aPtr, aSize); + if (new_ptr || !aSize) { + FdPrintf(sFd, "%zu %zu realloc(%p,%zu)=%p\n", GetPid(), GetTid(), aPtr, + aSize, new_ptr); + } + return new_ptr; +} + +void +replace_free(void* aPtr) +{ + AutoLock lock(sLock); + if (aPtr) { + FdPrintf(sFd, "%zu %zu free(%p)\n", GetPid(), GetTid(), aPtr); + } + sFuncs.free(aPtr); +} + +void* +replace_memalign(size_t aAlignment, size_t aSize) +{ + AutoLock lock(sLock); + void* ptr = sFuncs.memalign(aAlignment, aSize); + if (ptr) { + FdPrintf(sFd, "%zu %zu memalign(%zu,%zu)=%p\n", GetPid(), GetTid(), + aAlignment, aSize, ptr); + } + return ptr; +} + +#ifndef LOGALLOC_MINIMAL +void* +replace_valloc(size_t aSize) +{ + AutoLock lock(sLock); + void* ptr = sFuncs.valloc(aSize); + if (ptr) { + FdPrintf(sFd, "%zu %zu valloc(%zu)=%p\n", GetPid(), GetTid(), aSize, ptr); + } + return ptr; +} #endif + +void +replace_jemalloc_stats(jemalloc_stats_t* aStats) +{ + AutoLock lock(sLock); + sFuncs.jemalloc_stats(aStats); + FdPrintf(sFd, "%zu %zu jemalloc_stats()\n", GetPid(), GetTid()); }
--- a/memory/replace/logalloc/README +++ b/memory/replace/logalloc/README @@ -1,17 +1,29 @@ Logalloc is a replace-malloc library for Firefox (see memory/build/replace_malloc.h) that dumps a log of memory allocations to a given file descriptor or file name. That log can then be replayed against Firefox's default memory allocator independently or through another replace-malloc library, allowing the testing of other allocators under the exact same workload. -To get an allocation log the following environment variable when starting -Firefox: +To get an allocation log the following environment variables need to be set +when starting Firefox: +- on Linux: + LD_PRELOAD=/path/to/liblogalloc.so +- on Mac OSX: + DYLD_INSERT_LIBRARIES=/path/to/liblogalloc.dylib +- on Windows: + MOZ_REPLACE_MALLOC_LIB=/path/to/logalloc.dll +- on Android: + MOZ_REPLACE_MALLOC_LIB=/path/to/liblogalloc.so + (see https://wiki.mozilla.org/Mobile/Fennec/Android#Arguments_and_Environment_Variables + for how to pass environment variables to Firefox for Android) + +- on all platforms: MALLOC_LOG=/path/to/log-file or MALLOC_LOG=number When MALLOC_LOG is a number below 10000, it is considered as a file descriptor number that is fed to Firefox when it is started. Otherwise, it is considered as a file name.
--- a/memory/replace/logalloc/logalloc.mozbuild +++ b/memory/replace/logalloc/logalloc.mozbuild @@ -5,16 +5,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. SOURCES += [ 'FdPrintf.cpp', 'LogAlloc.cpp', ] DisableStlWrapping() +USE_STATIC_LIBS = True NO_PGO = True DEFINES['MOZ_NO_MOZALLOC'] = True # Avoid Lock_impl code depending on mozilla::Logger. DEFINES['NDEBUG'] = True DEFINES['DEBUG'] = False # Use locking code from the chromium stack. if CONFIG['OS_TARGET'] == 'WINNT': @@ -24,12 +25,12 @@ if CONFIG['OS_TARGET'] == 'WINNT': else: SOURCES += [ '../../../ipc/chromium/src/base/lock_impl_posix.cc', ] include('/ipc/chromium/chromium-config.mozbuild') # Android doesn't have pthread_atfork, but we have our own in mozglue. -if CONFIG['OS_TARGET'] == 'Android' and FORCE_SHARED_LIB: +if CONFIG['OS_TARGET'] == 'Android': USE_LIBS += [ 'mozglue', ]
--- a/memory/replace/logalloc/moz.build +++ b/memory/replace/logalloc/moz.build @@ -1,14 +1,14 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. -ReplaceMalloc('logalloc') +SharedLibrary('logalloc') include('logalloc.mozbuild') DIRS += [ 'minimal', 'replay', ]
--- a/memory/replace/logalloc/replay/Makefile.in +++ b/memory/replace/logalloc/replay/Makefile.in @@ -11,19 +11,17 @@ LOGALLOC_VAR = MOZ_REPLACE_MALLOC_LIB else ifeq ($(OS_TARGET),Darwin) LOGALLOC_VAR = DYLD_INSERT_LIBRARIES else LOGALLOC_VAR = LD_PRELOAD endif endif -ifndef MOZ_REPLACE_MALLOC_STATIC LOGALLOC = $(LOGALLOC_VAR)=$(CURDIR)/../$(DLL_PREFIX)logalloc$(DLL_SUFFIX) -endif LOGALLOC_MINIMAL = $(LOGALLOC_VAR)=$(CURDIR)/../minimal/$(DLL_PREFIX)logalloc_minimal$(DLL_SUFFIX) expected_output.log: $(srcdir)/replay.log # The logalloc-replay program will only replay entries from the first pid, # so the expected output only contains entries beginning with "1 " grep "^1 " $< > $@ check:: $(srcdir)/replay.log expected_output.log $(srcdir)/expected_output_minimal.log
--- a/memory/replace/logalloc/replay/moz.build +++ b/memory/replace/logalloc/replay/moz.build @@ -2,34 +2,22 @@ # vim: set filetype=python: # 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/. Program('logalloc-replay') SOURCES += [ + '../FdPrintf.cpp', '/mfbt/Assertions.cpp', '/mfbt/Unused.cpp', 'Replay.cpp', ] -if CONFIG['MOZ_REPLACE_MALLOC_STATIC'] and CONFIG['MOZ_DMD']: - UNIFIED_SOURCES += [ - '/mfbt/HashFunctions.cpp', - '/mfbt/JSONWriter.cpp', - '/mfbt/Poison.cpp', - '/mozglue/misc/StackWalk.cpp', - ] - -if not CONFIG['MOZ_REPLACE_MALLOC_STATIC']: - SOURCES += [ - '../FdPrintf.cpp', - ] - LOCAL_INCLUDES += [ '..', ] # Link replace-malloc and the default allocator. USE_LIBS += [ 'memory', ]
--- a/memory/replace/moz.build +++ b/memory/replace/moz.build @@ -1,20 +1,12 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. -@template -def ReplaceMalloc(name): - if CONFIG['MOZ_REPLACE_MALLOC_STATIC']: - DEFINES['MOZ_REPLACE_MALLOC_PREFIX'] = name.replace('-', '_') - FINAL_LIBRARY = 'memory' - else: - SharedLibrary(name) - DIRS += [ 'logalloc', ] if CONFIG['MOZ_DMD']: DIRS += ['dmd']
--- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -915,20 +915,43 @@ class RunProgram(MachCommandBase): if mode: dmd_params.append('--mode=' + mode) if stacks: dmd_params.append('--stacks=' + stacks) if show_dump_stats: dmd_params.append('--show-dump-stats=yes') + bin_dir = os.path.dirname(binpath) + lib_name = self.substs['DLL_PREFIX'] + 'dmd' + self.substs['DLL_SUFFIX'] + dmd_lib = os.path.join(bin_dir, lib_name) + if not os.path.exists(dmd_lib): + print("Please build with |--enable-dmd| to use DMD.") + return 1 + + env_vars = { + "Darwin": { + "DYLD_INSERT_LIBRARIES": dmd_lib, + "LD_LIBRARY_PATH": bin_dir, + }, + "Linux": { + "LD_PRELOAD": dmd_lib, + "LD_LIBRARY_PATH": bin_dir, + }, + "WINNT": { + "MOZ_REPLACE_MALLOC_LIB": dmd_lib, + }, + } + + arch = self.substs['OS_ARCH'] + if dmd_params: - extra_env['DMD'] = ' '.join(dmd_params) - else: - extra_env['DMD'] = '1' + env_vars[arch]["DMD"] = " ".join(dmd_params) + + extra_env.update(env_vars.get(arch, {})) return self.run_process(args=args, ensure_exit_code=False, pass_thru=True, append_env=extra_env) @CommandProvider class Buildsymbols(MachCommandBase): """Produce a package of debug symbols suitable for use with Breakpad."""
--- a/testing/awsy/mach_commands.py +++ b/testing/awsy/mach_commands.py @@ -139,20 +139,42 @@ class MachCommands(MachCommandBase): self.run_process(**unzip_args) # If '--preferences' was not specified supply our default set. if not kwargs['prefs_files']: kwargs['prefs_files'] = [os.path.join(awsy_source_dir, 'conf', 'prefs.json')] # Setup DMD env vars if necessary. if kwargs['dmd']: + dmd_params = [] + bin_dir = os.path.dirname(binary) + lib_name = self.substs['DLL_PREFIX'] + 'dmd' + self.substs['DLL_SUFFIX'] + dmd_lib = os.path.join(bin_dir, lib_name) + if not os.path.exists(dmd_lib): + print("Please build with |--enable-dmd| to use DMD.") + return 1 - if 'DMD' not in os.environ: - os.environ['DMD'] = '1' + env_vars = { + "Darwin": { + "DYLD_INSERT_LIBRARIES": dmd_lib, + "LD_LIBRARY_PATH": bin_dir, + }, + "Linux": { + "LD_PRELOAD": dmd_lib, + "LD_LIBRARY_PATH": bin_dir, + }, + "WINNT": { + "MOZ_REPLACE_MALLOC_LIB": dmd_lib, + }, + } + + arch = self.substs['OS_ARCH'] + for k, v in env_vars[arch].iteritems(): + os.environ[k] = v # Also add the bin dir to the python path so we can use dmd.py if bin_dir not in sys.path: sys.path.append(bin_dir) for k, v in kwargs.iteritems(): setattr(args, k, v)
--- a/testing/mochitest/mochitest_options.py +++ b/testing/mochitest/mochitest_options.py @@ -429,16 +429,22 @@ class MochitestArguments(ArgumentContain "default": False, "help": "Run tests with nested_oop preferences and test filtering enabled.", }], [["--dmd"], {"action": "store_true", "default": False, "help": "Run tests with DMD active.", }], + [["--dmd-path"], + {"default": None, + "dest": "dmdPath", + "help": "Specifies the path to the directory containing the shared library for DMD.", + "suppress": True, + }], [["--dump-output-directory"], {"default": None, "dest": "dumpOutputDirectory", "help": "Specifies the directory in which to place dumped memory reports.", }], [["--dump-about-memory-after-test"], {"action": "store_true", "default": False, @@ -690,16 +696,26 @@ class MochitestArguments(ArgumentContain # allow relative paths if options.xrePath: options.xrePath = self.get_full_path(options.xrePath, parser.oldcwd) if options.profilePath: options.profilePath = self.get_full_path(options.profilePath, parser.oldcwd) + if options.dmdPath: + options.dmdPath = self.get_full_path(options.dmdPath, parser.oldcwd) + + if options.dmd and not options.dmdPath: + if build_obj: + options.dmdPath = build_obj.bindir + else: + parser.error( + "could not find dmd libraries, specify them with --dmd-path") + if options.utilityPath: options.utilityPath = self.get_full_path(options.utilityPath, parser.oldcwd) if options.certPath: options.certPath = self.get_full_path(options.certPath, parser.oldcwd) elif build_obj: options.certPath = os.path.join(build_obj.topsrcdir, 'build', 'pgo', 'certs')
--- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -1617,30 +1617,28 @@ toolbar#nav-bar { ubsanPath = SCRIPT_DIR else: ubsanPath = None browserEnv = self.environment( xrePath=options.xrePath, env=env, debugger=debugger, + dmdPath=options.dmdPath, lsanPath=lsanPath, ubsanPath=ubsanPath) if hasattr(options, "topsrcdir"): browserEnv["MOZ_DEVELOPER_REPO_DIR"] = options.topsrcdir if hasattr(options, "topobjdir"): browserEnv["MOZ_DEVELOPER_OBJ_DIR"] = options.topobjdir if options.headless: browserEnv["MOZ_HEADLESS"] = '1' - if options.dmd: - browserEnv["DMD"] = os.environ.get('DMD', '1') - # These variables are necessary for correct application startup; change # via the commandline at your own risk. browserEnv["XPCOM_DEBUG_BREAK"] = "stack" # interpolate environment passed with options try: browserEnv.update( dict(
--- a/testing/mochitest/runtestsremote.py +++ b/testing/mochitest/runtestsremote.py @@ -275,18 +275,16 @@ class MochiRemote(MochitestDesktop): # remove desktop environment not used on device if "XPCOM_MEM_BLOAT_LOG" in browserEnv: del browserEnv["XPCOM_MEM_BLOAT_LOG"] # override mozLogs to avoid processing in MochitestDesktop base class self.mozLogs = None browserEnv["MOZ_LOG_FILE"] = os.path.join( self.remoteMozLog, self.mozLogName) - if options.dmd: - browserEnv['DMD'] = '1' return browserEnv def runApp(self, *args, **kwargs): """front-end automation.py's `runApp` functionality until FennecRunner is written""" # automation.py/remoteautomation `runApp` takes the profile path, # whereas runtest.py's `runApp` takes a mozprofile object. if 'profileDir' not in kwargs and 'profile' in kwargs: @@ -351,16 +349,23 @@ def run_test_harness(parser, options): # can be conditional on android_version. androidVersion = dm.shellCheckOutput(['getprop', 'ro.build.version.sdk']) log.info( "Android sdk version '%s'; will use this to filter manifests" % str(androidVersion)) mozinfo.info['android_version'] = androidVersion deviceRoot = dm.deviceRoot + if options.dmdPath: + dmdLibrary = "libdmd.so" + dmdPathOnDevice = os.path.join(deviceRoot, dmdLibrary) + dm.removeFile(dmdPathOnDevice) + dm.pushFile(os.path.join(options.dmdPath, dmdLibrary), dmdPathOnDevice) + options.dmdPath = deviceRoot + options.dumpOutputDirectory = deviceRoot procName = options.app.split('/')[-1] dm.killProcess(procName) if dm.processExist(procName): log.warning("unable to kill %s before running tests!" % procName) mochitest.mozLogName = "moz.log"
--- a/testing/mozbase/mozrunner/mozrunner/utils.py +++ b/testing/mozbase/mozrunner/mozrunner/utils.py @@ -79,33 +79,57 @@ def _find_marionette_in_args(*args, **kw def _raw_log(): import logging return logging.getLogger(__name__) def test_environment(xrePath, env=None, crashreporter=True, debugger=False, - lsanPath=None, ubsanPath=None, log=None): + dmdPath=None, lsanPath=None, ubsanPath=None, log=None): """ populate OS environment variables for mochitest and reftests. Originally comes from automationutils.py. Don't use that for new code. """ env = os.environ.copy() if env is None else env log = log or _raw_log() assert os.path.isabs(xrePath) if mozinfo.isMac: ldLibraryPath = os.path.join(os.path.dirname(xrePath), "MacOS") else: ldLibraryPath = xrePath + envVar = None + dmdLibrary = None + preloadEnvVar = None + if mozinfo.isUnix: + envVar = "LD_LIBRARY_PATH" + dmdLibrary = "libdmd.so" + preloadEnvVar = "LD_PRELOAD" + elif mozinfo.isMac: + envVar = "DYLD_LIBRARY_PATH" + dmdLibrary = "libdmd.dylib" + preloadEnvVar = "DYLD_INSERT_LIBRARIES" + elif mozinfo.isWin: + envVar = "PATH" + dmdLibrary = "dmd.dll" + preloadEnvVar = "MOZ_REPLACE_MALLOC_LIB" + if envVar: + envValue = ((env.get(envVar), str(ldLibraryPath)) + if mozinfo.isWin + else (ldLibraryPath, dmdPath, env.get(envVar))) + env[envVar] = os.path.pathsep.join([path for path in envValue if path]) + + if dmdPath and dmdLibrary and preloadEnvVar: + env[preloadEnvVar] = os.path.join(dmdPath, dmdLibrary) + # crashreporter env['GNOME_DISABLE_CRASH_DIALOG'] = '1' env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1' if crashreporter and not debugger: env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' env['MOZ_CRASHREPORTER'] = '1' else:
--- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -648,18 +648,33 @@ class XPCShellTestThread(Thread): cmdC = ['-e', 'const _JSCOV_DIR = null'] if self.jscovdir: cmdC = ['-e', 'const _JSCOV_DIR = "%s"' % self.jscovdir.replace('\\', '/')] self.complete_command = cmdH + cmdT + cmdI + cmdC + args else: self.complete_command = cmdH + cmdT + cmdI + args if self.test_object.get('dmd') == 'true': + if sys.platform.startswith('linux'): + preloadEnvVar = 'LD_PRELOAD' + libdmd = os.path.join(self.xrePath, 'libdmd.so') + elif sys.platform == 'osx' or sys.platform == 'darwin': + preloadEnvVar = 'DYLD_INSERT_LIBRARIES' + # self.xrePath is <prefix>/Contents/Resources. + # We need <prefix>/Contents/MacOS/libdmd.dylib. + contents_dir = os.path.dirname(self.xrePath) + libdmd = os.path.join(contents_dir, 'MacOS', 'libdmd.dylib') + elif sys.platform == 'win32': + preloadEnvVar = 'MOZ_REPLACE_MALLOC_LIB' + libdmd = os.path.join(self.xrePath, 'dmd.dll') + self.env['PYTHON'] = sys.executable self.env['BREAKPAD_SYMBOLS_PATH'] = self.symbolsPath + self.env['DMD_PRELOAD_VAR'] = preloadEnvVar + self.env['DMD_PRELOAD_VALUE'] = libdmd if self.test_object.get('subprocess') == 'true': self.env['PYTHON'] = sys.executable if self.test_object.get('headless', False): self.env["MOZ_HEADLESS"] = '1' self.env["DISPLAY"] = '77' # Set a fake display.