• 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/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