| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/dbus/arc_tracing_service_provider.h" |
| |
| #include "base/containers/span.h" |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/thread_pool.h" |
| #include "chrome/browser/ash/arc/tracing/arc_tracing_graphics_model.h" |
| #include "chrome/browser/ash/arc/tracing/overview_tracing_handler.h" |
| #include "dbus/bus.h" |
| #include "dbus/message.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| #include "ui/aura/window.h" |
| |
| namespace ash { |
| namespace { |
| constexpr int kMaxStatusMessagesCount = 20; |
| constexpr char kTraceStartedMsg[] = "Trace started"; |
| } // namespace |
| |
| ArcTracingServiceProvider::ArcTracingServiceProvider() = default; |
| |
| ArcTracingServiceProvider::~ArcTracingServiceProvider() = default; |
| |
| void ArcTracingServiceProvider::Start( |
| scoped_refptr<dbus::ExportedObject> exported_object) { |
| exported_object->ExportMethod( |
| arc::tracing::kArcTracingInterfaceName, |
| arc::tracing::kArcTracingStartMethod, |
| base::BindRepeating(&ArcTracingServiceProvider::StartTrace, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&ArcTracingServiceProvider::OnExported, |
| weak_ptr_factory_.GetWeakPtr())); |
| exported_object->ExportMethod( |
| arc::tracing::kArcTracingInterfaceName, |
| arc::tracing::kArcTracingGetStatusMethod, |
| base::BindRepeating(&ArcTracingServiceProvider::GetStatus, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&ArcTracingServiceProvider::OnExported, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ArcTracingServiceProvider::AddStatusMessage(std::string_view status) { |
| msgs_.emplace_back(status); |
| if (msgs_.size() > kMaxStatusMessagesCount) { |
| msgs_.pop_front(); |
| } |
| } |
| |
| void ArcTracingServiceProvider::OnExported(const std::string& interface_name, |
| const std::string& method_name, |
| bool success) { |
| LOG_IF(ERROR, !success) << "Failed to export " << interface_name << "." |
| << method_name; |
| } |
| |
| void ArcTracingServiceProvider::OnTraceEnd( |
| std::unique_ptr<arc::OverviewTracingResult> result) { |
| if (result->path.empty()) { |
| AddStatusMessage(result->status); |
| } else if (auto* information = |
| result->model.GetDict().FindDict(arc::kKeyInformation); |
| information) { |
| auto pfps = information->FindDouble(arc::kKeyPerceivedFps); |
| auto duration = information->FindDouble(arc::kKeyDuration); |
| AddStatusMessage(base::StringPrintf( |
| "%s: %s - perceived FPS=%.2f, duration=%.2fs", result->status.c_str(), |
| result->path.value().c_str(), pfps.value_or(0), |
| duration.value_or(0) / 1'000'000.0)); |
| } |
| // Do this in a separate task because the handler may still have code to run |
| // after we return. |
| base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon( |
| FROM_HERE, std::move(handler_)); |
| } |
| |
| std::unique_ptr<arc::OverviewTracingHandler> |
| ArcTracingServiceProvider::NewHandler() { |
| return std::make_unique<arc::OverviewTracingHandler>( |
| arc::OverviewTracingHandler::ArcWindowFocusChangeCb()); |
| } |
| |
| void ArcTracingServiceProvider::StartTrace( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender) { |
| if (handler_) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED, |
| "Trace already in progress")); |
| return; |
| } |
| |
| dbus::MessageReader reader(method_call); |
| |
| double max_trace_seconds; |
| if (!reader.PopDouble(&max_trace_seconds)) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, |
| "Expect max trace time as type double in seconds")); |
| return; |
| } |
| auto handler = NewHandler(); |
| |
| auto max_trace_time = base::Seconds(max_trace_seconds); |
| if (max_trace_time < base::Seconds(1)) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, |
| "Max trace seconds out of range; must be >= 1")); |
| return; |
| } |
| |
| if (!handler->arc_window_is_active()) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED, |
| "ARC window isn't active")); |
| return; |
| } |
| |
| auto extra_windows = handler->NonTraceTargetWindows(); |
| if (!extra_windows.empty()) { |
| std::vector<std::string> extra_win_msg = { |
| "Extra windows are open. Close them and try the trace again: "}; |
| std::string_view delim = ""; |
| for (auto window : extra_windows) { |
| extra_win_msg.emplace_back(delim); |
| extra_win_msg.emplace_back("|"); |
| extra_win_msg.emplace_back(base::UTF16ToUTF8(window->GetTitle())); |
| extra_win_msg.emplace_back("|"); |
| delim = ", "; |
| } |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED, |
| base::StrCat(extra_win_msg))); |
| return; |
| } |
| |
| handler_ = std::move(handler); |
| handler_->set_graphics_model_ready_cb(base::BindRepeating( |
| &ArcTracingServiceProvider::OnTraceEnd, weak_ptr_factory_.GetWeakPtr())); |
| handler_->set_start_build_model_cb( |
| base::BindRepeating(&ArcTracingServiceProvider::AddStatusMessage, |
| weak_ptr_factory_.GetWeakPtr(), "Building model...")); |
| handler_->StartTracing(trace_outdir_, max_trace_time); |
| |
| auto response = dbus::Response::FromMethodCall(method_call); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendString(kTraceStartedMsg); |
| AddStatusMessage(kTraceStartedMsg); |
| std::move(response_sender).Run(std::move(response)); |
| } |
| |
| void ArcTracingServiceProvider::GetStatus( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender) { |
| auto response = dbus::Response::FromMethodCall(method_call); |
| dbus::MessageWriter writer(response.get()); |
| for (const auto& msg : msgs_) { |
| writer.AppendString(msg); |
| } |
| std::move(response_sender).Run(std::move(response)); |
| } |
| |
| } // namespace ash |