• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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