Use packages.list to read profileability.
Bug: 153139002
Change-Id: Idc4d3bea3f6535765a323b9bf4bf62a16704e7db
diff --git a/Android.bp b/Android.bp
index 7ed5230..932a9b1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -86,6 +86,7 @@
":perfetto_src_profiling_memory_scoped_spinlock",
":perfetto_src_profiling_memory_wire_protocol",
":perfetto_src_protozero_protozero",
+ ":perfetto_src_traced_probes_packages_list_packages_list_parser",
":perfetto_src_tracing_common",
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_ipc_common",
@@ -335,6 +336,7 @@
":perfetto_src_profiling_memory_scoped_spinlock",
":perfetto_src_profiling_memory_wire_protocol",
":perfetto_src_protozero_protozero",
+ ":perfetto_src_traced_probes_packages_list_packages_list_parser",
":perfetto_src_tracing_common",
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_ipc_common",
@@ -518,6 +520,7 @@
":perfetto_src_traced_probes_initial_display_state_initial_display_state",
":perfetto_src_traced_probes_metatrace_metatrace",
":perfetto_src_traced_probes_packages_list_packages_list",
+ ":perfetto_src_traced_probes_packages_list_packages_list_parser",
":perfetto_src_traced_probes_power_power",
":perfetto_src_traced_probes_probes",
":perfetto_src_traced_probes_probes_src",
@@ -1058,6 +1061,7 @@
":perfetto_src_traced_probes_initial_display_state_initial_display_state",
":perfetto_src_traced_probes_metatrace_metatrace",
":perfetto_src_traced_probes_packages_list_packages_list",
+ ":perfetto_src_traced_probes_packages_list_packages_list_parser",
":perfetto_src_traced_probes_power_power",
":perfetto_src_traced_probes_probes_src",
":perfetto_src_traced_probes_ps_ps",
@@ -1312,6 +1316,7 @@
":perfetto_src_traced_probes_initial_display_state_initial_display_state",
":perfetto_src_traced_probes_metatrace_metatrace",
":perfetto_src_traced_probes_packages_list_packages_list",
+ ":perfetto_src_traced_probes_packages_list_packages_list_parser",
":perfetto_src_traced_probes_power_power",
":perfetto_src_traced_probes_probes_src",
":perfetto_src_traced_probes_ps_ps",
@@ -1716,6 +1721,7 @@
":perfetto_src_traced_probes_initial_display_state_initial_display_state",
":perfetto_src_traced_probes_metatrace_metatrace",
":perfetto_src_traced_probes_packages_list_packages_list",
+ ":perfetto_src_traced_probes_packages_list_packages_list_parser",
":perfetto_src_traced_probes_power_power",
":perfetto_src_traced_probes_probes_src",
":perfetto_src_traced_probes_ps_ps",
@@ -7966,11 +7972,19 @@
],
}
+// GN: //src/traced/probes/packages_list:packages_list_parser
+filegroup {
+ name: "perfetto_src_traced_probes_packages_list_packages_list_parser",
+ srcs: [
+ "src/traced/probes/packages_list/packages_list_parser.cc",
+ ],
+}
+
// GN: //src/traced/probes/packages_list:unittests
filegroup {
name: "perfetto_src_traced_probes_packages_list_unittests",
srcs: [
- "src/traced/probes/packages_list/packages_list_data_source_unittest.cc",
+ "src/traced/probes/packages_list/packages_list_unittest.cc",
],
}
@@ -8619,6 +8633,7 @@
":perfetto_src_traced_probes_initial_display_state_unittests",
":perfetto_src_traced_probes_metatrace_metatrace",
":perfetto_src_traced_probes_packages_list_packages_list",
+ ":perfetto_src_traced_probes_packages_list_packages_list_parser",
":perfetto_src_traced_probes_packages_list_unittests",
":perfetto_src_traced_probes_power_power",
":perfetto_src_traced_probes_probes_src",
diff --git a/BUILD b/BUILD
index 8e63d57..f528de8 100644
--- a/BUILD
+++ b/BUILD
@@ -186,6 +186,7 @@
":src_traced_probes_initial_display_state_initial_display_state",
":src_traced_probes_metatrace_metatrace",
":src_traced_probes_packages_list_packages_list",
+ ":src_traced_probes_packages_list_packages_list_parser",
":src_traced_probes_power_power",
":src_traced_probes_probes",
":src_traced_probes_probes_src",
@@ -1379,6 +1380,15 @@
],
)
+# GN target: //src/traced/probes/packages_list:packages_list_parser
+filegroup(
+ name = "src_traced_probes_packages_list_packages_list_parser",
+ srcs = [
+ "src/traced/probes/packages_list/packages_list_parser.cc",
+ "src/traced/probes/packages_list/packages_list_parser.h",
+ ],
+)
+
# GN target: //src/traced/probes/power:power
filegroup(
name = "src_traced_probes_power_power",
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index c9d9715..3933798 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -239,6 +239,7 @@
"../../../protos/perfetto/config/profiling:cpp",
"../../base",
"../../base:unix_socket",
+ "../../traced/probes/packages_list:packages_list_parser",
"../../tracing/core",
"../../tracing/ipc/producer",
"../common:callstack_trie",
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index 22091f2..4b1d02f 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -26,9 +26,11 @@
#include <sys/types.h>
#include <unistd.h>
+#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/thread_task_runner.h"
#include "perfetto/ext/base/watchdog_posix.h"
@@ -40,6 +42,11 @@
#include "src/profiling/common/profiler_guardrails.h"
#include "src/profiling/memory/unwound_messages.h"
#include "src/profiling/memory/wire_protocol.h"
+#include "src/traced/probes/packages_list/packages_list_parser.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/system_properties.h>
+#endif
namespace perfetto {
namespace profiling {
@@ -464,6 +471,7 @@
if (!HeapprofdConfigToClientConfiguration(heapprofd_config, &cli_config))
return;
data_source.config = heapprofd_config;
+ data_source.ds_config = ds_config;
data_source.normalized_cmdlines = std::move(normalized_cmdlines.value());
data_source.stop_timeout_ms = ds_config.stop_timeout_ms()
? ds_config.stop_timeout_ms()
@@ -935,6 +943,15 @@
}
RecordOtherSourcesAsRejected(data_source, process);
+ // In fork mode, right now we check whether the target is not profileable
+ // in the client, because we cannot read packages.list there.
+ if (mode_ == HeapprofdMode::kCentral &&
+ !CanProfile(data_source->ds_config, new_connection->peer_uid())) {
+ PERFETTO_ELOG("%d (%s) is not profileable.", process.pid,
+ process.cmdline.c_str());
+ return;
+ }
+
uint64_t shmem_size = data_source->config.shmem_size_bytes();
if (!shmem_size)
shmem_size = kDefaultShmemSize;
@@ -1239,5 +1256,61 @@
}
}
+bool CanProfile(const DataSourceConfig& ds_config, uint64_t uid) {
+#if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+ base::ignore_result(ds_config);
+ base::ignore_result(uid);
+ return true;
+#else
+ char buf[PROP_VALUE_MAX + 1] = {};
+ int ret = __system_property_get("ro.build.type", buf);
+ PERFETTO_CHECK(ret >= 0);
+ return CanProfileAndroid(ds_config, uid, std::string(buf),
+ "/data/system/packages.list");
+#endif
+}
+
+bool CanProfileAndroid(const DataSourceConfig& ds_config,
+ uint64_t uid,
+ const std::string& build_type,
+ const std::string& packages_list_path) {
+ // These are replicated constants from libcutils android_filesystem_config.h
+ constexpr auto kAidAppStart = 10000; // AID_APP_START
+ constexpr auto kAidAppEnd = 19999; // AID_APP_END
+ constexpr auto kAidUserOffset = 100000; // AID_USER_OFFSET
+
+ if (build_type != "user") {
+ return true;
+ }
+
+ if (ds_config.enable_extra_guardrails()) {
+ return false; // no extra guardrails on user builds.
+ }
+
+ uint64_t uid_without_profile = uid % kAidUserOffset;
+ if (uid_without_profile < kAidAppStart || kAidAppEnd < uid_without_profile) {
+ // TODO(fmayer): relax this.
+ return false; // no native services on user.
+ }
+
+ std::string content;
+ if (!base::ReadFile(packages_list_path, &content)) {
+ PERFETTO_ELOG("Failed to read %s.", packages_list_path.c_str());
+ return false;
+ }
+ for (base::StringSplitter ss(std::move(content), '\n'); ss.Next();) {
+ Package pkg;
+ if (!ReadPackagesListLine(ss.cur_token(), &pkg)) {
+ PERFETTO_ELOG("Failed to parse packages.list.");
+ return false;
+ }
+ if (pkg.uid == uid_without_profile &&
+ (pkg.profileable_from_shell || pkg.debuggable)) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace profiling
} // namespace perfetto
diff --git a/src/profiling/memory/heapprofd_producer.h b/src/profiling/memory/heapprofd_producer.h
index d913614..aa11ab7 100644
--- a/src/profiling/memory/heapprofd_producer.h
+++ b/src/profiling/memory/heapprofd_producer.h
@@ -35,6 +35,7 @@
#include "perfetto/ext/tracing/core/tracing_service.h"
#include "perfetto/tracing/core/data_source_config.h"
+#include "perfetto/tracing/core/forward_decls.h"
#include "src/profiling/common/interning_output.h"
#include "src/profiling/common/proc_utils.h"
#include "src/profiling/common/profiler_guardrails.h"
@@ -79,6 +80,12 @@
const HeapprofdConfig& heapprofd_config,
ClientConfiguration* cli_config);
+bool CanProfile(const DataSourceConfig& ds_config, uint64_t uid);
+bool CanProfileAndroid(const DataSourceConfig& ds_config,
+ uint64_t uid,
+ const std::string& build_type,
+ const std::string& packages_list_path);
+
// Heap profiling producer. Can be instantiated in two modes, central and
// child (also referred to as fork mode).
//
@@ -230,6 +237,7 @@
DataSourceInstanceID id;
std::unique_ptr<TraceWriter> trace_writer;
+ DataSourceConfig ds_config;
HeapprofdConfig config;
ClientConfiguration client_configuration;
std::vector<SystemProperties::Handle> properties;
diff --git a/src/profiling/memory/heapprofd_producer_unittest.cc b/src/profiling/memory/heapprofd_producer_unittest.cc
index 6509793..f9fcc9d 100644
--- a/src/profiling/memory/heapprofd_producer_unittest.cc
+++ b/src/profiling/memory/heapprofd_producer_unittest.cc
@@ -16,6 +16,8 @@
#include "src/profiling/memory/heapprofd_producer.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/temp_file.h"
#include "perfetto/ext/tracing/core/basic_types.h"
#include "perfetto/ext/tracing/core/commit_data_request.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
@@ -170,5 +172,95 @@
EXPECT_FALSE(HeapprofdConfigToClientConfiguration(cfg, &cli_config));
}
+TEST(CanProfileAndroidTest, NonUserSystemExtraGuardrails) {
+ DataSourceConfig ds_config;
+ ds_config.set_enable_extra_guardrails(true);
+ EXPECT_TRUE(CanProfileAndroid(ds_config, 1, "userdebug", "/dev/null"));
+}
+
+TEST(CanProfileAndroidTest, NonUserNonProfileableApp) {
+ DataSourceConfig ds_config;
+ ds_config.set_enable_extra_guardrails(false);
+ auto tmp = base::TempFile::Create();
+ constexpr char content[] =
+ "invalid.example.profileable 10001 0 "
+ "/data/user/0/invalid.example.profileable default:targetSdkVersion=10000 "
+ "none 0 1\n";
+ base::WriteAll(tmp.fd(), content, sizeof(content));
+ EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "userdebug", tmp.path()));
+}
+
+TEST(CanProfileAndroidTest, NonUserNonProfileableAppExtraGuardrails) {
+ DataSourceConfig ds_config;
+ ds_config.set_enable_extra_guardrails(true);
+ auto tmp = base::TempFile::Create();
+ constexpr char content[] =
+ "invalid.example.profileable 10001 0 "
+ "/data/user/0/invalid.example.profileable default:targetSdkVersion=10000 "
+ "none 0 1\n";
+ base::WriteAll(tmp.fd(), content, sizeof(content));
+ EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "userdebug", tmp.path()));
+}
+
+TEST(CanProfileAndroidTest, UserProfileableApp) {
+ DataSourceConfig ds_config;
+ ds_config.set_enable_extra_guardrails(false);
+ auto tmp = base::TempFile::Create();
+ constexpr char content[] =
+ "invalid.example.profileable 10001 0 "
+ "/data/user/0/invalid.example.profileable default:targetSdkVersion=10000 "
+ "none 1 1\n";
+ base::WriteAll(tmp.fd(), content, sizeof(content));
+ EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
+}
+
+TEST(CanProfileAndroidTest, UserProfileableAppExtraGuardrails) {
+ DataSourceConfig ds_config;
+ ds_config.set_enable_extra_guardrails(true);
+ auto tmp = base::TempFile::Create();
+ constexpr char content[] =
+ "invalid.example.profileable 10001 0 "
+ "/data/user/0/invalid.example.profileable default:targetSdkVersion=10000 "
+ "none 1 1\n";
+ base::WriteAll(tmp.fd(), content, sizeof(content));
+ EXPECT_FALSE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
+}
+
+TEST(CanProfileAndroidTest, UserProfileableAppMultiuser) {
+ DataSourceConfig ds_config;
+ ds_config.set_enable_extra_guardrails(false);
+ auto tmp = base::TempFile::Create();
+ constexpr char content[] =
+ "invalid.example.profileable 10001 0 "
+ "/data/user/0/invalid.example.profileable default:targetSdkVersion=10000 "
+ "none 1 1\n";
+ base::WriteAll(tmp.fd(), content, sizeof(content));
+ EXPECT_TRUE(CanProfileAndroid(ds_config, 210001, "user", tmp.path()));
+}
+
+TEST(CanProfileAndroidTest, UserNonProfileableApp) {
+ DataSourceConfig ds_config;
+ ds_config.set_enable_extra_guardrails(false);
+ auto tmp = base::TempFile::Create();
+ constexpr char content[] =
+ "invalid.example.profileable 10001 0 "
+ "/data/user/0/invalid.example.profileable default:targetSdkVersion=10000 "
+ "none 0 1\n";
+ base::WriteAll(tmp.fd(), content, sizeof(content));
+ EXPECT_FALSE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
+}
+
+TEST(CanProfileAndroidTest, UserDebuggableApp) {
+ DataSourceConfig ds_config;
+ ds_config.set_enable_extra_guardrails(false);
+ auto tmp = base::TempFile::Create();
+ constexpr char content[] =
+ "invalid.example.profileable 10001 1 "
+ "/data/user/0/invalid.example.profileable default:targetSdkVersion=10000 "
+ "none 0 1\n";
+ base::WriteAll(tmp.fd(), content, sizeof(content));
+ EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
+}
+
} // namespace profiling
} // namespace perfetto
diff --git a/src/traced/probes/packages_list/BUILD.gn b/src/traced/probes/packages_list/BUILD.gn
index 0f17596..669fa18 100644
--- a/src/traced/probes/packages_list/BUILD.gn
+++ b/src/traced/probes/packages_list/BUILD.gn
@@ -14,9 +14,21 @@
import("../../../../gn/test.gni")
+source_set("packages_list_parser") {
+ deps = [
+ "../../../../gn:default_deps",
+ "../../../base",
+ ]
+ sources = [
+ "packages_list_parser.cc",
+ "packages_list_parser.h",
+ ]
+}
+
source_set("packages_list") {
public_deps = [ "../../../tracing/core" ]
deps = [
+ ":packages_list_parser",
"..:data_source",
"../../../../gn:default_deps",
"../../../../include/perfetto/ext/traced",
@@ -36,6 +48,7 @@
testonly = true
deps = [
":packages_list",
+ ":packages_list_parser",
"../../../../gn:default_deps",
"../../../../gn:gtest_and_gmock",
"../../../../protos/perfetto/trace/android:cpp",
@@ -43,5 +56,5 @@
"../../../../src/base:test_support",
"../../../../src/tracing/test:test_support",
]
- sources = [ "packages_list_data_source_unittest.cc" ]
+ sources = [ "packages_list_unittest.cc" ]
}
diff --git a/src/traced/probes/packages_list/packages_list_data_source.cc b/src/traced/probes/packages_list/packages_list_data_source.cc
index 68fa662..f9919b8 100644
--- a/src/traced/probes/packages_list/packages_list_data_source.cc
+++ b/src/traced/probes/packages_list/packages_list_data_source.cc
@@ -22,6 +22,8 @@
#include "perfetto/ext/tracing/core/trace_writer.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/traced/probes/packages_list/packages_list_parser.h"
+
using perfetto::protos::pbzero::PackagesListConfig;
namespace perfetto {
@@ -57,60 +59,6 @@
return parsed_fully;
}
-bool ReadPackagesListLine(char* line, Package* package) {
- size_t idx = 0;
- for (base::StringSplitter ss(line, ' '); ss.Next();) {
- switch (idx) {
- case 0:
- package->name = std::string(ss.cur_token(), ss.cur_token_size());
- break;
- case 1: {
- char* end;
- long long uid = strtoll(ss.cur_token(), &end, 10);
- if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
- PERFETTO_ELOG("Failed to parse packages.list uid.");
- return false;
- }
- package->uid = static_cast<uint64_t>(uid);
- break;
- }
- case 2: {
- char* end;
- long long debuggable = strtoll(ss.cur_token(), &end, 10);
- if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
- PERFETTO_ELOG("Failed to parse packages.list debuggable.");
- return false;
- }
- package->debuggable = debuggable != 0;
- break;
- }
- case 6: {
- char* end;
- long long profilable_from_shell = strtoll(ss.cur_token(), &end, 10);
- if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
- PERFETTO_ELOG("Failed to parse packages.list profilable_from_shell.");
- return false;
- }
- package->profileable_from_shell = profilable_from_shell != 0;
- break;
- }
- case 7: {
- char* end;
- long long version_code = strtoll(ss.cur_token(), &end, 10);
- if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
- PERFETTO_ELOG("Failed to parse packages.list version_code: %s.",
- ss.cur_token());
- return false;
- }
- package->version_code = version_code;
- break;
- }
- }
- ++idx;
- }
- return true;
-}
-
PackagesListDataSource::PackagesListDataSource(
const DataSourceConfig& ds_config,
TracingSessionID session_id,
diff --git a/src/traced/probes/packages_list/packages_list_data_source.h b/src/traced/probes/packages_list/packages_list_data_source.h
index 5dd3fdc..b19472b 100644
--- a/src/traced/probes/packages_list/packages_list_data_source.h
+++ b/src/traced/probes/packages_list/packages_list_data_source.h
@@ -35,15 +35,6 @@
class TraceWriter;
-struct Package {
- std::string name;
- uint64_t uid = 0;
- bool debuggable = false;
- bool profileable_from_shell = false;
- int64_t version_code = 0;
-};
-
-bool ReadPackagesListLine(char* line, Package* package);
bool ParsePackagesListStream(protos::pbzero::PackagesList* packages_list,
const base::ScopedFstream& fs,
const std::set<std::string>& package_name_filter);
diff --git a/src/traced/probes/packages_list/packages_list_parser.cc b/src/traced/probes/packages_list/packages_list_parser.cc
new file mode 100644
index 0000000..bcbccaa
--- /dev/null
+++ b/src/traced/probes/packages_list/packages_list_parser.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 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 "src/traced/probes/packages_list/packages_list_parser.h"
+
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/string_splitter.h"
+
+namespace perfetto {
+
+bool ReadPackagesListLine(char* line, Package* package) {
+ size_t idx = 0;
+ for (base::StringSplitter ss(line, ' '); ss.Next();) {
+ switch (idx) {
+ case 0:
+ package->name = std::string(ss.cur_token(), ss.cur_token_size());
+ break;
+ case 1: {
+ char* end;
+ long long uid = strtoll(ss.cur_token(), &end, 10);
+ if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
+ PERFETTO_ELOG("Failed to parse packages.list uid.");
+ return false;
+ }
+ package->uid = static_cast<uint64_t>(uid);
+ break;
+ }
+ case 2: {
+ char* end;
+ long long debuggable = strtoll(ss.cur_token(), &end, 10);
+ if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
+ PERFETTO_ELOG("Failed to parse packages.list debuggable.");
+ return false;
+ }
+ package->debuggable = debuggable != 0;
+ break;
+ }
+ case 6: {
+ char* end;
+ long long profilable_from_shell = strtoll(ss.cur_token(), &end, 10);
+ if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
+ PERFETTO_ELOG("Failed to parse packages.list profilable_from_shell.");
+ return false;
+ }
+ package->profileable_from_shell = profilable_from_shell != 0;
+ break;
+ }
+ case 7: {
+ char* end;
+ long long version_code = strtoll(ss.cur_token(), &end, 10);
+ if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
+ PERFETTO_ELOG("Failed to parse packages.list version_code: %s.",
+ ss.cur_token());
+ return false;
+ }
+ package->version_code = version_code;
+ break;
+ }
+ }
+ ++idx;
+ }
+ return true;
+}
+
+} // namespace perfetto
diff --git a/src/traced/probes/packages_list/packages_list_parser.h b/src/traced/probes/packages_list/packages_list_parser.h
new file mode 100644
index 0000000..3deb3c5
--- /dev/null
+++ b/src/traced/probes/packages_list/packages_list_parser.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef SRC_TRACED_PROBES_PACKAGES_LIST_PACKAGES_LIST_PARSER_H_
+#define SRC_TRACED_PROBES_PACKAGES_LIST_PACKAGES_LIST_PARSER_H_
+
+#include <inttypes.h>
+#include <string>
+
+namespace perfetto {
+
+struct Package {
+ std::string name;
+ uint64_t uid = 0;
+ bool debuggable = false;
+ bool profileable_from_shell = false;
+ int64_t version_code = 0;
+};
+
+bool ReadPackagesListLine(char* line, Package* package);
+} // namespace perfetto
+
+#endif // SRC_TRACED_PROBES_PACKAGES_LIST_PACKAGES_LIST_PARSER_H_
diff --git a/src/traced/probes/packages_list/packages_list_data_source_unittest.cc b/src/traced/probes/packages_list/packages_list_unittest.cc
similarity index 98%
rename from src/traced/probes/packages_list/packages_list_data_source_unittest.cc
rename to src/traced/probes/packages_list/packages_list_unittest.cc
index cb311d2..acfc791 100644
--- a/src/traced/probes/packages_list/packages_list_data_source_unittest.cc
+++ b/src/traced/probes/packages_list/packages_list_unittest.cc
@@ -25,6 +25,7 @@
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "protos/perfetto/trace/android/packages_list.gen.h"
#include "protos/perfetto/trace/android/packages_list.pbzero.h"
+#include "src/traced/probes/packages_list/packages_list_parser.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {