blob: 333ecb1b56803ee1ae321640eb0af358873531f0 [file] [log] [blame]
// 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 "chrome/browser/ui/webui/performance_monitor/performance_monitor_handler.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/performance_monitor/database.h"
#include "chrome/browser/performance_monitor/event.h"
#include "chrome/browser/performance_monitor/metric.h"
#include "chrome/browser/performance_monitor/performance_monitor.h"
#include "chrome/browser/performance_monitor/performance_monitor_util.h"
#include "chrome/browser/ui/webui/performance_monitor/performance_monitor_l10n.h"
#include "chrome/browser/ui/webui/performance_monitor/performance_monitor_ui_constants.h"
#include "chrome/browser/ui/webui/performance_monitor/performance_monitor_ui_util.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/value_builder.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_ui.h"
using content::BrowserThread;
namespace performance_monitor {
namespace {
std::set<MetricType> GetMetricSetForCategory(MetricCategory category) {
std::set<MetricType> metric_set;
switch (category) {
case METRIC_CATEGORY_CPU:
metric_set.insert(METRIC_CPU_USAGE);
break;
case METRIC_CATEGORY_MEMORY:
metric_set.insert(METRIC_SHARED_MEMORY_USAGE);
metric_set.insert(METRIC_PRIVATE_MEMORY_USAGE);
break;
case METRIC_CATEGORY_TIMING:
metric_set.insert(METRIC_STARTUP_TIME);
metric_set.insert(METRIC_TEST_STARTUP_TIME);
metric_set.insert(METRIC_SESSION_RESTORE_TIME);
metric_set.insert(METRIC_PAGE_LOAD_TIME);
break;
case METRIC_CATEGORY_NETWORK:
metric_set.insert(METRIC_NETWORK_BYTES_READ);
break;
default:
NOTREACHED();
}
return metric_set;
}
std::set<EventType> GetEventSetForCategory(EventCategory category) {
std::set<EventType> event_set;
switch (category) {
case EVENT_CATEGORY_EXTENSIONS:
event_set.insert(EVENT_EXTENSION_INSTALL);
event_set.insert(EVENT_EXTENSION_UNINSTALL);
event_set.insert(EVENT_EXTENSION_UPDATE);
event_set.insert(EVENT_EXTENSION_ENABLE);
event_set.insert(EVENT_EXTENSION_DISABLE);
break;
case EVENT_CATEGORY_CHROME:
event_set.insert(EVENT_CHROME_UPDATE);
break;
case EVENT_CATEGORY_EXCEPTIONS:
event_set.insert(EVENT_RENDERER_HANG);
event_set.insert(EVENT_RENDERER_CRASH);
event_set.insert(EVENT_RENDERER_KILLED);
event_set.insert(EVENT_UNCLEAN_EXIT);
break;
default:
NOTREACHED();
}
return event_set;
}
Unit GetUnitForMetricCategory(MetricCategory category) {
switch (category) {
case METRIC_CATEGORY_CPU:
return UNIT_PERCENT;
case METRIC_CATEGORY_MEMORY:
return UNIT_MEGABYTES;
case METRIC_CATEGORY_TIMING:
return UNIT_SECONDS;
case METRIC_CATEGORY_NETWORK:
return UNIT_MEGABYTES;
default:
NOTREACHED();
}
return UNIT_UNDEFINED;
}
MetricCategory GetCategoryForMetric(MetricType type) {
switch (type) {
case METRIC_CPU_USAGE:
return METRIC_CATEGORY_CPU;
case METRIC_SHARED_MEMORY_USAGE:
case METRIC_PRIVATE_MEMORY_USAGE:
return METRIC_CATEGORY_MEMORY;
case METRIC_STARTUP_TIME:
case METRIC_TEST_STARTUP_TIME:
case METRIC_SESSION_RESTORE_TIME:
case METRIC_PAGE_LOAD_TIME:
return METRIC_CATEGORY_TIMING;
case METRIC_NETWORK_BYTES_READ:
return METRIC_CATEGORY_NETWORK;
default:
NOTREACHED();
}
return METRIC_CATEGORY_NUMBER_OF_CATEGORIES;
}
Unit GetUnitForMetricType(MetricType type) {
switch (type) {
case METRIC_CPU_USAGE:
return UNIT_PERCENT;
case METRIC_SHARED_MEMORY_USAGE:
case METRIC_PRIVATE_MEMORY_USAGE:
case METRIC_NETWORK_BYTES_READ:
return UNIT_BYTES;
case METRIC_STARTUP_TIME:
case METRIC_TEST_STARTUP_TIME:
case METRIC_SESSION_RESTORE_TIME:
case METRIC_PAGE_LOAD_TIME:
return UNIT_MICROSECONDS;
default:
NOTREACHED();
}
return UNIT_UNDEFINED;
}
// Returns a dictionary for the aggregation method. Aggregation strategies
// contain an id representing the method, and localized strings for the
// method name and method description.
scoped_ptr<DictionaryValue> GetAggregationMethod(
AggregationMethod method) {
scoped_ptr<DictionaryValue> value(new DictionaryValue());
value->SetInteger("id", method);
value->SetString("name", GetLocalizedStringFromAggregationMethod(method));
value->SetString(
"description",
GetLocalizedStringForAggregationMethodDescription(method));
return value.Pass();
}
// Returns a list of metric details, with one entry per metric. Metric details
// are dictionaries which contain the id representing the metric and localized
// strings for the metric name and metric description.
scoped_ptr<ListValue> GetMetricDetailsForCategory(MetricCategory category) {
scoped_ptr<ListValue> value(new ListValue());
std::set<MetricType> metric_set = GetMetricSetForCategory(category);
for (std::set<MetricType>::const_iterator iter = metric_set.begin();
iter != metric_set.end(); ++iter) {
DictionaryValue* metric_details = new DictionaryValue();
metric_details->SetInteger("metricId", *iter);
metric_details->SetString(
"name", GetLocalizedStringFromMetricType(*iter));
metric_details->SetString(
"description", GetLocalizedStringForMetricTypeDescription(*iter));
value->Append(metric_details);
}
return value.Pass();
}
// Returns a dictionary for the metric category. Metric categories contain
// an id representing the category; localized strings for the category name,
// the default unit in which the category is measured, and the category's
// description; and the metric details for each metric type in the category.
scoped_ptr<DictionaryValue> GetMetricCategory(MetricCategory category) {
scoped_ptr<DictionaryValue> value(new DictionaryValue());
value->SetInteger("metricCategoryId", category);
value->SetString(
"name", GetLocalizedStringFromMetricCategory(category));
value->SetString(
"unit",
GetLocalizedStringFromUnit(GetUnitForMetricCategory(category)));
value->SetString(
"description",
GetLocalizedStringForMetricCategoryDescription(category));
value->Set("details", GetMetricDetailsForCategory(category).release());
return value.Pass();
}
// Returns a list of event types, with one entry per event. Event types
// are dictionaries which contain the id representing the event and localized
// strings for the event name, event description, and a title suitable for a
// mouseover popup.
scoped_ptr<ListValue> GetEventTypesForCategory(EventCategory category) {
scoped_ptr<ListValue> value(new ListValue());
std::set<EventType> event_set = GetEventSetForCategory(category);
for (std::set<EventType>::const_iterator iter = event_set.begin();
iter != event_set.end(); ++iter) {
DictionaryValue* event_details = new DictionaryValue();
event_details->SetInteger("eventId", *iter);
event_details->SetString(
"name", GetLocalizedStringFromEventType(*iter));
event_details->SetString(
"description", GetLocalizedStringForEventTypeDescription(*iter));
event_details->SetString(
"popupTitle", GetLocalizedStringForEventTypeMouseover(*iter));
value->Append(event_details);
}
return value.Pass();
}
// Returns a dictionary for the event category. Event categories contain an
// id representing the category, localized strings for the event name and
// event description, and event details for each event type in the category.
scoped_ptr<DictionaryValue> GetEventCategory(EventCategory category) {
scoped_ptr<DictionaryValue> value(new DictionaryValue());
value->SetInteger("eventCategoryId", category);
value->SetString(
"name", GetLocalizedStringFromEventCategory(category));
value->SetString(
"description",
GetLocalizedStringForEventCategoryDescription(category));
value->Set("details", GetEventTypesForCategory(category).release());
return value.Pass();
}
// Queries the performance monitor database for active intervals between
// |start| and |end| times and appends the results to |results|.
void DoGetActiveIntervals(ListValue* results,
const base::Time& start,
const base::Time& end) {
Database* db = PerformanceMonitor::GetInstance()->database();
std::vector<TimeRange> intervals = db->GetActiveIntervals(start, end);
for (std::vector<TimeRange>::iterator it = intervals.begin();
it != intervals.end(); ++it) {
DictionaryValue* interval_value = new DictionaryValue();
interval_value->SetDouble("start", it->start.ToJsTime());
interval_value->SetDouble("end", it->end.ToJsTime());
results->Append(interval_value);
}
}
// Queries the PerformanceMonitor database for events of type |event_type|
// between |start| and |end| times, creates a new event with localized keys
// for display, and appends the results to |results|.
void DoGetEvents(ListValue* results,
const std::set<EventType>& event_types,
const base::Time& start,
const base::Time& end) {
Database* db = PerformanceMonitor::GetInstance()->database();
for (std::set<EventType>::const_iterator iter = event_types.begin();
iter != event_types.end(); ++iter) {
DictionaryValue* event_results = new DictionaryValue();
event_results->SetInteger("eventId", static_cast<int>(*iter));
ListValue* events = new ListValue();
event_results->Set("events", events);
results->Append(event_results);
Database::EventVector event_vector = db->GetEvents(*iter, start, end);
for (Database::EventVector::iterator event = event_vector.begin();
event != event_vector.end(); ++event) {
DictionaryValue* localized_event = new DictionaryValue();
for (DictionaryValue::Iterator data(*(*event)->data()); !data.IsAtEnd();
data.Advance()) {
Value* value = NULL;
// The property 'eventType' is set in HandleGetEvents as part of the
// entire result set, so we don't need to include this here in the
// event.
if (data.key() == "eventType")
continue;
else if (data.key() == "time") {
// The property 'time' is also used computationally, but must be
// converted to JS-style time.
double time = 0.0;
if (!data.value().GetAsDouble(&time)) {
LOG(ERROR) << "Failed to get 'time' field from event.";
continue;
}
value = Value::CreateDoubleValue(
base::Time::FromInternalValue(
static_cast<int64>(time)).ToJsTime());
} else {
// All other values are user-facing, so we create a new value for
// localized display.
DictionaryValue* localized_value = new DictionaryValue();
localized_value->SetString(
"label",
GetLocalizedStringFromEventProperty(data.key()));
localized_value->SetWithoutPathExpansion("value",
data.value().DeepCopy());
value = localized_value;
}
localized_event->SetWithoutPathExpansion(data.key(), value);
}
events->Append(localized_event);
}
}
}
// Populates results with a dictionary for each metric requested. The dictionary
// includes a metric id, the maximum value for the metric, and a list of lists
// of metric points, with each sublist containing the aggregated data for an
// interval for which PerformanceMonitor was active. This will also convert
// time to JS-style time.
void DoGetMetrics(ListValue* results,
const std::set<MetricType>& metric_types,
const base::Time& start,
const base::Time& end,
const base::TimeDelta& resolution,
AggregationMethod aggregation_method) {
Database* db = PerformanceMonitor::GetInstance()->database();
std::vector<TimeRange> intervals = db->GetActiveIntervals(start, end);
// For each metric type, populate a new dictionary and append it to results.
for (std::set<MetricType>::const_iterator metric_type = metric_types.begin();
metric_type != metric_types.end(); ++metric_type) {
double conversion_factor =
GetConversionFactor(*GetUnitDetails(GetUnitForMetricType(*metric_type)),
*GetUnitDetails(GetUnitForMetricCategory(
GetCategoryForMetric(*metric_type))));
DictionaryValue* metric_set = new DictionaryValue();
metric_set->SetInteger("metricId", static_cast<int>(*metric_type));
metric_set->SetDouble(
"maxValue",
db->GetMaxStatsForActivityAndMetric(*metric_type) * conversion_factor);
// Retrieve all metrics in the database, and aggregate them into a series
// of points for each active interval.
scoped_ptr<Database::MetricVector> metric_vector =
db->GetStatsForActivityAndMetric(*metric_type, start, end);
scoped_ptr<VectorOfMetricVectors> aggregated_metrics =
AggregateMetric(*metric_type,
metric_vector.get(),
start,
intervals,
resolution,
aggregation_method);
// The JS-side expects a list to be present, even if there are no metrics.
if (!aggregated_metrics) {
metric_set->Set("metrics", new ListValue());
results->Append(metric_set);
continue;
}
ListValue* metric_points_by_interval = new ListValue();
// For each metric point, record it in the expected format for the JS-side
// (a dictionary of time and value, with time as a JS-style time), and
// convert the values to be display-friendly.
for (VectorOfMetricVectors::const_iterator metric_series =
aggregated_metrics->begin();
metric_series != aggregated_metrics->end(); ++metric_series) {
ListValue* series_value = new ListValue();
for (Database::MetricVector::const_iterator metric_point =
metric_series->begin();
metric_point != metric_series->end(); ++metric_point) {
DictionaryValue* point_value = new DictionaryValue();
point_value->SetDouble("time", metric_point->time.ToJsTime());
point_value->SetDouble("value",
metric_point->value * conversion_factor);
series_value->Append(point_value);
}
metric_points_by_interval->Append(series_value);
}
metric_set->Set("metrics", metric_points_by_interval);
results->Append(metric_set);
}
}
} // namespace
PerformanceMonitorHandler::PerformanceMonitorHandler() {
// If we are not running the --run-performance-monitor flag, we will not have
// started PerformanceMonitor.
if (!PerformanceMonitor::initialized())
PerformanceMonitor::GetInstance()->Start();
}
PerformanceMonitorHandler::~PerformanceMonitorHandler() {}
void PerformanceMonitorHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"getActiveIntervals",
base::Bind(&PerformanceMonitorHandler::HandleGetActiveIntervals,
AsWeakPtr()));
web_ui()->RegisterMessageCallback(
"getFlagEnabled",
base::Bind(&PerformanceMonitorHandler::HandleGetFlagEnabled,
AsWeakPtr()));
web_ui()->RegisterMessageCallback(
"getAggregationTypes",
base::Bind(&PerformanceMonitorHandler::HandleGetAggregationTypes,
AsWeakPtr()));
web_ui()->RegisterMessageCallback(
"getEventTypes",
base::Bind(&PerformanceMonitorHandler::HandleGetEventTypes,
AsWeakPtr()));
web_ui()->RegisterMessageCallback(
"getEvents",
base::Bind(&PerformanceMonitorHandler::HandleGetEvents,
AsWeakPtr()));
web_ui()->RegisterMessageCallback(
"getMetricTypes",
base::Bind(&PerformanceMonitorHandler::HandleGetMetricTypes,
AsWeakPtr()));
web_ui()->RegisterMessageCallback(
"getMetrics",
base::Bind(&PerformanceMonitorHandler::HandleGetMetrics,
AsWeakPtr()));
}
void PerformanceMonitorHandler::ReturnResults(const std::string& function,
const Value* results) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
web_ui()->CallJavascriptFunction(function, *results);
}
void PerformanceMonitorHandler::HandleGetActiveIntervals(
const ListValue* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK_EQ(2u, args->GetSize());
double double_time = 0.0;
CHECK(args->GetDouble(0, &double_time));
base::Time start = base::Time::FromJsTime(double_time);
CHECK(args->GetDouble(1, &double_time));
base::Time end = base::Time::FromJsTime(double_time);
ListValue* results = new ListValue();
util::PostTaskToDatabaseThreadAndReply(
FROM_HERE,
base::Bind(&DoGetActiveIntervals, results, start, end),
base::Bind(&PerformanceMonitorHandler::ReturnResults, AsWeakPtr(),
"PerformanceMonitor.getActiveIntervalsCallback",
base::Owned(results)));
}
void PerformanceMonitorHandler::HandleGetFlagEnabled(const ListValue* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK_EQ(0u, args->GetSize());
scoped_ptr<Value> value(Value::CreateBooleanValue(
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kPerformanceMonitorGathering)));
ReturnResults("PerformanceMonitor.getFlagEnabledCallback", value.get());
}
void PerformanceMonitorHandler::HandleGetAggregationTypes(
const ListValue* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK_EQ(0u, args->GetSize());
ListValue results;
for (int i = 0; i < AGGREGATION_METHOD_NUMBER_OF_METHODS; ++i) {
results.Append(
GetAggregationMethod(static_cast<AggregationMethod>(i)).release());
}
ReturnResults(
"PerformanceMonitor.getAggregationTypesCallback", &results);
}
void PerformanceMonitorHandler::HandleGetEventTypes(const ListValue* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK_EQ(0u, args->GetSize());
ListValue results;
for (int i = 0; i < EVENT_CATEGORY_NUMBER_OF_CATEGORIES; ++i)
results.Append(GetEventCategory(static_cast<EventCategory>(i)).release());
ReturnResults("PerformanceMonitor.getEventTypesCallback", &results);
}
void PerformanceMonitorHandler::HandleGetEvents(const ListValue* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK_EQ(3u, args->GetSize());
const ListValue* event_type_list;
CHECK(args->GetList(0, &event_type_list));
std::set<EventType> event_types;
for (ListValue::const_iterator iter = event_type_list->begin();
iter != event_type_list->end(); ++iter) {
double event_type_double = 0.0;
CHECK((*iter)->GetAsDouble(&event_type_double));
CHECK(event_type_double < EVENT_NUMBER_OF_EVENTS &&
event_type_double > EVENT_UNDEFINED);
event_types.insert(
static_cast<EventType>(static_cast<int>(event_type_double)));
}
double double_time = 0.0;
CHECK(args->GetDouble(1, &double_time));
base::Time start = base::Time::FromJsTime(double_time);
CHECK(args->GetDouble(2, &double_time));
base::Time end = base::Time::FromJsTime(double_time);
ListValue* results = new ListValue();
util::PostTaskToDatabaseThreadAndReply(
FROM_HERE,
base::Bind(&DoGetEvents, results, event_types, start, end),
base::Bind(&PerformanceMonitorHandler::ReturnResults, AsWeakPtr(),
"PerformanceMonitor.getEventsCallback",
base::Owned(results)));
}
void PerformanceMonitorHandler::HandleGetMetricTypes(const ListValue* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK_EQ(0u, args->GetSize());
ListValue results;
for (int i = 0; i < METRIC_CATEGORY_NUMBER_OF_CATEGORIES; ++i)
results.Append(GetMetricCategory(static_cast<MetricCategory>(i)).release());
ReturnResults("PerformanceMonitor.getMetricTypesCallback", &results);
}
void PerformanceMonitorHandler::HandleGetMetrics(const ListValue* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK_EQ(5u, args->GetSize());
const ListValue* metric_type_list;
CHECK(args->GetList(0, &metric_type_list));
std::set<MetricType> metric_types;
for (ListValue::const_iterator iter = metric_type_list->begin();
iter != metric_type_list->end(); ++iter) {
double metric_type_double = 0.0;
CHECK((*iter)->GetAsDouble(&metric_type_double));
CHECK(metric_type_double < METRIC_NUMBER_OF_METRICS &&
metric_type_double > METRIC_UNDEFINED);
metric_types.insert(
static_cast<MetricType>(static_cast<int>(metric_type_double)));
}
double time_double = 0.0;
CHECK(args->GetDouble(1, &time_double));
base::Time start = base::Time::FromJsTime(time_double);
CHECK(args->GetDouble(2, &time_double));
base::Time end = base::Time::FromJsTime(time_double);
double resolution_in_milliseconds = 0.0;
CHECK(args->GetDouble(3, &resolution_in_milliseconds));
base::TimeDelta resolution =
base::TimeDelta::FromMilliseconds(resolution_in_milliseconds);
double aggregation_double = 0.0;
CHECK(args->GetDouble(4, &aggregation_double));
CHECK(aggregation_double < AGGREGATION_METHOD_NUMBER_OF_METHODS &&
aggregation_double >= 0);
AggregationMethod aggregation_method =
static_cast<AggregationMethod>(static_cast<int>(aggregation_double));
ListValue* results = new ListValue();
util::PostTaskToDatabaseThreadAndReply(
FROM_HERE,
base::Bind(&DoGetMetrics, results, metric_types,
start, end, resolution, aggregation_method),
base::Bind(&PerformanceMonitorHandler::ReturnResults, AsWeakPtr(),
"PerformanceMonitor.getMetricsCallback",
base::Owned(results)));
}
} // namespace performance_monitor