blob: 4db3773ea7b1974adc837e8017ccb0425a67fddb [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. 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)
: name_(name),
should_track_event_stats_(should_track_event_stats),
dom_stat_tracker_(new dom::DomStatTracker(name)),
layout_stat_tracker_(new layout::LayoutStatTracker(name)),
event_is_processing_(StringPrintf("Event.%s.IsProcessing", name.c_str()),
false, "Nonzero when an event is being processed."),
current_event_type_(kEventTypeInvalid),
current_event_dispatched_event_(nullptr) {
if (should_track_event_stats_) {
event_stats_list_.reserve(kNumEventTypes);
for (int i = 0; i < kNumEventTypes; ++i) {
EventType event_type = static_cast<EventType>(i);
event_stats_list_.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());
}
void WebModuleStatTracker::OnStartDispatchEvent(
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 if (event->type() == base::Tokens::pointerdown()) {
current_event_type_ = kEventTypePointerDown;
} else if (event->type() == base::Tokens::pointerup()) {
current_event_type_ = kEventTypePointerUp;
} else {
current_event_type_ = kEventTypeInvalid;
}
// If this is a valid event type, then start tracking it.
if (current_event_type_ != kEventTypeInvalid) {
DCHECK(!event_is_processing_);
event_is_processing_ = true;
current_event_dispatched_event_ = event;
current_event_start_time_ = base::TimeTicks::Now();
current_event_render_tree_produced_time_ = base::TimeTicks();
dom_stat_tracker_->StartTrackingEvent();
layout_stat_tracker_->StartTrackingEvent();
stop_watch_durations_[kStopWatchTypeDispatchEvent] = base::TimeDelta();
stop_watches_[kStopWatchTypeDispatchEvent].Start();
}
}
void WebModuleStatTracker::OnStopDispatchEvent(
const scoped_refptr<dom::Event>& event,
bool are_animation_frame_callbacks_pending,
bool is_new_render_tree_pending) {
// Verify that this dispatched event is the one currently being tracked.
if (event != current_event_dispatched_event_) {
return;
}
current_event_dispatched_event_ = nullptr;
stop_watches_[kStopWatchTypeDispatchEvent].Stop();
if (!are_animation_frame_callbacks_pending && !is_new_render_tree_pending &&
current_event_render_tree_produced_time_.is_null()) {
EndCurrentEvent(base::TimeTicks::Now());
}
}
void WebModuleStatTracker::OnRanAnimationFrameCallbacks(
bool is_new_render_tree_pending) {
if (current_event_type_ == kEventTypeInvalid) {
return;
}
if (!is_new_render_tree_pending &&
current_event_render_tree_produced_time_.is_null()) {
EndCurrentEvent(base::TimeTicks::Now());
}
}
void WebModuleStatTracker::OnRenderTreeProduced(
const base::TimeTicks& produced_time) {
// Flush the periodic tracking regardless of whether or not there is a current
// event. Periodic tracking is not tied to events.
dom_stat_tracker_->FlushPeriodicTracking();
layout_stat_tracker_->FlushPeriodicTracking();
if (current_event_type_ == kEventTypeInvalid) {
return;
}
// Event tracking stops when the first render tree being produced. At that
// point, processing switches to the rasterizer thread and any subsequent
// dom/layout work that occurs will not be associated with the event's first
// render tree.
if (current_event_render_tree_produced_time_.is_null()) {
current_event_render_tree_produced_time_ = produced_time;
dom_stat_tracker_->StopTrackingEvent();
layout_stat_tracker_->StopTrackingEvent();
}
}
void WebModuleStatTracker::OnRenderTreeRasterized(
const base::TimeTicks& produced_time,
const base::TimeTicks& rasterized_time) {
if (current_event_type_ == kEventTypeInvalid) {
return;
}
// End the event if the event's render tree has already been produced and
// the rasterized render tree is not older than the event's render tree.
if (!current_event_render_tree_produced_time_.is_null() &&
produced_time >= current_event_render_tree_produced_time_) {
EndCurrentEvent(rasterized_time);
}
}
WebModuleStatTracker::EventStats::EventStats(const std::string& name)
: start_time(StringPrintf("Event.Time.%s.Start", name.c_str()), 0,
"The time that the event started."),
produced_render_tree(
StringPrintf("Event.%s.ProducedRenderTree", name.c_str()), false,
"Nonzero when the event produced a render tree."),
count_dom_html_element(
StringPrintf("Event.Count.%s.DOM.HtmlElement", name.c_str()), 0,
"Total number of HTML elements."),
count_dom_html_element_created(
StringPrintf("Event.Count.%s.DOM.HtmlElement.Created", name.c_str()),
0, "Total number of HTML elements created."),
count_dom_html_element_destroyed(
StringPrintf("Event.Count.%s.DOM.HtmlElement.Destroyed",
name.c_str()),
0, "Total number of HTML elements destroyed."),
count_dom_html_element_document(
StringPrintf("Event.Count.%s.DOM.HtmlElement.Document", name.c_str()),
0, "Number of HTML elements in document."),
count_dom_html_element_document_added(
StringPrintf("Event.Count.%s.DOM.HtmlElement.Document.Added",
name.c_str()),
0, "Number of HTML elements added to document."),
count_dom_html_element_document_removed(
StringPrintf("Event.Count.%s.DOM.HtmlElement.Document.Removed",
name.c_str()),
0, "Number of HTML elements removed from document."),
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_box(StringPrintf("Event.Count.%s.Layout.Box", name.c_str()),
0, "Number of layout boxes."),
count_layout_box_created(
StringPrintf("Event.Count.%s.Layout.Box.Created", name.c_str()), 0,
"Number of layout boxes created."),
count_layout_box_destroyed(
StringPrintf("Event.Count.%s.Layout.Box.Destroyed", name.c_str()), 0,
"Number of layout boxes destroyed."),
count_layout_update_size(
StringPrintf("Event.Count.%s.Layout.Box.UpdateSize", name.c_str()), 0,
"Number of layout boxes that had their size updated."),
count_layout_render_and_animate(
StringPrintf("Event.Count.%s.Layout.Box.RenderAndAnimate",
name.c_str()),
0, "Number of layout 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 layout 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 dispatch until the "
"render tree is produced."),
duration_dom_dispatch_event(
StringPrintf("Event.Duration.%s.DOM.DispatchEvent", name.c_str()),
base::TimeDelta(),
"Dispatch 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)."),
duration_renderer_rasterize(
StringPrintf("Event.Duration.%s.Renderer.Rasterize", name.c_str()),
base::TimeDelta(), "Rasterize 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(base::TimeTicks event_end_time) {
if (current_event_type_ == kEventTypeInvalid) {
return;
}
DCHECK(event_is_processing_);
DCHECK(!current_event_start_time_.is_null());
// If no render tree was produced by this event, then tracking stops at the
// end of the event; otherwise, it already stopped when the render tree was
// produced.
if (current_event_render_tree_produced_time_.is_null()) {
dom_stat_tracker_->StopTrackingEvent();
layout_stat_tracker_->StopTrackingEvent();
}
// If a render tree was produced by this event, then the event is ending with
// the render tree's rasterization; otherwise, there was no rasterization.
base::TimeDelta renderer_rasterize_duration =
!current_event_render_tree_produced_time_.is_null()
? event_end_time - current_event_render_tree_produced_time_
: base::TimeDelta();
EventStats* event_stats = event_stats_list_[current_event_type_];
event_stats->start_time = current_event_start_time_.ToInternalValue();
event_stats->produced_render_tree =
!current_event_render_tree_produced_time_.is_null();
// Update event counts
event_stats->count_dom_html_element =
dom_stat_tracker_->EventCountHtmlElement();
event_stats->count_dom_html_element_created =
dom_stat_tracker_->event_count_html_element_created();
event_stats->count_dom_html_element_destroyed =
dom_stat_tracker_->event_count_html_element_destroyed();
event_stats->count_dom_html_element_document =
dom_stat_tracker_->EventCountHtmlElementDocument();
event_stats->count_dom_html_element_document_added =
dom_stat_tracker_->event_count_html_element_document_added();
event_stats->count_dom_html_element_document_removed =
dom_stat_tracker_->event_count_html_element_document_removed();
event_stats->count_dom_update_matching_rules =
dom_stat_tracker_->event_count_update_matching_rules();
event_stats->count_dom_update_computed_style =
dom_stat_tracker_->event_count_update_computed_style();
event_stats->count_dom_generate_html_element_computed_style =
dom_stat_tracker_->event_count_generate_html_element_computed_style();
event_stats->count_dom_generate_pseudo_element_computed_style =
dom_stat_tracker_->event_count_generate_pseudo_element_computed_style();
event_stats->count_layout_box = layout_stat_tracker_->EventCountBox();
event_stats->count_layout_box_created =
layout_stat_tracker_->event_count_box_created();
event_stats->count_layout_box_destroyed =
layout_stat_tracker_->event_count_box_destroyed();
event_stats->count_layout_update_size =
layout_stat_tracker_->event_count_update_size();
event_stats->count_layout_render_and_animate =
layout_stat_tracker_->event_count_render_and_animate();
event_stats->count_layout_update_cross_references =
layout_stat_tracker_->event_count_update_cross_references();
// Update event durations
event_stats->duration_total = event_end_time - current_event_start_time_;
event_stats->duration_dom_dispatch_event =
stop_watch_durations_[kStopWatchTypeDispatchEvent];
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);
event_stats->duration_renderer_rasterize = renderer_rasterize_duration;
#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 << "{"
<< "\"StartTime\":" << current_event_start_time_.ToInternalValue() << ", "
<< "\"ProducedRenderTree\":"
<< !current_event_render_tree_produced_time_.is_null() << ", "
<< "\"CntDomEventListeners\":"
<< dom::GlobalStats::GetInstance()->GetNumEventListeners() << ", "
<< "\"CntDomNodes\":" << dom::GlobalStats::GetInstance()->GetNumNodes()
<< ", "
<< "\"CntDomHtmlElements\":" << dom_stat_tracker_->EventCountHtmlElement()
<< ", "
<< "\"CntDomDocumentHtmlElements\":"
<< dom_stat_tracker_->EventCountHtmlElementDocument() << ", "
<< "\"CntDomHtmlElementsCreated\":"
<< dom_stat_tracker_->event_count_html_element_created() << ", "
<< "\"CntDomUpdateMatchingRules\":"
<< dom_stat_tracker_->event_count_update_matching_rules() << ", "
<< "\"CntDomUpdateComputedStyle\":"
<< dom_stat_tracker_->event_count_update_computed_style() << ", "
<< "\"CntDomGenerateHtmlComputedStyle\":"
<< dom_stat_tracker_->event_count_generate_html_element_computed_style()
<< ", "
<< "\"CntDomGeneratePseudoComputedStyle\":"
<< dom_stat_tracker_->event_count_generate_pseudo_element_computed_style()
<< ", "
<< "\"CntLayoutBoxes\":" << layout_stat_tracker_->EventCountBox() << ", "
<< "\"CntLayoutBoxesCreated\":"
<< layout_stat_tracker_->event_count_box_created() << ", "
<< "\"CntLayoutUpdateSize\":"
<< layout_stat_tracker_->event_count_update_size() << ", "
<< "\"CntLayoutRenderAndAnimate\":"
<< layout_stat_tracker_->event_count_render_and_animate() << ", "
<< "\"CntLayoutUpdateCrossReferences\":"
<< layout_stat_tracker_->event_count_update_cross_references() << ", "
<< "\"DurTotalUs\":"
<< (event_end_time - current_event_start_time_).InMicroseconds() << ", "
<< "\"DurDomInjectEventUs\":"
<< stop_watch_durations_[kStopWatchTypeDispatchEvent].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()
<< ", "
<< "\"DurRendererRasterizeUs\":"
<< renderer_rasterize_duration.InMicroseconds() << "}";
event_stats->value_dictionary = oss.str();
#endif // ENABLE_WEBDRIVER
event_is_processing_ = false;
current_event_type_ = kEventTypeInvalid;
}
std::string WebModuleStatTracker::GetEventTypeName(
WebModuleStatTracker::EventType event_type) {
switch (event_type) {
case WebModuleStatTracker::kEventTypeKeyDown:
return "KeyDown";
case WebModuleStatTracker::kEventTypeKeyUp:
return "KeyUp";
case WebModuleStatTracker::kEventTypePointerDown:
return "PointerDown";
case WebModuleStatTracker::kEventTypePointerUp:
return "PointerUp";
case WebModuleStatTracker::kEventTypeInvalid:
case WebModuleStatTracker::kNumEventTypes:
break;
}
NOTREACHED();
return "Invalid";
}
} // namespace browser
} // namespace cobalt