diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp index e663aecf0f70..8d96a3ee6a42 100644 --- a/browser/app/nsBrowserApp.cpp +++ b/browser/app/nsBrowserApp.cpp @@ -219,6 +219,10 @@ static int do_main(int argc, char* argv[], char* envp[]) { } static nsresult InitXPCOMGlue(LibLoadingStrategy aLibLoadingStrategy) { + if (gBootstrap) { + return NS_OK; + } + UniqueFreePtr exePath = BinaryPath::Get(); if (!exePath) { Output("Couldn't find the application directory.\n"); @@ -243,6 +247,33 @@ uint32_t gBlocklistInitFlags = eDllBlocklistInitFlagDefault; #endif int main(int argc, char* argv[], char* envp[]) { +#if defined(MOZ_ENABLE_FORKSERVER) + if (strcmp(argv[argc - 1], "forkserver") == 0) { + nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead); + if (NS_FAILED(rv)) { + return 255; + } + + // Run a fork server in this process, single thread. When it + // returns, it means the fork server have been stopped or a new + // content process is created. + // + // For the later case, XRE_ForkServer() will return false, running + // in a content process just forked from the fork server process. + // argc & argv will be updated with the values passing from the + // chrome process. With the new values, this function + // continues the reset of the code acting as a content process. + if(gBootstrap->XRE_ForkServer(&argc, &argv)) { + // Return from the fork server in the fork server process. + // Stop the fork server. + gBootstrap->NS_LogTerm(); + return 0; + } + // In a content process forked from the fork server. + // Start acting as a content process. + } +#endif + mozilla::TimeStamp start = mozilla::TimeStamp::Now(); AUTO_BASE_PROFILER_INIT; diff --git a/ipc/app/MozillaRuntimeMain.cpp b/ipc/app/MozillaRuntimeMain.cpp index b0a31cc23b0c..df61f8c47d13 100644 --- a/ipc/app/MozillaRuntimeMain.cpp +++ b/ipc/app/MozillaRuntimeMain.cpp @@ -13,18 +13,67 @@ using namespace mozilla; -int main(int argc, char* argv[]) { -#ifdef HAS_DLL_BLOCKLIST - DllBlocklist_Initialize(eDllBlocklistInitFlagIsChildProcess); +static bool +UseForkServer(int argc, char* argv[]) { +#if defined(MOZ_ENABLE_FORKSERVER) + return strcmp(argv[argc - 1], "forkserver") == 0; +#else + return false; #endif +} +static int +RunForkServer(Bootstrap::UniquePtr&& bootstrap, int argc, char* argv[]) { +#if defined(MOZ_ENABLE_FORKSERVER) + int ret = 0; + + bootstrap->NS_LogInit(); + + // Run a fork server in this process, single thread. When it + // returns, it means the fork server have been stopped or a new + // content process is created. + // + // For the later case, XRE_ForkServer() will return false, running + // in a content process just forked from the fork server process. + // argc & argv will be updated with the values passing from the + // chrome process. With the new values, this function + // continues the reset of the code acting as a content process. + if(bootstrap->XRE_ForkServer(&argc, &argv)) { + // Return from the fork server in the fork server process. + // Stop the fork server. + } else { + // In a content process forked from the fork server. + // Start acting as a content process. + ret = content_process_main(bootstrap.get(), argc, argv); + } + + bootstrap->NS_LogTerm(); + return ret; +#else + return 0; +#endif +} + +int main(int argc, char* argv[]) { Bootstrap::UniquePtr bootstrap = GetBootstrap(); if (!bootstrap) { return 2; } - int ret = content_process_main(bootstrap.get(), argc, argv); -#if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST) - DllBlocklist_Shutdown(); + + int ret; + if (UseForkServer(argc, argv)) { + ret = RunForkServer(std::move(bootstrap), argc, argv); + } else { +#ifdef HAS_DLL_BLOCKLIST + DllBlocklist_Initialize(eDllBlocklistInitFlagIsChildProcess); #endif + + ret = content_process_main(bootstrap.get(), argc, argv); + +#if defined(DEBUG) && defined(HAS_DLL_BLOCKLIST) + DllBlocklist_Shutdown(); +#endif + } + return ret; } diff --git a/ipc/chromium/src/base/process_util_linux.cc b/ipc/chromium/src/base/process_util_linux.cc index ea2b1edc34ac..0132072025cb 100644 --- a/ipc/chromium/src/base/process_util_linux.cc +++ b/ipc/chromium/src/base/process_util_linux.cc @@ -162,14 +162,32 @@ handle_sigchld(int s) { waitpid(-1, nullptr, WNOHANG); } -void -InitForkServerProcess() { +static void +InstallChildSignalHandler() { // Since content processes are not children of the chrome process // any more, the fork server process has to handle SIGCHLD, or // content process would remain zombie after dead. signal(SIGCHLD, handle_sigchld); } +static void +ReserveFileDescriptors() { + // Reserve the lower positions of the file descriptors to make sure + // debug files and other files don't take these positions. So we + // can keep their file descriptors during CloseSuperfluousFds() with + // out any confliction with mapping passing from the parent process. + int fd = open("/dev/null", O_RDONLY); + for (int i = 1; i < 10; i++) { + dup(fd); + } +} + +void +InitForkServerProcess() { + InstallChildSignalHandler(); + ReserveFileDescriptors(); +} + static bool LaunchAppWithForkServer(const std::vector& argv, const LaunchOptions& options, diff --git a/ipc/glue/ForkServer.cpp b/ipc/glue/ForkServer.cpp index caf85f0bb0f8..18b2f770e482 100644 --- a/ipc/glue/ForkServer.cpp +++ b/ipc/glue/ForkServer.cpp @@ -300,6 +300,9 @@ ForkServer::RunForkServer(int* aArgc, char*** aArgv) { forkserver.mAppProcBuilder->InitAppProcess(aArgc, aArgv); forkserver.mAppProcBuilder.reset(); + // Open log files again with the right names with the new PID. + nsTraceRefcnt::ResetLogFiles(); + return false; } diff --git a/ipc/glue/ForkServiceChild.cpp b/ipc/glue/ForkServiceChild.cpp index d148cc1127e2..071b77f4051d 100644 --- a/ipc/glue/ForkServiceChild.cpp +++ b/ipc/glue/ForkServiceChild.cpp @@ -112,5 +112,45 @@ ForkServiceChild::OnMessageReceived(IPC::Message&& message) { message.EndRead(iter__, message.type()); } +NS_IMPL_ISUPPORTS(ForkServerLauncher, nsIObserver) + +bool ForkServerLauncher::mHaveStartedClient = false; +StaticRefPtr ForkServerLauncher::mSingleton; + +ForkServerLauncher::ForkServerLauncher() { +} + +ForkServerLauncher::~ForkServerLauncher() { +} + +already_AddRefed +ForkServerLauncher::Create() { + if (mSingleton == nullptr) { + mSingleton = new ForkServerLauncher(); + } + RefPtr launcher = mSingleton; + return launcher.forget(); +} + +NS_IMETHODIMP +ForkServerLauncher::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) { + if (!mHaveStartedClient && strcmp(aTopic, NS_XPCOM_STARTUP_CATEGORY) == 0) { + mHaveStartedClient = true; + ForkServiceChild::StartForkServer(); + + nsCOMPtr obsSvc = mozilla::services::GetObserverService(); + MOZ_ASSERT(obsSvc != nullptr); + obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + } + + if (mHaveStartedClient && strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { + mHaveStartedClient = false; + ForkServiceChild::StopForkServer(); + } + return NS_OK; +} + } } diff --git a/ipc/glue/ForkServiceChild.h b/ipc/glue/ForkServiceChild.h index 7a8ed5a429c5..362e08803c1b 100644 --- a/ipc/glue/ForkServiceChild.h +++ b/ipc/glue/ForkServiceChild.h @@ -73,6 +73,24 @@ private: GeckoChildProcessHost* mProcess; }; +/** + * Start a fork server at |xpcom-startup| from the chrome process. + */ +class ForkServerLauncher : public nsIObserver { +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + ForkServerLauncher(); + static already_AddRefed Create(); + +private: + virtual ~ForkServerLauncher(); + + static bool mHaveStartedClient; + static StaticRefPtr mSingleton; +}; + } } diff --git a/ipc/glue/components.conf b/ipc/glue/components.conf new file mode 100644 index 000000000000..34048dbbfa20 --- /dev/null +++ b/ipc/glue/components.conf @@ -0,0 +1,22 @@ +# -*- 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/. + +Headers = [ + 'mozilla/ipc/ForkServer.h', +] + +Classes = [ + { + 'cid': '{cdb4757f-f51b-40c0-8b38-66d12c3bff7b}', + 'contract_ids': ['@mozilla.org/fork-server-launcher;1'], + 'singleton': True, + 'type': 'mozilla::ipc::ForkServerLauncher', + 'headers': ['mozilla/ipc/ForkServiceChild.h'], + 'constructor': 'mozilla::ipc::ForkServerLauncher::Create', + 'processes': ProcessSelector.MAIN_PROCESS_ONLY, + 'categories': {'xpcom-startup': 'Fork Server Launcher'}, + }, +] diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index 2b48dd8ff6e9..68a08a597e1b 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -231,6 +231,9 @@ if CONFIG['MOZ_ENABLE_FORKSERVER']: 'ForkServiceChild.cpp', 'MiniTransceiver.cpp', ] + XPCOM_MANIFESTS += [ + 'components.conf', + ] LOCAL_INCLUDES += [ '/dom/ipc', diff --git a/moz.configure b/moz.configure index 5e5c8ddcb1ba..58d2e03793f1 100755 --- a/moz.configure +++ b/moz.configure @@ -634,6 +634,19 @@ llvm_objdump = check_prog('LLVM_OBJDUMP', llvm_objdump, what='llvm-objdump', add_old_configure_assignment('LLVM_OBJDUMP', llvm_objdump) +# Fork server +option('--enable-forkserver', env='MOZ_ENABLE_FORKSERVER', help='Enable fork server') + +@depends('--enable-forkserver', target) +def forkserver_flag(value, target): + if target.os == 'Android' or \ + (target.os == 'GNU' and target.kernel == 'Linux'): + return bool(value) + pass + +set_config('MOZ_ENABLE_FORKSERVER', forkserver_flag) +set_define('MOZ_ENABLE_FORKSERVER', forkserver_flag, forkserver_flag) + # Please do not add configure checks from here on. diff --git a/toolkit/xre/Bootstrap.cpp b/toolkit/xre/Bootstrap.cpp index 9d7cb8f41977..cc6d30c8d06f 100644 --- a/toolkit/xre/Bootstrap.cpp +++ b/toolkit/xre/Bootstrap.cpp @@ -102,6 +102,12 @@ class BootstrapImpl final : public Bootstrap { return ::XRE_RunIPDLTest(argc, argv); } #endif + +#ifdef MOZ_ENABLE_FORKSERVER + virtual int XRE_ForkServer(int* argc, char*** argv) override { + return ::XRE_ForkServer(argc, argv); + } +#endif }; extern "C" NS_EXPORT void NS_FROZENCALL diff --git a/toolkit/xre/Bootstrap.h b/toolkit/xre/Bootstrap.h index 7ef9185a9fe9..af6840e646e0 100644 --- a/toolkit/xre/Bootstrap.h +++ b/toolkit/xre/Bootstrap.h @@ -126,6 +126,10 @@ class Bootstrap { #ifdef MOZ_IPDL_TESTS virtual int XRE_RunIPDLTest(int argc, char** argv) = 0; #endif + +#ifdef MOZ_ENABLE_FORKSERVER + virtual int XRE_ForkServer(int* argc, char*** argv) = 0; +#endif }; enum class LibLoadingStrategy { diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp index 942f98a53b78..e74ea0de0c8f 100644 --- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -133,6 +133,10 @@ using mozilla::_ipdltest::IPDLUnitTestProcessChild; # include "mozilla/sandboxing/sandboxLogging.h" #endif +#if defined(MOZ_ENABLE_FORKSERVER) +# include "mozilla/ipc/ForkServer.h" +#endif + #include "VRProcessChild.h" using namespace mozilla; @@ -264,7 +268,8 @@ void XRE_SetAndroidChildFds(JNIEnv* env, const XRE_AndroidChildFds& fds) { void XRE_SetProcessType(const char* aProcessTypeString) { static bool called = false; - if (called) { + if (called && + sChildProcessType != GeckoProcessType_ForkServer) { MOZ_CRASH(); } called = true; @@ -730,6 +735,12 @@ nsresult XRE_InitChildProcess(int aArgc, char* aArgv[], process = new RemoteSandboxBrokerProcessChild(parentPID); break; #endif + +#if defined(MOZ_ENABLE_FORKSERVER) + case GeckoProcessType_ForkServer: + MOZ_CRASH("Fork server should not go here"); + break; +#endif default: MOZ_CRASH("Unknown main thread class"); } @@ -1025,3 +1036,9 @@ void XRE_InstallX11ErrorHandler() { # endif } #endif + +#ifdef MOZ_ENABLE_FORKSERVER +int XRE_ForkServer(int* aArgc, char*** aArgv) { + return mozilla::ipc::ForkServer::RunForkServer(aArgc, aArgv) ? 1 : 0; +} +#endif diff --git a/xpcom/base/Logging.cpp b/xpcom/base/Logging.cpp index 5e822d3aefb1..358b21ba29c8 100644 --- a/xpcom/base/Logging.cpp +++ b/xpcom/base/Logging.cpp @@ -377,8 +377,7 @@ class LogModuleManager { void Print(const char* aName, LogLevel aLevel, const char* aFmt, va_list aArgs) MOZ_FORMAT_PRINTF(4, 0) { - // We don't do nuwa-style forking anymore, so our pid can't change. - static long pid = static_cast(base::GetCurrentProcId()); + long pid = static_cast(base::GetCurrentProcId()); const size_t kBuffSize = 1024; char buff[kBuffSize]; diff --git a/xpcom/base/nsTraceRefcnt.cpp b/xpcom/base/nsTraceRefcnt.cpp index f9357e89c749..aef1b2d2b11d 100644 --- a/xpcom/base/nsTraceRefcnt.cpp +++ b/xpcom/base/nsTraceRefcnt.cpp @@ -523,6 +523,9 @@ static bool InitLog(const EnvCharType* aEnvVar, const char* aMsg, #endif if (stream) { MozillaRegisterDebugFD(fileno(stream)); +#ifdef MOZ_ENABLE_FORKSERVER + base::RegisterForkServerNoCloseFD(fileno(stream)); +#endif *aResult = stream; fprintf(stderr, "### " ENVVAR_PRINTF " defined -- logging %s to " ENVVAR_PRINTF @@ -1157,3 +1160,32 @@ void nsTraceRefcnt::SetActivityIsLegal(bool aLegal) { PR_SetThreadPrivate(gActivityTLS, reinterpret_cast(!aLegal)); } + +#ifdef MOZ_ENABLE_FORKSERVER +void nsTraceRefcnt::ResetLogFiles() { +#ifdef XP_WIN +# define ENVVAR(x) u"" x +#else +# define ENVVAR(x) x +#endif + if (gBloatLog) { + maybeUnregisterAndCloseFile(gBloatLog); + bool defined = InitLog(ENVVAR("XPCOM_MEM_BLOAT_LOG"), "bloat/leaks", &gBloatLog); + if (!defined) { + InitLog(ENVVAR("XPCOM_MEM_LEAK_LOG"), "leaks", &gBloatLog); + } + } + if (gRefcntsLog) { + maybeUnregisterAndCloseFile(gRefcntsLog); + InitLog(ENVVAR("XPCOM_MEM_REFCNT_LOG"), "refcounts", &gRefcntsLog); + } + if (gAllocLog) { + maybeUnregisterAndCloseFile(gAllocLog); + InitLog(ENVVAR("XPCOM_MEM_ALLOC_LOG"), "new/delete", &gAllocLog); + } + if(gCOMPtrLog) { + maybeUnregisterAndCloseFile(gCOMPtrLog); + InitLog(ENVVAR("XPCOM_MEM_COMPTR_LOG"), "nsCOMPtr", &gCOMPtrLog); + } +} +#endif diff --git a/xpcom/base/nsTraceRefcnt.h b/xpcom/base/nsTraceRefcnt.h index 1516e9ab1a2e..35d142c4b643 100644 --- a/xpcom/base/nsTraceRefcnt.h +++ b/xpcom/base/nsTraceRefcnt.h @@ -28,6 +28,10 @@ class nsTraceRefcnt { * activity that occurs because of static constructors or destructors. */ static void SetActivityIsLegal(bool aLegal); + +#ifdef MOZ_ENABLE_FORKSERVER + static void ResetLogFiles(); +#endif }; #endif // nsTraceRefcnt_h diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h index 6712c36a6fe0..a82f0ac62be4 100644 --- a/xpcom/build/nsXULAppAPI.h +++ b/xpcom/build/nsXULAppAPI.h @@ -509,4 +509,10 @@ XRE_API(void, XRE_LibFuzzerSetDriver, (LibFuzzerDriver)) #endif // LIBFUZZER +#ifdef MOZ_ENABLE_FORKSERVER + +XRE_API(int, XRE_ForkServer, (int* aArgc, char*** aArgv)) + +#endif // MOZ_ENABLE_FORKSERVER + #endif // _nsXULAppAPI_h__