From b01ba223b2077fa647d66a5990d363377938893c Mon Sep 17 00:00:00 2001 From: thorsten sideb0ard Date: Tue, 14 Nov 2023 13:53:40 -0800 Subject: [PATCH] DevTools Performance Timeline revisited (#1972) The DevTools Performance Timeline is an implementation of a Trace viewer. On Chrome, the TracingController has several TracingAgents, each of which outputs events in the Trace format. These agents are mainly the Blink Trace agent, and a v8 js sampling profiler, plus on Android, a java trace agent. (https://github.com/youtube/cobalt/blob/f0d959a6920a8c481811c15ef4ebdbe8c6d0cc66/base/trace_event/tracing_agent.h#L21). This CL should make it easier to add an Android tracing agent in the future. Previously, the Cobalt implementation did not have a TracingController, and had a single agent which was plugged directly into the v8 profiler, so we were blind to the rest of the callstack. This CL introduces a TracingController with two agents, a TraceEventsAgent, analogous to the Chrome Blink agent, which captures the bulk of the data, and also a V8TracingAgent which is still plugged into V8. I think the V8 agent still needs more work so even though its implemented here, its currently disabled. I also think we can lean into CDP (chrome devtools protocol) to get rid of our custom CVal implmentation, which should get our Performance testing closer to Chrome. b/198688218 b/123369438 --- cobalt/debug/BUILD.gn | 4 +- cobalt/debug/backend/debug_module.cc | 11 +- cobalt/debug/backend/debug_module.h | 4 +- cobalt/debug/backend/tracing_agent.cc | 164 --------- cobalt/debug/backend/tracing_agent.h | 72 ---- cobalt/debug/backend/tracing_controller.cc | 310 ++++++++++++++++++ cobalt/debug/backend/tracing_controller.h | 153 +++++++++ .../devtools/front_end/sdk/TracingModel.js | 2 +- .../front_end/timeline_model/TimelineModel.js | 9 +- 9 files changed, 482 insertions(+), 247 deletions(-) delete mode 100644 cobalt/debug/backend/tracing_agent.cc delete mode 100644 cobalt/debug/backend/tracing_agent.h create mode 100644 cobalt/debug/backend/tracing_controller.cc create mode 100644 cobalt/debug/backend/tracing_controller.h diff --git a/cobalt/debug/BUILD.gn b/cobalt/debug/BUILD.gn index bfee5ef73de1..eb383aa536b1 100644 --- a/cobalt/debug/BUILD.gn +++ b/cobalt/debug/BUILD.gn @@ -46,8 +46,8 @@ static_library("debug") { "backend/render_overlay.h", "backend/script_debugger_agent.cc", "backend/script_debugger_agent.h", - "backend/tracing_agent.cc", - "backend/tracing_agent.h", + "backend/tracing_controller.cc", + "backend/tracing_controller.h", "command.h", "console/debug_hub.cc", "console/debug_hub.h", diff --git a/cobalt/debug/backend/debug_module.cc b/cobalt/debug/backend/debug_module.cc index ea767affeba6..b1b09c2e6152 100644 --- a/cobalt/debug/backend/debug_module.cc +++ b/cobalt/debug/backend/debug_module.cc @@ -30,7 +30,7 @@ constexpr char kDomAgent[] = "DomAgent"; constexpr char kCssAgent[] = "CssAgent"; constexpr char kOverlayAgent[] = "OverlayAgent"; constexpr char kPageAgent[] = "PageAgent"; -constexpr char kTracingAgent[] = "TracingAgent"; +constexpr char kTracingController[] = "TracingController"; // Move the state for a particular agent out of the dictionary holding the // state for all agents. Returns a NULL JSONObject if either |agents_state| is @@ -161,8 +161,8 @@ void DebugModule::BuildInternal(const ConstructionData& data) { std::move(page_render_layer), data.resource_provider)); } - tracing_agent_.reset( - new TracingAgent(debug_dispatcher_.get(), script_debugger_.get())); + tracing_controller_.reset( + new TracingController(debug_dispatcher_.get(), script_debugger_.get())); // Hook up hybrid agent JavaScript to the DebugBackend. debug_backend_->BindAgents(css_agent_); @@ -190,7 +190,7 @@ void DebugModule::BuildInternal(const ConstructionData& data) { overlay_agent_->Thaw(RemoveAgentState(kOverlayAgent, agents_state)); if (page_agent_) page_agent_->Thaw(RemoveAgentState(kPageAgent, agents_state)); - tracing_agent_->Thaw(RemoveAgentState(kTracingAgent, agents_state)); + tracing_controller_->Thaw(RemoveAgentState(kTracingController, agents_state)); is_frozen_ = false; } @@ -213,7 +213,8 @@ std::unique_ptr DebugModule::Freeze() { StoreAgentState(agents_state, kOverlayAgent, overlay_agent_->Freeze()); if (page_agent_) StoreAgentState(agents_state, kPageAgent, page_agent_->Freeze()); - StoreAgentState(agents_state, kTracingAgent, tracing_agent_->Freeze()); + StoreAgentState(agents_state, kTracingController, + tracing_controller_->Freeze()); // Take the clients from the dispatcher last so they still get events that the // agents might send as part of being frozen. diff --git a/cobalt/debug/backend/debug_module.h b/cobalt/debug/backend/debug_module.h index 4736968062b5..9185a3c3a15e 100644 --- a/cobalt/debug/backend/debug_module.h +++ b/cobalt/debug/backend/debug_module.h @@ -33,7 +33,7 @@ #include "cobalt/debug/backend/page_agent.h" #include "cobalt/debug/backend/render_overlay.h" #include "cobalt/debug/backend/script_debugger_agent.h" -#include "cobalt/debug/backend/tracing_agent.h" +#include "cobalt/debug/backend/tracing_controller.h" #include "cobalt/debug/json_object.h" #include "cobalt/dom/window.h" #include "cobalt/render_tree/resource_provider.h" @@ -142,7 +142,7 @@ class DebugModule : public script::ScriptDebugger::Delegate { std::unique_ptr overlay_agent_; std::unique_ptr page_agent_; std::unique_ptr script_debugger_agent_; - std::unique_ptr tracing_agent_; + std::unique_ptr tracing_controller_; }; } // namespace backend diff --git a/cobalt/debug/backend/tracing_agent.cc b/cobalt/debug/backend/tracing_agent.cc deleted file mode 100644 index 3c26d29fbfd2..000000000000 --- a/cobalt/debug/backend/tracing_agent.cc +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2019 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/debug/backend/tracing_agent.h" - -#include - -#include "base/bind.h" -#include "base/values.h" -#include "cobalt/script/script_debugger.h" - -namespace cobalt { -namespace debug { -namespace backend { - -namespace { -// Definitions from the set specified here: -// https://chromedevtools.github.io/devtools-protocol/tot/Tracing -constexpr char kInspectorDomain[] = "Tracing"; - -// State parameters -constexpr char kStarted[] = "started"; -constexpr char kCategories[] = "categories"; - -// Size in characters of JSON to batch dataCollected events. -constexpr size_t kDataCollectedSize = 24 * 1024; -} // namespace - -TracingAgent::TracingAgent(DebugDispatcher* dispatcher, - script::ScriptDebugger* script_debugger) - : dispatcher_(dispatcher), - script_debugger_(script_debugger), - tracing_started_(false), - collected_size_(0), - commands_(kInspectorDomain) { - DCHECK(dispatcher_); - - commands_["end"] = base::Bind(&TracingAgent::End, base::Unretained(this)); - commands_["start"] = base::Bind(&TracingAgent::Start, base::Unretained(this)); -} - -void TracingAgent::Thaw(JSONObject agent_state) { - dispatcher_->AddDomain(kInspectorDomain, commands_.Bind()); - if (!agent_state) return; - - // Restore state - categories_.clear(); - for (const auto& category : agent_state->FindKey(kCategories)->GetList()) { - categories_.emplace_back(category.GetString()); - } - tracing_started_ = agent_state->FindKey(kStarted)->GetBool(); - if (tracing_started_) { - script_debugger_->StartTracing(categories_, this); - } -} - -JSONObject TracingAgent::Freeze() { - if (tracing_started_) { - script_debugger_->StopTracing(); - } - - dispatcher_->RemoveDomain(kInspectorDomain); - - // Save state - JSONObject agent_state(new base::DictionaryValue()); - agent_state->SetKey(kStarted, base::Value(tracing_started_)); - base::Value::ListStorage categories_list; - for (const auto& category : categories_) { - categories_list.emplace_back(category); - } - agent_state->SetKey(kCategories, base::Value(std::move(categories_list))); - - return agent_state; -} - -void TracingAgent::End(Command command) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (!tracing_started_) { - command.SendErrorResponse(Command::kInvalidRequest, "Tracing not started"); - return; - } - tracing_started_ = false; - categories_.clear(); - command.SendResponse(); - - script_debugger_->StopTracing(); -} - -void TracingAgent::Start(Command command) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (tracing_started_) { - command.SendErrorResponse(Command::kInvalidRequest, - "Tracing already started"); - return; - } - - JSONObject params = JSONParse(command.GetParams()); - - // Parse comma-separated tracing categories parameter. - categories_.clear(); - std::string category_param; - if (params->GetString("categories", &category_param)) { - for (size_t pos = 0, comma; pos < category_param.size(); pos = comma + 1) { - comma = category_param.find(',', pos); - if (comma == std::string::npos) comma = category_param.size(); - std::string category = category_param.substr(pos, comma - pos); - categories_.push_back(category); - } - } - - tracing_started_ = true; - script_debugger_->StartTracing(categories_, this); - - command.SendResponse(); -} - -void TracingAgent::AppendTraceEvent(const std::string& trace_event_json) { - // We initialize a new list into which we collect events both when we start, - // and after each time it's released in |SendDataCollectedEvent|. - if (!collected_events_) { - collected_events_.reset(new base::ListValue()); - } - - JSONObject event = JSONParse(trace_event_json); - if (event) { - collected_events_->Append(std::move(event)); - collected_size_ += trace_event_json.size(); - } - - if (collected_size_ >= kDataCollectedSize) { - SendDataCollectedEvent(); - } -} - -void TracingAgent::FlushTraceEvents() { - SendDataCollectedEvent(); - dispatcher_->SendEvent(std::string(kInspectorDomain) + ".tracingComplete"); -} - -void TracingAgent::SendDataCollectedEvent() { - if (collected_events_) { - collected_size_ = 0; - JSONObject params(new base::DictionaryValue()); - // Releasing the list into the value param avoids copying it. - params->Set("value", std::move(collected_events_)); - dispatcher_->SendEvent(std::string(kInspectorDomain) + ".dataCollected", - params); - } -} - -} // namespace backend -} // namespace debug -} // namespace cobalt diff --git a/cobalt/debug/backend/tracing_agent.h b/cobalt/debug/backend/tracing_agent.h deleted file mode 100644 index f2101fbf1635..000000000000 --- a/cobalt/debug/backend/tracing_agent.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019 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. - -#ifndef COBALT_DEBUG_BACKEND_TRACING_AGENT_H_ -#define COBALT_DEBUG_BACKEND_TRACING_AGENT_H_ - -#include -#include - -#include "base/threading/thread_checker.h" -#include "base/time/time.h" -#include "cobalt/debug/backend/command_map.h" -#include "cobalt/debug/backend/debug_dispatcher.h" -#include "cobalt/debug/command.h" -#include "cobalt/script/script_debugger.h" - -namespace cobalt { -namespace debug { -namespace backend { - -// There aren't enable/disable commands in the Tracing domain, so the -// TracingAgent doesn't use AgentBase. -// -// https://chromedevtools.github.io/devtools-protocol/tot/Tracing -class TracingAgent : public script::ScriptDebugger::TraceDelegate { - public: - explicit TracingAgent(DebugDispatcher* dispatcher, - script::ScriptDebugger* script_debugger); - - void Thaw(JSONObject agent_state); - JSONObject Freeze(); - - // TraceDelegate - void AppendTraceEvent(const std::string& trace_event_json) override; - void FlushTraceEvents() override; - - private: - void End(Command command); - void Start(Command command); - - void SendDataCollectedEvent(); - - DebugDispatcher* dispatcher_; - script::ScriptDebugger* script_debugger_; - - THREAD_CHECKER(thread_checker_); - - bool tracing_started_; - std::vector categories_; - size_t collected_size_; - JSONList collected_events_; - - // Map of member functions implementing commands. - CommandMap commands_; -}; - -} // namespace backend -} // namespace debug -} // namespace cobalt - -#endif // COBALT_DEBUG_BACKEND_TRACING_AGENT_H_ diff --git a/cobalt/debug/backend/tracing_controller.cc b/cobalt/debug/backend/tracing_controller.cc new file mode 100644 index 000000000000..0a469dc4c39f --- /dev/null +++ b/cobalt/debug/backend/tracing_controller.cc @@ -0,0 +1,310 @@ +// Copyright 2023 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/debug/backend/tracing_controller.h" + +#include +#include +#include + +#include "base/bind.h" +#include "base/threading/thread.h" +#include "base/trace_event/trace_event.h" +#include "base/values.h" +#include "cobalt/script/script_debugger.h" +#include "starboard/common/string.h" + +namespace cobalt { +namespace debug { +namespace backend { + +namespace { +// Definitions from the set specified here: +// https://chromedevtools.github.io/devtools-protocol/tot/Tracing +constexpr char kInspectorDomain[] = "Tracing"; + +// State parameters +constexpr char kStarted[] = "started"; +constexpr char kCategories[] = "categories"; + +} // namespace + + +///////////////// TRACE EVENT AGENT /////////////////////////////////// + +void TraceEventAgent::StartAgentTracing( + const TraceConfig& trace_config, + StartAgentTracingCallback on_start_callback) { + json_output_.json_output.clear(); + trace_buffer_.SetOutputCallback(json_output_.GetCallback()); + + base::trace_event::TraceLog* tracelog = + base::trace_event::TraceLog::GetInstance(); + DCHECK(!tracelog->IsEnabled()); + + tracelog->SetEnabled(trace_config, + base::trace_event::TraceLog::RECORDING_MODE); + + std::move(on_start_callback).Run(agent_name_, true); +} + +void TraceEventAgent::StopAgentTracing( + StopAgentTracingCallback on_stop_callback) { + base::trace_event::TraceLog* trace_log = + base::trace_event::TraceLog::GetInstance(); + + DCHECK(trace_log->IsEnabled()); + trace_log->SetDisabled(base::trace_event::TraceLog::RECORDING_MODE); + + base::Thread thread("json_outputter"); + thread.Start(); + + trace_buffer_.Start(); + base::WaitableEvent waitable_event; + auto collect_data_callback = + base::Bind(&TraceEventAgent::CollectTraceData, base::Unretained(this), + base::BindRepeating(&base::WaitableEvent::Signal, + base::Unretained(&waitable_event))); + // Write out the actual data by calling Flush(). Within Flush(), this + // will call OutputTraceData(), possibly multiple times. We have to do this + // on a thread as there will be tasks posted to the current thread for data + // writing. + thread.message_loop()->task_runner()->PostTask( + FROM_HERE, base::BindRepeating(&base::trace_event::TraceLog::Flush, + base::Unretained(trace_log), + collect_data_callback, false)); + waitable_event.Wait(); + trace_buffer_.Finish(); + + std::move(on_stop_callback) + .Run(agent_name_, agent_event_label_, + base::RefCountedString::TakeString(&json_output_.json_output)); +} + + +void TraceEventAgent::CollectTraceData( + base::OnceClosure finished_cb, + const scoped_refptr& event_string, + bool has_more_events) { + trace_buffer_.AddFragment(event_string->data()); + if (!has_more_events) { + std::move(finished_cb).Run(); + } +} + +///////////////// TRACE V8 AGENT /////////////////////////////////// +TraceV8Agent::TraceV8Agent(script::ScriptDebugger* script_debugger) + : script_debugger_(script_debugger) {} + +void TraceV8Agent::AppendTraceEvent(const std::string& trace_event_json) { + trace_buffer_.AddFragment(trace_event_json); +} + +void TraceV8Agent::FlushTraceEvents() { + trace_buffer_.Finish(); + std::move(on_stop_callback_) + .Run(agent_name_, agent_event_label_, + base::RefCountedString::TakeString(&json_output_.json_output)); +} + +void TraceV8Agent::StartAgentTracing(const TraceConfig& trace_config, + StartAgentTracingCallback callback) { + json_output_.json_output.clear(); + trace_buffer_.SetOutputCallback(json_output_.GetCallback()); + trace_buffer_.Start(); + + script_debugger_->StartTracing(std::vector(), this); + std::move(callback).Run(agent_name_, true); +} + +void TraceV8Agent::StopAgentTracing(StopAgentTracingCallback callback) { + on_stop_callback_ = std::move(callback); + script_debugger_->StopTracing(); +} + +///////////////// TRACING CONTROLLER ////////////////////////////////// +TracingController::TracingController(DebugDispatcher* dispatcher, + script::ScriptDebugger* script_debugger) + : dispatcher_(dispatcher), + tracing_started_(false), + collected_size_(0), + commands_(kInspectorDomain) { + DCHECK(dispatcher_); + + commands_["end"] = + base::Bind(&TracingController::End, base::Unretained(this)); + commands_["start"] = + base::Bind(&TracingController::Start, base::Unretained(this)); + + agents_.push_back(std::make_unique()); + // agents_.push_back(std::make_unique(script_debugger)); +} + +void TracingController::Thaw(JSONObject agent_state) { + dispatcher_->AddDomain(kInspectorDomain, commands_.Bind()); + if (!agent_state) return; + + // Restore state + categories_.clear(); + for (const auto& category : agent_state->FindKey(kCategories)->GetList()) { + categories_.emplace_back(category.GetString()); + } + tracing_started_ = agent_state->FindKey(kStarted)->GetBool(); + if (tracing_started_) { + agents_responded_ = 0; + auto config = base::trace_event::TraceConfig(); + for (const auto& a : agents_) { + a->StartAgentTracing(config, + base::BindOnce(&TracingController::OnStartTracing, + base::Unretained(this))); + } + } +} + +JSONObject TracingController::Freeze() { + if (tracing_started_) { + for (const auto& a : agents_) { + a->StopAgentTracing(base::BindOnce(&TracingController::OnCancelTracing, + base::Unretained(this))); + } + } + + dispatcher_->RemoveDomain(kInspectorDomain); + + // Save state + JSONObject agent_state(new base::DictionaryValue()); + agent_state->SetKey(kStarted, base::Value(tracing_started_)); + base::Value::ListStorage categories_list; + for (const auto& category : categories_) { + categories_list.emplace_back(category); + } + agent_state->SetKey(kCategories, base::Value(std::move(categories_list))); + + return agent_state; +} + +void TracingController::End(Command command) { + if (!tracing_started_) { + command.SendErrorResponse(Command::kInvalidRequest, "Tracing not started"); + return; + } + tracing_started_ = false; + categories_.clear(); + command.SendResponse(); + + for (const auto& a : agents_) { + a->StopAgentTracing(base::BindOnce(&TracingController::OnStopTracing, + base::Unretained(this))); + } +} + +void TracingController::Start(Command command) { + if (tracing_started_) { + command.SendErrorResponse(Command::kInvalidRequest, + "Tracing already started"); + return; + } + JSONObject params = JSONParse(command.GetParams()); + // Parse comma-separated tracing categories parameter. + categories_.clear(); + std::string category_param; + if (params->GetString("categories", &category_param)) { + for (size_t pos = 0, comma; pos < category_param.size(); pos = comma + 1) { + comma = category_param.find(',', pos); + if (comma == std::string::npos) comma = category_param.size(); + std::string category = category_param.substr(pos, comma - pos); + categories_.push_back(category); + } + } + + collected_events_.reset(new base::ListValue()); + + agents_responded_ = 0; + auto config = base::trace_event::TraceConfig(); + for (const auto& a : agents_) { + a->StartAgentTracing(config, + base::BindOnce(&TracingController::OnStartTracing, + base::Unretained(this))); + } + tracing_started_ = true; + command.SendResponse(); +} + +void TracingController::OnStartTracing(const std::string& agent_name, + bool success) { + LOG(INFO) << "Tracing Agent:" << agent_name << " Start tracing successful? " + << (success ? "true" : "false"); +} + +void TracingController::OnStopTracing( + const std::string& agent_name, const std::string& events_label, + const scoped_refptr& events_str_ptr) { + LOG(INFO) << "Tracing Agent:" << agent_name << " Stop tracing."; + + std::unique_ptr root = + base::JSONReader::Read(events_str_ptr->data(), base::JSON_PARSE_RFC); + + if (!root.get()) { + LOG(ERROR) << "Couldn't parse the events string."; + } + + base::ListValue* root_list = nullptr; + root->GetAsList(&root_list); + + // Move items into our aggregate collection + while (root_list->GetSize()) { + if (!collected_events_) { + collected_events_.reset(new base::ListValue()); + } + std::unique_ptr item; + root_list->Remove(0, &item); + collected_events_->Append(std::move(item)); + + if (collected_events_->GetSize() >= 100) { + SendDataCollectedEvent(); + } + } + FlushTraceEvents(); +} + +void TracingController::OnCancelTracing( + const std::string& agent_name, const std::string& events_label, + const scoped_refptr& events_str_ptr) { + LOG(INFO) << "Tracing Agent:" << agent_name << " Cancel tracing."; +} + +void TracingController::SendDataCollectedEvent() { + if (collected_events_) { + std::string events; + collected_events_->GetString(0, &events); + JSONObject params(new base::DictionaryValue()); + // Releasing the list into the value param avoids copying it. + params->Set("value", std::move(collected_events_)); + dispatcher_->SendEvent(std::string(kInspectorDomain) + ".dataCollected", + params); + collected_size_ = 0; + } +} + +void TracingController::FlushTraceEvents() { + SendDataCollectedEvent(); + agents_responded_++; + if (agents_responded_ == static_cast(agents_.size())) { + dispatcher_->SendEvent(std::string(kInspectorDomain) + ".tracingComplete"); + } +} + +} // namespace backend +} // namespace debug +} // namespace cobalt diff --git a/cobalt/debug/backend/tracing_controller.h b/cobalt/debug/backend/tracing_controller.h new file mode 100644 index 000000000000..63b6735f0978 --- /dev/null +++ b/cobalt/debug/backend/tracing_controller.h @@ -0,0 +1,153 @@ +// Copyright 2023 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. + +#ifndef COBALT_DEBUG_BACKEND_TRACING_CONTROLLER_H_ +#define COBALT_DEBUG_BACKEND_TRACING_CONTROLLER_H_ + +#include +#include +#include + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_memory.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "base/trace_event/trace_buffer.h" +#include "base/trace_event/tracing_agent.h" +#include "cobalt/debug/backend/command_map.h" +#include "cobalt/debug/backend/debug_dispatcher.h" +#include "cobalt/debug/command.h" +#include "cobalt/script/script_debugger.h" + +namespace cobalt { +namespace debug { +namespace backend { + + +using base::trace_event::TraceConfig; +using base::trace_event::TracingAgent; + +////////////////////////////////////////////////////////////////////////////// + +class TraceEventAgent : public TracingAgent { + public: + TraceEventAgent() = default; + ~TraceEventAgent() = default; + + // TracingAgent Interface + std::string GetTracingAgentName() override { return agent_name_; } + std::string GetTraceEventLabel() override { return agent_event_label_; } + void StartAgentTracing(const TraceConfig& trace_config, + StartAgentTracingCallback callback) override; + void StopAgentTracing(StopAgentTracingCallback callback) override; + + void CancelTracing(); + + private: + void CollectTraceData( + base::OnceClosure finished_cb, + const scoped_refptr& event_string, + bool has_more_events); + + private: + std::string agent_name_{"TraceEventAgent"}; + std::string agent_event_label_{"Performance Tracing"}; + + base::trace_event::TraceResultBuffer trace_buffer_; + base::trace_event::TraceResultBuffer::SimpleOutput json_output_; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class TraceV8Agent : public TracingAgent, + public script::ScriptDebugger::TraceDelegate { + public: + explicit TraceV8Agent(script::ScriptDebugger* script_debugger); + ~TraceV8Agent() = default; + + // TraceDelegate Interface + void AppendTraceEvent(const std::string& trace_event_json) override; + void FlushTraceEvents() override; + + // TracingAgent Interface + std::string GetTracingAgentName() override { return agent_name_; } + std::string GetTraceEventLabel() override { return agent_event_label_; } + void StartAgentTracing(const TraceConfig& trace_config, + StartAgentTracingCallback callback) override; + void StopAgentTracing(StopAgentTracingCallback callback) override; + + private: + std::string agent_name_{"TraceV8Agent"}; + std::string agent_event_label_{"Performance Tracing"}; + + StopAgentTracingCallback on_stop_callback_; + + THREAD_CHECKER(thread_checker_); + script::ScriptDebugger* script_debugger_; + + // size_t collected_size_; + // JSONList collected_events_; + base::trace_event::TraceResultBuffer trace_buffer_; + base::trace_event::TraceResultBuffer::SimpleOutput json_output_; +}; + +////////////////////////////////////////////////////////////////////////////// +// https://chromedevtools.github.io/devtools-protocol/tot/Tracing +class TracingController { + public: + explicit TracingController(DebugDispatcher* dispatcher, + script::ScriptDebugger* script_debugger); + ~TracingController() = default; + + void Thaw(JSONObject agent_state); + JSONObject Freeze(); + + private: + void End(Command command); + void Start(Command command); + + void OnStartTracing(const std::string& agent_name, bool success); + void OnStopTracing( + const std::string& agent_name, const std::string& events_label, + const scoped_refptr& events_str_ptr); + void OnCancelTracing( + const std::string& agent_name, const std::string& events_label, + const scoped_refptr& events_str_ptr); + + + private: + void SendDataCollectedEvent(); + void FlushTraceEvents(); + + private: + DebugDispatcher* dispatcher_; + std::vector> agents_; + std::atomic_int agents_responded_{0}; + + bool tracing_started_; + std::vector categories_; + + size_t collected_size_; + JSONList collected_events_; + + // Map of member functions implementing commands. + CommandMap commands_; +}; + +} // namespace backend +} // namespace debug +} // namespace cobalt + +#endif // COBALT_DEBUG_BACKEND_TRACING_CONTROLLER_H_ diff --git a/third_party/devtools/front_end/sdk/TracingModel.js b/third_party/devtools/front_end/sdk/TracingModel.js index ee8632fac91b..f69b051ab1fe 100644 --- a/third_party/devtools/front_end/sdk/TracingModel.js +++ b/third_party/devtools/front_end/sdk/TracingModel.js @@ -105,7 +105,7 @@ export default class TracingModel { if (!processes.length) { return null; } - const browserMainThreadName = 'CrBrowserMain'; + const browserMainThreadName = 'cobalt'; const browserProcesses = []; const browserMainThreads = []; for (const process of processes) { diff --git a/third_party/devtools/front_end/timeline_model/TimelineModel.js b/third_party/devtools/front_end/timeline_model/TimelineModel.js index 68e9bf68736a..e1f3d0595072 100644 --- a/third_party/devtools/front_end/timeline_model/TimelineModel.js +++ b/third_party/devtools/front_end/timeline_model/TimelineModel.js @@ -815,8 +815,15 @@ export class TimelineModelImpl { } case recordTypes.Layout: { + let frameId = -1; + if (event.args && event.args['beginData'] && event.args['beginData']['frame']) { + frameId = event.args['beginData']['frame']; + } + if (frameId === -1) { + break; + } + this._invalidationTracker.didLayout(event); - const frameId = event.args['beginData']['frame']; timelineData.setInitiator(this._layoutInvalidate[frameId]); // In case we have no closing Layout event, endData is not available. if (event.args['endData']) {