1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker * Copyright (C) 2020 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker *
4*288bf522SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker *
8*288bf522SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker *
10*288bf522SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker */
16*288bf522SAndroid Build Coastguard Worker
17*288bf522SAndroid Build Coastguard Worker #include "report_utils.h"
18*288bf522SAndroid Build Coastguard Worker
19*288bf522SAndroid Build Coastguard Worker #include <stdlib.h>
20*288bf522SAndroid Build Coastguard Worker
21*288bf522SAndroid Build Coastguard Worker #include <android-base/parsebool.h>
22*288bf522SAndroid Build Coastguard Worker #include <android-base/scopeguard.h>
23*288bf522SAndroid Build Coastguard Worker #include <android-base/strings.h>
24*288bf522SAndroid Build Coastguard Worker
25*288bf522SAndroid Build Coastguard Worker #include "JITDebugReader.h"
26*288bf522SAndroid Build Coastguard Worker #include "RegEx.h"
27*288bf522SAndroid Build Coastguard Worker #include "utils.h"
28*288bf522SAndroid Build Coastguard Worker
29*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
30*288bf522SAndroid Build Coastguard Worker
AddProguardMappingFile(std::string_view mapping_file)31*288bf522SAndroid Build Coastguard Worker bool ProguardMappingRetrace::AddProguardMappingFile(std::string_view mapping_file) {
32*288bf522SAndroid Build Coastguard Worker // The mapping file format is described in
33*288bf522SAndroid Build Coastguard Worker // https://www.guardsquare.com/en/products/proguard/manual/retrace.
34*288bf522SAndroid Build Coastguard Worker // Additional info provided by R8 is described in
35*288bf522SAndroid Build Coastguard Worker // https://r8.googlesource.com/r8/+/refs/heads/main/doc/retrace.md.
36*288bf522SAndroid Build Coastguard Worker line_reader_.reset(new LineReader(mapping_file));
37*288bf522SAndroid Build Coastguard Worker android::base::ScopeGuard g([&]() { line_reader_ = nullptr; });
38*288bf522SAndroid Build Coastguard Worker
39*288bf522SAndroid Build Coastguard Worker if (!line_reader_->Ok()) {
40*288bf522SAndroid Build Coastguard Worker PLOG(ERROR) << "failed to read " << mapping_file;
41*288bf522SAndroid Build Coastguard Worker return false;
42*288bf522SAndroid Build Coastguard Worker }
43*288bf522SAndroid Build Coastguard Worker
44*288bf522SAndroid Build Coastguard Worker MoveToNextLine();
45*288bf522SAndroid Build Coastguard Worker while (cur_line_.type != LineType::LINE_EOF) {
46*288bf522SAndroid Build Coastguard Worker if (cur_line_.type == LineType::CLASS_LINE) {
47*288bf522SAndroid Build Coastguard Worker // Match line "original_classname -> obfuscated_classname:".
48*288bf522SAndroid Build Coastguard Worker std::string_view s = cur_line_.data;
49*288bf522SAndroid Build Coastguard Worker auto arrow_pos = s.find(" -> ");
50*288bf522SAndroid Build Coastguard Worker auto arrow_end_pos = arrow_pos + strlen(" -> ");
51*288bf522SAndroid Build Coastguard Worker if (auto colon_pos = s.find(':', arrow_end_pos); colon_pos != s.npos) {
52*288bf522SAndroid Build Coastguard Worker std::string_view original_classname = s.substr(0, arrow_pos);
53*288bf522SAndroid Build Coastguard Worker std::string obfuscated_classname(s.substr(arrow_end_pos, colon_pos - arrow_end_pos));
54*288bf522SAndroid Build Coastguard Worker MappingClass& cur_class = class_map_[obfuscated_classname];
55*288bf522SAndroid Build Coastguard Worker cur_class.original_classname = original_classname;
56*288bf522SAndroid Build Coastguard Worker MoveToNextLine();
57*288bf522SAndroid Build Coastguard Worker if (cur_line_.type == LineType::SYNTHESIZED_COMMENT) {
58*288bf522SAndroid Build Coastguard Worker cur_class.synthesized = true;
59*288bf522SAndroid Build Coastguard Worker MoveToNextLine();
60*288bf522SAndroid Build Coastguard Worker }
61*288bf522SAndroid Build Coastguard Worker
62*288bf522SAndroid Build Coastguard Worker while (cur_line_.type == LineType::METHOD_LINE) {
63*288bf522SAndroid Build Coastguard Worker ParseMethod(cur_class);
64*288bf522SAndroid Build Coastguard Worker }
65*288bf522SAndroid Build Coastguard Worker continue;
66*288bf522SAndroid Build Coastguard Worker }
67*288bf522SAndroid Build Coastguard Worker }
68*288bf522SAndroid Build Coastguard Worker
69*288bf522SAndroid Build Coastguard Worker // Skip unparsed line.
70*288bf522SAndroid Build Coastguard Worker MoveToNextLine();
71*288bf522SAndroid Build Coastguard Worker }
72*288bf522SAndroid Build Coastguard Worker return true;
73*288bf522SAndroid Build Coastguard Worker }
74*288bf522SAndroid Build Coastguard Worker
ParseMethod(MappingClass & mapping_class)75*288bf522SAndroid Build Coastguard Worker void ProguardMappingRetrace::ParseMethod(MappingClass& mapping_class) {
76*288bf522SAndroid Build Coastguard Worker // Match line "... [original_classname.]original_methodname(...)... -> obfuscated_methodname".
77*288bf522SAndroid Build Coastguard Worker std::string_view s = cur_line_.data;
78*288bf522SAndroid Build Coastguard Worker auto arrow_pos = s.find(" -> ");
79*288bf522SAndroid Build Coastguard Worker auto arrow_end_pos = arrow_pos + strlen(" -> ");
80*288bf522SAndroid Build Coastguard Worker if (auto left_brace_pos = s.rfind('(', arrow_pos); left_brace_pos != s.npos) {
81*288bf522SAndroid Build Coastguard Worker if (auto space_pos = s.rfind(' ', left_brace_pos); space_pos != s.npos) {
82*288bf522SAndroid Build Coastguard Worker std::string_view name = s.substr(space_pos + 1, left_brace_pos - space_pos - 1);
83*288bf522SAndroid Build Coastguard Worker bool contains_classname = name.find('.') != name.npos;
84*288bf522SAndroid Build Coastguard Worker if (contains_classname && android::base::StartsWith(name, mapping_class.original_classname)) {
85*288bf522SAndroid Build Coastguard Worker name.remove_prefix(mapping_class.original_classname.size() + 1);
86*288bf522SAndroid Build Coastguard Worker contains_classname = false;
87*288bf522SAndroid Build Coastguard Worker }
88*288bf522SAndroid Build Coastguard Worker std::string original_methodname(name);
89*288bf522SAndroid Build Coastguard Worker std::string obfuscated_methodname(s.substr(arrow_end_pos));
90*288bf522SAndroid Build Coastguard Worker bool synthesized = false;
91*288bf522SAndroid Build Coastguard Worker
92*288bf522SAndroid Build Coastguard Worker MoveToNextLine();
93*288bf522SAndroid Build Coastguard Worker if (cur_line_.type == LineType::SYNTHESIZED_COMMENT) {
94*288bf522SAndroid Build Coastguard Worker synthesized = true;
95*288bf522SAndroid Build Coastguard Worker MoveToNextLine();
96*288bf522SAndroid Build Coastguard Worker }
97*288bf522SAndroid Build Coastguard Worker
98*288bf522SAndroid Build Coastguard Worker auto& method_map = mapping_class.method_map;
99*288bf522SAndroid Build Coastguard Worker if (auto it = method_map.find(obfuscated_methodname); it != method_map.end()) {
100*288bf522SAndroid Build Coastguard Worker // The obfuscated method name already exists. We don't know which one to choose.
101*288bf522SAndroid Build Coastguard Worker // So just prefer the latter one unless it's synthesized.
102*288bf522SAndroid Build Coastguard Worker if (!synthesized) {
103*288bf522SAndroid Build Coastguard Worker it->second.original_name = original_methodname;
104*288bf522SAndroid Build Coastguard Worker it->second.contains_classname = contains_classname;
105*288bf522SAndroid Build Coastguard Worker it->second.synthesized = synthesized;
106*288bf522SAndroid Build Coastguard Worker }
107*288bf522SAndroid Build Coastguard Worker } else {
108*288bf522SAndroid Build Coastguard Worker auto& method = method_map[obfuscated_methodname];
109*288bf522SAndroid Build Coastguard Worker method.original_name = original_methodname;
110*288bf522SAndroid Build Coastguard Worker method.contains_classname = contains_classname;
111*288bf522SAndroid Build Coastguard Worker method.synthesized = synthesized;
112*288bf522SAndroid Build Coastguard Worker }
113*288bf522SAndroid Build Coastguard Worker return;
114*288bf522SAndroid Build Coastguard Worker }
115*288bf522SAndroid Build Coastguard Worker }
116*288bf522SAndroid Build Coastguard Worker
117*288bf522SAndroid Build Coastguard Worker // Skip unparsed line.
118*288bf522SAndroid Build Coastguard Worker MoveToNextLine();
119*288bf522SAndroid Build Coastguard Worker }
120*288bf522SAndroid Build Coastguard Worker
MoveToNextLine()121*288bf522SAndroid Build Coastguard Worker void ProguardMappingRetrace::MoveToNextLine() {
122*288bf522SAndroid Build Coastguard Worker std::string* line;
123*288bf522SAndroid Build Coastguard Worker while ((line = line_reader_->ReadLine()) != nullptr) {
124*288bf522SAndroid Build Coastguard Worker std::string_view s = *line;
125*288bf522SAndroid Build Coastguard Worker if (s.empty()) {
126*288bf522SAndroid Build Coastguard Worker continue;
127*288bf522SAndroid Build Coastguard Worker }
128*288bf522SAndroid Build Coastguard Worker size_t non_space_pos = s.find_first_not_of(' ');
129*288bf522SAndroid Build Coastguard Worker if (non_space_pos != s.npos && s[non_space_pos] == '#') {
130*288bf522SAndroid Build Coastguard Worker // Skip all comments unless it's synthesized comment.
131*288bf522SAndroid Build Coastguard Worker if (s.find("com.android.tools.r8.synthesized") != s.npos) {
132*288bf522SAndroid Build Coastguard Worker cur_line_.type = SYNTHESIZED_COMMENT;
133*288bf522SAndroid Build Coastguard Worker cur_line_.data = s;
134*288bf522SAndroid Build Coastguard Worker return;
135*288bf522SAndroid Build Coastguard Worker }
136*288bf522SAndroid Build Coastguard Worker continue;
137*288bf522SAndroid Build Coastguard Worker }
138*288bf522SAndroid Build Coastguard Worker if (s.find(" -> ") == s.npos) {
139*288bf522SAndroid Build Coastguard Worker // Skip unknown lines.
140*288bf522SAndroid Build Coastguard Worker continue;
141*288bf522SAndroid Build Coastguard Worker }
142*288bf522SAndroid Build Coastguard Worker cur_line_.data = s;
143*288bf522SAndroid Build Coastguard Worker if (s[0] == ' ') {
144*288bf522SAndroid Build Coastguard Worker cur_line_.type = METHOD_LINE;
145*288bf522SAndroid Build Coastguard Worker } else {
146*288bf522SAndroid Build Coastguard Worker cur_line_.type = CLASS_LINE;
147*288bf522SAndroid Build Coastguard Worker }
148*288bf522SAndroid Build Coastguard Worker return;
149*288bf522SAndroid Build Coastguard Worker }
150*288bf522SAndroid Build Coastguard Worker cur_line_.type = LINE_EOF;
151*288bf522SAndroid Build Coastguard Worker }
152*288bf522SAndroid Build Coastguard Worker
DeObfuscateJavaMethods(std::string_view obfuscated_name,std::string * original_name,bool * synthesized)153*288bf522SAndroid Build Coastguard Worker bool ProguardMappingRetrace::DeObfuscateJavaMethods(std::string_view obfuscated_name,
154*288bf522SAndroid Build Coastguard Worker std::string* original_name, bool* synthesized) {
155*288bf522SAndroid Build Coastguard Worker if (auto split_pos = obfuscated_name.rfind('.'); split_pos != obfuscated_name.npos) {
156*288bf522SAndroid Build Coastguard Worker std::string obfuscated_classname(obfuscated_name.substr(0, split_pos));
157*288bf522SAndroid Build Coastguard Worker
158*288bf522SAndroid Build Coastguard Worker if (auto it = class_map_.find(obfuscated_classname); it != class_map_.end()) {
159*288bf522SAndroid Build Coastguard Worker const MappingClass& mapping_class = it->second;
160*288bf522SAndroid Build Coastguard Worker const auto& method_map = mapping_class.method_map;
161*288bf522SAndroid Build Coastguard Worker std::string obfuscated_methodname(obfuscated_name.substr(split_pos + 1));
162*288bf522SAndroid Build Coastguard Worker
163*288bf522SAndroid Build Coastguard Worker if (auto method_it = method_map.find(obfuscated_methodname); method_it != method_map.end()) {
164*288bf522SAndroid Build Coastguard Worker const auto& method = method_it->second;
165*288bf522SAndroid Build Coastguard Worker if (method.contains_classname) {
166*288bf522SAndroid Build Coastguard Worker *original_name = method.original_name;
167*288bf522SAndroid Build Coastguard Worker } else {
168*288bf522SAndroid Build Coastguard Worker *original_name = mapping_class.original_classname + "." + method.original_name;
169*288bf522SAndroid Build Coastguard Worker }
170*288bf522SAndroid Build Coastguard Worker *synthesized = method.synthesized;
171*288bf522SAndroid Build Coastguard Worker } else {
172*288bf522SAndroid Build Coastguard Worker // Only the classname is obfuscated.
173*288bf522SAndroid Build Coastguard Worker *original_name = mapping_class.original_classname + "." + obfuscated_methodname;
174*288bf522SAndroid Build Coastguard Worker *synthesized = mapping_class.synthesized;
175*288bf522SAndroid Build Coastguard Worker }
176*288bf522SAndroid Build Coastguard Worker return true;
177*288bf522SAndroid Build Coastguard Worker }
178*288bf522SAndroid Build Coastguard Worker }
179*288bf522SAndroid Build Coastguard Worker return false;
180*288bf522SAndroid Build Coastguard Worker }
181*288bf522SAndroid Build Coastguard Worker
IsArtEntry(const CallChainReportEntry & entry,bool * is_jni_trampoline)182*288bf522SAndroid Build Coastguard Worker static bool IsArtEntry(const CallChainReportEntry& entry, bool* is_jni_trampoline) {
183*288bf522SAndroid Build Coastguard Worker if (entry.execution_type == CallChainExecutionType::NATIVE_METHOD) {
184*288bf522SAndroid Build Coastguard Worker // art_jni_trampoline/art_quick_generic_jni_trampoline are trampolines used to call jni
185*288bf522SAndroid Build Coastguard Worker // methods in art runtime. We want to hide them when hiding art frames.
186*288bf522SAndroid Build Coastguard Worker *is_jni_trampoline = android::base::EndsWith(entry.symbol->Name(), "jni_trampoline");
187*288bf522SAndroid Build Coastguard Worker return *is_jni_trampoline || android::base::EndsWith(entry.dso->Path(), "/libart.so") ||
188*288bf522SAndroid Build Coastguard Worker android::base::EndsWith(entry.dso->Path(), "/libartd.so");
189*288bf522SAndroid Build Coastguard Worker }
190*288bf522SAndroid Build Coastguard Worker return false;
191*288bf522SAndroid Build Coastguard Worker };
192*288bf522SAndroid Build Coastguard Worker
~CallChainReportModifier()193*288bf522SAndroid Build Coastguard Worker CallChainReportModifier::~CallChainReportModifier() {}
194*288bf522SAndroid Build Coastguard Worker
195*288bf522SAndroid Build Coastguard Worker // Remove art frames.
196*288bf522SAndroid Build Coastguard Worker class ArtFrameRemover : public CallChainReportModifier {
197*288bf522SAndroid Build Coastguard Worker public:
Modify(std::vector<CallChainReportEntry> & callchain)198*288bf522SAndroid Build Coastguard Worker void Modify(std::vector<CallChainReportEntry>& callchain) override {
199*288bf522SAndroid Build Coastguard Worker auto it =
200*288bf522SAndroid Build Coastguard Worker std::remove_if(callchain.begin(), callchain.end(), [](const CallChainReportEntry& entry) {
201*288bf522SAndroid Build Coastguard Worker return entry.execution_type == CallChainExecutionType::ART_METHOD;
202*288bf522SAndroid Build Coastguard Worker });
203*288bf522SAndroid Build Coastguard Worker callchain.erase(it, callchain.end());
204*288bf522SAndroid Build Coastguard Worker }
205*288bf522SAndroid Build Coastguard Worker };
206*288bf522SAndroid Build Coastguard Worker
207*288bf522SAndroid Build Coastguard Worker // Convert JIT methods to their corresponding interpreted Java methods.
208*288bf522SAndroid Build Coastguard Worker class JITFrameConverter : public CallChainReportModifier {
209*288bf522SAndroid Build Coastguard Worker public:
JITFrameConverter(const ThreadTree & thread_tree)210*288bf522SAndroid Build Coastguard Worker JITFrameConverter(const ThreadTree& thread_tree) : thread_tree_(thread_tree) {}
211*288bf522SAndroid Build Coastguard Worker
Modify(std::vector<CallChainReportEntry> & callchain)212*288bf522SAndroid Build Coastguard Worker void Modify(std::vector<CallChainReportEntry>& callchain) override {
213*288bf522SAndroid Build Coastguard Worker CollectJavaMethods();
214*288bf522SAndroid Build Coastguard Worker for (size_t i = 0; i < callchain.size();) {
215*288bf522SAndroid Build Coastguard Worker auto& entry = callchain[i];
216*288bf522SAndroid Build Coastguard Worker if (entry.execution_type == CallChainExecutionType::JIT_JVM_METHOD) {
217*288bf522SAndroid Build Coastguard Worker // This is a JIT java method, merge it with the interpreted java method having the same
218*288bf522SAndroid Build Coastguard Worker // name if possible. Otherwise, merge it with other JIT java methods having the same name
219*288bf522SAndroid Build Coastguard Worker // by assigning a common dso_name.
220*288bf522SAndroid Build Coastguard Worker if (auto it = java_method_map_.find(std::string(entry.symbol->FunctionName()));
221*288bf522SAndroid Build Coastguard Worker it != java_method_map_.end()) {
222*288bf522SAndroid Build Coastguard Worker entry.dso = it->second.dso;
223*288bf522SAndroid Build Coastguard Worker entry.symbol = it->second.symbol;
224*288bf522SAndroid Build Coastguard Worker // Not enough info to map an offset in a JIT method to an offset in a dex file. So just
225*288bf522SAndroid Build Coastguard Worker // use the symbol_addr.
226*288bf522SAndroid Build Coastguard Worker entry.vaddr_in_file = entry.symbol->addr;
227*288bf522SAndroid Build Coastguard Worker
228*288bf522SAndroid Build Coastguard Worker // ART may call from an interpreted Java method into its corresponding JIT method. To
229*288bf522SAndroid Build Coastguard Worker // avoid showing the method calling itself, remove the JIT frame.
230*288bf522SAndroid Build Coastguard Worker if (i + 1 < callchain.size() && callchain[i + 1].dso == entry.dso &&
231*288bf522SAndroid Build Coastguard Worker callchain[i + 1].symbol == entry.symbol) {
232*288bf522SAndroid Build Coastguard Worker callchain.erase(callchain.begin() + i);
233*288bf522SAndroid Build Coastguard Worker continue;
234*288bf522SAndroid Build Coastguard Worker }
235*288bf522SAndroid Build Coastguard Worker
236*288bf522SAndroid Build Coastguard Worker } else if (!JITDebugReader::IsPathInJITSymFile(entry.dso->Path())) {
237*288bf522SAndroid Build Coastguard Worker // Old JITSymFiles use names like "TemporaryFile-XXXXXX". So give them a better name.
238*288bf522SAndroid Build Coastguard Worker entry.dso_name = "[JIT cache]";
239*288bf522SAndroid Build Coastguard Worker }
240*288bf522SAndroid Build Coastguard Worker }
241*288bf522SAndroid Build Coastguard Worker i++;
242*288bf522SAndroid Build Coastguard Worker }
243*288bf522SAndroid Build Coastguard Worker }
244*288bf522SAndroid Build Coastguard Worker
245*288bf522SAndroid Build Coastguard Worker private:
246*288bf522SAndroid Build Coastguard Worker struct JavaMethod {
247*288bf522SAndroid Build Coastguard Worker Dso* dso;
248*288bf522SAndroid Build Coastguard Worker const Symbol* symbol;
JavaMethodsimpleperf::JITFrameConverter::JavaMethod249*288bf522SAndroid Build Coastguard Worker JavaMethod(Dso* dso, const Symbol* symbol) : dso(dso), symbol(symbol) {}
250*288bf522SAndroid Build Coastguard Worker };
251*288bf522SAndroid Build Coastguard Worker
CollectJavaMethods()252*288bf522SAndroid Build Coastguard Worker void CollectJavaMethods() {
253*288bf522SAndroid Build Coastguard Worker if (!java_method_initialized_) {
254*288bf522SAndroid Build Coastguard Worker java_method_initialized_ = true;
255*288bf522SAndroid Build Coastguard Worker for (Dso* dso : thread_tree_.GetAllDsos()) {
256*288bf522SAndroid Build Coastguard Worker if (dso->type() == DSO_DEX_FILE) {
257*288bf522SAndroid Build Coastguard Worker dso->LoadSymbols();
258*288bf522SAndroid Build Coastguard Worker for (auto& symbol : dso->GetSymbols()) {
259*288bf522SAndroid Build Coastguard Worker java_method_map_.emplace(symbol.Name(), JavaMethod(dso, &symbol));
260*288bf522SAndroid Build Coastguard Worker }
261*288bf522SAndroid Build Coastguard Worker }
262*288bf522SAndroid Build Coastguard Worker }
263*288bf522SAndroid Build Coastguard Worker }
264*288bf522SAndroid Build Coastguard Worker }
265*288bf522SAndroid Build Coastguard Worker
266*288bf522SAndroid Build Coastguard Worker const ThreadTree& thread_tree_;
267*288bf522SAndroid Build Coastguard Worker bool java_method_initialized_ = false;
268*288bf522SAndroid Build Coastguard Worker std::unordered_map<std::string, JavaMethod> java_method_map_;
269*288bf522SAndroid Build Coastguard Worker };
270*288bf522SAndroid Build Coastguard Worker
271*288bf522SAndroid Build Coastguard Worker // Use proguard mapping.txt to de-obfuscate minified symbols.
272*288bf522SAndroid Build Coastguard Worker class JavaMethodDeobfuscater : public CallChainReportModifier {
273*288bf522SAndroid Build Coastguard Worker public:
JavaMethodDeobfuscater(bool remove_r8_synthesized_frame)274*288bf522SAndroid Build Coastguard Worker JavaMethodDeobfuscater(bool remove_r8_synthesized_frame)
275*288bf522SAndroid Build Coastguard Worker : remove_r8_synthesized_frame_(remove_r8_synthesized_frame) {}
276*288bf522SAndroid Build Coastguard Worker
AddProguardMappingFile(std::string_view mapping_file)277*288bf522SAndroid Build Coastguard Worker bool AddProguardMappingFile(std::string_view mapping_file) {
278*288bf522SAndroid Build Coastguard Worker return retrace_.AddProguardMappingFile(mapping_file);
279*288bf522SAndroid Build Coastguard Worker }
280*288bf522SAndroid Build Coastguard Worker
Modify(std::vector<CallChainReportEntry> & callchain)281*288bf522SAndroid Build Coastguard Worker void Modify(std::vector<CallChainReportEntry>& callchain) override {
282*288bf522SAndroid Build Coastguard Worker for (size_t i = 0; i < callchain.size();) {
283*288bf522SAndroid Build Coastguard Worker auto& entry = callchain[i];
284*288bf522SAndroid Build Coastguard Worker if (!IsJavaEntry(entry)) {
285*288bf522SAndroid Build Coastguard Worker i++;
286*288bf522SAndroid Build Coastguard Worker continue;
287*288bf522SAndroid Build Coastguard Worker }
288*288bf522SAndroid Build Coastguard Worker std::string_view name = entry.symbol->FunctionName();
289*288bf522SAndroid Build Coastguard Worker std::string original_name;
290*288bf522SAndroid Build Coastguard Worker bool synthesized;
291*288bf522SAndroid Build Coastguard Worker if (retrace_.DeObfuscateJavaMethods(name, &original_name, &synthesized)) {
292*288bf522SAndroid Build Coastguard Worker if (synthesized && remove_r8_synthesized_frame_) {
293*288bf522SAndroid Build Coastguard Worker callchain.erase(callchain.begin() + i);
294*288bf522SAndroid Build Coastguard Worker continue;
295*288bf522SAndroid Build Coastguard Worker }
296*288bf522SAndroid Build Coastguard Worker entry.symbol->SetDemangledName(original_name);
297*288bf522SAndroid Build Coastguard Worker }
298*288bf522SAndroid Build Coastguard Worker i++;
299*288bf522SAndroid Build Coastguard Worker }
300*288bf522SAndroid Build Coastguard Worker }
301*288bf522SAndroid Build Coastguard Worker
302*288bf522SAndroid Build Coastguard Worker private:
IsJavaEntry(const CallChainReportEntry & entry)303*288bf522SAndroid Build Coastguard Worker bool IsJavaEntry(const CallChainReportEntry& entry) {
304*288bf522SAndroid Build Coastguard Worker static const char* COMPILED_JAVA_FILE_SUFFIXES[] = {".odex", ".oat", ".dex"};
305*288bf522SAndroid Build Coastguard Worker if (entry.execution_type == CallChainExecutionType::JIT_JVM_METHOD ||
306*288bf522SAndroid Build Coastguard Worker entry.execution_type == CallChainExecutionType::INTERPRETED_JVM_METHOD) {
307*288bf522SAndroid Build Coastguard Worker return true;
308*288bf522SAndroid Build Coastguard Worker }
309*288bf522SAndroid Build Coastguard Worker if (entry.execution_type == CallChainExecutionType::NATIVE_METHOD) {
310*288bf522SAndroid Build Coastguard Worker const std::string& path = entry.dso->Path();
311*288bf522SAndroid Build Coastguard Worker for (const char* suffix : COMPILED_JAVA_FILE_SUFFIXES) {
312*288bf522SAndroid Build Coastguard Worker if (android::base::EndsWith(path, suffix)) {
313*288bf522SAndroid Build Coastguard Worker return true;
314*288bf522SAndroid Build Coastguard Worker }
315*288bf522SAndroid Build Coastguard Worker }
316*288bf522SAndroid Build Coastguard Worker }
317*288bf522SAndroid Build Coastguard Worker return false;
318*288bf522SAndroid Build Coastguard Worker }
319*288bf522SAndroid Build Coastguard Worker
320*288bf522SAndroid Build Coastguard Worker const bool remove_r8_synthesized_frame_;
321*288bf522SAndroid Build Coastguard Worker ProguardMappingRetrace retrace_;
322*288bf522SAndroid Build Coastguard Worker };
323*288bf522SAndroid Build Coastguard Worker
324*288bf522SAndroid Build Coastguard Worker // Use regex to filter method names.
325*288bf522SAndroid Build Coastguard Worker class MethodNameFilter : public CallChainReportModifier {
326*288bf522SAndroid Build Coastguard Worker public:
RemoveMethod(std::string_view method_name_regex)327*288bf522SAndroid Build Coastguard Worker bool RemoveMethod(std::string_view method_name_regex) {
328*288bf522SAndroid Build Coastguard Worker if (auto regex = RegEx::Create(method_name_regex); regex != nullptr) {
329*288bf522SAndroid Build Coastguard Worker exclude_names_.emplace_back(std::move(regex));
330*288bf522SAndroid Build Coastguard Worker return true;
331*288bf522SAndroid Build Coastguard Worker }
332*288bf522SAndroid Build Coastguard Worker return false;
333*288bf522SAndroid Build Coastguard Worker }
334*288bf522SAndroid Build Coastguard Worker
Modify(std::vector<CallChainReportEntry> & callchain)335*288bf522SAndroid Build Coastguard Worker void Modify(std::vector<CallChainReportEntry>& callchain) override {
336*288bf522SAndroid Build Coastguard Worker auto it = std::remove_if(callchain.begin(), callchain.end(),
337*288bf522SAndroid Build Coastguard Worker [this](const CallChainReportEntry& entry) {
338*288bf522SAndroid Build Coastguard Worker return SearchInRegs(entry.symbol->DemangledName(), exclude_names_);
339*288bf522SAndroid Build Coastguard Worker });
340*288bf522SAndroid Build Coastguard Worker callchain.erase(it, callchain.end());
341*288bf522SAndroid Build Coastguard Worker }
342*288bf522SAndroid Build Coastguard Worker
343*288bf522SAndroid Build Coastguard Worker private:
344*288bf522SAndroid Build Coastguard Worker std::vector<std::unique_ptr<RegEx>> exclude_names_;
345*288bf522SAndroid Build Coastguard Worker };
346*288bf522SAndroid Build Coastguard Worker
CallChainReportBuilder(ThreadTree & thread_tree)347*288bf522SAndroid Build Coastguard Worker CallChainReportBuilder::CallChainReportBuilder(ThreadTree& thread_tree)
348*288bf522SAndroid Build Coastguard Worker : thread_tree_(thread_tree) {
349*288bf522SAndroid Build Coastguard Worker const char* env_name = "REMOVE_R8_SYNTHESIZED_FRAME";
350*288bf522SAndroid Build Coastguard Worker const char* s = getenv(env_name);
351*288bf522SAndroid Build Coastguard Worker if (s != nullptr) {
352*288bf522SAndroid Build Coastguard Worker auto result = android::base::ParseBool(s);
353*288bf522SAndroid Build Coastguard Worker if (result == android::base::ParseBoolResult::kError) {
354*288bf522SAndroid Build Coastguard Worker LOG(WARNING) << "invalid value in env variable " << env_name;
355*288bf522SAndroid Build Coastguard Worker } else if (result == android::base::ParseBoolResult::kTrue) {
356*288bf522SAndroid Build Coastguard Worker LOG(INFO) << "R8 synthesized frames will be removed.";
357*288bf522SAndroid Build Coastguard Worker remove_r8_synthesized_frame_ = true;
358*288bf522SAndroid Build Coastguard Worker }
359*288bf522SAndroid Build Coastguard Worker }
360*288bf522SAndroid Build Coastguard Worker SetRemoveArtFrame(true);
361*288bf522SAndroid Build Coastguard Worker SetConvertJITFrame(true);
362*288bf522SAndroid Build Coastguard Worker }
363*288bf522SAndroid Build Coastguard Worker
SetRemoveArtFrame(bool enable)364*288bf522SAndroid Build Coastguard Worker void CallChainReportBuilder::SetRemoveArtFrame(bool enable) {
365*288bf522SAndroid Build Coastguard Worker if (enable) {
366*288bf522SAndroid Build Coastguard Worker art_frame_remover_.reset(new ArtFrameRemover);
367*288bf522SAndroid Build Coastguard Worker } else {
368*288bf522SAndroid Build Coastguard Worker art_frame_remover_.reset(nullptr);
369*288bf522SAndroid Build Coastguard Worker }
370*288bf522SAndroid Build Coastguard Worker }
371*288bf522SAndroid Build Coastguard Worker
SetConvertJITFrame(bool enable)372*288bf522SAndroid Build Coastguard Worker void CallChainReportBuilder::SetConvertJITFrame(bool enable) {
373*288bf522SAndroid Build Coastguard Worker if (enable) {
374*288bf522SAndroid Build Coastguard Worker jit_frame_converter_.reset(new JITFrameConverter(thread_tree_));
375*288bf522SAndroid Build Coastguard Worker } else {
376*288bf522SAndroid Build Coastguard Worker jit_frame_converter_.reset(nullptr);
377*288bf522SAndroid Build Coastguard Worker }
378*288bf522SAndroid Build Coastguard Worker }
379*288bf522SAndroid Build Coastguard Worker
AddProguardMappingFile(std::string_view mapping_file)380*288bf522SAndroid Build Coastguard Worker bool CallChainReportBuilder::AddProguardMappingFile(std::string_view mapping_file) {
381*288bf522SAndroid Build Coastguard Worker if (!java_method_deobfuscater_) {
382*288bf522SAndroid Build Coastguard Worker java_method_deobfuscater_.reset(new JavaMethodDeobfuscater(remove_r8_synthesized_frame_));
383*288bf522SAndroid Build Coastguard Worker }
384*288bf522SAndroid Build Coastguard Worker return static_cast<JavaMethodDeobfuscater&>(*java_method_deobfuscater_)
385*288bf522SAndroid Build Coastguard Worker .AddProguardMappingFile(mapping_file);
386*288bf522SAndroid Build Coastguard Worker }
387*288bf522SAndroid Build Coastguard Worker
RemoveMethod(std::string_view method_name_regex)388*288bf522SAndroid Build Coastguard Worker bool CallChainReportBuilder::RemoveMethod(std::string_view method_name_regex) {
389*288bf522SAndroid Build Coastguard Worker if (!method_name_filter_) {
390*288bf522SAndroid Build Coastguard Worker method_name_filter_.reset(new MethodNameFilter);
391*288bf522SAndroid Build Coastguard Worker }
392*288bf522SAndroid Build Coastguard Worker return static_cast<MethodNameFilter&>(*method_name_filter_).RemoveMethod(method_name_regex);
393*288bf522SAndroid Build Coastguard Worker }
394*288bf522SAndroid Build Coastguard Worker
Build(const ThreadEntry * thread,const std::vector<uint64_t> & ips,size_t kernel_ip_count)395*288bf522SAndroid Build Coastguard Worker std::vector<CallChainReportEntry> CallChainReportBuilder::Build(const ThreadEntry* thread,
396*288bf522SAndroid Build Coastguard Worker const std::vector<uint64_t>& ips,
397*288bf522SAndroid Build Coastguard Worker size_t kernel_ip_count) {
398*288bf522SAndroid Build Coastguard Worker std::vector<CallChainReportEntry> result;
399*288bf522SAndroid Build Coastguard Worker result.reserve(ips.size());
400*288bf522SAndroid Build Coastguard Worker for (size_t i = 0; i < ips.size(); i++) {
401*288bf522SAndroid Build Coastguard Worker const MapEntry* map = thread_tree_.FindMap(thread, ips[i], i < kernel_ip_count);
402*288bf522SAndroid Build Coastguard Worker Dso* dso = map->dso;
403*288bf522SAndroid Build Coastguard Worker uint64_t vaddr_in_file;
404*288bf522SAndroid Build Coastguard Worker const Symbol* symbol = thread_tree_.FindSymbol(map, ips[i], &vaddr_in_file, &dso);
405*288bf522SAndroid Build Coastguard Worker CallChainExecutionType execution_type = CallChainExecutionType::NATIVE_METHOD;
406*288bf522SAndroid Build Coastguard Worker if (dso->IsForJavaMethod()) {
407*288bf522SAndroid Build Coastguard Worker if (dso->type() == DSO_DEX_FILE) {
408*288bf522SAndroid Build Coastguard Worker execution_type = CallChainExecutionType::INTERPRETED_JVM_METHOD;
409*288bf522SAndroid Build Coastguard Worker } else {
410*288bf522SAndroid Build Coastguard Worker execution_type = CallChainExecutionType::JIT_JVM_METHOD;
411*288bf522SAndroid Build Coastguard Worker }
412*288bf522SAndroid Build Coastguard Worker }
413*288bf522SAndroid Build Coastguard Worker result.resize(result.size() + 1);
414*288bf522SAndroid Build Coastguard Worker auto& entry = result.back();
415*288bf522SAndroid Build Coastguard Worker entry.ip = ips[i];
416*288bf522SAndroid Build Coastguard Worker entry.symbol = symbol;
417*288bf522SAndroid Build Coastguard Worker entry.dso = dso;
418*288bf522SAndroid Build Coastguard Worker entry.vaddr_in_file = vaddr_in_file;
419*288bf522SAndroid Build Coastguard Worker entry.map = map;
420*288bf522SAndroid Build Coastguard Worker entry.execution_type = execution_type;
421*288bf522SAndroid Build Coastguard Worker }
422*288bf522SAndroid Build Coastguard Worker MarkArtFrame(result);
423*288bf522SAndroid Build Coastguard Worker if (art_frame_remover_) {
424*288bf522SAndroid Build Coastguard Worker art_frame_remover_->Modify(result);
425*288bf522SAndroid Build Coastguard Worker }
426*288bf522SAndroid Build Coastguard Worker if (jit_frame_converter_) {
427*288bf522SAndroid Build Coastguard Worker jit_frame_converter_->Modify(result);
428*288bf522SAndroid Build Coastguard Worker }
429*288bf522SAndroid Build Coastguard Worker if (java_method_deobfuscater_) {
430*288bf522SAndroid Build Coastguard Worker java_method_deobfuscater_->Modify(result);
431*288bf522SAndroid Build Coastguard Worker }
432*288bf522SAndroid Build Coastguard Worker if (method_name_filter_) {
433*288bf522SAndroid Build Coastguard Worker method_name_filter_->Modify(result);
434*288bf522SAndroid Build Coastguard Worker }
435*288bf522SAndroid Build Coastguard Worker return result;
436*288bf522SAndroid Build Coastguard Worker }
437*288bf522SAndroid Build Coastguard Worker
MarkArtFrame(std::vector<CallChainReportEntry> & callchain)438*288bf522SAndroid Build Coastguard Worker void CallChainReportBuilder::MarkArtFrame(std::vector<CallChainReportEntry>& callchain) {
439*288bf522SAndroid Build Coastguard Worker // Mark art methods before or after a JVM method.
440*288bf522SAndroid Build Coastguard Worker bool near_java_method = false;
441*288bf522SAndroid Build Coastguard Worker bool is_jni_trampoline = false;
442*288bf522SAndroid Build Coastguard Worker std::vector<size_t> jni_trampoline_positions;
443*288bf522SAndroid Build Coastguard Worker for (size_t i = 0; i < callchain.size(); ++i) {
444*288bf522SAndroid Build Coastguard Worker auto& entry = callchain[i];
445*288bf522SAndroid Build Coastguard Worker if (entry.execution_type == CallChainExecutionType::INTERPRETED_JVM_METHOD ||
446*288bf522SAndroid Build Coastguard Worker entry.execution_type == CallChainExecutionType::JIT_JVM_METHOD) {
447*288bf522SAndroid Build Coastguard Worker near_java_method = true;
448*288bf522SAndroid Build Coastguard Worker
449*288bf522SAndroid Build Coastguard Worker // Mark art frames before this entry.
450*288bf522SAndroid Build Coastguard Worker for (int j = static_cast<int>(i) - 1; j >= 0; j--) {
451*288bf522SAndroid Build Coastguard Worker if (!IsArtEntry(callchain[j], &is_jni_trampoline)) {
452*288bf522SAndroid Build Coastguard Worker break;
453*288bf522SAndroid Build Coastguard Worker }
454*288bf522SAndroid Build Coastguard Worker callchain[j].execution_type = CallChainExecutionType::ART_METHOD;
455*288bf522SAndroid Build Coastguard Worker if (is_jni_trampoline) {
456*288bf522SAndroid Build Coastguard Worker jni_trampoline_positions.push_back(j);
457*288bf522SAndroid Build Coastguard Worker }
458*288bf522SAndroid Build Coastguard Worker }
459*288bf522SAndroid Build Coastguard Worker } else if (near_java_method && IsArtEntry(entry, &is_jni_trampoline)) {
460*288bf522SAndroid Build Coastguard Worker entry.execution_type = CallChainExecutionType::ART_METHOD;
461*288bf522SAndroid Build Coastguard Worker if (is_jni_trampoline) {
462*288bf522SAndroid Build Coastguard Worker jni_trampoline_positions.push_back(i);
463*288bf522SAndroid Build Coastguard Worker }
464*288bf522SAndroid Build Coastguard Worker } else {
465*288bf522SAndroid Build Coastguard Worker near_java_method = false;
466*288bf522SAndroid Build Coastguard Worker }
467*288bf522SAndroid Build Coastguard Worker }
468*288bf522SAndroid Build Coastguard Worker // Functions called by art_jni_trampoline are jni methods. And we don't want to hide them.
469*288bf522SAndroid Build Coastguard Worker for (auto i : jni_trampoline_positions) {
470*288bf522SAndroid Build Coastguard Worker if (i > 0 && callchain[i - 1].execution_type == CallChainExecutionType::ART_METHOD) {
471*288bf522SAndroid Build Coastguard Worker callchain[i - 1].execution_type = CallChainExecutionType::NATIVE_METHOD;
472*288bf522SAndroid Build Coastguard Worker }
473*288bf522SAndroid Build Coastguard Worker }
474*288bf522SAndroid Build Coastguard Worker }
475*288bf522SAndroid Build Coastguard Worker
AggregateThreads(const std::vector<std::string> & thread_name_regex)476*288bf522SAndroid Build Coastguard Worker bool ThreadReportBuilder::AggregateThreads(const std::vector<std::string>& thread_name_regex) {
477*288bf522SAndroid Build Coastguard Worker size_t i = thread_regs_.size();
478*288bf522SAndroid Build Coastguard Worker thread_regs_.resize(i + thread_name_regex.size());
479*288bf522SAndroid Build Coastguard Worker for (const auto& reg_str : thread_name_regex) {
480*288bf522SAndroid Build Coastguard Worker std::unique_ptr<RegEx> re = RegEx::Create(reg_str);
481*288bf522SAndroid Build Coastguard Worker if (!re) {
482*288bf522SAndroid Build Coastguard Worker return false;
483*288bf522SAndroid Build Coastguard Worker }
484*288bf522SAndroid Build Coastguard Worker thread_regs_[i++].re = std::move(re);
485*288bf522SAndroid Build Coastguard Worker }
486*288bf522SAndroid Build Coastguard Worker return true;
487*288bf522SAndroid Build Coastguard Worker }
488*288bf522SAndroid Build Coastguard Worker
Build(const ThreadEntry & thread)489*288bf522SAndroid Build Coastguard Worker ThreadReport ThreadReportBuilder::Build(const ThreadEntry& thread) {
490*288bf522SAndroid Build Coastguard Worker ThreadReport report(thread.pid, thread.tid, thread.comm);
491*288bf522SAndroid Build Coastguard Worker ModifyReportToAggregateThreads(report);
492*288bf522SAndroid Build Coastguard Worker return report;
493*288bf522SAndroid Build Coastguard Worker }
494*288bf522SAndroid Build Coastguard Worker
ModifyReportToAggregateThreads(ThreadReport & report)495*288bf522SAndroid Build Coastguard Worker void ThreadReportBuilder::ModifyReportToAggregateThreads(ThreadReport& report) {
496*288bf522SAndroid Build Coastguard Worker if (thread_regs_.empty()) {
497*288bf522SAndroid Build Coastguard Worker // No modification when there are no regular expressions.
498*288bf522SAndroid Build Coastguard Worker return;
499*288bf522SAndroid Build Coastguard Worker }
500*288bf522SAndroid Build Coastguard Worker const std::string thread_name = report.thread_name;
501*288bf522SAndroid Build Coastguard Worker if (auto it = thread_map_.find(thread_name); it != thread_map_.end()) {
502*288bf522SAndroid Build Coastguard Worker // Found cached result in thread_map_.
503*288bf522SAndroid Build Coastguard Worker if (it->second != -1) {
504*288bf522SAndroid Build Coastguard Worker report = thread_regs_[it->second].report;
505*288bf522SAndroid Build Coastguard Worker }
506*288bf522SAndroid Build Coastguard Worker return;
507*288bf522SAndroid Build Coastguard Worker }
508*288bf522SAndroid Build Coastguard Worker // Run the slow path to walk through every regular expression.
509*288bf522SAndroid Build Coastguard Worker size_t index;
510*288bf522SAndroid Build Coastguard Worker for (index = 0; index < thread_regs_.size(); ++index) {
511*288bf522SAndroid Build Coastguard Worker if (thread_regs_[index].re->Match(thread_name)) {
512*288bf522SAndroid Build Coastguard Worker break;
513*288bf522SAndroid Build Coastguard Worker }
514*288bf522SAndroid Build Coastguard Worker }
515*288bf522SAndroid Build Coastguard Worker if (index == thread_regs_.size()) {
516*288bf522SAndroid Build Coastguard Worker thread_map_[thread_name] = -1;
517*288bf522SAndroid Build Coastguard Worker } else {
518*288bf522SAndroid Build Coastguard Worker thread_map_[thread_name] = static_cast<int>(index);
519*288bf522SAndroid Build Coastguard Worker // Modify thread report.
520*288bf522SAndroid Build Coastguard Worker auto& aggregated_report = thread_regs_[index].report;
521*288bf522SAndroid Build Coastguard Worker if (aggregated_report.thread_name == nullptr) {
522*288bf522SAndroid Build Coastguard Worker // Use regular expression as the name of the aggregated thread. So users know it's an
523*288bf522SAndroid Build Coastguard Worker // aggregated thread.
524*288bf522SAndroid Build Coastguard Worker aggregated_report =
525*288bf522SAndroid Build Coastguard Worker ThreadReport(report.pid, report.tid, thread_regs_[index].re->GetPattern().c_str());
526*288bf522SAndroid Build Coastguard Worker }
527*288bf522SAndroid Build Coastguard Worker report = aggregated_report;
528*288bf522SAndroid Build Coastguard Worker }
529*288bf522SAndroid Build Coastguard Worker }
530*288bf522SAndroid Build Coastguard Worker
531*288bf522SAndroid Build Coastguard Worker } // namespace simpleperf
532