1 /*
2 * Copyright (C) 2020 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/trace_processor/util/profiler_util.h"
18
19 #include <cstddef>
20 #include <optional>
21 #include <string>
22
23 #include "perfetto/base/logging.h"
24 #include "perfetto/ext/base/string_utils.h"
25 #include "perfetto/ext/base/string_view.h"
26 #include "src/trace_processor/storage/stats.h"
27 #include "src/trace_processor/storage/trace_storage.h"
28
29 #include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
30
31 namespace perfetto::trace_processor {
32 namespace {
33
34 // Try to extract the package name from a path like:
35 // * /data/app/[packageName]-[randomString]/base.apk
36 // * /data/app/~~[randomStringA]/[packageName]-[randomStringB]/base.apk
37 // The latter is newer (R+), and was added to avoid leaking package names via
38 // mountinfo of incremental apk mounts.
PackageFromApp(base::StringView location)39 std::optional<base::StringView> PackageFromApp(base::StringView location) {
40 location = location.substr(base::StringView("/data/app/").size());
41 size_t start = 0;
42 if (location.at(0) == '~') {
43 size_t slash = location.find('/');
44 if (slash == base::StringView::npos) {
45 return std::nullopt;
46 }
47 start = slash + 1;
48 }
49 size_t end = location.find('/', start + 1);
50 if (end == base::StringView::npos) {
51 return std::nullopt;
52 }
53 location = location.substr(start, end);
54 size_t minus = location.find('-');
55 if (minus == base::StringView::npos) {
56 return std::nullopt;
57 }
58 return location.substr(0, minus);
59 }
60
61 } // namespace
62
PackageFromLocation(TraceStorage * storage,base::StringView location)63 std::optional<std::string> PackageFromLocation(TraceStorage* storage,
64 base::StringView location) {
65 // List of some hardcoded apps that do not follow the scheme used in
66 // PackageFromApp. Ask for yours to be added.
67 //
68 // TODO(b/153632336): Get rid of the hardcoded list of system apps.
69 base::StringView sysui(
70 "/system_ext/priv-app/SystemUIGoogle/SystemUIGoogle.apk");
71 if (location.size() >= sysui.size() &&
72 location.substr(0, sysui.size()) == sysui) {
73 return "com.android.systemui";
74 }
75
76 base::StringView phonesky("/product/priv-app/Phonesky/Phonesky.apk");
77 if (location.size() >= phonesky.size() &&
78 location.substr(0, phonesky.size()) == phonesky) {
79 return "com.android.vending";
80 }
81
82 base::StringView maps("/product/app/Maps/Maps.apk");
83 if (location.size() >= maps.size() &&
84 location.substr(0, maps.size()) == maps) {
85 return "com.google.android.apps.maps";
86 }
87
88 base::StringView launcher(
89 "/system_ext/priv-app/NexusLauncherRelease/NexusLauncherRelease.apk");
90 if (location.size() >= launcher.size() &&
91 location.substr(0, launcher.size()) == launcher) {
92 return "com.google.android.apps.nexuslauncher";
93 }
94
95 base::StringView photos("/product/app/Photos/Photos.apk");
96 if (location.size() >= photos.size() &&
97 location.substr(0, photos.size()) == photos) {
98 return "com.google.android.apps.photos";
99 }
100
101 base::StringView wellbeing(
102 "/product/priv-app/WellbeingPrebuilt/WellbeingPrebuilt.apk");
103 if (location.size() >= wellbeing.size() &&
104 location.substr(0, wellbeing.size()) == wellbeing) {
105 return "com.google.android.apps.wellbeing";
106 }
107
108 if (location.find("DevicePersonalizationPrebuilt") !=
109 base::StringView::npos ||
110 location.find("MatchMaker") != base::StringView::npos) {
111 return "com.google.android.as";
112 }
113
114 if (location.find("DeviceIntelligenceNetworkPrebuilt") !=
115 base::StringView::npos) {
116 return "com.google.android.as.oss";
117 }
118
119 if (location.find("SettingsIntelligenceGooglePrebuilt") !=
120 base::StringView::npos) {
121 return "com.google.android.settings.intelligence";
122 }
123
124 base::StringView gm("/product/app/PrebuiltGmail/PrebuiltGmail.apk");
125 if (location.size() >= gm.size() && location.substr(0, gm.size()) == gm) {
126 return "com.google.android.gm";
127 }
128
129 if (location.find("PrebuiltGmsCore") != base::StringView::npos ||
130 location.find("com.google.android.gms") != base::StringView::npos) {
131 return "com.google.android.gms";
132 }
133
134 base::StringView velvet("/product/priv-app/Velvet/Velvet.apk");
135 if (location.size() >= velvet.size() &&
136 location.substr(0, velvet.size()) == velvet) {
137 return "com.google.android.googlequicksearchbox";
138 }
139
140 base::StringView inputmethod(
141 "/product/app/LatinIMEGooglePrebuilt/LatinIMEGooglePrebuilt.apk");
142 if (location.size() >= inputmethod.size() &&
143 location.substr(0, inputmethod.size()) == inputmethod) {
144 return "com.google.android.inputmethod.latin";
145 }
146
147 base::StringView messaging("/product/app/PrebuiltBugle/PrebuiltBugle.apk");
148 if (location.size() >= messaging.size() &&
149 location.substr(0, messaging.size()) == messaging) {
150 return "com.google.android.apps.messaging";
151 }
152
153 // Deal with paths to /data/app/...
154
155 auto extract_package =
156 [storage](base::StringView path) -> std::optional<std::string> {
157 auto package = PackageFromApp(path);
158 if (!package) {
159 PERFETTO_DLOG("Failed to parse %s", path.ToStdString().c_str());
160 storage->IncrementStats(stats::deobfuscate_location_parse_error);
161 return std::nullopt;
162 }
163 return package->ToStdString();
164 };
165
166 base::StringView data_app("/data/app/");
167 size_t data_app_sz = data_app.size();
168 if (location.substr(0, data_app.size()) == data_app) {
169 return extract_package(location);
170 }
171
172 // Check for in-memory decompressed dexfile, example prefixes:
173 // * "[anon:dalvik-classes.dex extracted in memory from"
174 // * "/dev/ashmem/dalvik-classes.dex extracted in memory from"
175 // The latter form is for older devices (Android P and before).
176 // We cannot hardcode the filename since it could be for example
177 // "classes2.dex" for multidex apks.
178 base::StringView inmem_dex("dex extracted in memory from /data/app/");
179 size_t match_pos = location.find(inmem_dex);
180 if (match_pos != base::StringView::npos) {
181 auto data_app_path =
182 location.substr(match_pos + inmem_dex.size() - data_app_sz);
183 return extract_package(data_app_path);
184 }
185
186 return std::nullopt;
187 }
188
FullyQualifiedDeobfuscatedName(protos::pbzero::ObfuscatedClass::Decoder & cls,protos::pbzero::ObfuscatedMember::Decoder & member)189 std::string FullyQualifiedDeobfuscatedName(
190 protos::pbzero::ObfuscatedClass::Decoder& cls,
191 protos::pbzero::ObfuscatedMember::Decoder& member) {
192 std::string member_deobfuscated_name =
193 member.deobfuscated_name().ToStdString();
194 if (base::Contains(member_deobfuscated_name, '.')) {
195 // Fully qualified name.
196 return member_deobfuscated_name;
197 } else {
198 // Name relative to class.
199 return cls.deobfuscated_name().ToStdString() + "." +
200 member_deobfuscated_name;
201 }
202 }
203
204 } // namespace perfetto::trace_processor
205