Commit 28f576f3 authored by chrisha@chromium.org's avatar chrisha@chromium.org

Make a channel and time limited persistent PreRead experiment

Our previous PreRead experiment involved flipping a coin on every launch, but we wish to persist the coin-toss between launches. Additionally, this version of the field trial includes an expiry date and limits it to the canary and dev channels.

We are unable to use the FieldTrial mechanism due to PreRead occuring prior to chrome.dll being loaded. As such, we use the registry for persisting the coin-toss and environment variables for communicating between chrome.exe and chrome.dll.

Initially Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=98319

Reverted: http://src.chromium.org/viewvc/chrome?view=rev&revision=98388

Review URL: http://codereview.chromium.org/7712014

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98482 0039d316-1c4b-4281-b951-d872f2087c98
parent 5e8c3b35
......@@ -11,6 +11,7 @@
#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/rand_util.h" // For PreRead experiment.
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/version.h"
......@@ -23,6 +24,7 @@
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/channel_info.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/google_chrome_sxs_distribution.h" // PreRead.
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chrome/installer/util/util_constants.h"
......@@ -83,10 +85,90 @@ bool EnvQueryStr(const wchar_t* key_name, std::wstring* value) {
return true;
}
#if defined(OS_WIN)
// These constants are used by the PreRead experiment.
const wchar_t kPreReadRegistryValue[] = L"PreReadExperimentGroup";
const int kPreReadExpiryYear = 2012;
const int kPreReadExpiryMonth = 1;
const int kPreReadExpiryDay = 1;
bool PreReadShouldRun() {
base::Time::Exploded exploded = { 0 };
exploded.year = kPreReadExpiryYear;
exploded.month = kPreReadExpiryMonth;
exploded.day_of_month = kPreReadExpiryDay;
base::Time expiration_time = base::Time::FromLocalExploded(exploded);
// Get the build time. This code is copied from
// base::FieldTrial::GetBuildTime. We can't use MetricsLogBase::GetBuildTime
// because that's in seconds since Unix epoch, which base::Time can't use.
base::Time build_time;
const char* kDateTime = __DATE__ " " __TIME__;
bool result = base::Time::FromString(kDateTime, &build_time);
DCHECK(result);
// If the experiment is expired, don't run it.
if (build_time > expiration_time)
return false;
#if defined(GOOGLE_CHROME_BUILD)
// The experiment should only run on canary and dev.
BrowserDistribution* dist = BrowserDistribution::GetDistribution();
std::wstring channel;
if (!dist->GetChromeChannel(&channel))
return false;
return channel == GoogleChromeSxSDistribution::ChannelName() ||
channel == L"dev";
#else
// If we're running Chromium, we have no concept of channel so we
// run the experiment.
return true;
#endif
}
// Checks to see if the experiment is running. If so, either tosses a coin
// and persists it, or gets the already persistent coin-toss value. Returns
// the coin-toss via |pre_read|. Returns true if the experiment is running and
// pre_read has been written, false otherwise. |pre_read| is only written to
// when this function returns true. |key| must be open with read-write access,
// and be valid.
bool GetPreReadExperimentGroup(base::win::RegKey* key,
DWORD* pre_read) {
DCHECK(key != NULL);
DCHECK(pre_read != NULL);
if (!PreReadShouldRun()) {
// The experiment is no longer running. Remove the registry key
// storing the coin toss.
key->DeleteValue(kPreReadRegistryValue);
return false;
}
// Coin already tossed?
DWORD coin_toss = 0;
if (key->ReadValueDW(kPreReadRegistryValue, &coin_toss) == ERROR_SUCCESS) {
*pre_read = coin_toss;
return true;
}
// Coin not tossed? Toss it, and save the value to the registry.
coin_toss = base::RandInt(0, 1);
DCHECK(coin_toss == 0 || coin_toss == 1);
if (key->WriteValue(kPreReadRegistryValue, coin_toss) == ERROR_SUCCESS) {
*pre_read = coin_toss;
return true;
}
return false;
}
#endif // if defined(OS_WIN)
// 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 LoadChromeWithDirectory(std::wstring* dir) {
HMODULE LoadChromeWithDirectory(const std::wstring& reg_key, // For PreRead.
std::wstring* dir) {
::SetCurrentDirectoryW(dir->c_str());
const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
#ifdef _WIN64
......@@ -138,6 +220,32 @@ HMODULE LoadChromeWithDirectory(std::wstring* dir) {
key.Close();
}
// The PreRead experiment is unable to use the standard FieldTrial
// mechanism as pre-reading happens in chrome.exe prior to loading
// chrome.dll. As such, we use a custom approach. If the experiment is
// running (not expired, and we're running a version of chrome from an
// appropriate channel) then we look to the registry for the experiment
// group. If it is not found, we toss a coin and persist the result in the
// registry. The coin-toss value is communicated to chrome.dll via an
// environment variable, which indicates to chrome.dll that the experiment
// is running, causing it to report sub-histogram results. If the experiment
// is expired or not running, the registry key is removed.
base::win::RegKey exp_key(
HKEY_CURRENT_USER, reg_key.c_str(),
KEY_CREATE_SUB_KEY | KEY_QUERY_VALUE | KEY_SET_VALUE);
if (exp_key.Valid()) {
// If the experiment is running, indicate it to chrome.dll via an
// environment variable.
if (GetPreReadExperimentGroup(&exp_key, &pre_read)) {
scoped_ptr<base::Environment> env(base::Environment::Create());
env->SetVar(chrome::kPreReadEnvironmentVariable,
pre_read ? "1" : "0");
}
exp_key.Close();
}
if (pre_read) {
TRACE_EVENT_BEGIN_ETW("PreReadImage", 0, "");
file_util::PreReadImage(dir->c_str(), pre_read_size, pre_read_step_size);
......@@ -161,7 +269,7 @@ void ClearDidRun(const std::wstring& dll_path) {
GoogleUpdateSettings::UpdateDidRunState(false, system_level);
}
}
} // namespace
//=============================================================================
MainDllLoader::MainDllLoader() : dll_(NULL) {
......@@ -183,7 +291,7 @@ MainDllLoader::~MainDllLoader() {
HMODULE MainDllLoader::Load(std::wstring* out_version, std::wstring* out_file) {
std::wstring dir(GetExecutablePath());
*out_file = dir;
HMODULE dll = LoadChromeWithDirectory(out_file);
HMODULE dll = LoadChromeWithDirectory(GetRegistryPath(), out_file);
if (dll)
return dll;
......@@ -222,7 +330,7 @@ HMODULE MainDllLoader::Load(std::wstring* out_version, std::wstring* out_file) {
*out_file = dir;
*out_version = version_string;
out_file->append(*out_version).append(L"\\");
dll = LoadChromeWithDirectory(out_file);
dll = LoadChromeWithDirectory(GetRegistryPath(), out_file);
if (!dll) {
LOG(ERROR) << "Failed to load Chrome DLL from " << out_file;
}
......
......@@ -165,6 +165,7 @@
#include <commctrl.h>
#include <shellapi.h>
#include "base/environment.h" // For PreRead experiment.
#include "base/win/scoped_com_initializer.h"
#include "base/win/windows_version.h"
#include "chrome/browser/browser_trial.h"
......@@ -814,6 +815,20 @@ bool IsCrashReportingEnabled(const PrefService* local_state) {
}
#endif // #if defined(USE_LINUX_BREAKPAD)
// This code is specific to the Windows-only PreReadExperiment field-trial.
void AddPreReadHistogramTime(const char* name, base::TimeDelta time) {
const base::TimeDelta kMin(base::TimeDelta::FromMilliseconds(1));
const base::TimeDelta kMax(base::TimeDelta::FromHours(1));
static const size_t kBuckets(100);
// FactoryTimeGet will always return a pointer to the same histogram object,
// keyed on its name. There's no need for us to store it explicitly anywhere.
base::Histogram* counter = base::Histogram::FactoryTimeGet(
name, kMin, kMax, kBuckets, base::Histogram::kUmaTargetedHistogramFlag);
counter->AddTime(time);
}
} // namespace
namespace chrome_browser {
......@@ -2053,12 +2068,8 @@ int BrowserMain(const MainFunctionParams& parameters) {
if (pool)
pool->Recycle();
UMA_HISTOGRAM_CUSTOM_TIMES(
"Startup.BrowserOpenTabs",
base::TimeTicks::Now() - browser_open_start,
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromHours(1),
100);
RecordPreReadExperimentTime("Startup.BrowserOpenTabs",
base::TimeTicks::Now() - browser_open_start);
// TODO(mad): Move this call in a proper place on CrOS.
// http://crosbug.com/17687
......@@ -2153,3 +2164,30 @@ int BrowserMain(const MainFunctionParams& parameters) {
TRACE_EVENT_END_ETW("BrowserMain", 0, 0);
return result_code;
}
// This code is specific to the Windows-only PreReadExperiment field-trial.
void RecordPreReadExperimentTime(const char* name, base::TimeDelta time) {
DCHECK(name != NULL);
// This gets called with different histogram names, so we don't want to use
// the UMA_HISTOGRAM_CUSTOM_TIMES macro--it uses a static variable, and the
// first call wins.
AddPreReadHistogramTime(name, time);
#if defined(OS_WIN)
// The pre-read experiment is Windows specific.
scoped_ptr<base::Environment> env(base::Environment::Create());
// Only record the sub-histogram result if the experiment is running
// (environment variable is set, and valid).
std::string pre_read;
if (env->GetVar(chrome::kPreReadEnvironmentVariable, &pre_read) &&
(pre_read == "0" || pre_read == "1")) {
std::string uma_name(name);
uma_name += "_PreRead";
uma_name += pre_read == "1" ? "Enabled" : "Disabled";
AddPreReadHistogramTime(uma_name.c_str(), time);
}
#endif
}
......@@ -213,4 +213,9 @@ void ShowMissingLocaleMessageBox();
// the UMA histogram |metric_name|.
void RecordBrowserStartupTime();
// Records a time value to an UMA histogram in the context of the
// PreReadExperiment field-trial. This also reports to the appropriate
// sub-histogram (_PreRead(Enabled|Disabled)).
void RecordPreReadExperimentTime(const char* name, base::TimeDelta time);
#endif // CHROME_BROWSER_BROWSER_MAIN_H_
......@@ -96,12 +96,8 @@ void RecordBrowserStartupTime() {
::GetProcessTimes(::GetCurrentProcess(), &creation_time, &ignore, &ignore,
&ignore);
UMA_HISTOGRAM_CUSTOM_TIMES(
"Startup.BrowserMessageLoopStartTime",
base::Time::Now() - base::Time::FromFileTime(creation_time),
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromHours(1),
100);
RecordPreReadExperimentTime("Startup.BrowserMessageLoopStartTime",
base::Time::Now() - base::Time::FromFileTime(creation_time));
}
int AskForUninstallConfirmation() {
......
......@@ -188,6 +188,11 @@ extern const int kLowestRendererOomScore = 300;
extern const int kHighestRendererOomScore = 1000;
#endif
#if defined(OS_WIN)
// This is used by the PreRead experiment.
const char kPreReadEnvironmentVariable[] = "CHROME_PRE_READ_EXPERIMENT";
#endif
} // namespace chrome
#undef FPL
......@@ -116,6 +116,11 @@ extern const int kLowestRendererOomScore;
extern const int kHighestRendererOomScore;
#endif
#if defined(OS_WIN)
// This is used by the PreRead experiment.
extern const char kPreReadEnvironmentVariable[];
#endif
} // namespace chrome
#endif // CHROME_COMMON_CHROME_CONSTANTS_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment