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/trace_processor/importers/proto/stack_profile_tracker.h"
18
19 #include "src/trace_processor/importers/proto/profiler_util.h"
20 #include "src/trace_processor/types/trace_processor_context.h"
21
22 #include "perfetto/base/logging.h"
23 #include "perfetto/ext/base/string_utils.h"
24
25 namespace perfetto {
26 namespace trace_processor {
27
28 SequenceStackProfileTracker::InternLookup::~InternLookup() = default;
29
SequenceStackProfileTracker(TraceProcessorContext * context)30 SequenceStackProfileTracker::SequenceStackProfileTracker(
31 TraceProcessorContext* context)
32 : context_(context), empty_(kNullStringId) {}
33
34 SequenceStackProfileTracker::~SequenceStackProfileTracker() = default;
35
GetEmptyStringId()36 StringId SequenceStackProfileTracker::GetEmptyStringId() {
37 if (empty_ == kNullStringId) {
38 empty_ = context_->storage->InternString({"", 0});
39 }
40
41 return empty_;
42 }
43
AddString(SourceStringId id,base::StringView str)44 void SequenceStackProfileTracker::AddString(SourceStringId id,
45 base::StringView str) {
46 string_map_.emplace(id, str.ToStdString());
47 }
48
AddMapping(SourceMappingId id,const SourceMapping & mapping,const InternLookup * intern_lookup)49 base::Optional<MappingId> SequenceStackProfileTracker::AddMapping(
50 SourceMappingId id,
51 const SourceMapping& mapping,
52 const InternLookup* intern_lookup) {
53 std::string path;
54 for (SourceStringId str_id : mapping.name_ids) {
55 auto opt_str = FindOrInsertString(str_id, intern_lookup,
56 InternedStringType::kMappingPath);
57 if (!opt_str)
58 break;
59 path += "/" + *opt_str;
60 }
61
62 auto opt_build_id = FindAndInternString(mapping.build_id, intern_lookup,
63 InternedStringType::kBuildId);
64 if (!opt_build_id) {
65 context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
66 PERFETTO_DLOG("Invalid string.");
67 return base::nullopt;
68 }
69 const StringId raw_build_id = opt_build_id.value();
70 NullTermStringView raw_build_id_str =
71 context_->storage->GetString(raw_build_id);
72 StringId build_id = GetEmptyStringId();
73 if (!raw_build_id_str.empty()) {
74 // If the build_id is 33 characters long, we assume it's a Breakpad debug
75 // identifier which is already in Hex and doesn't need conversion.
76 // TODO(b/148109467): Remove workaround once all active Chrome versions
77 // write raw bytes instead of a string as build_id.
78 if (raw_build_id_str.size() == 33) {
79 build_id = raw_build_id;
80 } else {
81 std::string hex_build_id =
82 base::ToHex(raw_build_id_str.c_str(), raw_build_id_str.size());
83 build_id =
84 context_->storage->InternString(base::StringView(hex_build_id));
85 }
86 }
87
88 tables::StackProfileMappingTable::Row row{
89 build_id,
90 static_cast<int64_t>(mapping.exact_offset),
91 static_cast<int64_t>(mapping.start_offset),
92 static_cast<int64_t>(mapping.start),
93 static_cast<int64_t>(mapping.end),
94 static_cast<int64_t>(mapping.load_bias),
95 context_->storage->InternString(base::StringView(path))};
96
97 tables::StackProfileMappingTable* mappings =
98 context_->storage->mutable_stack_profile_mapping_table();
99 base::Optional<MappingId> cur_id;
100 auto it = mapping_idx_.find(row);
101 if (it != mapping_idx_.end()) {
102 cur_id = it->second;
103 } else {
104 std::vector<MappingId> db_mappings =
105 context_->global_stack_profile_tracker->FindMappingRow(row.name,
106 row.build_id);
107 for (const MappingId preexisting_mapping : db_mappings) {
108 uint32_t preexisting_row = *mappings->id().IndexOf(preexisting_mapping);
109 tables::StackProfileMappingTable::Row preexisting_data{
110 mappings->build_id()[preexisting_row],
111 mappings->exact_offset()[preexisting_row],
112 mappings->start_offset()[preexisting_row],
113 mappings->start()[preexisting_row],
114 mappings->end()[preexisting_row],
115 mappings->load_bias()[preexisting_row],
116 mappings->name()[preexisting_row]};
117
118 if (row == preexisting_data) {
119 cur_id = preexisting_mapping;
120 }
121 }
122 if (!cur_id) {
123 MappingId mapping_id = mappings->Insert(row).id;
124 context_->global_stack_profile_tracker->InsertMappingId(
125 row.name, row.build_id, mapping_id);
126 cur_id = mapping_id;
127 }
128 mapping_idx_.emplace(row, *cur_id);
129 }
130 mapping_ids_.emplace(id, *cur_id);
131 return cur_id;
132 }
133
AddFrame(SourceFrameId id,const SourceFrame & frame,const InternLookup * intern_lookup)134 base::Optional<FrameId> SequenceStackProfileTracker::AddFrame(
135 SourceFrameId id,
136 const SourceFrame& frame,
137 const InternLookup* intern_lookup) {
138 base::Optional<std::string> opt_name = FindOrInsertString(
139 frame.name_id, intern_lookup, InternedStringType::kFunctionName);
140 if (!opt_name) {
141 context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
142 PERFETTO_DLOG("Invalid string.");
143 return base::nullopt;
144 }
145 const std::string& name = *opt_name;
146 const StringId str_id =
147 context_->storage->InternString(base::StringView(name));
148
149 auto opt_mapping = FindOrInsertMapping(frame.mapping_id, intern_lookup);
150 if (!opt_mapping) {
151 context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
152 return base::nullopt;
153 }
154 MappingId mapping_id = *opt_mapping;
155 const auto& mappings = context_->storage->stack_profile_mapping_table();
156 StringId mapping_name_id =
157 mappings.name()[*mappings.id().IndexOf(mapping_id)];
158 auto mapping_name = context_->storage->GetString(mapping_name_id);
159
160 tables::StackProfileFrameTable::Row row{str_id, mapping_id,
161 static_cast<int64_t>(frame.rel_pc)};
162
163 auto* frames = context_->storage->mutable_stack_profile_frame_table();
164
165 base::Optional<FrameId> cur_id;
166 auto it = frame_idx_.find(row);
167 if (it != frame_idx_.end()) {
168 cur_id = it->second;
169 } else {
170 std::vector<FrameId> db_frames =
171 context_->global_stack_profile_tracker->FindFrameIds(mapping_id,
172 frame.rel_pc);
173 for (const FrameId preexisting_frame : db_frames) {
174 uint32_t preexisting_row_id = *frames->id().IndexOf(preexisting_frame);
175 tables::StackProfileFrameTable::Row preexisting_row{
176 frames->name()[preexisting_row_id],
177 frames->mapping()[preexisting_row_id],
178 frames->rel_pc()[preexisting_row_id]};
179
180 if (row == preexisting_row) {
181 cur_id = preexisting_frame;
182 }
183 }
184 if (!cur_id) {
185 cur_id = frames->Insert(row).id;
186 context_->global_stack_profile_tracker->InsertFrameRow(
187 mapping_id, static_cast<uint64_t>(row.rel_pc), *cur_id);
188 if (name.find('.') != std::string::npos) {
189 // Java frames always contain a '.'
190 base::Optional<std::string> package =
191 PackageFromLocation(context_->storage.get(), mapping_name);
192 if (package) {
193 NameInPackage nip{str_id, context_->storage->InternString(
194 base::StringView(*package))};
195 context_->global_stack_profile_tracker->InsertJavaFrameForName(
196 nip, *cur_id);
197 } else if (mapping_name.find("/memfd:") == 0) {
198 NameInPackage nip{str_id, context_->storage->InternString("memfd")};
199 context_->global_stack_profile_tracker->InsertJavaFrameForName(
200 nip, *cur_id);
201 }
202 }
203 }
204 frame_idx_.emplace(row, *cur_id);
205 }
206 frame_ids_.emplace(id, *cur_id);
207 return cur_id;
208 }
209
AddCallstack(SourceCallstackId id,const SourceCallstack & frame_ids,const InternLookup * intern_lookup)210 base::Optional<CallsiteId> SequenceStackProfileTracker::AddCallstack(
211 SourceCallstackId id,
212 const SourceCallstack& frame_ids,
213 const InternLookup* intern_lookup) {
214 if (frame_ids.empty())
215 return base::nullopt;
216
217 base::Optional<CallsiteId> parent_id;
218 for (uint32_t depth = 0; depth < frame_ids.size(); ++depth) {
219 auto opt_frame_id = FindOrInsertFrame(frame_ids[depth], intern_lookup);
220 if (!opt_frame_id) {
221 context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
222 return base::nullopt;
223 }
224 FrameId frame_id = *opt_frame_id;
225
226 tables::StackProfileCallsiteTable::Row row{depth, parent_id, frame_id};
227 CallsiteId self_id;
228 auto callsite_it = callsite_idx_.find(row);
229 if (callsite_it != callsite_idx_.end()) {
230 self_id = callsite_it->second;
231 } else {
232 auto* callsite =
233 context_->storage->mutable_stack_profile_callsite_table();
234 self_id = callsite->Insert(row).id;
235 callsite_idx_.emplace(row, self_id);
236 }
237 parent_id = self_id;
238 }
239 PERFETTO_DCHECK(parent_id); // The loop ran at least once.
240 callstack_ids_.emplace(id, *parent_id);
241 return parent_id;
242 }
243
GetDatabaseFrameIdForTesting(SourceFrameId frame_id)244 FrameId SequenceStackProfileTracker::GetDatabaseFrameIdForTesting(
245 SourceFrameId frame_id) {
246 auto it = frame_ids_.find(frame_id);
247 if (it == frame_ids_.end()) {
248 PERFETTO_DLOG("Invalid frame.");
249 return {};
250 }
251 return it->second;
252 }
253
FindAndInternString(SourceStringId id,const InternLookup * intern_lookup,SequenceStackProfileTracker::InternedStringType type)254 base::Optional<StringId> SequenceStackProfileTracker::FindAndInternString(
255 SourceStringId id,
256 const InternLookup* intern_lookup,
257 SequenceStackProfileTracker::InternedStringType type) {
258 if (id == 0)
259 return GetEmptyStringId();
260
261 auto opt_str = FindOrInsertString(id, intern_lookup, type);
262 if (!opt_str)
263 return GetEmptyStringId();
264
265 return context_->storage->InternString(base::StringView(*opt_str));
266 }
267
FindOrInsertString(SourceStringId id,const InternLookup * intern_lookup,SequenceStackProfileTracker::InternedStringType type)268 base::Optional<std::string> SequenceStackProfileTracker::FindOrInsertString(
269 SourceStringId id,
270 const InternLookup* intern_lookup,
271 SequenceStackProfileTracker::InternedStringType type) {
272 if (id == 0)
273 return "";
274
275 auto it = string_map_.find(id);
276 if (it == string_map_.end()) {
277 if (intern_lookup) {
278 auto str = intern_lookup->GetString(id, type);
279 if (!str) {
280 context_->storage->IncrementStats(
281 stats::stackprofile_invalid_string_id);
282 PERFETTO_DLOG("Invalid string.");
283 return base::nullopt;
284 }
285 return str->ToStdString();
286 }
287 return base::nullopt;
288 }
289
290 return it->second;
291 }
292
FindOrInsertMapping(SourceMappingId mapping_id,const InternLookup * intern_lookup)293 base::Optional<MappingId> SequenceStackProfileTracker::FindOrInsertMapping(
294 SourceMappingId mapping_id,
295 const InternLookup* intern_lookup) {
296 base::Optional<MappingId> res;
297 auto it = mapping_ids_.find(mapping_id);
298 if (it == mapping_ids_.end()) {
299 if (intern_lookup) {
300 auto interned_mapping = intern_lookup->GetMapping(mapping_id);
301 if (interned_mapping) {
302 res = AddMapping(mapping_id, *interned_mapping, intern_lookup);
303 return res;
304 }
305 }
306 context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
307 return res;
308 }
309 res = it->second;
310 return res;
311 }
312
FindOrInsertFrame(SourceFrameId frame_id,const InternLookup * intern_lookup)313 base::Optional<FrameId> SequenceStackProfileTracker::FindOrInsertFrame(
314 SourceFrameId frame_id,
315 const InternLookup* intern_lookup) {
316 base::Optional<FrameId> res;
317 auto it = frame_ids_.find(frame_id);
318 if (it == frame_ids_.end()) {
319 if (intern_lookup) {
320 auto interned_frame = intern_lookup->GetFrame(frame_id);
321 if (interned_frame) {
322 res = AddFrame(frame_id, *interned_frame, intern_lookup);
323 return res;
324 }
325 }
326 context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
327 PERFETTO_DLOG("Unknown frame %" PRIu64 " : %zu", frame_id,
328 frame_ids_.size());
329 return res;
330 }
331 res = it->second;
332 return res;
333 }
334
FindOrInsertCallstack(SourceCallstackId callstack_id,const InternLookup * intern_lookup)335 base::Optional<CallsiteId> SequenceStackProfileTracker::FindOrInsertCallstack(
336 SourceCallstackId callstack_id,
337 const InternLookup* intern_lookup) {
338 base::Optional<CallsiteId> res;
339 auto it = callstack_ids_.find(callstack_id);
340 if (it == callstack_ids_.end()) {
341 auto interned_callstack = intern_lookup->GetCallstack(callstack_id);
342 if (interned_callstack) {
343 res = AddCallstack(callstack_id, *interned_callstack, intern_lookup);
344 return res;
345 }
346 context_->storage->IncrementStats(stats::stackprofile_invalid_callstack_id);
347 PERFETTO_DLOG("Unknown callstack %" PRIu64 " : %zu", callstack_id,
348 callstack_ids_.size());
349 return res;
350 }
351 res = it->second;
352 return res;
353 }
354
ClearIndices()355 void SequenceStackProfileTracker::ClearIndices() {
356 string_map_.clear();
357 mapping_ids_.clear();
358 callstack_ids_.clear();
359 frame_ids_.clear();
360 }
361
362 } // namespace trace_processor
363 } // namespace perfetto
364