• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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/profile_module.h"
18 #include <string>
19 
20 #include "perfetto/base/logging.h"
21 #include "perfetto/ext/base/flat_hash_map.h"
22 #include "perfetto/ext/base/string_utils.h"
23 #include "src/trace_processor/importers/common/args_translation_table.h"
24 #include "src/trace_processor/importers/common/clock_tracker.h"
25 #include "src/trace_processor/importers/common/deobfuscation_mapping_table.h"
26 #include "src/trace_processor/importers/common/event_tracker.h"
27 #include "src/trace_processor/importers/common/process_tracker.h"
28 #include "src/trace_processor/importers/proto/heap_profile_tracker.h"
29 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
30 #include "src/trace_processor/importers/proto/perf_sample_tracker.h"
31 #include "src/trace_processor/importers/proto/profile_packet_utils.h"
32 #include "src/trace_processor/importers/proto/profiler_util.h"
33 #include "src/trace_processor/importers/proto/stack_profile_tracker.h"
34 #include "src/trace_processor/sorter/trace_sorter.h"
35 #include "src/trace_processor/storage/stats.h"
36 #include "src/trace_processor/storage/trace_storage.h"
37 #include "src/trace_processor/tables/profiler_tables_py.h"
38 #include "src/trace_processor/types/trace_processor_context.h"
39 #include "src/trace_processor/util/stack_traces_util.h"
40 
41 #include "protos/perfetto/common/builtin_clock.pbzero.h"
42 #include "protos/perfetto/common/perf_events.pbzero.h"
43 #include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
44 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
45 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
46 #include "protos/perfetto/trace/profiling/smaps.pbzero.h"
47 
48 namespace perfetto {
49 namespace trace_processor {
50 
51 using perfetto::protos::pbzero::TracePacket;
52 using protozero::ConstBytes;
53 
ProfileModule(TraceProcessorContext * context)54 ProfileModule::ProfileModule(TraceProcessorContext* context)
55     : context_(context) {
56   RegisterForField(TracePacket::kStreamingProfilePacketFieldNumber, context);
57   RegisterForField(TracePacket::kPerfSampleFieldNumber, context);
58   RegisterForField(TracePacket::kProfilePacketFieldNumber, context);
59   RegisterForField(TracePacket::kModuleSymbolsFieldNumber, context);
60   // note: deobfuscation mappings also handled by HeapGraphModule.
61   RegisterForField(TracePacket::kDeobfuscationMappingFieldNumber, context);
62   RegisterForField(TracePacket::kSmapsPacketFieldNumber, context);
63 }
64 
65 ProfileModule::~ProfileModule() = default;
66 
TokenizePacket(const TracePacket::Decoder & decoder,TraceBlobView * packet,int64_t,PacketSequenceState * state,uint32_t field_id)67 ModuleResult ProfileModule::TokenizePacket(const TracePacket::Decoder& decoder,
68                                            TraceBlobView* packet,
69                                            int64_t /*packet_timestamp*/,
70                                            PacketSequenceState* state,
71                                            uint32_t field_id) {
72   switch (field_id) {
73     case TracePacket::kStreamingProfilePacketFieldNumber:
74       return TokenizeStreamingProfilePacket(state, packet,
75                                             decoder.streaming_profile_packet());
76   }
77   return ModuleResult::Ignored();
78 }
79 
ParseTracePacketData(const protos::pbzero::TracePacket::Decoder & decoder,int64_t ts,const TracePacketData & data,uint32_t field_id)80 void ProfileModule::ParseTracePacketData(
81     const protos::pbzero::TracePacket::Decoder& decoder,
82     int64_t ts,
83     const TracePacketData& data,
84     uint32_t field_id) {
85   switch (field_id) {
86     case TracePacket::kStreamingProfilePacketFieldNumber:
87       ParseStreamingProfilePacket(ts, data.sequence_state.get(),
88                                   decoder.streaming_profile_packet());
89       return;
90     case TracePacket::kPerfSampleFieldNumber:
91       ParsePerfSample(ts, data.sequence_state.get(), decoder);
92       return;
93     case TracePacket::kProfilePacketFieldNumber:
94       ParseProfilePacket(ts, data.sequence_state.get(),
95                          decoder.trusted_packet_sequence_id(),
96                          decoder.profile_packet());
97       return;
98     case TracePacket::kModuleSymbolsFieldNumber:
99       ParseModuleSymbols(decoder.module_symbols());
100       return;
101     case TracePacket::kDeobfuscationMappingFieldNumber:
102       ParseDeobfuscationMapping(ts, data.sequence_state.get(),
103                                 decoder.trusted_packet_sequence_id(),
104                                 decoder.deobfuscation_mapping());
105       return;
106     case TracePacket::kSmapsPacketFieldNumber:
107       ParseSmapsPacket(ts, decoder.smaps_packet());
108       return;
109   }
110 }
111 
TokenizeStreamingProfilePacket(PacketSequenceState * sequence_state,TraceBlobView * packet,ConstBytes streaming_profile_packet)112 ModuleResult ProfileModule::TokenizeStreamingProfilePacket(
113     PacketSequenceState* sequence_state,
114     TraceBlobView* packet,
115     ConstBytes streaming_profile_packet) {
116   protos::pbzero::StreamingProfilePacket::Decoder decoder(
117       streaming_profile_packet.data, streaming_profile_packet.size);
118 
119   // We have to resolve the reference timestamp of a StreamingProfilePacket
120   // during tokenization. If we did this during parsing instead, the
121   // tokenization of a subsequent ThreadDescriptor with a new reference
122   // timestamp would cause us to later calculate timestamps based on the wrong
123   // reference value during parsing. Since StreamingProfilePackets only need to
124   // be sorted correctly with respect to process/thread metadata events (so that
125   // pid/tid are resolved correctly during parsing), we forward the packet as a
126   // whole through the sorter, using the "root" timestamp of the packet, i.e.
127   // the current timestamp of the packet sequence.
128   auto packet_ts =
129       sequence_state->IncrementAndGetTrackEventTimeNs(/*delta_ns=*/0);
130   base::StatusOr<int64_t> trace_ts = context_->clock_tracker->ToTraceTime(
131       protos::pbzero::BUILTIN_CLOCK_MONOTONIC, packet_ts);
132   if (trace_ts.ok())
133     packet_ts = *trace_ts;
134 
135   // Increment the sequence's timestamp by all deltas.
136   for (auto timestamp_it = decoder.timestamp_delta_us(); timestamp_it;
137        ++timestamp_it) {
138     sequence_state->IncrementAndGetTrackEventTimeNs(*timestamp_it * 1000);
139   }
140 
141   context_->sorter->PushTracePacket(
142       packet_ts, sequence_state->current_generation(), std::move(*packet));
143   return ModuleResult::Handled();
144 }
145 
ParseStreamingProfilePacket(int64_t timestamp,PacketSequenceStateGeneration * sequence_state,ConstBytes streaming_profile_packet)146 void ProfileModule::ParseStreamingProfilePacket(
147     int64_t timestamp,
148     PacketSequenceStateGeneration* sequence_state,
149     ConstBytes streaming_profile_packet) {
150   protos::pbzero::StreamingProfilePacket::Decoder packet(
151       streaming_profile_packet.data, streaming_profile_packet.size);
152 
153   ProcessTracker* procs = context_->process_tracker.get();
154   TraceStorage* storage = context_->storage.get();
155   SequenceStackProfileTracker& sequence_stack_profile_tracker =
156       sequence_state->state()->sequence_stack_profile_tracker();
157   ProfilePacketInternLookup intern_lookup(sequence_state);
158 
159   uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
160   uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
161   UniqueTid utid = procs->UpdateThread(tid, pid);
162 
163   // Iterate through timestamps and callstacks simultaneously.
164   auto timestamp_it = packet.timestamp_delta_us();
165   for (auto callstack_it = packet.callstack_iid(); callstack_it;
166        ++callstack_it, ++timestamp_it) {
167     if (!timestamp_it) {
168       context_->storage->IncrementStats(stats::stackprofile_parser_error);
169       PERFETTO_ELOG(
170           "StreamingProfilePacket has less callstack IDs than timestamps!");
171       break;
172     }
173 
174     auto opt_cs_id = sequence_stack_profile_tracker.FindOrInsertCallstack(
175         *callstack_it, &intern_lookup);
176     if (!opt_cs_id) {
177       context_->storage->IncrementStats(stats::stackprofile_parser_error);
178       continue;
179     }
180 
181     // Resolve the delta timestamps based on the packet's root timestamp.
182     timestamp += *timestamp_it * 1000;
183 
184     tables::CpuProfileStackSampleTable::Row sample_row{
185         timestamp, *opt_cs_id, utid, packet.process_priority()};
186     storage->mutable_cpu_profile_stack_sample_table()->Insert(sample_row);
187   }
188 }
189 
ParsePerfSample(int64_t ts,PacketSequenceStateGeneration * sequence_state,const TracePacket::Decoder & decoder)190 void ProfileModule::ParsePerfSample(
191     int64_t ts,
192     PacketSequenceStateGeneration* sequence_state,
193     const TracePacket::Decoder& decoder) {
194   using PerfSample = protos::pbzero::PerfSample;
195   const auto& sample_raw = decoder.perf_sample();
196   PerfSample::Decoder sample(sample_raw.data, sample_raw.size);
197 
198   uint32_t seq_id = decoder.trusted_packet_sequence_id();
199   PerfSampleTracker::SamplingStreamInfo sampling_stream =
200       context_->perf_sample_tracker->GetSamplingStreamInfo(
201           seq_id, sample.cpu(), sequence_state->GetTracePacketDefaults());
202 
203   // Not a sample, but an indication of data loss in the ring buffer shared with
204   // the kernel.
205   if (sample.kernel_records_lost() > 0) {
206     PERFETTO_DCHECK(sample.pid() == 0);
207 
208     context_->storage->IncrementIndexedStats(
209         stats::perf_cpu_lost_records, static_cast<int>(sample.cpu()),
210         static_cast<int64_t>(sample.kernel_records_lost()));
211     return;
212   }
213 
214   // Sample that looked relevant for the tracing session, but had to be skipped.
215   // Either we failed to look up the procfs file descriptors necessary for
216   // remote stack unwinding (not unexpected in most cases), or the unwind queue
217   // was out of capacity (producer lost data on its own).
218   if (sample.has_sample_skipped_reason()) {
219     context_->storage->IncrementStats(stats::perf_samples_skipped);
220 
221     if (sample.sample_skipped_reason() ==
222         PerfSample::PROFILER_SKIP_UNWIND_ENQUEUE)
223       context_->storage->IncrementStats(stats::perf_samples_skipped_dataloss);
224 
225     return;
226   }
227 
228   // Not a sample, but an event from the producer.
229   // TODO(rsavitski): this stat is indexed by the session id, but the older
230   // stats (see above) aren't. The indexing is relevant if a trace contains more
231   // than one profiling data source. So the older stats should be changed to
232   // being indexed as well.
233   if (sample.has_producer_event()) {
234     PerfSample::ProducerEvent::Decoder producer_event(sample.producer_event());
235     if (producer_event.source_stop_reason() ==
236         PerfSample::ProducerEvent::PROFILER_STOP_GUARDRAIL) {
237       context_->storage->SetIndexedStats(
238           stats::perf_guardrail_stop_ts,
239           static_cast<int>(sampling_stream.perf_session_id), ts);
240     }
241     return;
242   }
243 
244   // Proper sample, populate the |perf_sample| table with everything except the
245   // recorded counter values, which go to |counter|.
246   context_->event_tracker->PushCounter(
247       ts, static_cast<double>(sample.timebase_count()),
248       sampling_stream.timebase_track_id);
249 
250   SequenceStackProfileTracker& stack_tracker =
251       sequence_state->state()->sequence_stack_profile_tracker();
252   ProfilePacketInternLookup intern_lookup(sequence_state);
253   uint64_t callstack_iid = sample.callstack_iid();
254   std::optional<CallsiteId> cs_id =
255       stack_tracker.FindOrInsertCallstack(callstack_iid, &intern_lookup);
256 
257   // A failed lookup of the interned callstack can mean either:
258   // (a) This is a counter-only profile without callstacks. Due to an
259   //     implementation quirk, these packets still set callstack_iid
260   //     corresponding to a callstack with no frames. To reliably identify this
261   //     case (without resorting to config parsing) we further need to rely on
262   //     the fact that the implementation (callstack_trie.h) always assigns this
263   //     callstack the id "1". Such callstacks should not occur outside of
264   //     counter-only profiles, as there should always be at least a synthetic
265   //     error frame if the unwinding completely failed.
266   // (b) This is a ring-buffer profile where some of the referenced internings
267   //     have been overwritten, and the build predates perf_sample_defaults and
268   //     SEQ_NEEDS_INCREMENTAL_STATE sequence flag in perf_sample packets.
269   //     Such packets should be discarded.
270   if (!cs_id && callstack_iid != 1) {
271     PERFETTO_DLOG("Discarding perf_sample since callstack_iid [%" PRIu64
272                   "] references a missing/partially lost interning according "
273                   "to stack_profile_tracker",
274                   callstack_iid);
275     return;
276   }
277 
278   UniqueTid utid =
279       context_->process_tracker->UpdateThread(sample.tid(), sample.pid());
280 
281   using protos::pbzero::Profiling;
282   TraceStorage* storage = context_->storage.get();
283 
284   auto cpu_mode = static_cast<Profiling::CpuMode>(sample.cpu_mode());
285   StringPool::Id cpu_mode_id =
286       storage->InternString(ProfilePacketUtils::StringifyCpuMode(cpu_mode));
287 
288   std::optional<StringPool::Id> unwind_error_id;
289   if (sample.has_unwind_error()) {
290     auto unwind_error =
291         static_cast<Profiling::StackUnwindError>(sample.unwind_error());
292     unwind_error_id = storage->InternString(
293         ProfilePacketUtils::StringifyStackUnwindError(unwind_error));
294   }
295   tables::PerfSampleTable::Row sample_row(ts, utid, sample.cpu(), cpu_mode_id,
296                                           cs_id, unwind_error_id,
297                                           sampling_stream.perf_session_id);
298   context_->storage->mutable_perf_sample_table()->Insert(sample_row);
299 }
300 
ParseProfilePacket(int64_t ts,PacketSequenceStateGeneration * sequence_state,uint32_t seq_id,ConstBytes blob)301 void ProfileModule::ParseProfilePacket(
302     int64_t ts,
303     PacketSequenceStateGeneration* sequence_state,
304     uint32_t seq_id,
305     ConstBytes blob) {
306   protos::pbzero::ProfilePacket::Decoder packet(blob.data, blob.size);
307   context_->heap_profile_tracker->SetProfilePacketIndex(seq_id, packet.index());
308 
309   for (auto it = packet.strings(); it; ++it) {
310     protos::pbzero::InternedString::Decoder entry(*it);
311 
312     const char* str = reinterpret_cast<const char*>(entry.str().data);
313     auto str_view = base::StringView(str, entry.str().size);
314     sequence_state->state()->sequence_stack_profile_tracker().AddString(
315         entry.iid(), str_view);
316   }
317 
318   for (auto it = packet.mappings(); it; ++it) {
319     protos::pbzero::Mapping::Decoder entry(*it);
320     SequenceStackProfileTracker::SourceMapping src_mapping =
321         ProfilePacketUtils::MakeSourceMapping(entry);
322     sequence_state->state()->sequence_stack_profile_tracker().AddMapping(
323         entry.iid(), src_mapping);
324   }
325 
326   for (auto it = packet.frames(); it; ++it) {
327     protos::pbzero::Frame::Decoder entry(*it);
328     SequenceStackProfileTracker::SourceFrame src_frame =
329         ProfilePacketUtils::MakeSourceFrame(entry);
330     sequence_state->state()->sequence_stack_profile_tracker().AddFrame(
331         entry.iid(), src_frame);
332   }
333 
334   for (auto it = packet.callstacks(); it; ++it) {
335     protos::pbzero::Callstack::Decoder entry(*it);
336     SequenceStackProfileTracker::SourceCallstack src_callstack =
337         ProfilePacketUtils::MakeSourceCallstack(entry);
338     sequence_state->state()->sequence_stack_profile_tracker().AddCallstack(
339         entry.iid(), src_callstack);
340   }
341 
342   for (auto it = packet.process_dumps(); it; ++it) {
343     protos::pbzero::ProfilePacket::ProcessHeapSamples::Decoder entry(*it);
344 
345     base::StatusOr<int64_t> maybe_timestamp =
346         context_->clock_tracker->ToTraceTime(
347             protos::pbzero::BUILTIN_CLOCK_MONOTONIC_COARSE,
348             static_cast<int64_t>(entry.timestamp()));
349 
350     // ToTraceTime() increments the clock_sync_failure error stat in this case.
351     if (!maybe_timestamp.ok())
352       continue;
353 
354     int64_t timestamp = *maybe_timestamp;
355 
356     int pid = static_cast<int>(entry.pid());
357     context_->storage->SetIndexedStats(stats::heapprofd_last_profile_timestamp,
358                                        pid, ts);
359 
360     if (entry.disconnected())
361       context_->storage->IncrementIndexedStats(
362           stats::heapprofd_client_disconnected, pid);
363     if (entry.buffer_corrupted())
364       context_->storage->IncrementIndexedStats(
365           stats::heapprofd_buffer_corrupted, pid);
366     if (entry.buffer_overran() ||
367         entry.client_error() ==
368             protos::pbzero::ProfilePacket::ProcessHeapSamples::
369                 CLIENT_ERROR_HIT_TIMEOUT) {
370       context_->storage->IncrementIndexedStats(stats::heapprofd_buffer_overran,
371                                                pid);
372     }
373     if (entry.client_error()) {
374       context_->storage->SetIndexedStats(stats::heapprofd_client_error, pid,
375                                          entry.client_error());
376     }
377     if (entry.rejected_concurrent())
378       context_->storage->IncrementIndexedStats(
379           stats::heapprofd_rejected_concurrent, pid);
380     if (entry.hit_guardrail())
381       context_->storage->IncrementIndexedStats(stats::heapprofd_hit_guardrail,
382                                                pid);
383     if (entry.orig_sampling_interval_bytes()) {
384       context_->storage->SetIndexedStats(
385           stats::heapprofd_sampling_interval_adjusted, pid,
386           static_cast<int64_t>(entry.sampling_interval_bytes()) -
387               static_cast<int64_t>(entry.orig_sampling_interval_bytes()));
388     }
389 
390     protos::pbzero::ProfilePacket::ProcessStats::Decoder stats(entry.stats());
391     context_->storage->IncrementIndexedStats(
392         stats::heapprofd_unwind_time_us, static_cast<int>(entry.pid()),
393         static_cast<int64_t>(stats.total_unwinding_time_us()));
394     context_->storage->IncrementIndexedStats(
395         stats::heapprofd_unwind_samples, static_cast<int>(entry.pid()),
396         static_cast<int64_t>(stats.heap_samples()));
397     context_->storage->IncrementIndexedStats(
398         stats::heapprofd_client_spinlock_blocked, static_cast<int>(entry.pid()),
399         static_cast<int64_t>(stats.client_spinlock_blocked_us()));
400 
401     // orig_sampling_interval_bytes was introduced slightly after a bug with
402     // self_max_count was fixed in the producer. We use this as a proxy
403     // whether or not we are getting this data from a fixed producer or not.
404     bool trustworthy_max_count = entry.orig_sampling_interval_bytes() > 0;
405 
406     for (auto sample_it = entry.samples(); sample_it; ++sample_it) {
407       protos::pbzero::ProfilePacket::HeapSample::Decoder sample(*sample_it);
408 
409       HeapProfileTracker::SourceAllocation src_allocation;
410       src_allocation.pid = entry.pid();
411       if (entry.heap_name().size != 0) {
412         src_allocation.heap_name =
413             context_->storage->InternString(entry.heap_name());
414       } else {
415         src_allocation.heap_name = context_->storage->InternString("malloc");
416       }
417       src_allocation.timestamp = timestamp;
418       src_allocation.callstack_id = sample.callstack_id();
419       if (sample.has_self_max()) {
420         src_allocation.self_allocated = sample.self_max();
421         if (trustworthy_max_count)
422           src_allocation.alloc_count = sample.self_max_count();
423       } else {
424         src_allocation.self_allocated = sample.self_allocated();
425         src_allocation.self_freed = sample.self_freed();
426         src_allocation.alloc_count = sample.alloc_count();
427         src_allocation.free_count = sample.free_count();
428       }
429 
430       context_->heap_profile_tracker->StoreAllocation(seq_id, src_allocation);
431     }
432   }
433   if (!packet.continued()) {
434     PERFETTO_CHECK(sequence_state);
435     ProfilePacketInternLookup intern_lookup(sequence_state);
436     context_->heap_profile_tracker->FinalizeProfile(
437         seq_id, &sequence_state->state()->sequence_stack_profile_tracker(),
438         &intern_lookup);
439   }
440 }
441 
ParseModuleSymbols(ConstBytes blob)442 void ProfileModule::ParseModuleSymbols(ConstBytes blob) {
443   protos::pbzero::ModuleSymbols::Decoder module_symbols(blob.data, blob.size);
444   StringId build_id;
445   // TODO(b/148109467): Remove workaround once all active Chrome versions
446   // write raw bytes instead of a string as build_id.
447   if (util::IsHexModuleId(module_symbols.build_id())) {
448     build_id = context_->storage->InternString(module_symbols.build_id());
449   } else {
450     build_id = context_->storage->InternString(base::StringView(base::ToHex(
451         module_symbols.build_id().data, module_symbols.build_id().size)));
452   }
453 
454   auto mapping_ids = context_->global_stack_profile_tracker->FindMappingRow(
455       context_->storage->InternString(module_symbols.path()), build_id);
456   if (mapping_ids.empty()) {
457     context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
458     return;
459   }
460   for (auto addr_it = module_symbols.address_symbols(); addr_it; ++addr_it) {
461     protos::pbzero::AddressSymbols::Decoder address_symbols(*addr_it);
462 
463     uint32_t symbol_set_id = context_->storage->symbol_table().row_count();
464 
465     bool has_lines = false;
466     // Taking the last (i.e. the least interned) location if there're several.
467     ArgsTranslationTable::SourceLocation last_location;
468     for (auto line_it = address_symbols.lines(); line_it; ++line_it) {
469       protos::pbzero::Line::Decoder line(*line_it);
470       context_->storage->mutable_symbol_table()->Insert(
471           {symbol_set_id, context_->storage->InternString(line.function_name()),
472            context_->storage->InternString(line.source_file_name()),
473            line.line_number()});
474       last_location = ArgsTranslationTable::SourceLocation{
475           line.source_file_name().ToStdString(),
476           line.function_name().ToStdString(), line.line_number()};
477       has_lines = true;
478     }
479     if (!has_lines) {
480       continue;
481     }
482     bool frame_found = false;
483     for (MappingId mapping_id : mapping_ids) {
484       context_->args_translation_table->AddNativeSymbolTranslationRule(
485           mapping_id, address_symbols.address(), last_location);
486       std::vector<FrameId> frame_ids =
487           context_->global_stack_profile_tracker->FindFrameIds(
488               mapping_id, address_symbols.address());
489 
490       for (const FrameId frame_id : frame_ids) {
491         auto* frames = context_->storage->mutable_stack_profile_frame_table();
492         uint32_t frame_row = *frames->id().IndexOf(frame_id);
493         frames->mutable_symbol_set_id()->Set(frame_row, symbol_set_id);
494         frame_found = true;
495       }
496     }
497 
498     if (!frame_found) {
499       context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
500       continue;
501     }
502   }
503 }
504 
ParseDeobfuscationMapping(int64_t,PacketSequenceStateGeneration *,uint32_t,ConstBytes blob)505 void ProfileModule::ParseDeobfuscationMapping(int64_t,
506                                               PacketSequenceStateGeneration*,
507                                               uint32_t /* seq_id */,
508                                               ConstBytes blob) {
509   DeobfuscationMappingTable deobfuscation_mapping_table;
510   protos::pbzero::DeobfuscationMapping::Decoder deobfuscation_mapping(
511       blob.data, blob.size);
512   if (deobfuscation_mapping.package_name().size == 0)
513     return;
514 
515   auto opt_package_name_id = context_->storage->string_pool().GetId(
516       deobfuscation_mapping.package_name());
517   auto opt_memfd_id = context_->storage->string_pool().GetId("memfd");
518   if (!opt_package_name_id && !opt_memfd_id)
519     return;
520 
521   for (auto class_it = deobfuscation_mapping.obfuscated_classes(); class_it;
522        ++class_it) {
523     protos::pbzero::ObfuscatedClass::Decoder cls(*class_it);
524     base::FlatHashMap<StringId, StringId> obfuscated_to_deobfuscated_members;
525     for (auto member_it = cls.obfuscated_methods(); member_it; ++member_it) {
526       protos::pbzero::ObfuscatedMember::Decoder member(*member_it);
527       std::string merged_obfuscated = cls.obfuscated_name().ToStdString() +
528                                       "." +
529                                       member.obfuscated_name().ToStdString();
530       auto merged_obfuscated_id = context_->storage->string_pool().GetId(
531           base::StringView(merged_obfuscated));
532       if (!merged_obfuscated_id)
533         continue;
534       std::string merged_deobfuscated =
535           FullyQualifiedDeobfuscatedName(cls, member);
536 
537       std::vector<tables::StackProfileFrameTable::Id> frames;
538       if (opt_package_name_id) {
539         const std::vector<tables::StackProfileFrameTable::Id>* pkg_frames =
540             context_->global_stack_profile_tracker->JavaFramesForName(
541                 {*merged_obfuscated_id, *opt_package_name_id});
542         if (pkg_frames) {
543           frames.insert(frames.end(), pkg_frames->begin(), pkg_frames->end());
544         }
545       }
546       if (opt_memfd_id) {
547         const std::vector<tables::StackProfileFrameTable::Id>* memfd_frames =
548             context_->global_stack_profile_tracker->JavaFramesForName(
549                 {*merged_obfuscated_id, *opt_memfd_id});
550         if (memfd_frames) {
551           frames.insert(frames.end(), memfd_frames->begin(),
552                         memfd_frames->end());
553         }
554       }
555 
556       for (tables::StackProfileFrameTable::Id frame_id : frames) {
557         auto* frames_tbl =
558             context_->storage->mutable_stack_profile_frame_table();
559         frames_tbl->mutable_deobfuscated_name()->Set(
560             *frames_tbl->id().IndexOf(frame_id),
561             context_->storage->InternString(
562                 base::StringView(merged_deobfuscated)));
563       }
564       obfuscated_to_deobfuscated_members[context_->storage->InternString(
565           member.obfuscated_name())] =
566           context_->storage->InternString(member.deobfuscated_name());
567     }
568     // Members can contain a class name (e.g "ClassA.FunctionF")
569     deobfuscation_mapping_table.AddClassTranslation(
570         DeobfuscationMappingTable::PackageId{
571             deobfuscation_mapping.package_name().ToStdString(),
572             deobfuscation_mapping.version_code()},
573         context_->storage->InternString(cls.obfuscated_name()),
574         context_->storage->InternString(cls.deobfuscated_name()),
575         std::move(obfuscated_to_deobfuscated_members));
576   }
577   context_->args_translation_table->AddDeobfuscationMappingTable(
578       std::move(deobfuscation_mapping_table));
579 }
580 
ParseSmapsPacket(int64_t ts,ConstBytes blob)581 void ProfileModule::ParseSmapsPacket(int64_t ts, ConstBytes blob) {
582   protos::pbzero::SmapsPacket::Decoder sp(blob.data, blob.size);
583   auto upid = context_->process_tracker->GetOrCreateProcess(sp.pid());
584 
585   for (auto it = sp.entries(); it; ++it) {
586     protos::pbzero::SmapsEntry::Decoder e(*it);
587     context_->storage->mutable_profiler_smaps_table()->Insert(
588         {upid, ts, context_->storage->InternString(e.path()),
589          static_cast<int64_t>(e.size_kb()),
590          static_cast<int64_t>(e.private_dirty_kb()),
591          static_cast<int64_t>(e.swap_kb()),
592          context_->storage->InternString(e.file_name()),
593          static_cast<int64_t>(e.start_address()),
594          static_cast<int64_t>(e.module_timestamp()),
595          context_->storage->InternString(e.module_debugid()),
596          context_->storage->InternString(e.module_debug_path()),
597          static_cast<int32_t>(e.protection_flags()),
598          static_cast<int64_t>(e.private_clean_resident_kb()),
599          static_cast<int64_t>(e.shared_dirty_resident_kb()),
600          static_cast<int64_t>(e.shared_clean_resident_kb()),
601          static_cast<int64_t>(e.locked_kb()),
602          static_cast<int64_t>(e.proportional_resident_kb())});
603   }
604 }
605 
NotifyEndOfFile()606 void ProfileModule::NotifyEndOfFile() {
607   for (auto it = context_->storage->stack_profile_mapping_table().IterateRows();
608        it; ++it) {
609     NullTermStringView path = context_->storage->GetString(it.name());
610     NullTermStringView build_id = context_->storage->GetString(it.build_id());
611 
612     if (path.StartsWith("/data/local/tmp/") && build_id.empty()) {
613       context_->storage->IncrementStats(
614           stats::symbolization_tmp_build_id_not_found);
615     }
616   }
617 }
618 
619 }  // namespace trace_processor
620 }  // namespace perfetto
621