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