blob: 2ce543af92da14571172cd2d41d6ffe7ea901398 [file] [log] [blame]
// Copyright 2021 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/arc/tracing/overview_tracing_handler.h"
#include "ash/constants/ash_switches.h"
#include "base/strings/string_util.h"
#include "base/test/test_file_util.h"
#include "chrome/browser/ash/app_list/arc/arc_app_test.h"
#include "chrome/browser/ash/arc/tracing/arc_tracing_graphics_model.h"
#include "chrome/browser/ash/arc/tracing/test/overview_tracing_test_base.h"
#include "chrome/browser/ash/arc/tracing/test/overview_tracing_test_handler.h"
#include "chrome/test/base/chrome_ash_test_base.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/experiences/arc/arc_prefs.h"
#include "chromeos/ash/experiences/arc/test/arc_task_window_builder.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
namespace arc {
using EventType = arc::ArcTracingGraphicsModel::EventType;
namespace {
class OverviewTracingHandlerTest : public OverviewTracingTestBase {
public:
OverviewTracingHandlerTest() : weak_ptr_factory_(this) {}
~OverviewTracingHandlerTest() override = default;
OverviewTracingHandlerTest(const OverviewTracingHandlerTest&) = delete;
OverviewTracingHandlerTest& operator=(const OverviewTracingHandlerTest&) =
delete;
void SetUp() override {
OverviewTracingTestBase::SetUp();
handler_ = std::make_unique<OverviewTracingTestHandler>(
base::BindRepeating(&OverviewTracingHandlerTest::OnArcWindowFocusChange,
weak_ptr_factory_.GetWeakPtr()));
handler_->set_start_build_model_cb(
base::BindRepeating(&OverviewTracingHandlerTest::OnStartBuildModel,
weak_ptr_factory_.GetWeakPtr()));
handler_->set_graphics_model_ready_cb(
base::BindRepeating(&OverviewTracingHandlerTest::OnGraphicsModelReady,
weak_ptr_factory_.GetWeakPtr()));
}
void TearDown() override {
handler_.reset();
OverviewTracingTestBase::TearDown();
}
protected:
void OnArcWindowFocusChange(aura::Window* window) {
if (window) {
events_.push_back("new ARC window focused");
} else {
events_.push_back("ARC window loses focus");
}
}
void OnStartBuildModel() { events_.push_back("start building model"); }
void OnGraphicsModelReady(std::unique_ptr<OverviewTracingResult> results) {
events_.push_back(
base::StrCat({"graphics model status: ", results->status}));
model_ = std::move(results->model);
}
std::unique_ptr<OverviewTracingTestHandler> handler_;
std::vector<std::string> events_;
base::Value model_;
base::WeakPtrFactory<OverviewTracingHandlerTest> weak_ptr_factory_;
};
TEST_F(OverviewTracingHandlerTest, ModelName) {
base::FilePath download_path = base::FilePath::FromASCII("/mnt/downloads");
base::Time timestamp = base::Time::UnixEpoch() + base::Seconds(1);
std::unique_ptr<icu::TimeZone> tz;
SetTimeZone("America/Chicago");
EXPECT_EQ("overview_tracing_test_title_1_1969-12-31_18-00-01.json",
handler_->GetModelBaseNameFromTitle("Test Title #:1", timestamp));
SetTimeZone("Indian/Maldives");
EXPECT_EQ(
"overview_tracing_0123456789012345678901234567890_"
"1970-01-01_05-00-01.json",
handler_->GetModelBaseNameFromTitle(
"0123456789012345678901234567890123456789", timestamp));
SetTimeZone("Etc/UTC");
timestamp = base::Time::UnixEpoch() + base::Days(50);
EXPECT_EQ("overview_tracing_xyztitle_1970-02-20_00-00-00.json",
handler_->GetModelBaseNameFromTitle("xyztitle", timestamp));
SetTimeZone("Japan");
EXPECT_EQ("overview_tracing_secret_app_1970-02-20_09-00-00.json",
handler_->GetModelBaseNameFromTitle("Secret App", timestamp));
}
TEST_F(OverviewTracingHandlerTest, FilterSystemTraceByTimestamp) {
handler_->set_now(
base::Time::FromMillisecondsSinceUnixEpoch(1'500'088'880'000));
handler_->set_trace_time_base(
base::Time::FromMillisecondsSinceUnixEpoch(1'500'000'000'000));
exo::Surface s;
auto arc_widget = arc::ArcTaskWindowBuilder()
.SetShellRootSurface(&s)
.BuildOwnsNativeWidget();
arc_widget->Show();
auto max_time = base::Seconds(5);
handler_->StartTracing(download_path_, max_time);
handler_->StartTracingOnControllerRespond();
// Fast forward past the max tracing interval.
FastForwardClockAndTaskQueue(handler_.get(),
max_time + base::Milliseconds(500));
// Pass results from trace controller to handler. First and last events should
// not be in the model.
handler_->StopTracingOnControllerRespond(std::make_unique<std::string>(
R"(
{
"traceEvents": [],
"systemTraceEvents":
" <idle>-0 [000] d..0 88879.800000: sched_wakeup: comm=foo pid=99 prio=115 target_cpu=000
<idle>-0 [000] d..0 88882.000001: cpu_idle: state=0 cpu_id=0
<idle>-0 [000] dn.0 88883.000002: cpu_idle: state=4294967295 cpu_id=0
<idle>-0 [000] dnh3 88884.000003: sched_wakeup: comm=foo pid=25821 prio=115 target_cpu=000
<idle>-0 [000] d..3 88884.500004: sched_switch: prev_comm=bar prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=baz next_pid=25891 next_prio=115
<idle>-0 [000] d..3 88885.500004: sched_switch: prev_comm=baz prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=foo next_pid=33921 next_prio=115
"
})"));
ASSERT_EQ(("new ARC window focused\n"
"start building model"),
base::JoinString(events_, "\n"));
task_environment()->RunUntilIdle();
ASSERT_EQ(("new ARC window focused\n"
"start building model\n"
"graphics model status: Tracing model is ready"),
base::JoinString(events_, "\n"));
{
const auto& dict = model_.GetDict();
const auto* events_by_cpu = dict.FindListByDottedPath("system.cpu");
ASSERT_TRUE(events_by_cpu);
// Only one CPU in log.
ASSERT_EQ(1UL, events_by_cpu->size());
const auto& cpu_events = (*events_by_cpu)[0].GetList();
ASSERT_EQ(4UL, cpu_events.size()) << cpu_events;
EXPECT_EQ(25821.0, cpu_events[2].GetList()[2].GetDouble());
EXPECT_EQ(25891.0, cpu_events[3].GetList()[2].GetDouble());
}
}
TEST_F(OverviewTracingHandlerTest, SwitchWindowDuringModelBuild) {
handler_->set_now(
base::Time::FromMillisecondsSinceUnixEpoch(1'600'044'440'000));
handler_->set_trace_time_base(
base::Time::FromMillisecondsSinceUnixEpoch(1'600'000'000'000));
exo::Surface s;
auto arc_widget = arc::ArcTaskWindowBuilder()
.SetTaskId(22)
.SetPackageName("org.funstuff.client")
.SetShellRootSurface(&s)
.BuildOwnsNativeWidget();
auto other_arc_widget = arc::ArcTaskWindowBuilder()
.SetTaskId(88)
.SetPackageName("net.differentapp")
.SetShellRootSurface(&s)
.BuildOwnsNativeWidget();
arc_widget->Show();
other_arc_widget->ShowInactive();
auto max_time = base::Seconds(5);
handler_->StartTracing(download_path_, max_time);
handler_->StartTracingOnControllerRespond();
// Fast forward past the max tracing interval. This will stop the trace at the
// end of the fast-forward, which is 400ms after the timeout.
FastForwardClockAndTaskQueue(handler_.get(),
max_time + base::Milliseconds(400));
// While model is being built, switch to the ARC window to change
// min_tracing_time_. This sets the min trace time to 300ms after the end of
// the trace.
FastForwardClockAndTaskQueue(handler_.get(), base::Milliseconds(300));
other_arc_widget->Activate();
// Pass results from trace controller to handler.
handler_->StopTracingOnControllerRespond(
std::make_unique<std::string>(kBasicSystrace));
task_environment()->RunUntilIdle();
{
const auto& dict = model_.GetDict();
const auto* events_by_cpu = dict.FindListByDottedPath("system.cpu");
ASSERT_TRUE(events_by_cpu);
ASSERT_EQ(4UL, events_by_cpu->size());
const auto& cpu_events = (*events_by_cpu)[3].GetList();
ASSERT_EQ(1UL, cpu_events.size()) << cpu_events;
}
}
TEST_F(OverviewTracingHandlerTest, SwitchWindowDuringTrace) {
handler_->set_now(
base::Time::FromMillisecondsSinceUnixEpoch(1'600'044'440'000));
handler_->set_trace_time_base(
base::Time::FromMillisecondsSinceUnixEpoch(1'600'000'000'000));
exo::Surface s;
auto arc_widget = arc::ArcTaskWindowBuilder()
.SetTaskId(22)
.SetPackageName("org.funstuff.client")
.SetShellRootSurface(&s)
.SetTitle("the first app")
.BuildOwnsNativeWidget();
auto other_arc_widget = arc::ArcTaskWindowBuilder()
.SetTaskId(88)
.SetPackageName("net.differentapp")
.SetShellRootSurface(&s)
.SetTitle("another ARC app")
.BuildOwnsNativeWidget();
arc_widget->Show();
other_arc_widget->ShowInactive();
auto max_time = base::Seconds(5);
handler_->StartTracing(download_path_, max_time);
handler_->StartTracingOnControllerRespond();
FastForwardClockAndTaskQueue(handler_.get(), base::Seconds(1));
other_arc_widget->Activate();
FastForwardClockAndTaskQueue(handler_.get(), base::Seconds(1));
task_environment()->RunUntilIdle();
// Fast forward past the max tracing interval. This will stop the trace at the
// end of the fast-forward, which is 400ms after the timeout.
FastForwardClockAndTaskQueue(handler_.get(), base::Milliseconds(4500));
// Pass results from trace controller to handler.
handler_->StopTracingOnControllerRespond(
std::make_unique<std::string>(kBasicSystrace));
task_environment()->RunUntilIdle();
// The current behavior when activating a separate window during a trace is to
// stop the trace and switch to the web UI. This behavior may change, but this
// test was originally added to avoid a DCHECK failure upon switching the
// active window. NOTE even if the below asserts are no longer valid, this
// test is still important for what has occurred before this line.
const auto& dict = model_.GetDict();
EXPECT_EQ(*dict.FindStringByDottedPath("information.title"), "the first app");
}
TEST_F(OverviewTracingHandlerTest, SwitchWindowBeforeTraceStart) {
handler_->set_now(
base::Time::FromMillisecondsSinceUnixEpoch(1'600'044'440'000));
handler_->set_trace_time_base(
base::Time::FromMillisecondsSinceUnixEpoch(1'600'000'000'000));
exo::Surface s1, s2;
auto arc_widget = arc::ArcTaskWindowBuilder()
.SetTaskId(22)
.SetPackageName("org.funstuff.client")
.SetShellRootSurface(&s1)
.BuildOwnsNativeWidget();
auto other_arc_widget = arc::ArcTaskWindowBuilder()
.SetTaskId(88)
.SetPackageName("net.differentapp")
.SetTitle("i will be traced")
.SetShellRootSurface(&s2)
.BuildOwnsNativeWidget();
arc_widget->Show();
other_arc_widget->ShowInactive();
handler_->StartTracing(download_path_, base::Seconds(5));
other_arc_widget->Activate();
handler_->StartTracingOnControllerRespond();
FastForwardClockAndTaskQueue(handler_.get(), base::Seconds(6));
handler_->VerifyNoUnrespondedCallback();
// We should be able to do a trace now - no trace state should be lingering.
// The web UI will be active - switch back to an ARC app.
other_arc_widget->Activate();
handler_->StartTracing(download_path_, base::Seconds(5));
handler_->StartTracingOnControllerRespond();
FastForwardClockAndTaskQueue(handler_.get(), base::Seconds(6));
handler_->StopTracingOnControllerRespond(
std::make_unique<std::string>(kBasicSystrace));
task_environment()->RunUntilIdle();
const auto& dict = model_.GetDict();
EXPECT_EQ(*dict.FindStringByDottedPath("information.title"),
"i will be traced");
}
TEST_F(OverviewTracingHandlerTest, CommitAndPresentTimestampsInModel) {
handler_->set_now(
base::Time::FromMillisecondsSinceUnixEpoch(1'600'044'440'000));
handler_->set_trace_time_base(
base::Time::FromMillisecondsSinceUnixEpoch(1'600'000'000'000));
exo::Surface s;
auto arc_widget = arc::ArcTaskWindowBuilder()
.SetTaskId(22)
.SetPackageName("org.funstuff.client")
.SetShellRootSurface(&s)
.SetTitle("the first app")
.BuildOwnsNativeWidget();
arc_widget->Show();
handler_->StartTracing(download_path_, base::Seconds(5));
handler_->StartTracingOnControllerRespond();
FastForwardClockAndTaskQueue(handler_.get(), base::Seconds(1));
CommitAndPresentFrames(handler_.get(), &s, 5, base::Milliseconds(42));
FastForwardClockAndTaskQueue(handler_.get(), base::Seconds(5));
handler_->StopTracingOnControllerRespond(
std::make_unique<std::string>(kBasicSystrace));
task_environment()->RunUntilIdle();
const auto& dict = model_.GetDict();
LOG(INFO) << "checking JSON model: " << dict;
auto validate_events = [&](EventType type, const base::Value::List* events) {
ASSERT_TRUE(events);
std::queue<double> expected_times({0, 42000, 84000, 126000, 168000});
ASSERT_EQ(events->size(), expected_times.size());
for (const auto& event : *events) {
const auto& list = event.GetList();
ASSERT_EQ(list.size(), 2ul);
ASSERT_EQ(list[0].GetInt(), static_cast<int>(type));
ASSERT_EQ(list[1].GetDouble(), expected_times.front());
expected_times.pop();
}
};
validate_events(EventType::kChromeOSPresentationDone,
dict.FindListByDottedPath("chrome.global_events"));
const auto* events_by_buffer =
(*dict.FindListByDottedPath("views"))[0].GetDict().FindListByDottedPath(
"buffers");
ASSERT_TRUE(events_by_buffer);
ASSERT_EQ(events_by_buffer->size(), 1ul);
validate_events(EventType::kExoSurfaceCommit,
(*events_by_buffer)[0].GetIfList());
}
} // namespace
} // namespace arc