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