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