Bug 1470591 - Part 6: Create a fork server process. r=gsvelto

This patch make changes of Gecko infrastrutures to run a fork server
process.

 - ForkServerLauncher is a component, which creates a fork server
   process at XPCOM startup.

 - nsBrowserApp.cpp and related files have been chagned to start a
   fork server in a process.

 - Logging and nsTraceRefcnt were changed to make it work with the
   fork server.

Depends on D46883

Differential Revision: https://phabricator.services.mozilla.com/D46884

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Thinker Li 2019-12-05 00:02:40 +00:00
parent c50a650261
commit 7cfdf6a788
16 changed files with 276 additions and 11 deletions

View file

@ -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<char> 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;

View file

@ -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;
}

View file

@ -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<std::string>& argv,
const LaunchOptions& options,

View file

@ -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;
}

View file

@ -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> ForkServerLauncher::mSingleton;
ForkServerLauncher::ForkServerLauncher() {
}
ForkServerLauncher::~ForkServerLauncher() {
}
already_AddRefed<ForkServerLauncher>
ForkServerLauncher::Create() {
if (mSingleton == nullptr) {
mSingleton = new ForkServerLauncher();
}
RefPtr<ForkServerLauncher> 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<nsIObserverService> 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;
}
}
}

View file

@ -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<ForkServerLauncher> Create();
private:
virtual ~ForkServerLauncher();
static bool mHaveStartedClient;
static StaticRefPtr<ForkServerLauncher> mSingleton;
};
}
}

22
ipc/glue/components.conf Normal file
View file

@ -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'},
},
]

View file

@ -231,6 +231,9 @@ if CONFIG['MOZ_ENABLE_FORKSERVER']:
'ForkServiceChild.cpp',
'MiniTransceiver.cpp',
]
XPCOM_MANIFESTS += [
'components.conf',
]
LOCAL_INCLUDES += [
'/dom/ipc',

View file

@ -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.

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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<long>(base::GetCurrentProcId());
long pid = static_cast<long>(base::GetCurrentProcId());
const size_t kBuffSize = 1024;
char buff[kBuffSize];

View file

@ -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<void*>(!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

View file

@ -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

View file

@ -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__