1 /*
2 * Copyright (C) 2021 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/annotated_callsites.h"
18
19 #include <iostream>
20 #include <optional>
21
22 #include "src/trace_processor/tables/profiler_tables_py.h"
23 #include "src/trace_processor/types/trace_processor_context.h"
24
25 namespace perfetto {
26 namespace trace_processor {
27
AnnotatedCallsites(const TraceProcessorContext * context)28 AnnotatedCallsites::AnnotatedCallsites(const TraceProcessorContext* context)
29 : context_(*context),
30 // String to identify trampoline frames. If the string does not exist in
31 // TraceProcessor's StringPool (nullopt) then there will be no trampoline
32 // frames in the trace so there is no point in adding it to the pool to do
33 // all comparisons, instead we initialize the member to std::nullopt and
34 // the string comparisons will all fail.
35 art_jni_trampoline_(
36 context->storage->string_pool().GetId("art_jni_trampoline")) {}
37
GetState(std::optional<CallsiteId> id)38 AnnotatedCallsites::State AnnotatedCallsites::GetState(
39 std::optional<CallsiteId> id) {
40 if (!id) {
41 return State::kInitial;
42 }
43 auto it = states_.find(*id);
44 if (it != states_.end()) {
45 return it->second;
46 }
47
48 State state =
49 Get(*context_.storage->stack_profile_callsite_table().FindById(*id))
50 .first;
51 states_.emplace(*id, state);
52 return state;
53 }
54
55 std::pair<AnnotatedCallsites::State, CallsiteAnnotation>
Get(const tables::StackProfileCallsiteTable::ConstRowReference & callsite)56 AnnotatedCallsites::Get(
57 const tables::StackProfileCallsiteTable::ConstRowReference& callsite) {
58 State state = GetState(callsite.parent_id());
59
60 // Keep immediate callee of a JNI trampoline, but keep tagging all
61 // successive libart frames as common.
62 if (state == State::kKeepNext) {
63 return {State::kEraseLibart, CallsiteAnnotation::kNone};
64 }
65
66 // Special-case "art_jni_trampoline" frames, keeping their immediate callee
67 // even if it is in libart, as it could be a native implementation of a
68 // managed method. Example for "java.lang.reflect.Method.Invoke":
69 // art_jni_trampoline
70 // art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)
71 //
72 // Simpleperf also relies on this frame name, so it should be fairly stable.
73 // TODO(rsavitski): consider detecting standard JNI upcall entrypoints -
74 // _JNIEnv::Call*. These are sometimes inlined into other DSOs, so erasing
75 // only the libart frames does not clean up all of the JNI-related frames.
76 auto frame = *context_.storage->stack_profile_frame_table().FindById(
77 callsite.frame_id());
78 // art_jni_trampoline_ could be std::nullopt if the string does not exist in
79 // the StringPool, but that also means no frame will ever have that name.
80 if (art_jni_trampoline_.has_value() &&
81 frame.name() == art_jni_trampoline_.value()) {
82 return {State::kKeepNext, CallsiteAnnotation::kCommonFrame};
83 }
84
85 MapType map_type = GetMapType(frame.mapping());
86
87 // Annotate managed frames.
88 if (map_type == MapType::kArtInterp || //
89 map_type == MapType::kArtJit || //
90 map_type == MapType::kArtAot) {
91 // Now know to be in a managed callstack - erase subsequent ART frames.
92 if (state == State::kInitial) {
93 state = State::kEraseLibart;
94 }
95
96 if (map_type == MapType::kArtInterp)
97 return {state, CallsiteAnnotation::kArtInterpreted};
98 if (map_type == MapType::kArtJit)
99 return {state, CallsiteAnnotation::kArtJit};
100 if (map_type == MapType::kArtAot)
101 return {state, CallsiteAnnotation::kArtAot};
102 }
103
104 // Mixed callstack, tag libart frames as uninteresting (common-frame).
105 // Special case a subset of interpreter implementation frames as
106 // "common-frame-interp" using frame name prefixes. Those functions are
107 // actually executed, whereas the managed "interp" frames are synthesised as
108 // their caller by the unwinding library (based on the dex_pc virtual
109 // register restored using the libart's DWARF info). The heuristic covers
110 // the "nterp" and "switch" interpreter implementations.
111 //
112 // Example:
113 // <towards root>
114 // android.view.WindowLayout.computeFrames [interp]
115 // nterp_op_iget_object_slow_path [common-frame-interp]
116 //
117 // This annotation is helpful when trying to answer "what mode was the
118 // process in?" based on the leaf frame of the callstack. As we want to
119 // classify such cases as interpreted, even though the leaf frame is
120 // libart.so.
121 //
122 // For "switch" interpreter, we match any frame starting with
123 // "art::interpreter::" according to itanium mangling.
124 if (state == State::kEraseLibart && map_type == MapType::kNativeLibart) {
125 NullTermStringView fname = context_.storage->GetString(frame.name());
126 if (fname.StartsWith("nterp_") || fname.StartsWith("Nterp") ||
127 fname.StartsWith("ExecuteNterp") ||
128 fname.StartsWith("ExecuteSwitchImpl") ||
129 fname.StartsWith("_ZN3art11interpreter")) {
130 return {state, CallsiteAnnotation::kCommonFrameInterp};
131 }
132 return {state, CallsiteAnnotation::kCommonFrame};
133 }
134
135 return {state, CallsiteAnnotation::kNone};
136 }
137
GetMapType(MappingId id)138 AnnotatedCallsites::MapType AnnotatedCallsites::GetMapType(MappingId id) {
139 auto it = map_types_.find(id);
140 if (it != map_types_.end()) {
141 return it->second;
142 }
143
144 return map_types_
145 .emplace(id, ClassifyMap(context_.storage->GetString(
146 context_.storage->stack_profile_mapping_table()
147 .FindById(id)
148 ->name())))
149 .first->second;
150 }
151
ClassifyMap(NullTermStringView map)152 AnnotatedCallsites::MapType AnnotatedCallsites::ClassifyMap(
153 NullTermStringView map) {
154 if (map.empty())
155 return MapType::kOther;
156
157 // Primary mapping where modern ART puts jitted code.
158 // The Zygote's JIT region is inherited by all descendant apps, so it can
159 // still appear in their callstacks.
160 if (map.StartsWith("/memfd:jit-cache") ||
161 map.StartsWith("/memfd:jit-zygote-cache")) {
162 return MapType::kArtJit;
163 }
164
165 size_t last_slash_pos = map.rfind('/');
166 if (last_slash_pos != NullTermStringView::npos) {
167 base::StringView suffix = map.substr(last_slash_pos);
168 if (suffix.StartsWith("/libart.so") || suffix.StartsWith("/libartd.so"))
169 return MapType::kNativeLibart;
170 }
171
172 size_t extension_pos = map.rfind('.');
173 if (extension_pos != NullTermStringView::npos) {
174 base::StringView suffix = map.substr(extension_pos);
175 if (suffix.StartsWith(".so"))
176 return MapType::kNativeOther;
177 // unqualified dex
178 if (suffix.StartsWith(".dex"))
179 return MapType::kArtInterp;
180 // dex with verification speedup info, produced by dex2oat
181 if (suffix.StartsWith(".vdex"))
182 return MapType::kArtInterp;
183 // possibly uncompressed dex in a jar archive
184 if (suffix.StartsWith(".jar"))
185 return MapType::kArtInterp;
186 // android package (zip file), this can contain uncompressed dexes or
187 // native libraries that are mmap'd directly into the process. We rely on
188 // libunwindstack's MapInfo::GetFullName, which suffixes the mapping with
189 // "!lib.so" if it knows that the referenced piece of the archive is an
190 // uncompressed ELF file. So an unadorned ".apk" is assumed to be a dex
191 // file.
192 if (suffix.StartsWith(".apk"))
193 return MapType::kArtInterp;
194 // ahead of time compiled ELFs
195 if (suffix.StartsWith(".oat"))
196 return MapType::kArtAot;
197 // older/alternative name for .oat
198 if (suffix.StartsWith(".odex"))
199 return MapType::kArtAot;
200 }
201 return MapType::kOther;
202 }
203
204 } // namespace trace_processor
205 } // namespace perfetto
206