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