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