• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/traced/probes/packages_list/packages_list_data_source.h"
18 
19 #include "perfetto/base/scoped_file.h"
20 #include "perfetto/base/string_splitter.h"
21 #include "perfetto/config/android/packages_list_config.pbzero.h"
22 #include "perfetto/trace/android/packages_list.pbzero.h"
23 #include "perfetto/trace/trace_packet.pbzero.h"
24 #include "perfetto/tracing/core/data_source_config.h"
25 #include "perfetto/tracing/core/packages_list_config.h"
26 #include "perfetto/tracing/core/trace_writer.h"
27 
28 using perfetto::protos::pbzero::PackagesListConfig;
29 
30 namespace perfetto {
31 
ParsePackagesListStream(protos::pbzero::PackagesList * packages_list_packet,const base::ScopedFstream & fs,const std::set<std::string> & package_name_filter)32 bool ParsePackagesListStream(protos::pbzero::PackagesList* packages_list_packet,
33                              const base::ScopedFstream& fs,
34                              const std::set<std::string>& package_name_filter) {
35   bool parsed_fully = true;
36   char line[2048];
37   while (fgets(line, sizeof(line), *fs) != nullptr) {
38     Package pkg_struct;
39     if (!ReadPackagesListLine(line, &pkg_struct)) {
40       parsed_fully = false;
41       continue;
42     }
43     if (!package_name_filter.empty() &&
44         package_name_filter.count(pkg_struct.name) == 0) {
45       continue;
46     }
47     auto* package = packages_list_packet->add_packages();
48     package->set_name(pkg_struct.name.c_str(), pkg_struct.name.size());
49     package->set_uid(pkg_struct.uid);
50     package->set_debuggable(pkg_struct.debuggable);
51     package->set_profileable_from_shell(pkg_struct.profileable_from_shell);
52     package->set_version_code(pkg_struct.version_code);
53   }
54   return parsed_fully;
55 }
56 
ReadPackagesListLine(char * line,Package * package)57 bool ReadPackagesListLine(char* line, Package* package) {
58   size_t idx = 0;
59   for (base::StringSplitter ss(line, ' '); ss.Next();) {
60     switch (idx) {
61       case 0:
62         package->name = std::string(ss.cur_token(), ss.cur_token_size());
63         break;
64       case 1: {
65         char* end;
66         long long uid = strtoll(ss.cur_token(), &end, 10);
67         if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
68           PERFETTO_ELOG("Failed to parse packages.list uid.");
69           return false;
70         }
71         package->uid = static_cast<uint64_t>(uid);
72         break;
73       }
74       case 2: {
75         char* end;
76         long long debuggable = strtoll(ss.cur_token(), &end, 10);
77         if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
78           PERFETTO_ELOG("Failed to parse packages.list debuggable.");
79           return false;
80         }
81         package->debuggable = debuggable != 0;
82         break;
83       }
84       case 6: {
85         char* end;
86         long long profilable_from_shell = strtoll(ss.cur_token(), &end, 10);
87         if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
88           PERFETTO_ELOG("Failed to parse packages.list profilable_from_shell.");
89           return false;
90         }
91         package->profileable_from_shell = profilable_from_shell != 0;
92         break;
93       }
94       case 7: {
95         char* end;
96         long long version_code = strtoll(ss.cur_token(), &end, 10);
97         if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
98           PERFETTO_ELOG("Failed to parse packages.list version_code: %s.",
99                         ss.cur_token());
100           return false;
101         }
102         package->version_code = version_code;
103         break;
104       }
105     }
106     ++idx;
107   }
108   return true;
109 }
110 
PackagesListDataSource(const DataSourceConfig & ds_config,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer)111 PackagesListDataSource::PackagesListDataSource(
112     const DataSourceConfig& ds_config,
113     TracingSessionID session_id,
114     std::unique_ptr<TraceWriter> writer)
115     : ProbesDataSource(session_id, kTypeId), writer_(std::move(writer)) {
116   for (const auto& name :
117        ds_config.packages_list_config().package_name_filter()) {
118     package_name_filter_.emplace(name);
119   }
120 }
121 
Start()122 void PackagesListDataSource::Start() {
123   base::ScopedFstream fs(fopen("/data/system/packages.list", "r"));
124   auto trace_packet = writer_->NewTracePacket();
125   auto* packages_list_packet = trace_packet->set_packages_list();
126   if (!fs) {
127     PERFETTO_ELOG("Failed to open packages.list");
128     packages_list_packet->set_read_error(true);
129     trace_packet->Finalize();
130     writer_->Flush();
131     return;
132   }
133 
134   bool parsed_fully =
135       ParsePackagesListStream(packages_list_packet, fs, package_name_filter_);
136   if (!parsed_fully)
137     packages_list_packet->set_parse_error(true);
138 
139   if (ferror(*fs))
140     packages_list_packet->set_read_error(true);
141 
142   trace_packet->Finalize();
143   writer_->Flush();
144 }
145 
Flush(FlushRequestID,std::function<void ()> callback)146 void PackagesListDataSource::Flush(FlushRequestID,
147                                    std::function<void()> callback) {
148   // Flush is no-op. We flush after the first write.
149   callback();
150 }
151 
152 PackagesListDataSource::~PackagesListDataSource() = default;
153 
154 }  // namespace perfetto
155