blob: cc3d4fd115fb7fdf078c62c70c22bf5f24ea5a48 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <thread>
#include "base/metrics/histogram_macros.h"
#include "base/test/test_trace_processor.h"
#include "base/test/trace_test_utils.h"
#include "build/build_config.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer_proto.h"
#include "third_party/perfetto/protos/perfetto/config/chrome/chrome_config.gen.h"
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
namespace content {
namespace {
const char kDetailedDumpMode[] = "detailed";
const char kBackgroundDumpMode[] = "background";
perfetto::protos::gen::TraceConfig TraceConfigWithHistograms(
const std::string& category_filter_string,
const std::vector<std::string>& histograms) {
base::trace_event::TraceConfig trace_event_config(category_filter_string, "");
for (const auto& histogram : histograms) {
trace_event_config.EnableHistogram(histogram);
}
auto perfetto_config =
base::test::DefaultTraceConfig(category_filter_string, false);
for (auto& ds : *perfetto_config.mutable_data_sources()) {
if (ds.config().name() == "track_event") {
ds.mutable_config()->mutable_chrome_config()->set_trace_config(
trace_event_config.ToString());
}
}
return perfetto_config;
}
perfetto::protos::gen::TraceConfig TraceConfigWithMemoryDumps(
const char* mode) {
const std::string trace_event_config_str = base::StringPrintf(
"{\"record_mode\":\"record-until-full\","
"\"included_categories\":[\"disabled-by-default-memory-infra\"],"
"\"excluded_categories\":[\"*\"],\"memory_dump_config\":{"
"\"allowed_dump_modes\":[\"%s\"],"
"\"triggers\":[{\"min_time_between_dumps_ms\":10000,"
"\"mode\":\"%s\",\"type\":\"periodic_interval\"}]}}",
mode, mode);
auto perfetto_config = base::test::DefaultTraceConfig(
"-*,disabled-by-default-memory-infra", false);
auto* ds = perfetto_config.add_data_sources();
ds->mutable_config()->set_name("org.chromium.memory_instrumentation");
ds->mutable_config()->mutable_chrome_config()->set_trace_config(
trace_event_config_str);
return perfetto_config;
}
} // namespace
class TracingEndToEndBrowserTest : public ContentBrowserTest {};
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest, SimpleTraceEvent) {
base::test::TestTraceProcessor ttp;
ttp.StartTrace(base::test::DefaultTraceConfig("foo", false),
perfetto::kCustomBackend);
{
// A simple trace event
TRACE_EVENT("foo", "test_event");
}
absl::Status status = ttp.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
std::string query = "SELECT name from slice";
auto result = ttp.RunQuery(query);
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(result.value(),
::testing::ElementsAre(std::vector<std::string>{"name"},
std::vector<std::string>{"test_event"}));
}
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest,
MemoryInstrumentationBackground) {
base::WaitableEvent dump_completed;
memory_instrumentation::TracingObserverProto::GetInstance()
->SetOnChromeDumpCallbackForTesting(
base::BindOnce([](base::WaitableEvent* event) { event->Signal(); },
&dump_completed));
base::test::TestTraceProcessor ttp;
ttp.StartTrace(TraceConfigWithMemoryDumps(kBackgroundDumpMode));
while (!dump_completed.IsSignaled()) {
base::RunLoop().RunUntilIdle();
}
absl::Status status = ttp.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
auto result = ttp.RunQuery("SELECT detail_level FROM memory_snapshot");
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(
result.value(),
::testing::ElementsAre(std::vector<std::string>{"detail_level"},
std::vector<std::string>{kBackgroundDumpMode}));
result = ttp.RunQuery(
"SELECT COUNT(DISTINCT process_snapshot_id) > 1 AS has_other_processes "
"FROM memory_snapshot_node");
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(
result.value(),
::testing::ElementsAre(std::vector<std::string>{"has_other_processes"},
std::vector<std::string>{"1"}));
}
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest,
MemoryInstrumentationDetailed) {
base::WaitableEvent dump_completed;
memory_instrumentation::TracingObserverProto::GetInstance()
->SetOnChromeDumpCallbackForTesting(
base::BindOnce([](base::WaitableEvent* event) { event->Signal(); },
&dump_completed));
base::test::TestTraceProcessor ttp;
ttp.StartTrace(TraceConfigWithMemoryDumps(kDetailedDumpMode));
while (!dump_completed.IsSignaled()) {
base::RunLoop().RunUntilIdle();
}
absl::Status status = ttp.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
auto result = ttp.RunQuery("SELECT detail_level FROM memory_snapshot");
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(result.value(), ::testing::ElementsAre(
std::vector<std::string>{"detail_level"},
std::vector<std::string>{kDetailedDumpMode}));
result = ttp.RunQuery(
"SELECT COUNT(DISTINCT process_snapshot_id) > 1 AS has_other_processes "
"FROM memory_snapshot_node");
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(
result.value(),
::testing::ElementsAre(std::vector<std::string>{"has_other_processes"},
std::vector<std::string>{"1"}));
}
#if BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest,
PackageNameRecordedTraceLogSet) {
base::trace_event::TraceLog::GetInstance()->SetRecordHostAppPackageName(true);
base::test::TestTraceProcessor ttp;
ttp.StartTrace(base::test::DefaultTraceConfig("foo", false),
perfetto::kCustomBackend);
{
// A simple trace event
TRACE_EVENT("foo", "test_event");
}
absl::Status status = ttp.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
std::string query =
"SELECT "
"DISTINCT(EXTRACT_ARG(arg_set_id, \"chrome.host_app_package_name\")) "
"AS name "
"FROM process "
"WHERE "
"EXTRACT_ARG(arg_set_id, \"chrome.host_app_package_name\") IS NOT NULL";
auto result = ttp.RunQuery(query);
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(
result.value(),
::testing::ElementsAre(
std::vector<std::string>{"name"},
std::vector<std::string>{"org.chromium.content_browsertests_apk"}));
}
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest,
PackageNameNotRecordedTraceLogNotSet) {
base::trace_event::TraceLog::GetInstance()->SetRecordHostAppPackageName(
false);
base::test::TestTraceProcessor ttp;
ttp.StartTrace(base::test::DefaultTraceConfig("foo", false),
perfetto::kCustomBackend);
{
// A simple trace event
TRACE_EVENT("foo", "test_event");
}
absl::Status status = ttp.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
std::string query =
"SELECT "
"DISTINCT(EXTRACT_ARG(arg_set_id, \"chrome.host_app_package_name\")) "
"AS name "
"FROM process "
"WHERE "
"EXTRACT_ARG(arg_set_id, \"chrome.host_app_package_name\") IS NOT NULL";
auto result = ttp.RunQuery(query);
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(result.value(),
::testing::ElementsAre(std::vector<std::string>{"name"}));
}
#endif // BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest, TwoSessionsSimple) {
base::test::TestTraceProcessor ttp1, ttp2;
ttp1.StartTrace("foo,cat");
ttp2.StartTrace("test,cat");
{
TRACE_EVENT("foo", "foo_event");
TRACE_EVENT("test", "test_event");
TRACE_EVENT("cat", "cat_event");
}
absl::Status status = ttp1.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
std::string query = "SELECT name from slice";
auto result1 = ttp1.RunQuery(query);
ASSERT_TRUE(result1.has_value()) << result1.error();
EXPECT_THAT(result1.value(),
::testing::ElementsAre(std::vector<std::string>{"name"},
std::vector<std::string>{"foo_event"},
std::vector<std::string>{"cat_event"}));
status = ttp2.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
auto result2 = ttp2.RunQuery(query);
ASSERT_TRUE(result2.has_value()) << result2.error();
EXPECT_THAT(result2.value(),
::testing::ElementsAre(std::vector<std::string>{"name"},
std::vector<std::string>{"test_event"},
std::vector<std::string>{"cat_event"}));
}
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest, TwoSessionsHistograms) {
// Start the first session that records "Test.Foo" histograms
auto config = TraceConfigWithHistograms(
"disabled-by-default-histogram_samples", {"Test.Foo"});
base::test::TestTraceProcessor ttp1;
ttp1.StartTrace(config);
UMA_HISTOGRAM_BOOLEAN("Test.Foo", true);
UMA_HISTOGRAM_BOOLEAN("Test.Bar", true);
// Start the second session that records "Test.Bar" histograms
config = TraceConfigWithHistograms("disabled-by-default-histogram_samples",
{"Test.Bar"});
base::test::TestTraceProcessor ttp2;
ttp2.StartTrace(config);
UMA_HISTOGRAM_BOOLEAN("Test.Foo", true);
UMA_HISTOGRAM_BOOLEAN("Test.Bar", true);
// Stop the second session. Its trace should contain only one "Test.Bar"
// sample that was recorded while the session was active.
// It will also contain a "Test.Foo" sample because right now the list of
// monitored histograms is shared between sessions.
// TODO(khokhlov): Fix the test expectations when CustomEventRecorder
// correctly tracks which samples go to which sessions.
absl::Status status = ttp2.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
std::string query =
"SELECT "
"EXTRACT_ARG(arg_set_id, \"chrome_histogram_sample.name\") AS name "
"FROM slice "
"WHERE cat = \"disabled-by-default-histogram_samples\"";
auto result = ttp2.RunQuery(query);
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(result.value(),
::testing::ElementsAre(std::vector<std::string>{"name"},
std::vector<std::string>{"Test.Foo"},
std::vector<std::string>{"Test.Bar"}));
UMA_HISTOGRAM_BOOLEAN("Test.Foo", true);
UMA_HISTOGRAM_BOOLEAN("Test.Bar", true);
// Stop the first session. Its trace should contain all three "Test.Foo"
// samples that were recorded while the session was active.
// Some "Test.Bar" samples will also be there (see the previous comment).
status = ttp1.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
result = ttp1.RunQuery(query);
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(result.value(),
::testing::ElementsAre(std::vector<std::string>{"name"},
std::vector<std::string>{"Test.Foo"},
std::vector<std::string>{"Test.Foo"},
std::vector<std::string>{"Test.Bar"},
std::vector<std::string>{"Test.Foo"},
std::vector<std::string>{"Test.Bar"}));
}
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest, TwoSessionsProcessNames) {
base::test::TestTraceProcessor ttp1, ttp2;
ttp1.StartTrace("foo");
ttp2.StartTrace("test");
{
TRACE_EVENT("foo", "foo_event");
TRACE_EVENT("test", "test_event");
}
absl::Status status = ttp1.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
std::string query =
"SELECT COUNT(*) AS cnt FROM process WHERE name = \"Browser\"";
auto result1 = ttp1.RunQuery(query);
ASSERT_TRUE(result1.has_value()) << result1.error();
EXPECT_THAT(result1.value(),
::testing::ElementsAre(std::vector<std::string>{"cnt"},
std::vector<std::string>{"1"}));
status = ttp2.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
auto result2 = ttp2.RunQuery(query);
ASSERT_TRUE(result2.has_value()) << result2.error();
EXPECT_THAT(result2.value(),
::testing::ElementsAre(std::vector<std::string>{"cnt"},
std::vector<std::string>{"1"}));
}
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest,
TwoSessionsMemoryInstrumentation) {
base::WaitableEvent dump_completed;
memory_instrumentation::TracingObserverProto::GetInstance()
->SetOnChromeDumpCallbackForTesting(
base::BindOnce([](base::WaitableEvent* event) { event->Signal(); },
&dump_completed));
base::test::TestTraceProcessor ttp1, ttp2;
ttp1.StartTrace(TraceConfigWithMemoryDumps(kDetailedDumpMode));
ttp2.StartTrace("foo");
{
TRACE_EVENT("foo", "foo_event");
while (!dump_completed.IsSignaled()) {
base::RunLoop().RunUntilIdle();
}
}
absl::Status status = ttp1.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
std::string query = "SELECT detail_level FROM memory_snapshot";
auto result1 = ttp1.RunQuery(query);
ASSERT_TRUE(result1.has_value()) << result1.error();
EXPECT_THAT(
result1.value(),
::testing::ElementsAre(std::vector<std::string>{"detail_level"},
std::vector<std::string>{kDetailedDumpMode}));
status = ttp2.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
auto result2 = ttp2.RunQuery(query);
ASSERT_TRUE(result2.has_value()) << result2.error();
EXPECT_THAT(result2.value(),
::testing::ElementsAre(std::vector<std::string>{"detail_level"}));
}
} // namespace content
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)