/* * Copyright (C) 2024 The Android Open Source Project * * 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 #include "test/gtest_and_gmock.h" #include #include #include #include #include #include "perfetto/base/logging.h" #include "perfetto/ext/base/file_utils.h" #include "perfetto/ext/base/string_utils.h" #include "src/base/test/utils.h" #include "src/traceconv/pprof_reader.h" #include "src/traceconv/trace_to_profile.h" namespace perfetto { namespace { using testing::Contains; pprof::PprofProfileReader ConvertTraceToPprof( const std::string& input_file_name) { const std::string trace_file = base::GetTestDataPath(input_file_name); std::ifstream file_istream; file_istream.open(trace_file, std::ios_base::in | std::ios_base::binary); PERFETTO_CHECK(file_istream.is_open()); std::stringstream ss; std::ostream os(ss.rdbuf()); trace_to_text::TraceToJavaHeapProfile(&file_istream, &os, /*pid=*/0, /*timestamps=*/{}, /*annotate_frames=*/false); auto conv_stdout = base::SplitString(ss.str(), " "); PERFETTO_CHECK(!conv_stdout.empty()); std::string out_dirname = base::TrimWhitespace(conv_stdout.back()); std::vector filenames; base::ListFilesRecursive(out_dirname, filenames); // assumption: all test inputs contain exactly one profile PERFETTO_CHECK(filenames.size() == 1); std::string profile_path = out_dirname + "/" + filenames[0]; // read in the profile contents and then clean up the temp files pprof::PprofProfileReader pprof_reader(profile_path); unlink(profile_path.c_str()); PERFETTO_CHECK(base::Rmdir(out_dirname)); return pprof_reader; } std::vector> get_samples_function_names( const pprof::PprofProfileReader& pprof, const std::string& last_function_name) { const auto samples = pprof.get_samples(last_function_name); std::vector> samples_function_names; for (const auto& sample : samples) { samples_function_names.push_back(pprof.get_sample_function_names(sample)); } return samples_function_names; } class TraceToPprofTest : public ::testing::Test { public: pprof::PprofProfileReader* pprof = nullptr; void SetUp() override { #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) GTEST_SKIP() << "do not run traceconv tests on Android target"; #endif } void TearDown() override { delete pprof; } }; TEST_F(TraceToPprofTest, SummaryValues) { const auto pprof = ConvertTraceToPprof("test/data/heap_graph/heap_graph.pb"); EXPECT_EQ(pprof.get_samples_value_sum("Foo", "Total allocation count"), 1); EXPECT_EQ(pprof.get_samples_value_sum("Foo", "Total allocation size"), 32); EXPECT_EQ(pprof.get_samples("Foo").size(), 1U); EXPECT_EQ(pprof.get_sample_count(), 3U); const std::vector expected_function_names = { "Foo", "FactoryProducerDelegateImplActor [ROOT_JAVA_FRAME]"}; EXPECT_THAT(get_samples_function_names(pprof, "Foo"), Contains(expected_function_names)); } TEST_F(TraceToPprofTest, TreeLocationFunctionNames) { const auto pprof = ConvertTraceToPprof("test/data/heap_graph/heap_graph_branching.pb"); EXPECT_THAT(get_samples_function_names(pprof, "LeftChild0"), Contains(std::vector{"LeftChild0", "RootNode [ROOT_JAVA_FRAME]"})); EXPECT_THAT(get_samples_function_names(pprof, "LeftChild1"), Contains(std::vector{"LeftChild1", "LeftChild0", "RootNode [ROOT_JAVA_FRAME]"})); EXPECT_THAT(get_samples_function_names(pprof, "RightChild0"), Contains(std::vector{"RightChild0", "RootNode [ROOT_JAVA_FRAME]"})); EXPECT_THAT(get_samples_function_names(pprof, "RightChild1"), Contains(std::vector{"RightChild1", "RightChild0", "RootNode [ROOT_JAVA_FRAME]"})); } TEST_F(TraceToPprofTest, HugeSizes) { const auto pprof = ConvertTraceToPprof("test/data/heap_graph/heap_graph_huge_size.pb"); EXPECT_EQ(pprof.get_samples_value_sum("dev.perfetto.BigStuff", "Total allocation size"), 3000000000); } class TraceToPprofRealTraceTest : public ::testing::Test { public: void SetUp() override { #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) GTEST_SKIP() << "do not run traceconv tests on Android target"; #endif #if defined(LEAK_SANITIZER) GTEST_SKIP() << "trace is too big to be tested in sanitizer builds"; #endif } }; TEST_F(TraceToPprofRealTraceTest, AllocationCountForClass) { const auto pprof = ConvertTraceToPprof("test/data/system-server-heap-graph-new.pftrace"); EXPECT_EQ(pprof.get_samples_value_sum( "android.content.pm.parsing.component.ParsedActivity", "Total allocation count"), 5108); EXPECT_EQ(pprof.get_samples_value_sum( "android.content.pm.parsing.component.ParsedActivity", "Total allocation size"), 817280); EXPECT_EQ( pprof.get_samples("android.content.pm.parsing.component.ParsedActivity") .size(), 5U); EXPECT_EQ(pprof.get_sample_count(), 83256U); const std::vector expected_function_names = { "android.content.pm.parsing.component.ParsedActivity", "java.lang.Object[]", "java.util.ArrayList", "com.android.server.pm.parsing.pkg.PackageImpl", "com.android.server.pm.PackageSetting", "java.lang.Object[]", "android.util.ArrayMap", "com.android.server.pm.Settings", "com.android.server.pm.PackageManagerService [ROOT_JNI_GLOBAL]"}; EXPECT_THAT(get_samples_function_names( pprof, "android.content.pm.parsing.component.ParsedActivity"), Contains(expected_function_names)); } } // namespace } // namespace perfetto