Index: chrome/browser/component_updater/sw_reporter_installer_win.cc |
diff --git a/chrome/browser/component_updater/sw_reporter_installer_win.cc b/chrome/browser/component_updater/sw_reporter_installer_win.cc |
index 64773b32c7138767bbc092e9d3611231b70bc2a4..12b3fcb6fa1e67889fcec81479c4b45abde28c57 100644 |
--- a/chrome/browser/component_updater/sw_reporter_installer_win.cc |
+++ b/chrome/browser/component_updater/sw_reporter_installer_win.cc |
@@ -6,13 +6,17 @@ |
#include <stdint.h> |
+#include <algorithm> |
#include <map> |
#include <memory> |
+#include <queue> |
#include <string> |
#include <utility> |
#include <vector> |
#include "base/base_paths.h" |
+#include "base/command_line.h" |
+#include "base/feature_list.h" |
#include "base/files/file_path.h" |
#include "base/files/file_util.h" |
#include "base/logging.h" |
@@ -20,6 +24,9 @@ |
#include "base/metrics/sparse_histogram.h" |
#include "base/path_service.h" |
#include "base/strings/string_tokenizer.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/sys_info.h" |
#include "base/threading/thread_task_runner_handle.h" |
#include "base/threading/worker_pool.h" |
#include "base/time/time.h" |
@@ -35,7 +42,9 @@ |
#include "components/prefs/pref_registry_simple.h" |
#include "components/update_client/update_client.h" |
#include "components/update_client/utils.h" |
+#include "components/variations/variations_associated_data.h" |
#include "content/public/browser/browser_thread.h" |
+#include "third_party/re2/src/re2/re2.h" |
namespace component_updater { |
@@ -67,6 +76,9 @@ const wchar_t kExitCodeValueName[] = L"ExitCode"; |
const wchar_t kUploadResultsValueName[] = L"UploadResults"; |
const wchar_t kVersionValueName[] = L"Version"; |
+const base::Feature kExperimentalEngineFeature{ |
+ "ExperimentalSwReporterEngine", base::FEATURE_DISABLED_BY_DEFAULT}; |
+ |
void SRTHasCompleted(SRTCompleted value) { |
UMA_HISTOGRAM_ENUMERATION("SoftwareReporter.Cleaner.HasCompleted", value, |
SRT_COMPLETED_MAX); |
@@ -103,6 +115,15 @@ void ReportUploadsWithUma(const base::string16& upload_results) { |
UMA_HISTOGRAM_BOOLEAN("SoftwareReporter.LastUploadResult", last_result); |
} |
+bool IsExperimentalEngineEnabled() { |
+ // The experiment is only enabled on x86. There's no way to check this in the |
+ // variations config so we'll hard-code it. |
+ if (base::SysInfo::OperatingSystemArchitecture() != "x86") |
+ return false; |
+ |
+ return base::FeatureList::IsEnabled(kExperimentalEngineFeature); |
+} |
+ |
class SwReporterInstallerTraits : public ComponentInstallerTraits { |
public: |
SwReporterInstallerTraits() {} |
@@ -131,6 +152,10 @@ class SwReporterInstallerTraits : public ComponentInstallerTraits { |
std::unique_ptr<base::DictionaryValue> manifest) override { |
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
base::FilePath exe_path(install_dir.Append(kSwReporterExeName)); |
+ if (IsExperimentalEngineEnabled()) { |
+ RunExperimentalSwReporter(exe_path, version); |
+ return; |
+ } |
safe_browsing::RunSwReporter(safe_browsing::SwReporterInvocation(exe_path), |
version, base::ThreadTaskRunnerHandle::Get(), |
base::WorkerPool::GetTaskRunner(true)); |
@@ -145,7 +170,26 @@ class SwReporterInstallerTraits : public ComponentInstallerTraits { |
std::string GetName() const override { return "Software Reporter Tool"; } |
update_client::InstallerAttributes GetInstallerAttributes() const override { |
- return update_client::InstallerAttributes(); |
+ update_client::InstallerAttributes attributes; |
+ if (IsExperimentalEngineEnabled()) { |
+ // Pass the "tag" parameter to the installer; it will be used to choose |
+ // which binary is downloaded. |
+ constexpr char kTagParam[] = "tag"; |
+ std::string tag = variations::GetVariationParamValueByFeature( |
+ kExperimentalEngineFeature, kTagParam); |
+ |
+ // Validate that the tag is valid (regexp taken from |
+ // ComponentInstallerTraits::InstallerAttributes). If not set it to a |
+ // valid but unrecognized value so that nothing will be downloaded. |
+ if (tag.empty() || |
+ !re2::RE2::FullMatch(tag, "^[-.,;+_=a-zA-Z0-9]{0,256}$")) { |
+ ReportExperimentError(kBadTagParam); |
+ attributes[kTagParam] = "missing_tag"; |
+ } else { |
+ attributes[kTagParam] = tag; |
+ } |
+ } |
+ return attributes; |
} |
std::vector<std::string> GetMimeTypes() const override { |
return std::vector<std::string>(); |
@@ -163,6 +207,118 @@ class SwReporterInstallerTraits : public ComponentInstallerTraits { |
DCHECK(hash); |
hash->assign(kSha256Hash, kSha256Hash + sizeof(kSha256Hash)); |
} |
+ |
+ enum ExperimentError { |
+ kBadTagParam, |
grt (UTC plus 2)
2016/08/15 19:39:40
enums use SHOUTY_STYLE.
since this goes straight i
macourteau
2016/09/12 19:53:14
Done.
|
+ kBadVariationParams, |
+ kNumErrorTypes |
+ }; |
+ |
+ void ReportExperimentError(ExperimentError error) const { |
+ UMA_HISTOGRAM_ENUMERATION("SoftwareReporter.ExperimentErrors", error, |
+ kNumErrorTypes); |
+ } |
+ |
+ // Read the command-line params and an UMA histogram suffix from the |
+ // experiment parameters, and launch the SwReporter one or more times with |
+ // those parameters. If anything goes wrong the SwReporter should not be |
+ // run at all - do not fall back to the default. |
+ void RunExperimentalSwReporter(const base::FilePath& exe_path, |
+ const base::Version& version) { |
+ DCHECK(IsExperimentalEngineEnabled()); |
+ |
+ // Command-line arguments are passed as args0, args1, ..., argsN. |
+ // Suffixes are passed as suffix0, suffix1, ..., suffixN. |
+ // |
+ // Read all the parameters whose names match these patterns into maps, |
+ // then make sure that all indices 0 through N are stored in both maps. |
+ // If any are missing, the experiment config is badly formatted. If it's |
+ // ok, add each pair (command line, suffix) to the invocations vector. |
+ std::map<std::string, std::string> experiment_params; |
+ if (!variations::GetVariationParamsByFeature(kExperimentalEngineFeature, |
+ &experiment_params)) { |
+ ReportExperimentError(kBadVariationParams); |
+ return; |
+ } |
+ |
+ re2::RE2 args_pattern("args(\\d+)"); |
+ std::map<int, std::string> arg_strings; |
+ |
+ re2::RE2 suffix_pattern("suffix(\\d+)"); |
+ std::map<int, std::string> suffixes; |
+ |
+ re2::RE2 valid_suffix_pattern("[A-Za-z0-9]+"); |
+ |
+ int max_index = 0; |
+ for (const auto& param : experiment_params) { |
+ const std::string& key = param.first; |
+ const std::string& value = param.second; |
+ |
+ int index = 0; |
+ if (re2::RE2::FullMatch(key, args_pattern, &index)) { |
+ arg_strings[index] = value; |
+ } else if (re2::RE2::FullMatch(key, suffix_pattern, &index)) { |
+ if (!re2::RE2::FullMatch(value, valid_suffix_pattern)) { |
+ ReportExperimentError(kBadVariationParams); |
+ return; |
+ } |
+ suffixes[index] = value; |
+ } else { |
+ // Unrecognized parameter. |
+ continue; |
+ } |
+ |
+ if (index < 0) { |
+ ReportExperimentError(kBadVariationParams); |
+ return; |
+ } |
+ max_index = std::max(index, max_index); |
+ } |
+ |
+ std::queue<safe_browsing::SwReporterInvocation> invocations; |
+ |
+ for (int index = 0; index <= max_index; ++index) { |
+ auto args = arg_strings.find(index); |
+ auto suffix = suffixes.find(index); |
+ if (args == arg_strings.end() || suffix == suffixes.end()) { |
+ ReportExperimentError(kBadVariationParams); |
+ return; |
+ } |
+ |
+ // Now we have a string to parse into a CommandLine object, but it |
+ // doesn't start with the exe path. We can't create the CommandLine with |
+ // the path and then append the argument string, because CommandLine only |
+ // has methods to append individual arguments. So tack a placeholder |
+ // which is guaranteed not to need quoting to the beginning of the |
+ // argument string, then replace it with the real path, which may contain |
+ // spaces and other weirdness. |
+ auto command_line = base::CommandLine::FromString(base::UTF8ToUTF16( |
+ base::StringPrintf("placeholder %s", args->second))); |
+ command_line.SetProgram(exe_path); |
+ |
+ // Add the histogram suffix to the command-line as well, so that the |
+ // reporter will add the same suffix to registry keys where it writes |
+ // metrics. |
+ command_line.AppendSwitchASCII("registry-suffix", suffix->second); |
+ |
+ safe_browsing::SwReporterInvocation invocation(command_line); |
+ invocation.suffix = suffix->second; |
+ invocation.is_experimental = true; |
+ invocations.push(invocation); |
+ } |
+ |
+ // If there were no command-lines and suffixes given, run once with no args |
+ // and an empty suffix. (The control group would be set up this way.) |
+ if (invocations.empty()) { |
+ safe_browsing::SwReporterInvocation invocation(exe_path); |
+ invocation.is_experimental = true; |
+ invocations.push(invocation); |
+ } |
+ |
+ safe_browsing::RunSwReporters(invocations, version, |
+ base::ThreadTaskRunnerHandle::Get(), |
+ base::WorkerPool::GetTaskRunner(true)); |
+ } |
}; |
} // namespace |