1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/debug/trace_event.h"
11 #include "base/environment.h"
12 #include "base/file_version_info.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/rand_util.h" // For PreRead experiment.
17 #include "base/sha1.h" // For PreRead experiment.
18 #include "base/strings/string16.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/version.h"
23 #include "base/win/windows_version.h"
24 #include "chrome/app/chrome_crash_reporter_client.h"
25 #include "chrome/app/client_util.h"
26 #include "chrome/app/image_pre_reader_win.h"
27 #include "chrome/common/chrome_constants.h"
28 #include "chrome/common/chrome_result_codes.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/env_vars.h"
31 #include "chrome/installer/util/google_update_constants.h"
32 #include "chrome/installer/util/google_update_settings.h"
33 #include "chrome/installer/util/install_util.h"
34 #include "chrome/installer/util/util_constants.h"
35 #include "components/crash/app/breakpad_win.h"
36 #include "components/crash/app/crash_reporter_client.h"
37 #include "components/metrics/client_info.h"
38 #include "content/public/app/startup_helper_win.h"
39 #include "sandbox/win/src/sandbox.h"
42 // The entry point signature of chrome.dll.
43 typedef int (*DLL_MAIN
)(HINSTANCE
, sandbox::SandboxInterfaceInfo
*);
45 typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc
)();
47 base::LazyInstance
<chrome::ChromeCrashReporterClient
>::Leaky
48 g_chrome_crash_client
= LAZY_INSTANCE_INITIALIZER
;
50 // Returns true if the build date for this module precedes the expiry date
51 // for the pre-read experiment.
52 bool PreReadExperimentIsActive() {
53 const int kPreReadExpiryYear
= 2014;
54 const int kPreReadExpiryMonth
= 7;
55 const int kPreReadExpiryDay
= 1;
56 const char kBuildTimeStr
[] = __DATE__
" " __TIME__
;
58 // Get the timestamp of the build.
59 base::Time build_time
;
60 bool result
= base::Time::FromString(kBuildTimeStr
, &build_time
);
63 // Get the timestamp at which the experiment expires.
64 base::Time::Exploded exploded
= {0};
65 exploded
.year
= kPreReadExpiryYear
;
66 exploded
.month
= kPreReadExpiryMonth
;
67 exploded
.day_of_month
= kPreReadExpiryDay
;
68 base::Time expiration_time
= base::Time::FromLocalExploded(exploded
);
70 // Return true if the build time predates the expiration time..
71 return build_time
< expiration_time
;
74 // Get random unit values, i.e., in the range (0, 1), denoting a die-toss for
75 // being in an experiment population and experimental group thereof.
76 void GetPreReadPopulationAndGroup(double* population
, double* group
) {
77 // By default we use the metrics id for the user as stable pseudo-random
79 scoped_ptr
<metrics::ClientInfo
> client_info
=
80 GoogleUpdateSettings::LoadMetricsClientInfo();
82 // If this user has no metrics id, we fall back to a purely random value per
84 const size_t kLength
= 16;
85 std::string
random_value(client_info
? client_info
->client_id
86 : base::RandBytesAsString(kLength
));
88 // To interpret the value as a random number we hash it and read the first 8
89 // bytes of the hash as a unit-interval representing a die-toss for being in
90 // the experiment population and the second 8 bytes as a die-toss for being
91 // in various experiment groups.
92 unsigned char sha1_hash
[base::kSHA1Length
];
94 reinterpret_cast<const unsigned char*>(random_value
.c_str()),
95 random_value
.size() * sizeof(random_value
[0]),
97 COMPILE_ASSERT(2 * sizeof(uint64
) < sizeof(sha1_hash
), need_more_data
);
98 const uint64
* random_bits
= reinterpret_cast<uint64
*>(&sha1_hash
[0]);
100 // Convert the bits into unit-intervals and return.
101 *population
= base::BitsToOpenEndedUnitInterval(random_bits
[0]);
102 *group
= base::BitsToOpenEndedUnitInterval(random_bits
[1]);
105 // Gets the amount of pre-read to use as well as the experiment group in which
107 size_t InitPreReadPercentage() {
108 // By default use the old behaviour: read 100%.
109 const int kDefaultPercentage
= 100;
110 const char kDefaultFormatStr
[] = "%d-pct-default";
111 const char kControlFormatStr
[] = "%d-pct-control";
112 const char kGroupFormatStr
[] = "%d-pct";
114 COMPILE_ASSERT(kDefaultPercentage
<= 100, default_percentage_too_large
);
115 COMPILE_ASSERT(kDefaultPercentage
% 5 == 0, default_percentage_not_mult_5
);
117 // Roll the dice to determine if this user is in the experiment and if so,
118 // in which experimental group.
119 double population
= 0.0;
121 GetPreReadPopulationAndGroup(&population
, &group
);
123 // We limit experiment populations to 1% of the Stable and 10% of each of
124 // the other channels.
125 const base::string16
channel(GoogleUpdateSettings::GetChromeChannel(
126 GoogleUpdateSettings::IsSystemInstall()));
127 double threshold
= (channel
== installer::kChromeChannelStable
) ? 0.01 : 0.10;
129 // If the experiment has expired use the default pre-read level. Otherwise,
130 // those not in the experiment population also use the default pre-read level.
131 size_t value
= kDefaultPercentage
;
132 const char* format_str
= kDefaultFormatStr
;
133 if (PreReadExperimentIsActive() && (population
<= threshold
)) {
134 // We divide the experiment population into groups pre-reading at 5 percent
135 // increments in the range [0, 100].
136 value
= static_cast<size_t>(group
* 21.0) * 5;
137 DCHECK_LE(value
, 100u);
138 DCHECK_EQ(0u, value
% 5);
140 (value
== kDefaultPercentage
) ? kControlFormatStr
: kGroupFormatStr
;
143 // Generate the group name corresponding to this percentage value.
144 std::string group_name
;
145 base::SStringPrintf(&group_name
, format_str
, value
);
147 // Persist the group name to the environment so that it can be used for
149 scoped_ptr
<base::Environment
> env(base::Environment::Create());
150 env
->SetVar(chrome::kPreReadEnvironmentVariable
, group_name
);
152 // Return the percentage value to be used.
156 // Expects that |dir| has a trailing backslash. |dir| is modified so it
157 // contains the full path that was tried. Caller must check for the return
158 // value not being null to determine if this path contains a valid dll.
159 HMODULE
LoadModuleWithDirectory(base::string16
* dir
,
160 const wchar_t* dll_name
,
162 ::SetCurrentDirectoryW(dir
->c_str());
163 dir
->append(dll_name
);
166 #if !defined(WIN_DISABLE_PREREAD)
167 // We pre-read the binary to warm the memory caches (fewer hard faults to
168 // page parts of the binary in).
169 const size_t kStepSize
= 1024 * 1024;
170 size_t percentage
= InitPreReadPercentage();
171 ImagePreReader::PartialPreReadImage(dir
->c_str(), percentage
, kStepSize
);
175 return ::LoadLibraryExW(dir
->c_str(), NULL
,
176 LOAD_WITH_ALTERED_SEARCH_PATH
);
179 void RecordDidRun(const base::string16
& dll_path
) {
180 bool system_level
= !InstallUtil::IsPerUserInstall(dll_path
.c_str());
181 GoogleUpdateSettings::UpdateDidRunState(true, system_level
);
184 void ClearDidRun(const base::string16
& dll_path
) {
185 bool system_level
= !InstallUtil::IsPerUserInstall(dll_path
.c_str());
186 GoogleUpdateSettings::UpdateDidRunState(false, system_level
);
191 ::GetCommandLineW(), L
" -ServerName:DefaultBrowserServer") != NULL
);
194 typedef int (*InitMetro
)();
198 base::string16
GetExecutablePath() {
199 wchar_t path
[MAX_PATH
];
200 ::GetModuleFileNameW(NULL
, path
, MAX_PATH
);
201 if (!::PathRemoveFileSpecW(path
))
202 return base::string16();
203 base::string16
exe_path(path
);
204 return exe_path
.append(1, L
'\\');
207 base::string16
GetCurrentModuleVersion() {
208 scoped_ptr
<FileVersionInfo
> file_version_info(
209 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
210 if (file_version_info
.get()) {
211 base::string16
version_string(file_version_info
->file_version());
212 if (Version(base::UTF16ToASCII(version_string
)).IsValid())
213 return version_string
;
215 return base::string16();
218 //=============================================================================
220 MainDllLoader::MainDllLoader()
221 : dll_(NULL
), metro_mode_(InMetroMode()) {
224 MainDllLoader::~MainDllLoader() {
227 // Loading chrome is an interesting affair. First we try loading from the
228 // current directory to support run-what-you-compile and other development
230 // If that fails then we look at the version resource in the current
231 // module. This is the expected path for chrome.exe browser instances in an
233 HMODULE
MainDllLoader::Load(base::string16
* version
,
234 base::string16
* out_file
) {
235 const base::string16
executable_dir(GetExecutablePath());
236 *out_file
= executable_dir
;
238 const wchar_t* dll_name
= metro_mode_
?
239 installer::kChromeMetroDll
:
240 #if !defined(CHROME_MULTIPLE_DLL)
241 installer::kChromeDll
;
243 (process_type_
== "service") || process_type_
.empty() ?
244 installer::kChromeDll
:
245 installer::kChromeChildDll
;
247 const bool pre_read
= !metro_mode_
;
248 HMODULE dll
= LoadModuleWithDirectory(out_file
, dll_name
, pre_read
);
250 base::string16
version_string(GetCurrentModuleVersion());
251 if (version_string
.empty()) {
252 LOG(ERROR
) << "No valid Chrome version found";
255 *out_file
= executable_dir
;
256 *version
= version_string
;
257 out_file
->append(version_string
).append(1, L
'\\');
258 dll
= LoadModuleWithDirectory(out_file
, dll_name
, pre_read
);
260 PLOG(ERROR
) << "Failed to load Chrome DLL from " << *out_file
;
269 // Launching is a matter of loading the right dll, setting the CHROME_VERSION
270 // environment variable and just calling the entry point. Derived classes can
271 // add custom code in the OnBeforeLaunch callback.
272 int MainDllLoader::Launch(HINSTANCE instance
) {
273 const CommandLine
& cmd_line
= *CommandLine::ForCurrentProcess();
274 process_type_
= cmd_line
.GetSwitchValueASCII(switches::kProcessType
);
276 base::string16 version
;
280 HMODULE metro_dll
= Load(&version
, &file
);
282 return chrome::RESULT_CODE_MISSING_DATA
;
284 InitMetro chrome_metro_main
=
285 reinterpret_cast<InitMetro
>(::GetProcAddress(metro_dll
, "InitMetro"));
286 return chrome_metro_main();
289 // Initialize the sandbox services.
290 sandbox::SandboxInterfaceInfo sandbox_info
= {0};
291 content::InitializeSandboxInfo(&sandbox_info
);
293 crash_reporter::SetCrashReporterClient(g_chrome_crash_client
.Pointer());
294 bool exit_now
= true;
295 if (process_type_
.empty()) {
296 if (breakpad::ShowRestartDialogIfCrashed(&exit_now
)) {
297 // We restarted because of a previous crash. Ask user if we should
298 // Relaunch. Only for the browser process. See crbug.com/132119.
300 return content::RESULT_CODE_NORMAL_EXIT
;
303 breakpad::InitCrashReporter(process_type_
);
305 dll_
= Load(&version
, &file
);
307 return chrome::RESULT_CODE_MISSING_DATA
;
309 scoped_ptr
<base::Environment
> env(base::Environment::Create());
310 env
->SetVar(chrome::kChromeVersionEnvVar
, base::WideToUTF8(version
));
312 OnBeforeLaunch(file
);
313 DLL_MAIN chrome_main
=
314 reinterpret_cast<DLL_MAIN
>(::GetProcAddress(dll_
, "ChromeMain"));
315 int rc
= chrome_main(instance
, &sandbox_info
);
316 return OnBeforeExit(rc
, file
);
319 void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
323 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function
=
324 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc
>(
325 ::GetProcAddress(dll_
,
326 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
327 if (!relaunch_function
) {
328 LOG(ERROR
) << "Could not find exported function "
329 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
335 //=============================================================================
337 class ChromeDllLoader
: public MainDllLoader
{
339 virtual void OnBeforeLaunch(const base::string16
& dll_path
) {
340 RecordDidRun(dll_path
);
343 virtual int OnBeforeExit(int return_code
, const base::string16
& dll_path
) {
344 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
345 // so we need to reset the did_run signal so omaha does not count
346 // this run as active usage.
347 if (chrome::RESULT_CODE_NORMAL_EXIT_CANCEL
== return_code
) {
348 ClearDidRun(dll_path
);
354 //=============================================================================
356 class ChromiumDllLoader
: public MainDllLoader
{
358 virtual void OnBeforeLaunch(const base::string16
& dll_path
) OVERRIDE
{
360 virtual int OnBeforeExit(int return_code
,
361 const base::string16
& dll_path
) OVERRIDE
{
366 MainDllLoader
* MakeMainDllLoader() {
367 #if defined(GOOGLE_CHROME_BUILD)
368 return new ChromeDllLoader();
370 return new ChromiumDllLoader();