blob: bb5f370f590118b16b1b866f40f2dfef465731cf [file] [log] [blame]
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/browser/web_module_stat_tracker.h"
#if defined(ENABLE_WEBDRIVER)
#include <sstream>
#endif // ENABLE_WEBDRIVER
#include "base/stringprintf.h"
#include "cobalt/base/tokens.h"
#include "cobalt/dom/event.h"
#if defined(ENABLE_WEBDRIVER)
#include "cobalt/dom/global_stats.h"
#endif // ENABLE_WEBDRIVER
namespace cobalt {
namespace browser {
WebModuleStatTracker::WebModuleStatTracker(const std::string& name,
bool should_track_event_stats)
: dom_stat_tracker_(new dom::DomStatTracker(name)),
layout_stat_tracker_(new layout::LayoutStatTracker(name)),
should_track_event_stats_(should_track_event_stats),
current_event_type_(kEventTypeInvalid),
name_(name),
event_is_processing_(StringPrintf("Event.%s.IsProcessing", name.c_str()),
false, "Nonzero when an event is being processed.") {
if (should_track_event_stats_) {
event_stats_.reserve(kNumEventTypes);
for (int i = 0; i < kNumEventTypes; ++i) {
EventType event_type = static_cast<EventType>(i);
event_stats_.push_back(new EventStats(StringPrintf(
"%s.%s", name.c_str(), GetEventTypeName(event_type).c_str())));
}
}
stop_watches_.reserve(kNumStopWatchTypes);
for (int i = 0; i < kNumStopWatchTypes; ++i) {
stop_watches_.push_back(
base::StopWatch(i, base::StopWatch::kAutoStartOff, this));
}
stop_watch_durations_.resize(kNumStopWatchTypes, base::TimeDelta());
}
WebModuleStatTracker::~WebModuleStatTracker() { EndCurrentEvent(false); }
void WebModuleStatTracker::OnStartInjectEvent(
const scoped_refptr<dom::Event>& event) {
if (!should_track_event_stats_) {
return;
}
// If an event is already being tracked, then don't track this event. It needs
// to be allowed to finish.
if (current_event_type_ != kEventTypeInvalid) {
return;
}
// Determine the event type.
if (event->type() == base::Tokens::keydown()) {
current_event_type_ = kEventTypeKeyDown;
} else if (event->type() == base::Tokens::keyup()) {
current_event_type_ = kEventTypeKeyUp;
} else {
current_event_type_ = kEventTypeInvalid;
}
// If this is a valid event type, then start tracking it.
if (current_event_type_ != kEventTypeInvalid) {
event_is_processing_ = true;
dom_stat_tracker_->OnStartEvent();
layout_stat_tracker_->OnStartEvent();
stop_watch_durations_[kStopWatchTypeEvent] = base::TimeDelta();
stop_watch_durations_[kStopWatchTypeInjectEvent] = base::TimeDelta();
stop_watches_[kStopWatchTypeEvent].Start();
stop_watches_[kStopWatchTypeInjectEvent].Start();
}
}
void WebModuleStatTracker::OnEndInjectEvent(
bool are_animation_frame_callbacks_pending,
bool is_new_render_tree_pending) {
// If the injection isn't currently being timed, then this event injection
// isn't being tracked. Simply return.
if (!stop_watches_[kStopWatchTypeInjectEvent].IsCounting()) {
return;
}
stop_watches_[kStopWatchTypeInjectEvent].Stop();
if (!are_animation_frame_callbacks_pending && !is_new_render_tree_pending) {
EndCurrentEvent(false);
}
}
void WebModuleStatTracker::OnRanAnimationFrameCallbacks(
bool is_new_render_tree_pending) {
if (!is_new_render_tree_pending) {
EndCurrentEvent(false);
}
}
void WebModuleStatTracker::OnRenderTreeProduced() { EndCurrentEvent(true); }
WebModuleStatTracker::EventStats::EventStats(const std::string& name)
: produced_render_tree_(
StringPrintf("Event.%s.ProducedRenderTree", name.c_str()), false,
"Nonzero when the event produced a render tree."),
count_dom_html_elements_created(
StringPrintf("Event.Count.%s.DOM.HtmlElement.Created", name.c_str()),
0, "Number of HTML elements created."),
count_dom_html_elements_destroyed(
StringPrintf("Event.Count.%s.DOM.HtmlElement.Destroyed",
name.c_str()),
0, "Number of HTML elements destroyed."),
count_dom_update_matching_rules(
StringPrintf("Event.Count.%s.DOM.HtmlElement.UpdateMatchingRules",
name.c_str()),
0, "Number of HTML elements that had their matching rules updated."),
count_dom_update_computed_style(
StringPrintf("Event.Count.%s.DOM.HtmlElement.UpdateComputedStyle",
name.c_str()),
0, "Number of HTML elements that had their computed style updated."),
count_dom_generate_html_element_computed_style(
StringPrintf(
"Event.Count.%s.DOM.HtmlElement.GenerateHtmlElementComputedStyle",
name.c_str()),
0,
"Number of HTML elements that had their computed style generated."),
count_dom_generate_pseudo_element_computed_style(
StringPrintf("Event.Count.%s.DOM.HtmlElement."
"GeneratePseudoElementComputedStyle",
name.c_str()),
0,
"Number of pseudo elements that had their computed style generated."),
count_layout_boxes_created(
StringPrintf("Event.Count.%s.Layout.Box.Created", name.c_str()), 0,
"Number of boxes created."),
count_layout_boxes_destroyed(
StringPrintf("Event.Count.%s.Layout.Box.Destroyed", name.c_str()), 0,
"Number of boxes destroyed."),
count_layout_update_size(
StringPrintf("Event.Count.%s.Layout.Box.UpdateSize", name.c_str()), 0,
"Number of boxes that had their size updated."),
count_layout_render_and_animate(
StringPrintf("Event.Count.%s.Layout.Box.RenderAndAnimate",
name.c_str()),
0, "Number of boxes that had their render tree node updated."),
count_layout_update_cross_references(
StringPrintf("Event.Count.%s.Layout.Box.UpdateCrossReferences",
name.c_str()),
0, "Number of boxes that had their cross references updated."),
duration_total(StringPrintf("Event.Duration.%s", name.c_str()),
base::TimeDelta(),
"Total duration of the event (in microseconds). This is "
"the time elapsed from the event injection until the "
"render tree is produced."),
duration_dom_inject_event(
StringPrintf("Event.Duration.%s.DOM.InjectEvent", name.c_str()),
base::TimeDelta(),
"Injection duration, which includes JS, for event (in "
"microseconds). This does not include subsequent DOM and Layout "
"processing."),
duration_dom_run_animation_frame_callbacks(
StringPrintf("Event.Duration.%s.DOM.RunAnimationFrameCallbacks",
name.c_str()),
base::TimeDelta(),
"Run animation frame callbacks duration for event (in "
"microseconds)."),
duration_dom_update_computed_style(
StringPrintf("Event.Duration.%s.DOM.UpdateComputedStyle",
name.c_str()),
base::TimeDelta(),
"UpdateComputedStyle duration for event (in microseconds)."),
duration_layout_box_tree(
StringPrintf("Event.Duration.%s.Layout.BoxTree", name.c_str()),
base::TimeDelta(),
"Layout box tree duration for event (in microseconds)."),
duration_layout_box_generation(
StringPrintf("Event.Duration.%s.Layout.BoxTree.BoxGeneration",
name.c_str()),
base::TimeDelta(),
"BoxGeneration duration for event (in microseconds)."),
duration_layout_update_used_sizes(
StringPrintf("Event.Duration.%s.Layout.BoxTree.UpdateUsedSizes",
name.c_str()),
base::TimeDelta(),
"UpdateUsedSizes duration for event (in microseconds)."),
duration_layout_render_and_animate(
StringPrintf("Event.Duration.%s.Layout.RenderAndAnimate",
name.c_str()),
base::TimeDelta(),
"RenderAndAnimate duration for event (in microseconds).")
#if defined(ENABLE_WEBDRIVER)
,
value_dictionary(
StringPrintf("Event.%s.ValueDictionary", name.c_str()),
"{}"
"All event values represented as a dictionary in a string.")
#endif // ENABLE_WEBDRIVER
{
}
bool WebModuleStatTracker::IsStopWatchEnabled(int /*id*/) const { return true; }
void WebModuleStatTracker::OnStopWatchStopped(int id,
base::TimeDelta time_elapsed) {
stop_watch_durations_[static_cast<size_t>(id)] += time_elapsed;
}
void WebModuleStatTracker::EndCurrentEvent(bool was_render_tree_produced) {
if (current_event_type_ == kEventTypeInvalid) {
return;
}
event_is_processing_ = false;
stop_watches_[kStopWatchTypeEvent].Stop();
EventStats* event_stats = event_stats_[current_event_type_];
event_stats->produced_render_tree_ = was_render_tree_produced;
// Update event counts
event_stats->count_dom_html_elements_created =
dom_stat_tracker_->html_elements_created_count();
event_stats->count_dom_html_elements_destroyed =
dom_stat_tracker_->html_elements_destroyed_count();
event_stats->count_dom_update_matching_rules =
dom_stat_tracker_->update_matching_rules_count();
event_stats->count_dom_update_computed_style =
dom_stat_tracker_->update_computed_style_count();
event_stats->count_dom_generate_html_element_computed_style =
dom_stat_tracker_->generate_html_element_computed_style_count();
event_stats->count_dom_generate_pseudo_element_computed_style =
dom_stat_tracker_->generate_pseudo_element_computed_style_count();
event_stats->count_layout_boxes_created =
layout_stat_tracker_->boxes_created_count();
event_stats->count_layout_boxes_destroyed =
layout_stat_tracker_->boxes_destroyed_count();
event_stats->count_layout_update_size =
layout_stat_tracker_->update_size_count();
event_stats->count_layout_render_and_animate =
layout_stat_tracker_->render_and_animate_count();
event_stats->count_layout_update_cross_references =
layout_stat_tracker_->update_cross_references_count();
// Update event durations
event_stats->duration_total = stop_watch_durations_[kStopWatchTypeEvent];
event_stats->duration_dom_inject_event =
stop_watch_durations_[kStopWatchTypeInjectEvent];
event_stats->duration_dom_run_animation_frame_callbacks =
dom_stat_tracker_->GetStopWatchTypeDuration(
dom::DomStatTracker::kStopWatchTypeRunAnimationFrameCallbacks);
event_stats->duration_dom_update_computed_style =
dom_stat_tracker_->GetStopWatchTypeDuration(
dom::DomStatTracker::kStopWatchTypeUpdateComputedStyle);
event_stats->duration_layout_box_tree =
layout_stat_tracker_->GetStopWatchTypeDuration(
layout::LayoutStatTracker::kStopWatchTypeLayoutBoxTree);
event_stats->duration_layout_box_generation =
layout_stat_tracker_->GetStopWatchTypeDuration(
layout::LayoutStatTracker::kStopWatchTypeBoxGeneration);
event_stats->duration_layout_update_used_sizes =
layout_stat_tracker_->GetStopWatchTypeDuration(
layout::LayoutStatTracker::kStopWatchTypeUpdateUsedSizes);
event_stats->duration_layout_render_and_animate =
layout_stat_tracker_->GetStopWatchTypeDuration(
layout::LayoutStatTracker::kStopWatchTypeRenderAndAnimate);
#if defined(ENABLE_WEBDRIVER)
// When the Webdriver is enabled, all of the event's values are stored within
// a single string representing a dictionary of key-value pairs. This allows
// the Webdriver to query a single CVal to retrieve all of the event's values.
std::ostringstream oss;
oss << "{"
<< "\"ProducedRenderTree\":" << was_render_tree_produced << ", "
<< "\"CntDomEventListeners\":"
<< dom::GlobalStats::GetInstance()->GetNumEventListeners() << ", "
<< "\"CntDomNodes\":" << dom::GlobalStats::GetInstance()->GetNumNodes()
<< ", "
<< "\"CntDomHtmlElements\":" << dom_stat_tracker_->total_html_elements()
<< ", "
<< "\"CntDomHtmlElementsCreated\":"
<< dom_stat_tracker_->html_elements_created_count() << ", "
<< "\"CntDomUpdateMatchingRules\":"
<< dom_stat_tracker_->update_matching_rules_count() << ", "
<< "\"CntDomUpdateComputedStyle\":"
<< dom_stat_tracker_->update_computed_style_count() << ", "
<< "\"CntDomGenerateHtmlComputedStyle\":"
<< dom_stat_tracker_->generate_html_element_computed_style_count() << ", "
<< "\"CntDomGeneratePseudoComputedStyle\":"
<< dom_stat_tracker_->generate_pseudo_element_computed_style_count()
<< ", "
<< "\"CntLayoutBoxes\":" << layout_stat_tracker_->total_boxes() << ", "
<< "\"CntLayoutBoxesCreated\":"
<< layout_stat_tracker_->boxes_created_count() << ", "
<< "\"CntLayoutUpdateSize\":" << layout_stat_tracker_->update_size_count()
<< ", "
<< "\"CntLayoutRenderAndAnimate\":"
<< layout_stat_tracker_->render_and_animate_count() << ", "
<< "\"CntLayoutUpdateCrossReferences\":"
<< layout_stat_tracker_->update_cross_references_count() << ", "
<< "\"DurTotalUs\":"
<< stop_watch_durations_[kStopWatchTypeEvent].InMicroseconds() << ", "
<< "\"DurDomInjectEventUs\":"
<< stop_watch_durations_[kStopWatchTypeInjectEvent].InMicroseconds()
<< ", "
<< "\"DurDomRunAnimationFrameCallbacksUs\":"
<< dom_stat_tracker_
->GetStopWatchTypeDuration(
dom::DomStatTracker::kStopWatchTypeRunAnimationFrameCallbacks)
.InMicroseconds()
<< ", "
<< "\"DurDomUpdateComputedStyleUs\":"
<< dom_stat_tracker_
->GetStopWatchTypeDuration(
dom::DomStatTracker::kStopWatchTypeUpdateComputedStyle)
.InMicroseconds()
<< ", "
<< "\"DurLayoutBoxTreeUs\":"
<< layout_stat_tracker_
->GetStopWatchTypeDuration(
layout::LayoutStatTracker::kStopWatchTypeLayoutBoxTree)
.InMicroseconds()
<< ", "
<< "\"DurLayoutBoxTreeBoxGenerationUs\":"
<< layout_stat_tracker_
->GetStopWatchTypeDuration(
layout::LayoutStatTracker::kStopWatchTypeBoxGeneration)
.InMicroseconds()
<< ", "
<< "\"DurLayoutBoxTreeUpdateUsedSizesUs\":"
<< layout_stat_tracker_
->GetStopWatchTypeDuration(
layout::LayoutStatTracker::kStopWatchTypeUpdateUsedSizes)
.InMicroseconds()
<< ", "
<< "\"DurLayoutRenderAndAnimateUs\":"
<< layout_stat_tracker_
->GetStopWatchTypeDuration(
layout::LayoutStatTracker::kStopWatchTypeRenderAndAnimate)
.InMicroseconds()
<< "}";
event_stats->value_dictionary = oss.str();
#endif // ENABLE_WEBDRIVER
current_event_type_ = kEventTypeInvalid;
dom_stat_tracker_->OnEndEvent();
layout_stat_tracker_->OnEndEvent();
}
std::string WebModuleStatTracker::GetEventTypeName(
WebModuleStatTracker::EventType event_type) {
switch (event_type) {
case WebModuleStatTracker::kEventTypeKeyDown:
return "KeyDown";
case WebModuleStatTracker::kEventTypeKeyUp:
return "KeyUp";
case WebModuleStatTracker::kEventTypeInvalid:
case WebModuleStatTracker::kNumEventTypes:
default:
NOTREACHED();
return "Invalid";
}
}
} // namespace browser
} // namespace cobalt