diff options
author | rogerta@chromium.org <rogerta@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-26 18:08:43 +0000 |
---|---|---|
committer | rogerta@chromium.org <rogerta@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-26 18:08:43 +0000 |
commit | 438772df5a7055847d061d49a8c18e6fc77daedb (patch) | |
tree | c748b7ff544f07d888a6978cac99c7c1b5d261b6 /chrome/browser/extensions | |
parent | 46ef2ce78952914d3723b07aae28b024a1e8a6ad (diff) | |
download | chromium_src-438772df5a7055847d061d49a8c18e6fc77daedb.zip chromium_src-438772df5a7055847d061d49a8c18e6fc77daedb.tar.gz chromium_src-438772df5a7055847d061d49a8c18e6fc77daedb.tar.bz2 |
Add a metrics extensions API.
See http://docs.google.com/View?id=dd4ngnpz_0dpsb8n96 for API proposal.
BUG=0
TEST=See unit tests as part of this CL
Review URL: http://codereview.chromium.org/657037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40130 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
5 files changed, 390 insertions, 0 deletions
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc index 25eff22..e5a0768 100644 --- a/chrome/browser/extensions/extension_apitest.cc +++ b/chrome/browser/extensions/extension_apitest.cc @@ -7,6 +7,7 @@ #include "chrome/browser/browser.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/notification_registrar.h" #include "chrome/test/ui_test_utils.h" @@ -103,4 +104,7 @@ Extension* ExtensionApiTest::GetSingleLoadedExtension() { void ExtensionApiTest::SetUpCommandLine(CommandLine* command_line) { ExtensionBrowserTest::SetUpCommandLine(command_line); test_data_dir_ = test_data_dir_.AppendASCII("api_test"); + + // Needed for metrics extension API tests. + command_line->AppendSwitch(switches::kEnableMetricsExtensionApi); } diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 35b73a5..8f868eb 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -20,6 +20,7 @@ #include "chrome/browser/extensions/extension_history_api.h" #include "chrome/browser/extensions/extension_i18n_api.h" #include "chrome/browser/extensions/extension_message_service.h" +#include "chrome/browser/extensions/extension_metrics_module.h" #include "chrome/browser/extensions/extension_page_actions_module.h" #include "chrome/browser/extensions/extension_page_actions_module_constants.h" #include "chrome/browser/extensions/extension_popup_api.h" @@ -34,6 +35,7 @@ #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/render_messages.h" #include "chrome/common/result_codes.h" #include "chrome/common/url_constants.h" @@ -169,6 +171,20 @@ void FactoryRegistry::ResetFunctions() { // Processes. RegisterFunction<GetProcessForTabFunction>(); + // Metrics. + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableMetricsExtensionApi)) { + RegisterFunction<MetricsRecordUserActionFunction>(); + RegisterFunction<MetricsRecordValueFunction>(); + RegisterFunction<MetricsRecordPercentageFunction>(); + RegisterFunction<MetricsRecordCountFunction>(); + RegisterFunction<MetricsRecordSmallCountFunction>(); + RegisterFunction<MetricsRecordMediumCountFunction>(); + RegisterFunction<MetricsRecordTimeFunction>(); + RegisterFunction<MetricsRecordMediumTimeFunction>(); + RegisterFunction<MetricsRecordLongTimeFunction>(); + } + // Test. RegisterFunction<ExtensionTestPassFunction>(); RegisterFunction<ExtensionTestFailFunction>(); diff --git a/chrome/browser/extensions/extension_metrics_apitest.cc b/chrome/browser/extensions/extension_metrics_apitest.cc new file mode 100644 index 0000000..39d342a --- /dev/null +++ b/chrome/browser/extensions/extension_metrics_apitest.cc @@ -0,0 +1,152 @@ +// Copyright (c) 2010 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 <map> + +#include "base/histogram.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/notification_registrar.h" + +namespace { + +// The tests that are run by this extension are expected to record the following +// user actions, with the specified counts. If the tests in test.js are +// modified, this array may need to be updated. +struct RecordedUserAction { + const char* name; // base name of metric without extension id. + int count; // number of times the metric was recorded. +} g_user_actions[] = { + {"test.ua.1", 1}, + {"test.ua.2", 2}, +}; + +// The tests that are run by this extension are expected to record the following +// histograms. If the tests in test.js are modified, this array may need to be +// updated. +struct RecordedHistogram { + const char* name; // base name of metric without extension id. + Histogram::ClassType type; + int min; + int max; + size_t buckets; +} g_histograms[] = { + {"test.h.1", Histogram::HISTOGRAM, 1, 100, 50}, // custom + {"test.h.2", Histogram::LINEAR_HISTOGRAM, 1, 200, 50}, // custom + {"test.h.3", Histogram::LINEAR_HISTOGRAM, 1, 101, 102}, // percentage + {"test.time", Histogram::HISTOGRAM, 1, 10000, 50}, + {"test.medium.time", Histogram::HISTOGRAM, 1, 180000, 50}, + {"test.long.time", Histogram::HISTOGRAM, 1, 3600000, 50}, + {"test.count", Histogram::HISTOGRAM, 1, 1000000, 50}, + {"test.medium.count", Histogram::HISTOGRAM, 1, 10000, 50}, + {"test.small.count", Histogram::HISTOGRAM, 1, 100, 50}, +}; + +// Build the full name of a metrics for the given extension. Each metric +// is made up of the unique name within the extension followed by the +// extension's id. +std::string BuildFullName(const std::string& name, const Extension* extension) { + std::string full_name(name); + full_name += extension->id(); + return full_name; +} + +// This class observes and collects user action notifications that are sent +// by the tests, so that they can be examined afterwards for correctness. +class UserActionObserver : public NotificationObserver { + public: + UserActionObserver(); + + void ValidateUserActions(const Extension* extension, + const RecordedUserAction* recorded, + int count); + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + typedef std::map<std::string, int> UserActionCountMap; + + int num_metrics() const { + return count_map_.size(); + } + + int GetMetricCount(const std::string& name) const { + UserActionCountMap::const_iterator i = count_map_.find(name); + return i == count_map_.end() ? -1 : i->second; + } + + NotificationRegistrar registrar_; + UserActionCountMap count_map_; +}; + +UserActionObserver::UserActionObserver() { + registrar_.Add(this, NotificationType::USER_ACTION, + NotificationService::AllSources()); +} + +void UserActionObserver::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + const char* name = *Details<const char*>(details).ptr(); + ++(count_map_[name]); +} + +void UserActionObserver::ValidateUserActions(const Extension* extension, + const RecordedUserAction* recorded, + int count) { + EXPECT_EQ(count, num_metrics()); + + for (int i = 0; i < count; ++i) { + const RecordedUserAction& ua = recorded[i]; + EXPECT_EQ(ua.count, GetMetricCount(BuildFullName(ua.name, extension))); + } +} + +void ValidateHistograms(const Extension* extension, + const RecordedHistogram* recorded, + int count) { + StatisticsRecorder::Histograms histograms; + StatisticsRecorder::GetHistograms(&histograms); + + // Code other than the tests tun here will record some histogram values, but + // we will ignore those. This function validates that all the histogram we + // expect to see are present in the list, and that their basic info is + // correct. + for (int i = 0; i < count; ++i) { + const RecordedHistogram& r = recorded[i]; + std::string name(BuildFullName(r.name, extension)); + + size_t j = 0; + for (j = 0; j < histograms.size(); ++j) { + scoped_refptr<Histogram> histogram(histograms[j]); + + if (name == histogram->histogram_name()) { + EXPECT_EQ(r.type, histogram->histogram_type()); + EXPECT_EQ(r.min, histogram->declared_min()); + EXPECT_EQ(r.max, histogram->declared_max()); + EXPECT_EQ(r.buckets, histogram->bucket_count()); + break; + } + } + EXPECT_LT(j, histograms.size()); + } +} + +} // anonymous namespace + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Metrics) { + UserActionObserver observer; + + ASSERT_TRUE(RunExtensionTest("metrics")) << message_; + Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension); + + observer.ValidateUserActions(extension, + g_user_actions, + arraysize(g_user_actions)); + ValidateHistograms(extension, g_histograms, arraysize(g_histograms)); +} diff --git a/chrome/browser/extensions/extension_metrics_module.cc b/chrome/browser/extensions/extension_metrics_module.cc new file mode 100644 index 0000000..30db486 --- /dev/null +++ b/chrome/browser/extensions/extension_metrics_module.cc @@ -0,0 +1,153 @@ +// Copyright (c) 2010 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 "chrome/browser/extensions/extension_metrics_module.h" + +#include "base/histogram.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/browser/metrics/user_metrics.h" + +namespace { + +// Build the full name of a metrics for the given extension. Each metric +// is made up of the unique name within the extension followed by the +// extension's id. This keeps the metrics from one extension unique from +// other extensions, as well as those metrics from chrome itself. +std::string BuildMetricName(const std::string& name, + const Extension* extension) { + std::string full_name(name); + full_name += extension->id(); + return full_name; +} + +} // anonymous namespace + +// These extension function classes are enabled only if the +// enable-metrics-extension-api command line switch is used. Refer to +// extension_function_dispatcher.cc to see how they are enabled. + +bool MetricsRecordUserActionFunction::RunImpl() { + std::string name; + EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_STRING)); + EXTENSION_FUNCTION_VALIDATE(args_->GetAsString(&name)); + + name = BuildMetricName(name, GetExtension()); + UserMetrics::RecordComputedAction(name, profile()); + return true; +} + +bool MetricsHistogramHelperFunction::GetNameAndSample(std::string* name, + int* sample) { + EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST)); + const ListValue* args = args_as_list(); + + EXTENSION_FUNCTION_VALIDATE(args->GetString(0, name)); + EXTENSION_FUNCTION_VALIDATE(args->GetInteger(1, sample)); + return true; +} + +bool MetricsHistogramHelperFunction::RecordValue(const std::string& name, + Histogram::ClassType type, + int min, + int max, + size_t buckets, + int sample) { + std::string full_name = BuildMetricName(name, GetExtension()); + scoped_refptr<Histogram> counter; + if (type == Histogram::LINEAR_HISTOGRAM) { + counter = LinearHistogram::FactoryGet(full_name, + min, + max, + buckets, + Histogram::kUmaTargetedHistogramFlag); + } else { + counter = Histogram::FactoryGet(full_name, + min, + max, + buckets, + Histogram::kUmaTargetedHistogramFlag); + } + + counter->Add(sample); + return true; +} + +bool MetricsRecordValueFunction::RunImpl() { + EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST)); + const ListValue* args = args_as_list(); + + int sample; + EXTENSION_FUNCTION_VALIDATE(args->GetInteger(1, &sample)); + + // Get the histogram parameters from the metric type object. + DictionaryValue* metric_type; + EXTENSION_FUNCTION_VALIDATE(args->GetDictionary(0, &metric_type)); + + std::string name; + std::string type; + int min; + int max; + int buckets; + EXTENSION_FUNCTION_VALIDATE(metric_type->GetString(L"metricName", &name)); + EXTENSION_FUNCTION_VALIDATE(metric_type->GetString(L"type", &type)); + EXTENSION_FUNCTION_VALIDATE(metric_type->GetInteger(L"min", &min)); + EXTENSION_FUNCTION_VALIDATE(metric_type->GetInteger(L"max", &max)); + EXTENSION_FUNCTION_VALIDATE(metric_type->GetInteger(L"buckets", &buckets)); + + Histogram::ClassType histogram_type(type == "histogram-linear" ? + Histogram::LINEAR_HISTOGRAM : Histogram::HISTOGRAM); + return RecordValue(name, histogram_type, min, max, buckets, sample); +} + +bool MetricsRecordPercentageFunction::RunImpl() { + std::string name; + int sample; + EXTENSION_FUNCTION_VALIDATE(GetNameAndSample(&name, &sample)); + return RecordValue(name, Histogram::LINEAR_HISTOGRAM, 1, 101, 102, sample); +} + +bool MetricsRecordCountFunction::RunImpl() { + std::string name; + int sample; + EXTENSION_FUNCTION_VALIDATE(GetNameAndSample(&name, &sample)); + return RecordValue(name, Histogram::HISTOGRAM, 1, 1000000, 50, sample); +} + +bool MetricsRecordSmallCountFunction::RunImpl() { + std::string name; + int sample; + EXTENSION_FUNCTION_VALIDATE(GetNameAndSample(&name, &sample)); + return RecordValue(name, Histogram::HISTOGRAM, 1, 100, 50, sample); +} + +bool MetricsRecordMediumCountFunction::RunImpl() { + std::string name; + int sample; + EXTENSION_FUNCTION_VALIDATE(GetNameAndSample(&name, &sample)); + return RecordValue(name, Histogram::HISTOGRAM, 1, 10000, 50, sample); +} + +bool MetricsRecordTimeFunction::RunImpl() { + std::string name; + int sample; + EXTENSION_FUNCTION_VALIDATE(GetNameAndSample(&name, &sample)); + static const int kTenSecMs = 10 * 1000; + return RecordValue(name, Histogram::HISTOGRAM, 1, kTenSecMs, 50, sample); +} + +bool MetricsRecordMediumTimeFunction::RunImpl() { + std::string name; + int sample; + EXTENSION_FUNCTION_VALIDATE(GetNameAndSample(&name, &sample)); + static const int kThreeMinMs = 3 * 60 * 1000; + return RecordValue(name, Histogram::HISTOGRAM, 1, kThreeMinMs, 50, sample); +} + +bool MetricsRecordLongTimeFunction::RunImpl() { + std::string name; + int sample; + EXTENSION_FUNCTION_VALIDATE(GetNameAndSample(&name, &sample)); + static const int kOneHourMs = 60 * 60 * 1000; + return RecordValue(name, Histogram::HISTOGRAM, 1, kOneHourMs, 50, sample); +} diff --git a/chrome/browser/extensions/extension_metrics_module.h b/chrome/browser/extensions/extension_metrics_module.h new file mode 100644 index 0000000..c2ffdd2 --- /dev/null +++ b/chrome/browser/extensions/extension_metrics_module.h @@ -0,0 +1,65 @@ +// Copyright (c) 2010 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_METRICS_MODULE_H__ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_METRICS_MODULE_H__ + +#include <string> + +#include "base/histogram.h" +#include "chrome/browser/extensions/extension_function.h" + +class MetricsRecordUserActionFunction : public SyncExtensionFunction { + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("metrics.recordUserAction") +}; + +class MetricsHistogramHelperFunction : public SyncExtensionFunction { + protected: + bool GetNameAndSample(std::string* name, int* sample); + virtual bool RecordValue(const std::string& name, Histogram::ClassType type, + int min, int max, size_t buckets, int sample); +}; + +class MetricsRecordValueFunction : public MetricsHistogramHelperFunction { + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("metrics.recordValue") +}; + +class MetricsRecordPercentageFunction : public MetricsHistogramHelperFunction { + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("metrics.recordPercentage") +}; + +class MetricsRecordCountFunction : public MetricsHistogramHelperFunction { + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("metrics.recordCount") +}; + +class MetricsRecordSmallCountFunction : public MetricsHistogramHelperFunction { + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("metrics.recordSmallCount") +}; + +class MetricsRecordMediumCountFunction : public MetricsHistogramHelperFunction { + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("metrics.recordMediumCount") +}; + +class MetricsRecordTimeFunction : public MetricsHistogramHelperFunction { + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("metrics.recordTime") +}; + +class MetricsRecordMediumTimeFunction : public MetricsHistogramHelperFunction { + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("metrics.recordMediumTime") +}; + +class MetricsRecordLongTimeFunction : public MetricsHistogramHelperFunction { + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("metrics.recordLongTime") +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_METRICS_MODULE_H__ |