// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // TODO(port): the ifdefs in here are a first step towards trying to determine // the correct abstraction for all the OS functionality required at this // stage of process initialization. It should not be taken as a final // abstraction. #include "build/build_config.h" #if defined(OS_WIN) #include #include #include #include #elif defined(OS_MACOSX) extern "C" { #include } #endif #include "base/at_exit.h" #include "base/command_line.h" #include "base/icu_util.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/stats_table.h" #include "base/string_util.h" #if defined(OS_WIN) #include "base/win_util.h" #include "chrome/browser/render_process_host.h" #endif #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_counters.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/logging_chrome.h" #if defined(OS_WIN) #include "chrome/common/resource_bundle.h" #endif #include "sandbox/src/sandbox.h" #if defined(OS_WIN) #include "tools/memory_watcher/memory_watcher.h" #endif extern int BrowserMain(CommandLine&, sandbox::BrokerServices*); extern int RendererMain(CommandLine&, sandbox::TargetServices*); extern int PluginMain(CommandLine&, sandbox::TargetServices*); #if defined(OS_WIN) // TODO(erikkay): isn't this already defined somewhere? #define DLLEXPORT __declspec(dllexport) // We use extern C for the prototype DLLEXPORT to avoid C++ name mangling. extern "C" { DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance, sandbox::SandboxInterfaceInfo* sandbox_info, TCHAR* command_line); } #elif defined(OS_POSIX) extern "C" { int ChromeMain(int argc, const char** argv); } #endif namespace { #if defined(OS_WIN) const wchar_t kProfilingDll[] = L"memory_watcher.dll"; // Load the memory profiling DLL. All it needs to be activated // is to be loaded. Return true on success, false otherwise. bool LoadMemoryProfiler() { HMODULE prof_module = LoadLibrary(kProfilingDll); return prof_module != NULL; } CAppModule _Module; #pragma optimize("", off) // Handlers for invalid parameter and pure call. They generate a breakpoint to // tell breakpad that it needs to dump the process. void InvalidParameter(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t reserved) { __debugbreak(); } void PureCall() { __debugbreak(); } int OnNoMemory(size_t memory_size) { __debugbreak(); // Return memory_size so it is not optimized out. Make sure the return value // is at least 1 so malloc/new is retried, especially useful when under a // debugger. return memory_size ? static_cast(memory_size) : 1; } // Handlers to silently dump the current process when there is an assert in // chrome. void ChromeAssert(const std::string& str) { // Get the breakpad pointer from chrome.exe typedef void (__stdcall *DumpProcessFunction)(); DumpProcessFunction DumpProcess = reinterpret_cast( ::GetProcAddress(::GetModuleHandle(L"chrome.exe"), "DumpProcess")); if (DumpProcess) DumpProcess(); } #pragma optimize("", on) #endif // OS_WIN // Called before/after the call to BrowseMain() to handle platform-specific // setup/teardown. void PreBrowserMain() { #if defined(OS_WIN) int ole_result = OleInitialize(NULL); DCHECK(ole_result == S_OK); #endif } void PostBrowserMain() { #if defined(OS_WIN) OleUninitialize(); #endif } // Register the invalid param handler and pure call handler to be able to // notify breakpad when it happens. void RegisterInvalidParamHandler() { #if defined(OS_WIN) _set_invalid_parameter_handler(InvalidParameter); _set_purecall_handler(PureCall); // Gather allocation failure. _set_new_handler(&OnNoMemory); // Make sure malloc() calls the new handler too. _set_new_mode(1); #endif } void SetupCRT(const CommandLine& parsed_command_line) { #if defined(OS_WIN) #ifdef _CRTDBG_MAP_ALLOC _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); #else if (!parsed_command_line.HasSwitch(switches::kDisableBreakpad)) { _CrtSetReportMode(_CRT_ASSERT, 0); } #endif // Enable the low fragmentation heap for the CRT heap. The heap is not changed // if the process is run under the debugger is enabled or if certain gflags // are set. bool use_lfh = false; if (parsed_command_line.HasSwitch(switches::kUseLowFragHeapCrt)) use_lfh = parsed_command_line.GetSwitchValue(switches::kUseLowFragHeapCrt) != L"false"; if (use_lfh) { void* crt_heap = reinterpret_cast(_get_heap_handle()); ULONG enable_lfh = 2; HeapSetInformation(crt_heap, HeapCompatibilityInformation, &enable_lfh, sizeof(enable_lfh)); } #endif } // Enable the heap profiler if the appropriate command-line switch is // present, bailing out of the app we can't. void EnableHeapProfiler(const CommandLine& parsed_command_line) { #if defined(OS_WIN) if (parsed_command_line.HasSwitch(switches::kMemoryProfiling)) if (!LoadMemoryProfiler()) exit(-1); #endif } // Checks if the sandbox is enabled in this process and initializes it if this // is the case. Sandboxing is only valid on render and plugin processes. It's // meaningless for the browser process. void InitializeSandbox(const CommandLine& parsed_command_line, const std::wstring process_type, sandbox::BrokerServices* broker_services, sandbox::TargetServices* target_services) { if (target_services && !parsed_command_line.HasSwitch(switches::kNoSandbox)) { if ((process_type == switches::kRendererProcess) || (process_type == switches::kPluginProcess && parsed_command_line.HasSwitch(switches::kSafePlugins))) { #if defined(OS_WIN) target_services->Init(); #elif defined(OS_MACOSX) // TODO(pinkerton): note, this leaks |error_buff|. What do we want to // do with the error? Pass it back to main? char* error_buff; int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED, &error_buff); if (error) exit(-1); #endif } } } } // namespace #if defined(OS_WIN) DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance, sandbox::SandboxInterfaceInfo* sandbox_info, TCHAR* command_line) { #elif defined(OS_POSIX) int ChromeMain(int argc, const char** argv) { #endif RegisterInvalidParamHandler(); // The exit manager is in charge of calling the dtors of singleton objects. base::AtExitManager exit_manager; // Initialize the command line. #if defined(OS_POSIX) CommandLine::SetArgcArgv(argc, argv); #endif CommandLine parsed_command_line; SetupCRT(parsed_command_line); // Initialize the Chrome path provider. chrome::RegisterPathProvider(); // Initialize the Stats Counters table. With this initialized, // the StatsViewer can be utilized to read counters outside of // Chrome. These lines can be commented out to effectively turn // counters 'off'. The table is created and exists for the life // of the process. It is not cleaned up. // TODO(port): we probably need to shut this down correctly to avoid // leaking shared memory regions on posix platforms. std::string statsfile = chrome::kStatsFilename; std::string pid_string = StringPrintf("-%d", base::GetCurrentProcId()); statsfile += pid_string; StatsTable *stats_table = new StatsTable(statsfile, chrome::kStatsMaxThreads, chrome::kStatsMaxCounters); StatsTable::set_current(stats_table); StatsScope startup_timer(chrome::Counters::chrome_main()); // Enable the heap profiler as early as possible! EnableHeapProfiler(parsed_command_line); // Enable Message Loop related state asap. if (parsed_command_line.HasSwitch(switches::kMessageLoopHistogrammer)) MessageLoop::EnableHistogrammer(true); std::wstring process_type = parsed_command_line.GetSwitchValue(switches::kProcessType); sandbox::BrokerServices* broker_services = NULL; sandbox::TargetServices* target_services = NULL; #if defined(OS_WIN) if (sandbox_info) { target_services = sandbox_info->target_services; broker_services = sandbox_info->broker_services; } #endif // Checks if the sandbox is enabled in this process and initializes it if this // is the case. The crash handler depends on this so it has to be done before // its initialization. InitializeSandbox(parsed_command_line, process_type, broker_services, target_services); #if defined(OS_WIN) _Module.Init(NULL, instance); #endif // Notice a user data directory override if any const std::wstring user_data_dir = parsed_command_line.GetSwitchValue(switches::kUserDataDir); if (!user_data_dir.empty()) PathService::Override(chrome::DIR_USER_DATA, user_data_dir); #if defined(OS_WIN) // TODO(port): pull in when render_process_host.h compiles on posix. There's // nothing win-specific about these lines. bool single_process = parsed_command_line.HasSwitch(switches::kSingleProcess); if (single_process) RenderProcessHost::set_run_renderer_in_process(true); #endif bool icu_result = icu_util::Initialize(); CHECK(icu_result); logging::OldFileDeletionState file_state = logging::APPEND_TO_OLD_LOG_FILE; if (process_type.empty()) { file_state = logging::DELETE_OLD_LOG_FILE; } logging::InitChromeLogging(parsed_command_line, file_state); #ifdef NDEBUG if (parsed_command_line.HasSwitch(switches::kSilentDumpOnDCHECK) && parsed_command_line.HasSwitch(switches::kEnableDCHECK)) { #if defined(OS_WIN) logging::SetLogAssertHandler(ChromeAssert); #endif } #endif // NDEBUG if (!process_type.empty()) { #if defined(OS_WIN) // Initialize ResourceBundle which handles files loaded from external // sources. The language should have been passed in to us from the // browser process as a command line flag. // TODO(port): enable when we figure out resource bundle issues ResourceBundle::InitSharedInstance(std::wstring()); #endif } startup_timer.Stop(); // End of Startup Time Measurement. // TODO(port): turn on these main() functions as they've been de-winified. int rv = -1; if (process_type == switches::kRendererProcess) { #if defined(OS_WIN) rv = RendererMain(parsed_command_line, target_services); #endif } else if (process_type == switches::kPluginProcess) { #if defined(OS_WIN) rv = PluginMain(parsed_command_line, target_services); #endif } else if (process_type.empty()) { PreBrowserMain(); rv = BrowserMain(parsed_command_line, broker_services); PostBrowserMain(); } else { NOTREACHED() << "Unknown process type"; } // TODO(pinkerton): nothing after this point will be hit on Mac if this is the // browser process, as NSApplicationMain doesn't return. There are a couple of // possible solutions, including breaking this up into pre/post code (won't // work for stack-based objects) or making sure we fall out of the runloop // ourselves, then manually call |-NSApp terminate:|. We'll most likely // need to do the latter. if (!process_type.empty()) { #if defined(OS_WIN) // TODO(port): enable when we figure out resource bundle issues ResourceBundle::CleanupSharedInstance(); #endif } #if defined(OS_WIN) #ifdef _CRTDBG_MAP_ALLOC _CrtDumpMemoryLeaks(); #endif // _CRTDBG_MAP_ALLOC _Module.Term(); #endif logging::CleanupChromeLogging(); return rv; }