1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/webui/performance_monitor/performance_monitor_handler.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/command_line.h" 10 #include "base/time/time.h" 11 #include "base/values.h" 12 #include "chrome/browser/performance_monitor/database.h" 13 #include "chrome/browser/performance_monitor/event.h" 14 #include "chrome/browser/performance_monitor/metric.h" 15 #include "chrome/browser/performance_monitor/performance_monitor.h" 16 #include "chrome/browser/performance_monitor/performance_monitor_util.h" 17 #include "chrome/browser/ui/webui/performance_monitor/performance_monitor_l10n.h" 18 #include "chrome/browser/ui/webui/performance_monitor/performance_monitor_ui_constants.h" 19 #include "chrome/browser/ui/webui/performance_monitor/performance_monitor_ui_util.h" 20 #include "chrome/common/chrome_switches.h" 21 #include "chrome/common/extensions/value_builder.h" 22 #include "content/public/browser/browser_thread.h" 23 #include "content/public/browser/web_ui.h" 24 25 using content::BrowserThread; 26 27 namespace performance_monitor { 28 namespace { 29 30 std::set<MetricType> GetMetricSetForCategory(MetricCategory category) { 31 std::set<MetricType> metric_set; 32 switch (category) { 33 case METRIC_CATEGORY_CPU: 34 metric_set.insert(METRIC_CPU_USAGE); 35 break; 36 case METRIC_CATEGORY_MEMORY: 37 metric_set.insert(METRIC_SHARED_MEMORY_USAGE); 38 metric_set.insert(METRIC_PRIVATE_MEMORY_USAGE); 39 break; 40 case METRIC_CATEGORY_TIMING: 41 metric_set.insert(METRIC_STARTUP_TIME); 42 metric_set.insert(METRIC_TEST_STARTUP_TIME); 43 metric_set.insert(METRIC_SESSION_RESTORE_TIME); 44 metric_set.insert(METRIC_PAGE_LOAD_TIME); 45 break; 46 case METRIC_CATEGORY_NETWORK: 47 metric_set.insert(METRIC_NETWORK_BYTES_READ); 48 break; 49 default: 50 NOTREACHED(); 51 } 52 return metric_set; 53 } 54 55 std::set<EventType> GetEventSetForCategory(EventCategory category) { 56 std::set<EventType> event_set; 57 switch (category) { 58 case EVENT_CATEGORY_EXTENSIONS: 59 event_set.insert(EVENT_EXTENSION_INSTALL); 60 event_set.insert(EVENT_EXTENSION_UNINSTALL); 61 event_set.insert(EVENT_EXTENSION_UPDATE); 62 event_set.insert(EVENT_EXTENSION_ENABLE); 63 event_set.insert(EVENT_EXTENSION_DISABLE); 64 break; 65 case EVENT_CATEGORY_CHROME: 66 event_set.insert(EVENT_CHROME_UPDATE); 67 break; 68 case EVENT_CATEGORY_EXCEPTIONS: 69 event_set.insert(EVENT_RENDERER_HANG); 70 event_set.insert(EVENT_RENDERER_CRASH); 71 event_set.insert(EVENT_RENDERER_KILLED); 72 event_set.insert(EVENT_UNCLEAN_EXIT); 73 break; 74 default: 75 NOTREACHED(); 76 } 77 return event_set; 78 } 79 80 Unit GetUnitForMetricCategory(MetricCategory category) { 81 switch (category) { 82 case METRIC_CATEGORY_CPU: 83 return UNIT_PERCENT; 84 case METRIC_CATEGORY_MEMORY: 85 return UNIT_MEGABYTES; 86 case METRIC_CATEGORY_TIMING: 87 return UNIT_SECONDS; 88 case METRIC_CATEGORY_NETWORK: 89 return UNIT_MEGABYTES; 90 default: 91 NOTREACHED(); 92 } 93 return UNIT_UNDEFINED; 94 } 95 96 MetricCategory GetCategoryForMetric(MetricType type) { 97 switch (type) { 98 case METRIC_CPU_USAGE: 99 return METRIC_CATEGORY_CPU; 100 case METRIC_SHARED_MEMORY_USAGE: 101 case METRIC_PRIVATE_MEMORY_USAGE: 102 return METRIC_CATEGORY_MEMORY; 103 case METRIC_STARTUP_TIME: 104 case METRIC_TEST_STARTUP_TIME: 105 case METRIC_SESSION_RESTORE_TIME: 106 case METRIC_PAGE_LOAD_TIME: 107 return METRIC_CATEGORY_TIMING; 108 case METRIC_NETWORK_BYTES_READ: 109 return METRIC_CATEGORY_NETWORK; 110 default: 111 NOTREACHED(); 112 } 113 return METRIC_CATEGORY_NUMBER_OF_CATEGORIES; 114 } 115 116 Unit GetUnitForMetricType(MetricType type) { 117 switch (type) { 118 case METRIC_CPU_USAGE: 119 return UNIT_PERCENT; 120 case METRIC_SHARED_MEMORY_USAGE: 121 case METRIC_PRIVATE_MEMORY_USAGE: 122 case METRIC_NETWORK_BYTES_READ: 123 return UNIT_BYTES; 124 case METRIC_STARTUP_TIME: 125 case METRIC_TEST_STARTUP_TIME: 126 case METRIC_SESSION_RESTORE_TIME: 127 case METRIC_PAGE_LOAD_TIME: 128 return UNIT_MICROSECONDS; 129 default: 130 NOTREACHED(); 131 } 132 return UNIT_UNDEFINED; 133 } 134 135 // Returns a dictionary for the aggregation method. Aggregation strategies 136 // contain an id representing the method, and localized strings for the 137 // method name and method description. 138 scoped_ptr<DictionaryValue> GetAggregationMethod( 139 AggregationMethod method) { 140 scoped_ptr<DictionaryValue> value(new DictionaryValue()); 141 value->SetInteger("id", method); 142 value->SetString("name", GetLocalizedStringFromAggregationMethod(method)); 143 value->SetString( 144 "description", 145 GetLocalizedStringForAggregationMethodDescription(method)); 146 return value.Pass(); 147 } 148 149 // Returns a list of metric details, with one entry per metric. Metric details 150 // are dictionaries which contain the id representing the metric and localized 151 // strings for the metric name and metric description. 152 scoped_ptr<ListValue> GetMetricDetailsForCategory(MetricCategory category) { 153 scoped_ptr<ListValue> value(new ListValue()); 154 std::set<MetricType> metric_set = GetMetricSetForCategory(category); 155 for (std::set<MetricType>::const_iterator iter = metric_set.begin(); 156 iter != metric_set.end(); ++iter) { 157 DictionaryValue* metric_details = new DictionaryValue(); 158 metric_details->SetInteger("metricId", *iter); 159 metric_details->SetString( 160 "name", GetLocalizedStringFromMetricType(*iter)); 161 metric_details->SetString( 162 "description", GetLocalizedStringForMetricTypeDescription(*iter)); 163 value->Append(metric_details); 164 } 165 return value.Pass(); 166 } 167 168 // Returns a dictionary for the metric category. Metric categories contain 169 // an id representing the category; localized strings for the category name, 170 // the default unit in which the category is measured, and the category's 171 // description; and the metric details for each metric type in the category. 172 scoped_ptr<DictionaryValue> GetMetricCategory(MetricCategory category) { 173 scoped_ptr<DictionaryValue> value(new DictionaryValue()); 174 value->SetInteger("metricCategoryId", category); 175 value->SetString( 176 "name", GetLocalizedStringFromMetricCategory(category)); 177 value->SetString( 178 "unit", 179 GetLocalizedStringFromUnit(GetUnitForMetricCategory(category))); 180 value->SetString( 181 "description", 182 GetLocalizedStringForMetricCategoryDescription(category)); 183 value->Set("details", GetMetricDetailsForCategory(category).release()); 184 return value.Pass(); 185 } 186 187 // Returns a list of event types, with one entry per event. Event types 188 // are dictionaries which contain the id representing the event and localized 189 // strings for the event name, event description, and a title suitable for a 190 // mouseover popup. 191 scoped_ptr<ListValue> GetEventTypesForCategory(EventCategory category) { 192 scoped_ptr<ListValue> value(new ListValue()); 193 std::set<EventType> event_set = GetEventSetForCategory(category); 194 for (std::set<EventType>::const_iterator iter = event_set.begin(); 195 iter != event_set.end(); ++iter) { 196 DictionaryValue* event_details = new DictionaryValue(); 197 event_details->SetInteger("eventId", *iter); 198 event_details->SetString( 199 "name", GetLocalizedStringFromEventType(*iter)); 200 event_details->SetString( 201 "description", GetLocalizedStringForEventTypeDescription(*iter)); 202 event_details->SetString( 203 "popupTitle", GetLocalizedStringForEventTypeMouseover(*iter)); 204 value->Append(event_details); 205 } 206 return value.Pass(); 207 } 208 209 // Returns a dictionary for the event category. Event categories contain an 210 // id representing the category, localized strings for the event name and 211 // event description, and event details for each event type in the category. 212 scoped_ptr<DictionaryValue> GetEventCategory(EventCategory category) { 213 scoped_ptr<DictionaryValue> value(new DictionaryValue()); 214 value->SetInteger("eventCategoryId", category); 215 value->SetString( 216 "name", GetLocalizedStringFromEventCategory(category)); 217 value->SetString( 218 "description", 219 GetLocalizedStringForEventCategoryDescription(category)); 220 value->Set("details", GetEventTypesForCategory(category).release()); 221 return value.Pass(); 222 } 223 224 // Queries the performance monitor database for active intervals between 225 // |start| and |end| times and appends the results to |results|. 226 void DoGetActiveIntervals(ListValue* results, 227 const base::Time& start, 228 const base::Time& end) { 229 Database* db = PerformanceMonitor::GetInstance()->database(); 230 std::vector<TimeRange> intervals = db->GetActiveIntervals(start, end); 231 232 for (std::vector<TimeRange>::iterator it = intervals.begin(); 233 it != intervals.end(); ++it) { 234 DictionaryValue* interval_value = new DictionaryValue(); 235 interval_value->SetDouble("start", it->start.ToJsTime()); 236 interval_value->SetDouble("end", it->end.ToJsTime()); 237 results->Append(interval_value); 238 } 239 } 240 241 // Queries the PerformanceMonitor database for events of type |event_type| 242 // between |start| and |end| times, creates a new event with localized keys 243 // for display, and appends the results to |results|. 244 void DoGetEvents(ListValue* results, 245 const std::set<EventType>& event_types, 246 const base::Time& start, 247 const base::Time& end) { 248 Database* db = PerformanceMonitor::GetInstance()->database(); 249 250 for (std::set<EventType>::const_iterator iter = event_types.begin(); 251 iter != event_types.end(); ++iter) { 252 DictionaryValue* event_results = new DictionaryValue(); 253 event_results->SetInteger("eventId", static_cast<int>(*iter)); 254 ListValue* events = new ListValue(); 255 event_results->Set("events", events); 256 results->Append(event_results); 257 258 Database::EventVector event_vector = db->GetEvents(*iter, start, end); 259 260 for (Database::EventVector::iterator event = event_vector.begin(); 261 event != event_vector.end(); ++event) { 262 DictionaryValue* localized_event = new DictionaryValue(); 263 264 for (DictionaryValue::Iterator data(*(*event)->data()); !data.IsAtEnd(); 265 data.Advance()) { 266 Value* value = NULL; 267 268 // The property 'eventType' is set in HandleGetEvents as part of the 269 // entire result set, so we don't need to include this here in the 270 // event. 271 if (data.key() == "eventType") 272 continue; 273 else if (data.key() == "time") { 274 // The property 'time' is also used computationally, but must be 275 // converted to JS-style time. 276 double time = 0.0; 277 if (!data.value().GetAsDouble(&time)) { 278 LOG(ERROR) << "Failed to get 'time' field from event."; 279 continue; 280 } 281 value = Value::CreateDoubleValue( 282 base::Time::FromInternalValue( 283 static_cast<int64>(time)).ToJsTime()); 284 } else { 285 // All other values are user-facing, so we create a new value for 286 // localized display. 287 DictionaryValue* localized_value = new DictionaryValue(); 288 localized_value->SetString( 289 "label", 290 GetLocalizedStringFromEventProperty(data.key())); 291 localized_value->SetWithoutPathExpansion("value", 292 data.value().DeepCopy()); 293 value = localized_value; 294 } 295 296 localized_event->SetWithoutPathExpansion(data.key(), value); 297 } 298 events->Append(localized_event); 299 } 300 } 301 } 302 303 // Populates results with a dictionary for each metric requested. The dictionary 304 // includes a metric id, the maximum value for the metric, and a list of lists 305 // of metric points, with each sublist containing the aggregated data for an 306 // interval for which PerformanceMonitor was active. This will also convert 307 // time to JS-style time. 308 void DoGetMetrics(ListValue* results, 309 const std::set<MetricType>& metric_types, 310 const base::Time& start, 311 const base::Time& end, 312 const base::TimeDelta& resolution, 313 AggregationMethod aggregation_method) { 314 Database* db = PerformanceMonitor::GetInstance()->database(); 315 std::vector<TimeRange> intervals = db->GetActiveIntervals(start, end); 316 317 // For each metric type, populate a new dictionary and append it to results. 318 for (std::set<MetricType>::const_iterator metric_type = metric_types.begin(); 319 metric_type != metric_types.end(); ++metric_type) { 320 double conversion_factor = 321 GetConversionFactor(*GetUnitDetails(GetUnitForMetricType(*metric_type)), 322 *GetUnitDetails(GetUnitForMetricCategory( 323 GetCategoryForMetric(*metric_type)))); 324 325 DictionaryValue* metric_set = new DictionaryValue(); 326 metric_set->SetInteger("metricId", static_cast<int>(*metric_type)); 327 metric_set->SetDouble( 328 "maxValue", 329 db->GetMaxStatsForActivityAndMetric(*metric_type) * conversion_factor); 330 331 // Retrieve all metrics in the database, and aggregate them into a series 332 // of points for each active interval. 333 scoped_ptr<Database::MetricVector> metric_vector = 334 db->GetStatsForActivityAndMetric(*metric_type, start, end); 335 336 scoped_ptr<VectorOfMetricVectors> aggregated_metrics = 337 AggregateMetric(*metric_type, 338 metric_vector.get(), 339 start, 340 intervals, 341 resolution, 342 aggregation_method); 343 344 // The JS-side expects a list to be present, even if there are no metrics. 345 if (!aggregated_metrics) { 346 metric_set->Set("metrics", new ListValue()); 347 results->Append(metric_set); 348 continue; 349 } 350 351 ListValue* metric_points_by_interval = new ListValue(); 352 353 // For each metric point, record it in the expected format for the JS-side 354 // (a dictionary of time and value, with time as a JS-style time), and 355 // convert the values to be display-friendly. 356 for (VectorOfMetricVectors::const_iterator metric_series = 357 aggregated_metrics->begin(); 358 metric_series != aggregated_metrics->end(); ++metric_series) { 359 ListValue* series_value = new ListValue(); 360 for (Database::MetricVector::const_iterator metric_point = 361 metric_series->begin(); 362 metric_point != metric_series->end(); ++metric_point) { 363 DictionaryValue* point_value = new DictionaryValue(); 364 point_value->SetDouble("time", metric_point->time.ToJsTime()); 365 point_value->SetDouble("value", 366 metric_point->value * conversion_factor); 367 series_value->Append(point_value); 368 } 369 metric_points_by_interval->Append(series_value); 370 } 371 372 metric_set->Set("metrics", metric_points_by_interval); 373 results->Append(metric_set); 374 } 375 } 376 377 } // namespace 378 379 PerformanceMonitorHandler::PerformanceMonitorHandler() { 380 // If we are not running the --run-performance-monitor flag, we will not have 381 // started PerformanceMonitor. 382 if (!PerformanceMonitor::initialized()) 383 PerformanceMonitor::GetInstance()->Start(); 384 } 385 386 PerformanceMonitorHandler::~PerformanceMonitorHandler() {} 387 388 void PerformanceMonitorHandler::RegisterMessages() { 389 web_ui()->RegisterMessageCallback( 390 "getActiveIntervals", 391 base::Bind(&PerformanceMonitorHandler::HandleGetActiveIntervals, 392 AsWeakPtr())); 393 web_ui()->RegisterMessageCallback( 394 "getFlagEnabled", 395 base::Bind(&PerformanceMonitorHandler::HandleGetFlagEnabled, 396 AsWeakPtr())); 397 web_ui()->RegisterMessageCallback( 398 "getAggregationTypes", 399 base::Bind(&PerformanceMonitorHandler::HandleGetAggregationTypes, 400 AsWeakPtr())); 401 web_ui()->RegisterMessageCallback( 402 "getEventTypes", 403 base::Bind(&PerformanceMonitorHandler::HandleGetEventTypes, 404 AsWeakPtr())); 405 web_ui()->RegisterMessageCallback( 406 "getEvents", 407 base::Bind(&PerformanceMonitorHandler::HandleGetEvents, 408 AsWeakPtr())); 409 web_ui()->RegisterMessageCallback( 410 "getMetricTypes", 411 base::Bind(&PerformanceMonitorHandler::HandleGetMetricTypes, 412 AsWeakPtr())); 413 web_ui()->RegisterMessageCallback( 414 "getMetrics", 415 base::Bind(&PerformanceMonitorHandler::HandleGetMetrics, 416 AsWeakPtr())); 417 } 418 419 void PerformanceMonitorHandler::ReturnResults(const std::string& function, 420 const Value* results) { 421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 422 web_ui()->CallJavascriptFunction(function, *results); 423 } 424 425 void PerformanceMonitorHandler::HandleGetActiveIntervals( 426 const ListValue* args) { 427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 428 CHECK_EQ(2u, args->GetSize()); 429 double double_time = 0.0; 430 CHECK(args->GetDouble(0, &double_time)); 431 base::Time start = base::Time::FromJsTime(double_time); 432 CHECK(args->GetDouble(1, &double_time)); 433 base::Time end = base::Time::FromJsTime(double_time); 434 435 ListValue* results = new ListValue(); 436 util::PostTaskToDatabaseThreadAndReply( 437 FROM_HERE, 438 base::Bind(&DoGetActiveIntervals, results, start, end), 439 base::Bind(&PerformanceMonitorHandler::ReturnResults, AsWeakPtr(), 440 "PerformanceMonitor.getActiveIntervalsCallback", 441 base::Owned(results))); 442 } 443 444 void PerformanceMonitorHandler::HandleGetFlagEnabled(const ListValue* args) { 445 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 446 CHECK_EQ(0u, args->GetSize()); 447 scoped_ptr<Value> value(Value::CreateBooleanValue( 448 CommandLine::ForCurrentProcess()->HasSwitch( 449 switches::kPerformanceMonitorGathering))); 450 ReturnResults("PerformanceMonitor.getFlagEnabledCallback", value.get()); 451 } 452 453 void PerformanceMonitorHandler::HandleGetAggregationTypes( 454 const ListValue* args) { 455 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 456 CHECK_EQ(0u, args->GetSize()); 457 ListValue results; 458 for (int i = 0; i < AGGREGATION_METHOD_NUMBER_OF_METHODS; ++i) { 459 results.Append( 460 GetAggregationMethod(static_cast<AggregationMethod>(i)).release()); 461 } 462 463 ReturnResults( 464 "PerformanceMonitor.getAggregationTypesCallback", &results); 465 } 466 467 void PerformanceMonitorHandler::HandleGetEventTypes(const ListValue* args) { 468 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 469 CHECK_EQ(0u, args->GetSize()); 470 ListValue results; 471 for (int i = 0; i < EVENT_CATEGORY_NUMBER_OF_CATEGORIES; ++i) 472 results.Append(GetEventCategory(static_cast<EventCategory>(i)).release()); 473 474 ReturnResults("PerformanceMonitor.getEventTypesCallback", &results); 475 } 476 477 void PerformanceMonitorHandler::HandleGetEvents(const ListValue* args) { 478 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 479 CHECK_EQ(3u, args->GetSize()); 480 481 const ListValue* event_type_list; 482 CHECK(args->GetList(0, &event_type_list)); 483 std::set<EventType> event_types; 484 for (ListValue::const_iterator iter = event_type_list->begin(); 485 iter != event_type_list->end(); ++iter) { 486 double event_type_double = 0.0; 487 CHECK((*iter)->GetAsDouble(&event_type_double)); 488 CHECK(event_type_double < EVENT_NUMBER_OF_EVENTS && 489 event_type_double > EVENT_UNDEFINED); 490 event_types.insert( 491 static_cast<EventType>(static_cast<int>(event_type_double))); 492 } 493 494 double double_time = 0.0; 495 CHECK(args->GetDouble(1, &double_time)); 496 base::Time start = base::Time::FromJsTime(double_time); 497 CHECK(args->GetDouble(2, &double_time)); 498 base::Time end = base::Time::FromJsTime(double_time); 499 500 ListValue* results = new ListValue(); 501 util::PostTaskToDatabaseThreadAndReply( 502 FROM_HERE, 503 base::Bind(&DoGetEvents, results, event_types, start, end), 504 base::Bind(&PerformanceMonitorHandler::ReturnResults, AsWeakPtr(), 505 "PerformanceMonitor.getEventsCallback", 506 base::Owned(results))); 507 } 508 509 void PerformanceMonitorHandler::HandleGetMetricTypes(const ListValue* args) { 510 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 511 CHECK_EQ(0u, args->GetSize()); 512 ListValue results; 513 for (int i = 0; i < METRIC_CATEGORY_NUMBER_OF_CATEGORIES; ++i) 514 results.Append(GetMetricCategory(static_cast<MetricCategory>(i)).release()); 515 516 ReturnResults("PerformanceMonitor.getMetricTypesCallback", &results); 517 } 518 519 void PerformanceMonitorHandler::HandleGetMetrics(const ListValue* args) { 520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 521 CHECK_EQ(5u, args->GetSize()); 522 523 const ListValue* metric_type_list; 524 CHECK(args->GetList(0, &metric_type_list)); 525 std::set<MetricType> metric_types; 526 for (ListValue::const_iterator iter = metric_type_list->begin(); 527 iter != metric_type_list->end(); ++iter) { 528 double metric_type_double = 0.0; 529 CHECK((*iter)->GetAsDouble(&metric_type_double)); 530 CHECK(metric_type_double < METRIC_NUMBER_OF_METRICS && 531 metric_type_double > METRIC_UNDEFINED); 532 metric_types.insert( 533 static_cast<MetricType>(static_cast<int>(metric_type_double))); 534 } 535 536 double time_double = 0.0; 537 CHECK(args->GetDouble(1, &time_double)); 538 base::Time start = base::Time::FromJsTime(time_double); 539 CHECK(args->GetDouble(2, &time_double)); 540 base::Time end = base::Time::FromJsTime(time_double); 541 542 double resolution_in_milliseconds = 0.0; 543 CHECK(args->GetDouble(3, &resolution_in_milliseconds)); 544 base::TimeDelta resolution = 545 base::TimeDelta::FromMilliseconds(resolution_in_milliseconds); 546 547 double aggregation_double = 0.0; 548 CHECK(args->GetDouble(4, &aggregation_double)); 549 CHECK(aggregation_double < AGGREGATION_METHOD_NUMBER_OF_METHODS && 550 aggregation_double >= 0); 551 AggregationMethod aggregation_method = 552 static_cast<AggregationMethod>(static_cast<int>(aggregation_double)); 553 554 ListValue* results = new ListValue(); 555 util::PostTaskToDatabaseThreadAndReply( 556 FROM_HERE, 557 base::Bind(&DoGetMetrics, results, metric_types, 558 start, end, resolution, aggregation_method), 559 base::Bind(&PerformanceMonitorHandler::ReturnResults, AsWeakPtr(), 560 "PerformanceMonitor.getMetricsCallback", 561 base::Owned(results))); 562 } 563 564 } // namespace performance_monitor 565