| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "src/trace_processor/proto_trace_parser.h" |
| |
| #include <inttypes.h> |
| #include <string.h> |
| |
| #include <string> |
| |
| #include "perfetto/base/logging.h" |
| #include "perfetto/ext/base/metatrace_events.h" |
| #include "perfetto/ext/base/string_utils.h" |
| #include "perfetto/ext/base/string_view.h" |
| #include "perfetto/ext/base/string_writer.h" |
| #include "perfetto/ext/base/utils.h" |
| #include "perfetto/ext/traced/sys_stats_counters.h" |
| #include "perfetto/protozero/proto_decoder.h" |
| #include "src/trace_processor/args_tracker.h" |
| #include "src/trace_processor/clock_tracker.h" |
| #include "src/trace_processor/event_tracker.h" |
| #include "src/trace_processor/ftrace_descriptors.h" |
| #include "src/trace_processor/heap_graph_tracker.h" |
| #include "src/trace_processor/heap_profile_tracker.h" |
| #include "src/trace_processor/metadata.h" |
| #include "src/trace_processor/process_tracker.h" |
| #include "src/trace_processor/proto_incremental_state.h" |
| #include "src/trace_processor/stack_profile_tracker.h" |
| #include "src/trace_processor/syscall_tracker.h" |
| #include "src/trace_processor/systrace_parser.h" |
| #include "src/trace_processor/timestamped_trace_piece.h" |
| #include "src/trace_processor/trace_processor_context.h" |
| #include "src/trace_processor/track_tracker.h" |
| #include "src/trace_processor/variadic.h" |
| |
| #include "protos/perfetto/common/android_log_constants.pbzero.h" |
| #include "protos/perfetto/common/trace_stats.pbzero.h" |
| #include "protos/perfetto/config/trace_config.pbzero.h" |
| #include "protos/perfetto/trace/android/android_log.pbzero.h" |
| #include "protos/perfetto/trace/android/packages_list.pbzero.h" |
| #include "protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h" |
| #include "protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h" |
| #include "protos/perfetto/trace/clock_snapshot.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/ftrace.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/ftrace_stats.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/generic.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/kmem.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/lowmemorykiller.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/mm_event.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/oom.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/power.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/raw_syscalls.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/sched.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/signal.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/systrace.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/task.pbzero.h" |
| #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h" |
| #include "protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.h" |
| #include "protos/perfetto/trace/power/battery_counters.pbzero.h" |
| #include "protos/perfetto/trace/power/power_rails.pbzero.h" |
| #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h" |
| #include "protos/perfetto/trace/profiling/profile_common.pbzero.h" |
| #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h" |
| #include "protos/perfetto/trace/ps/process_stats.pbzero.h" |
| #include "protos/perfetto/trace/ps/process_tree.pbzero.h" |
| #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h" |
| #include "protos/perfetto/trace/system_info.pbzero.h" |
| #include "protos/perfetto/trace/trace.pbzero.h" |
| #include "protos/perfetto/trace/trace_packet.pbzero.h" |
| #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h" |
| #include "protos/perfetto/trace/track_event/log_message.pbzero.h" |
| #include "protos/perfetto/trace/track_event/source_location.pbzero.h" |
| #include "protos/perfetto/trace/track_event/task_execution.pbzero.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| |
| namespace { |
| |
| // kthreadd is the parent process for all kernel threads and always has |
| // pid == 2 on Linux and Android. |
| const uint32_t kKthreaddPid = 2; |
| const char kKthreaddName[] = "kthreadd"; |
| |
| using protozero::ProtoDecoder; |
| |
| StackProfileTracker::SourceMapping MakeSourceMapping( |
| const protos::pbzero::Mapping::Decoder& entry) { |
| StackProfileTracker::SourceMapping src_mapping{}; |
| src_mapping.build_id = entry.build_id(); |
| src_mapping.exact_offset = entry.exact_offset(); |
| src_mapping.start_offset = entry.start_offset(); |
| src_mapping.start = entry.start(); |
| src_mapping.end = entry.end(); |
| src_mapping.load_bias = entry.load_bias(); |
| for (auto path_string_id_it = entry.path_string_ids(); path_string_id_it; |
| ++path_string_id_it) |
| src_mapping.name_ids.emplace_back(path_string_id_it->as_uint32()); |
| return src_mapping; |
| } |
| |
| StackProfileTracker::SourceFrame MakeSourceFrame( |
| const protos::pbzero::Frame::Decoder& entry) { |
| StackProfileTracker::SourceFrame src_frame; |
| src_frame.name_id = entry.function_name_id(); |
| src_frame.mapping_id = entry.mapping_id(); |
| src_frame.rel_pc = entry.rel_pc(); |
| return src_frame; |
| } |
| |
| StackProfileTracker::SourceCallstack MakeSourceCallstack( |
| const protos::pbzero::Callstack::Decoder& entry) { |
| StackProfileTracker::SourceCallstack src_callstack; |
| for (auto frame_it = entry.frame_ids(); frame_it; ++frame_it) |
| src_callstack.emplace_back(frame_it->as_uint64()); |
| return src_callstack; |
| } |
| |
| class ProfilePacketInternLookup : public StackProfileTracker::InternLookup { |
| public: |
| ProfilePacketInternLookup( |
| ProtoIncrementalState::PacketSequenceState* seq_state, |
| size_t seq_state_generation) |
| : seq_state_(seq_state), seq_state_generation_(seq_state_generation) {} |
| |
| base::Optional<base::StringView> GetString( |
| StackProfileTracker::SourceStringId iid, |
| StackProfileTracker::InternedStringType type) const override { |
| protos::pbzero::InternedString::Decoder* decoder = nullptr; |
| switch (type) { |
| case StackProfileTracker::InternedStringType::kBuildId: |
| decoder = seq_state_->LookupInternedMessage< |
| protos::pbzero::InternedData::kBuildIdsFieldNumber, |
| protos::pbzero::InternedString>(seq_state_generation_, iid); |
| break; |
| case StackProfileTracker::InternedStringType::kFunctionName: |
| decoder = seq_state_->LookupInternedMessage< |
| protos::pbzero::InternedData::kFunctionNamesFieldNumber, |
| protos::pbzero::InternedString>(seq_state_generation_, iid); |
| break; |
| case StackProfileTracker::InternedStringType::kMappingPath: |
| decoder = seq_state_->LookupInternedMessage< |
| protos::pbzero::InternedData::kMappingPathsFieldNumber, |
| protos::pbzero::InternedString>(seq_state_generation_, iid); |
| break; |
| } |
| if (!decoder) |
| return base::nullopt; |
| return base::StringView(reinterpret_cast<const char*>(decoder->str().data), |
| decoder->str().size); |
| } |
| |
| base::Optional<StackProfileTracker::SourceMapping> GetMapping( |
| StackProfileTracker::SourceMappingId iid) const override { |
| auto* decoder = seq_state_->LookupInternedMessage< |
| protos::pbzero::InternedData::kMappingsFieldNumber, |
| protos::pbzero::Mapping>(seq_state_generation_, iid); |
| if (!decoder) |
| return base::nullopt; |
| return MakeSourceMapping(*decoder); |
| } |
| |
| base::Optional<StackProfileTracker::SourceFrame> GetFrame( |
| StackProfileTracker::SourceFrameId iid) const override { |
| auto* decoder = seq_state_->LookupInternedMessage< |
| protos::pbzero::InternedData::kFramesFieldNumber, |
| protos::pbzero::Frame>(seq_state_generation_, iid); |
| if (!decoder) |
| return base::nullopt; |
| return MakeSourceFrame(*decoder); |
| } |
| |
| base::Optional<StackProfileTracker::SourceCallstack> GetCallstack( |
| StackProfileTracker::SourceCallstackId iid) const override { |
| auto* decoder = seq_state_->LookupInternedMessage< |
| protos::pbzero::InternedData::kCallstacksFieldNumber, |
| protos::pbzero::Callstack>(seq_state_generation_, iid); |
| if (!decoder) |
| return base::nullopt; |
| return MakeSourceCallstack(*decoder); |
| } |
| |
| private: |
| ProtoIncrementalState::PacketSequenceState* seq_state_; |
| size_t seq_state_generation_; |
| }; |
| |
| namespace { |
| // Slices which have been opened but haven't been closed yet will be marked |
| // with these placeholder values. |
| constexpr int64_t kPendingThreadDuration = -1; |
| constexpr int64_t kPendingThreadInstructionDelta = -1; |
| } // namespace |
| |
| const char* HeapGraphRootTypeToString(int32_t type) { |
| switch (type) { |
| case protos::pbzero::HeapGraphRoot::ROOT_UNKNOWN: |
| return "ROOT_UNKNOWN"; |
| case protos::pbzero::HeapGraphRoot::ROOT_JNI_GLOBAL: |
| return "ROOT_JNI_GLOBAL"; |
| case protos::pbzero::HeapGraphRoot::ROOT_JNI_LOCAL: |
| return "ROOT_JNI_LOCAL"; |
| case protos::pbzero::HeapGraphRoot::ROOT_JAVA_FRAME: |
| return "ROOT_JAVA_FRAME"; |
| case protos::pbzero::HeapGraphRoot::ROOT_NATIVE_STACK: |
| return "ROOT_NATIVE_STACK"; |
| case protos::pbzero::HeapGraphRoot::ROOT_STICKY_CLASS: |
| return "ROOT_STICKY_CLASS"; |
| case protos::pbzero::HeapGraphRoot::ROOT_THREAD_BLOCK: |
| return "ROOT_THREAD_BLOCK"; |
| case protos::pbzero::HeapGraphRoot::ROOT_MONITOR_USED: |
| return "ROOT_MONITOR_USED"; |
| case protos::pbzero::HeapGraphRoot::ROOT_THREAD_OBJECT: |
| return "ROOT_THREAD_OBJECT"; |
| case protos::pbzero::HeapGraphRoot::ROOT_INTERNED_STRING: |
| return "ROOT_INTERNED_STRING"; |
| case protos::pbzero::HeapGraphRoot::ROOT_FINALIZING: |
| return "ROOT_FINALIZING"; |
| case protos::pbzero::HeapGraphRoot::ROOT_DEBUGGER: |
| return "ROOT_DEBUGGER"; |
| case protos::pbzero::HeapGraphRoot::ROOT_REFERENCE_CLEANUP: |
| return "ROOT_REFERENCE_CLEANUP"; |
| case protos::pbzero::HeapGraphRoot::ROOT_VM_INTERNAL: |
| return "ROOT_VM_INTERNAL"; |
| case protos::pbzero::HeapGraphRoot::ROOT_JNI_MONITOR: |
| return "ROOT_JNI_MONITOR"; |
| default: |
| return "ROOT_UNKNOWN"; |
| } |
| } |
| |
| } // namespace |
| |
| ProtoTraceParser::ProtoTraceParser(TraceProcessorContext* context) |
| : context_(context), |
| graphics_event_parser_(new GraphicsEventParser(context_)), |
| utid_name_id_(context->storage->InternString("utid")), |
| sched_wakeup_name_id_(context->storage->InternString("sched_wakeup")), |
| sched_waking_name_id_(context->storage->InternString("sched_waking")), |
| cpu_freq_name_id_(context->storage->InternString("cpufreq")), |
| cpu_idle_name_id_(context->storage->InternString("cpuidle")), |
| gpu_freq_name_id_(context->storage->InternString("gpufreq")), |
| comm_name_id_(context->storage->InternString("comm")), |
| num_forks_name_id_(context->storage->InternString("num_forks")), |
| num_irq_total_name_id_(context->storage->InternString("num_irq_total")), |
| num_softirq_total_name_id_( |
| context->storage->InternString("num_softirq_total")), |
| num_irq_name_id_(context->storage->InternString("num_irq")), |
| num_softirq_name_id_(context->storage->InternString("num_softirq")), |
| cpu_times_user_ns_id_( |
| context->storage->InternString("cpu.times.user_ns")), |
| cpu_times_user_nice_ns_id_( |
| context->storage->InternString("cpu.times.user_nice_ns")), |
| cpu_times_system_mode_ns_id_( |
| context->storage->InternString("cpu.times.system_mode_ns")), |
| cpu_times_idle_ns_id_( |
| context->storage->InternString("cpu.times.idle_ns")), |
| cpu_times_io_wait_ns_id_( |
| context->storage->InternString("cpu.times.io_wait_ns")), |
| cpu_times_irq_ns_id_(context->storage->InternString("cpu.times.irq_ns")), |
| cpu_times_softirq_ns_id_( |
| context->storage->InternString("cpu.times.softirq_ns")), |
| signal_deliver_id_(context->storage->InternString("signal_deliver")), |
| signal_generate_id_(context->storage->InternString("signal_generate")), |
| batt_charge_id_(context->storage->InternString("batt.charge_uah")), |
| batt_capacity_id_(context->storage->InternString("batt.capacity_pct")), |
| batt_current_id_(context->storage->InternString("batt.current_ua")), |
| batt_current_avg_id_( |
| context->storage->InternString("batt.current.avg_ua")), |
| lmk_id_(context->storage->InternString("mem.lmk")), |
| oom_score_adj_id_(context->storage->InternString("oom_score_adj")), |
| ion_total_unknown_id_(context->storage->InternString("mem.ion.unknown")), |
| ion_change_unknown_id_( |
| context->storage->InternString("mem.ion_change.unknown")), |
| metatrace_id_(context->storage->InternString("metatrace")), |
| task_file_name_args_key_id_( |
| context->storage->InternString("task.posted_from.file_name")), |
| task_function_name_args_key_id_( |
| context->storage->InternString("task.posted_from.function_name")), |
| task_line_number_args_key_id_( |
| context->storage->InternString("task.posted_from.line_number")), |
| log_message_body_key_id_( |
| context->storage->InternString("track_event.log_message")), |
| data_name_id_(context->storage->InternString("data")), |
| raw_chrome_metadata_event_id_( |
| context->storage->InternString("chrome_event.metadata")), |
| raw_chrome_legacy_system_trace_event_id_( |
| context->storage->InternString("chrome_event.legacy_system_trace")), |
| raw_chrome_legacy_user_trace_event_id_( |
| context->storage->InternString("chrome_event.legacy_user_trace")), |
| raw_legacy_event_id_( |
| context->storage->InternString("track_event.legacy_event")), |
| legacy_event_category_key_id_( |
| context->storage->InternString("legacy_event.category")), |
| legacy_event_name_key_id_( |
| context->storage->InternString("legacy_event.name")), |
| legacy_event_phase_key_id_( |
| context->storage->InternString("legacy_event.phase")), |
| legacy_event_duration_ns_key_id_( |
| context->storage->InternString("legacy_event.duration_ns")), |
| legacy_event_thread_timestamp_ns_key_id_( |
| context->storage->InternString("legacy_event.thread_timestamp_ns")), |
| legacy_event_thread_duration_ns_key_id_( |
| context->storage->InternString("legacy_event.thread_duration_ns")), |
| legacy_event_thread_instruction_count_key_id_( |
| context->storage->InternString( |
| "legacy_event.thread_instruction_count")), |
| legacy_event_thread_instruction_delta_key_id_( |
| context->storage->InternString( |
| "legacy_event.thread_instruction_delta")), |
| legacy_event_use_async_tts_key_id_( |
| context->storage->InternString("legacy_event.use_async_tts")), |
| legacy_event_global_id_key_id_( |
| context->storage->InternString("legacy_event.global_id")), |
| legacy_event_local_id_key_id_( |
| context->storage->InternString("legacy_event.local_id")), |
| legacy_event_id_scope_key_id_( |
| context->storage->InternString("legacy_event.id_scope")), |
| legacy_event_bind_id_key_id_( |
| context->storage->InternString("legacy_event.bind_id")), |
| legacy_event_bind_to_enclosing_key_id_( |
| context->storage->InternString("legacy_event.bind_to_enclosing")), |
| legacy_event_flow_direction_key_id_( |
| context->storage->InternString("legacy_event.flow_direction")), |
| flow_direction_value_in_id_(context->storage->InternString("in")), |
| flow_direction_value_out_id_(context->storage->InternString("out")), |
| flow_direction_value_inout_id_(context->storage->InternString("inout")) { |
| for (const auto& name : BuildMeminfoCounterNames()) { |
| meminfo_strs_id_.emplace_back(context->storage->InternString(name)); |
| } |
| for (const auto& name : BuildVmstatCounterNames()) { |
| vmstat_strs_id_.emplace_back(context->storage->InternString(name)); |
| } |
| rss_members_.emplace_back(context->storage->InternString("mem.rss.file")); |
| rss_members_.emplace_back(context->storage->InternString("mem.rss.anon")); |
| rss_members_.emplace_back(context->storage->InternString("mem.swap")); |
| rss_members_.emplace_back(context->storage->InternString("mem.rss.shmem")); |
| rss_members_.emplace_back( |
| context->storage->InternString("mem.rss.unknown")); // Keep this last. |
| |
| using ProcessStats = protos::pbzero::ProcessStats; |
| proc_stats_process_names_[ProcessStats::Process::kVmSizeKbFieldNumber] = |
| context->storage->InternString("mem.virt"); |
| proc_stats_process_names_[ProcessStats::Process::kVmRssKbFieldNumber] = |
| context->storage->InternString("mem.rss"); |
| proc_stats_process_names_[ProcessStats::Process::kRssAnonKbFieldNumber] = |
| context->storage->InternString("mem.rss.anon"); |
| proc_stats_process_names_[ProcessStats::Process::kRssFileKbFieldNumber] = |
| context->storage->InternString("mem.rss.file"); |
| proc_stats_process_names_[ProcessStats::Process::kRssShmemKbFieldNumber] = |
| context->storage->InternString("mem.rss.shmem"); |
| proc_stats_process_names_[ProcessStats::Process::kVmSwapKbFieldNumber] = |
| context->storage->InternString("mem.swap"); |
| proc_stats_process_names_[ProcessStats::Process::kVmLockedKbFieldNumber] = |
| context->storage->InternString("mem.locked"); |
| proc_stats_process_names_[ProcessStats::Process::kVmHwmKbFieldNumber] = |
| context->storage->InternString("mem.rss.watermark"); |
| proc_stats_process_names_[ProcessStats::Process::kOomScoreAdjFieldNumber] = |
| oom_score_adj_id_; |
| |
| mm_event_counter_names_ = { |
| {MmEventCounterNames( |
| context->storage->InternString("mem.mm.min_flt.count"), |
| context->storage->InternString("mem.mm.min_flt.max_lat"), |
| context->storage->InternString("mem.mm.min_flt.avg_lat")), |
| MmEventCounterNames( |
| context->storage->InternString("mem.mm.maj_flt.count"), |
| context->storage->InternString("mem.mm.maj_flt.max_lat"), |
| context->storage->InternString("mem.mm.maj_flt.avg_lat")), |
| MmEventCounterNames( |
| context->storage->InternString("mem.mm.read_io.count"), |
| context->storage->InternString("mem.mm.read_io.max_lat"), |
| context->storage->InternString("mem.mm.read_io.avg_lat")), |
| MmEventCounterNames( |
| context->storage->InternString("mem.mm.compaction.count"), |
| context->storage->InternString("mem.mm.compaction.max_lat"), |
| context->storage->InternString("mem.mm.compaction.avg_lat")), |
| MmEventCounterNames( |
| context->storage->InternString("mem.mm.reclaim.count"), |
| context->storage->InternString("mem.mm.reclaim.max_lat"), |
| context->storage->InternString("mem.mm.reclaim.avg_lat")), |
| MmEventCounterNames( |
| context->storage->InternString("mem.mm.swp_flt.count"), |
| context->storage->InternString("mem.mm.swp_flt.max_lat"), |
| context->storage->InternString("mem.mm.swp_flt.avg_lat")), |
| MmEventCounterNames( |
| context->storage->InternString("mem.mm.kern_alloc.count"), |
| context->storage->InternString("mem.mm.kern_alloc.max_lat"), |
| context->storage->InternString("mem.mm.kern_alloc.avg_lat"))}}; |
| |
| // TODO(140860736): Once we support null values for |
| // stack_profile_frame.symbol_set_id remove this hack |
| context_->storage->mutable_symbol_table()->Insert({0, 0, 0, 0}); |
| |
| // Build the lookup table for the strings inside ftrace events (e.g. the |
| // name of ftrace event fields and the names of their args). |
| for (size_t i = 0; i < GetDescriptorsSize(); i++) { |
| auto* descriptor = GetMessageDescriptorForId(i); |
| if (!descriptor->name) { |
| ftrace_message_strings_.emplace_back(); |
| continue; |
| } |
| |
| FtraceMessageStrings ftrace_strings; |
| ftrace_strings.message_name_id = |
| context->storage->InternString(descriptor->name); |
| |
| for (size_t fid = 0; fid <= descriptor->max_field_id; fid++) { |
| const auto& field = descriptor->fields[fid]; |
| if (!field.name) |
| continue; |
| ftrace_strings.field_name_ids[fid] = |
| context->storage->InternString(field.name); |
| } |
| ftrace_message_strings_.emplace_back(ftrace_strings); |
| } |
| } |
| |
| ProtoTraceParser::~ProtoTraceParser() = default; |
| |
| void ProtoTraceParser::ParseTracePacket(int64_t ts, TimestampedTracePiece ttp) { |
| PERFETTO_DCHECK(ttp.json_value == nullptr); |
| const TraceBlobView& blob = ttp.blob_view; |
| |
| protos::pbzero::TracePacket::Decoder packet(blob.data(), blob.length()); |
| |
| if (packet.has_process_tree()) |
| ParseProcessTree(packet.process_tree()); |
| |
| if (packet.has_process_stats()) |
| ParseProcessStats(ts, packet.process_stats()); |
| |
| if (packet.has_sys_stats()) |
| ParseSysStats(ts, packet.sys_stats()); |
| |
| if (packet.has_battery()) |
| ParseBatteryCounters(ts, packet.battery()); |
| |
| if (packet.has_power_rails()) |
| ParsePowerRails(packet.power_rails()); |
| |
| if (packet.has_trace_stats()) |
| ParseTraceStats(packet.trace_stats()); |
| |
| if (packet.has_ftrace_stats()) |
| ParseFtraceStats(packet.ftrace_stats()); |
| |
| if (packet.has_android_log()) |
| ParseAndroidLogPacket(packet.android_log()); |
| |
| if (packet.has_profile_packet()) { |
| ParseProfilePacket(ts, ttp.packet_sequence_state, |
| ttp.packet_sequence_state_generation, |
| packet.profile_packet()); |
| } |
| |
| if (packet.has_streaming_profile_packet()) { |
| ParseStreamingProfilePacket(ttp.packet_sequence_state, |
| ttp.packet_sequence_state_generation, |
| packet.streaming_profile_packet()); |
| } |
| |
| if (packet.has_system_info()) |
| ParseSystemInfo(packet.system_info()); |
| |
| if (packet.has_track_event()) { |
| ParseTrackEvent(ts, ttp.thread_timestamp, ttp.thread_instruction_count, |
| ttp.packet_sequence_state, |
| ttp.packet_sequence_state_generation, packet.track_event()); |
| } |
| |
| if (packet.has_chrome_benchmark_metadata()) { |
| ParseChromeBenchmarkMetadata(packet.chrome_benchmark_metadata()); |
| } |
| |
| if (packet.has_chrome_events()) { |
| ParseChromeEvents(ts, packet.chrome_events()); |
| } |
| |
| if (packet.has_perfetto_metatrace()) { |
| ParseMetatraceEvent(ts, packet.perfetto_metatrace()); |
| } |
| |
| if (packet.has_gpu_counter_event()) { |
| graphics_event_parser_->ParseGpuCounterEvent(ts, |
| packet.gpu_counter_event()); |
| } |
| |
| if (packet.has_gpu_render_stage_event()) { |
| graphics_event_parser_->ParseGpuRenderStageEvent( |
| ts, packet.gpu_render_stage_event()); |
| } |
| |
| if (packet.has_trace_config()) { |
| ParseTraceConfig(packet.trace_config()); |
| } |
| |
| if (packet.has_gpu_log()) { |
| graphics_event_parser_->ParseGpuLog(ts, packet.gpu_log()); |
| } |
| |
| if (packet.has_packages_list()) { |
| ParseAndroidPackagesList(packet.packages_list()); |
| } |
| |
| if (packet.has_graphics_frame_event()) { |
| graphics_event_parser_->ParseGraphicsFrameEvent( |
| ts, packet.graphics_frame_event()); |
| } |
| |
| if (packet.has_module_symbols()) { |
| ParseModuleSymbols(packet.module_symbols()); |
| } |
| |
| if (packet.has_heap_graph()) { |
| ParseHeapGraph(ts, packet.heap_graph()); |
| } |
| |
| if (packet.has_vulkan_memory_event()) { |
| graphics_event_parser_->ParseVulkanMemoryEvent( |
| packet.vulkan_memory_event()); |
| } |
| |
| // TODO(lalitm): maybe move this to the flush method in the trace processor |
| // once we have it. This may reduce performance in the ArgsTracker though so |
| // needs to be handled carefully. |
| context_->args_tracker->Flush(); |
| PERFETTO_DCHECK(!packet.bytes_left()); |
| } |
| |
| void ProtoTraceParser::ParseSysStats(int64_t ts, ConstBytes blob) { |
| protos::pbzero::SysStats::Decoder sys_stats(blob.data, blob.size); |
| |
| for (auto it = sys_stats.meminfo(); it; ++it) { |
| protos::pbzero::SysStats::MeminfoValue::Decoder mi(it->data(), it->size()); |
| auto key = static_cast<size_t>(mi.key()); |
| if (PERFETTO_UNLIKELY(key >= meminfo_strs_id_.size())) { |
| PERFETTO_ELOG("MemInfo key %zu is not recognized.", key); |
| context_->storage->IncrementStats(stats::meminfo_unknown_keys); |
| continue; |
| } |
| // /proc/meminfo counters are in kB, convert to bytes |
| context_->event_tracker->PushCounter( |
| ts, mi.value() * 1024L, meminfo_strs_id_[key], 0, RefType::kRefNoRef); |
| } |
| |
| for (auto it = sys_stats.vmstat(); it; ++it) { |
| protos::pbzero::SysStats::VmstatValue::Decoder vm(it->data(), it->size()); |
| auto key = static_cast<size_t>(vm.key()); |
| if (PERFETTO_UNLIKELY(key >= vmstat_strs_id_.size())) { |
| PERFETTO_ELOG("VmStat key %zu is not recognized.", key); |
| context_->storage->IncrementStats(stats::vmstat_unknown_keys); |
| continue; |
| } |
| context_->event_tracker->PushCounter(ts, vm.value(), vmstat_strs_id_[key], |
| 0, RefType::kRefNoRef); |
| } |
| |
| for (auto it = sys_stats.cpu_stat(); it; ++it) { |
| protos::pbzero::SysStats::CpuTimes::Decoder ct(it->data(), it->size()); |
| if (PERFETTO_UNLIKELY(!ct.has_cpu_id())) { |
| PERFETTO_ELOG("CPU field not found in CpuTimes"); |
| context_->storage->IncrementStats(stats::invalid_cpu_times); |
| continue; |
| } |
| context_->event_tracker->PushCounter(ts, ct.user_ns(), |
| cpu_times_user_ns_id_, ct.cpu_id(), |
| RefType::kRefCpuId); |
| context_->event_tracker->PushCounter(ts, ct.user_ice_ns(), |
| cpu_times_user_nice_ns_id_, |
| ct.cpu_id(), RefType::kRefCpuId); |
| context_->event_tracker->PushCounter(ts, ct.system_mode_ns(), |
| cpu_times_system_mode_ns_id_, |
| ct.cpu_id(), RefType::kRefCpuId); |
| context_->event_tracker->PushCounter(ts, ct.idle_ns(), |
| cpu_times_idle_ns_id_, ct.cpu_id(), |
| RefType::kRefCpuId); |
| context_->event_tracker->PushCounter(ts, ct.io_wait_ns(), |
| cpu_times_io_wait_ns_id_, ct.cpu_id(), |
| RefType::kRefCpuId); |
| context_->event_tracker->PushCounter(ts, ct.irq_ns(), cpu_times_irq_ns_id_, |
| ct.cpu_id(), RefType::kRefCpuId); |
| context_->event_tracker->PushCounter(ts, ct.softirq_ns(), |
| cpu_times_softirq_ns_id_, ct.cpu_id(), |
| RefType::kRefCpuId); |
| } |
| |
| for (auto it = sys_stats.num_irq(); it; ++it) { |
| protos::pbzero::SysStats::InterruptCount::Decoder ic(it->data(), |
| it->size()); |
| context_->event_tracker->PushCounter(ts, ic.count(), num_irq_name_id_, |
| ic.irq(), RefType::kRefIrq); |
| } |
| |
| for (auto it = sys_stats.num_softirq(); it; ++it) { |
| protos::pbzero::SysStats::InterruptCount::Decoder ic(it->data(), |
| it->size()); |
| context_->event_tracker->PushCounter(ts, ic.count(), num_softirq_name_id_, |
| ic.irq(), RefType::kRefSoftIrq); |
| } |
| |
| if (sys_stats.has_num_forks()) { |
| context_->event_tracker->PushCounter( |
| ts, sys_stats.num_forks(), num_forks_name_id_, 0, RefType::kRefNoRef); |
| } |
| |
| if (sys_stats.has_num_irq_total()) { |
| context_->event_tracker->PushCounter(ts, sys_stats.num_irq_total(), |
| num_irq_total_name_id_, 0, |
| RefType::kRefNoRef); |
| } |
| |
| if (sys_stats.has_num_softirq_total()) { |
| context_->event_tracker->PushCounter(ts, sys_stats.num_softirq_total(), |
| num_softirq_total_name_id_, 0, |
| RefType::kRefNoRef); |
| } |
| } |
| |
| void ProtoTraceParser::ParseProcessTree(ConstBytes blob) { |
| protos::pbzero::ProcessTree::Decoder ps(blob.data, blob.size); |
| |
| for (auto it = ps.processes(); it; ++it) { |
| protos::pbzero::ProcessTree::Process::Decoder proc(it->data(), it->size()); |
| if (!proc.has_cmdline()) |
| continue; |
| auto pid = static_cast<uint32_t>(proc.pid()); |
| auto ppid = static_cast<uint32_t>(proc.ppid()); |
| |
| // If the parent pid is kthreadd's pid, even though this pid is of a |
| // "process", we want to treat it as being a child thread of kthreadd. |
| if (ppid == kKthreaddPid) { |
| context_->process_tracker->SetProcessMetadata(kKthreaddPid, base::nullopt, |
| kKthreaddName); |
| context_->process_tracker->UpdateThread(pid, kKthreaddPid); |
| } else { |
| context_->process_tracker->SetProcessMetadata( |
| pid, ppid, proc.cmdline()->as_string()); |
| } |
| } |
| |
| for (auto it = ps.threads(); it; ++it) { |
| protos::pbzero::ProcessTree::Thread::Decoder thd(it->data(), it->size()); |
| auto tid = static_cast<uint32_t>(thd.tid()); |
| auto tgid = static_cast<uint32_t>(thd.tgid()); |
| context_->process_tracker->UpdateThread(tid, tgid); |
| |
| if (thd.has_name()) { |
| StringId threadNameId = context_->storage->InternString(thd.name()); |
| context_->process_tracker->UpdateThreadName(tid, threadNameId); |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseProcessStats(int64_t ts, ConstBytes blob) { |
| protos::pbzero::ProcessStats::Decoder stats(blob.data, blob.size); |
| const auto kOomScoreAdjFieldNumber = |
| protos::pbzero::ProcessStats::Process::kOomScoreAdjFieldNumber; |
| for (auto it = stats.processes(); it; ++it) { |
| // Maps a process counter field it to its value. |
| // E.g., 4 := 1024 -> "mem.rss.anon" := 1024. |
| std::array<int64_t, kProcStatsProcessSize> counter_values{}; |
| std::array<bool, kProcStatsProcessSize> has_counter{}; |
| |
| ProtoDecoder proc(it->data(), it->size()); |
| uint32_t pid = 0; |
| for (auto fld = proc.ReadField(); fld.valid(); fld = proc.ReadField()) { |
| if (fld.id() == protos::pbzero::ProcessStats::Process::kPidFieldNumber) { |
| pid = fld.as_uint32(); |
| continue; |
| } |
| bool is_counter_field = fld.id() < proc_stats_process_names_.size() && |
| proc_stats_process_names_[fld.id()] != 0; |
| if (is_counter_field) { |
| // Memory counters are in KB, keep values in bytes in the trace |
| // processor. |
| counter_values[fld.id()] = fld.id() == kOomScoreAdjFieldNumber |
| ? fld.as_int64() |
| : fld.as_int64() * 1024; |
| has_counter[fld.id()] = true; |
| } else { |
| context_->storage->IncrementStats(stats::proc_stat_unknown_counters); |
| } |
| } |
| |
| // Skip field_id 0 (invalid) and 1 (pid). |
| for (size_t field_id = 2; field_id < counter_values.size(); field_id++) { |
| if (!has_counter[field_id]) |
| continue; |
| |
| // Lookup the interned string id from the field name using the |
| // pre-cached |proc_stats_process_names_| map. |
| StringId name = proc_stats_process_names_[field_id]; |
| int64_t value = counter_values[field_id]; |
| UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid); |
| context_->event_tracker->PushCounter(ts, value, name, upid, |
| RefType::kRefUpid); |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseFtracePacket(uint32_t cpu, |
| int64_t ts, |
| TimestampedTracePiece ttp) { |
| PERFETTO_DCHECK(ttp.json_value == nullptr); |
| |
| // Handle the (optional) alternative encoding format for sched_switch. |
| if (ttp.inline_event.type == InlineEvent::Type::kSchedSwitch) { |
| const auto& event = ttp.inline_event.sched_switch; |
| context_->event_tracker->PushSchedSwitchCompact( |
| cpu, ts, event.prev_state, static_cast<uint32_t>(event.next_pid), |
| event.next_prio, event.next_comm); |
| |
| context_->args_tracker->Flush(); |
| return; |
| } |
| |
| const TraceBlobView& ftrace = ttp.blob_view; |
| |
| ProtoDecoder decoder(ftrace.data(), ftrace.length()); |
| uint64_t raw_pid = 0; |
| if (auto pid_field = |
| decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber)) { |
| raw_pid = pid_field.as_uint64(); |
| } else { |
| PERFETTO_ELOG("Pid field not found in ftrace packet"); |
| return; |
| } |
| uint32_t pid = static_cast<uint32_t>(raw_pid); |
| |
| for (auto fld = decoder.ReadField(); fld.valid(); fld = decoder.ReadField()) { |
| bool is_metadata_field = |
| fld.id() == protos::pbzero::FtraceEvent::kPidFieldNumber || |
| fld.id() == protos::pbzero::FtraceEvent::kTimestampFieldNumber; |
| if (is_metadata_field) |
| continue; |
| |
| ConstBytes data = fld.as_bytes(); |
| if (fld.id() == protos::pbzero::FtraceEvent::kGenericFieldNumber) { |
| ParseGenericFtrace(ts, cpu, pid, data); |
| } else if (fld.id() != |
| protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber) { |
| ParseTypedFtraceToRaw(fld.id(), ts, cpu, pid, data); |
| } |
| |
| switch (fld.id()) { |
| case protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber: { |
| ParseSchedSwitch(cpu, ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kSchedWakeupFieldNumber: { |
| ParseSchedWakeup(ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kSchedWakingFieldNumber: { |
| ParseSchedWaking(ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kSchedProcessFreeFieldNumber: { |
| ParseSchedProcessFree(ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kCpuFrequencyFieldNumber: { |
| ParseCpuFreq(ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kGpuFrequencyFieldNumber: { |
| ParseGpuFreq(ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kCpuIdleFieldNumber: { |
| ParseCpuIdle(ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kPrintFieldNumber: { |
| ParsePrint(cpu, ts, pid, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kZeroFieldNumber: { |
| ParseZero(cpu, ts, pid, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kRssStatFieldNumber: { |
| ParseRssStat(ts, pid, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kIonHeapGrowFieldNumber: { |
| ParseIonHeapGrowOrShrink(ts, pid, data, true); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kIonHeapShrinkFieldNumber: { |
| ParseIonHeapGrowOrShrink(ts, pid, data, false); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kSignalGenerateFieldNumber: { |
| ParseSignalGenerate(ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kSignalDeliverFieldNumber: { |
| ParseSignalDeliver(ts, pid, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kLowmemoryKillFieldNumber: { |
| ParseLowmemoryKill(ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kOomScoreAdjUpdateFieldNumber: { |
| ParseOOMScoreAdjUpdate(ts, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kMmEventRecordFieldNumber: { |
| ParseMmEventRecord(ts, pid, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kSysEnterFieldNumber: { |
| ParseSysEvent(ts, pid, true, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kSysExitFieldNumber: { |
| ParseSysEvent(ts, pid, false, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kTaskNewtaskFieldNumber: { |
| ParseTaskNewTask(ts, pid, data); |
| break; |
| } |
| case protos::pbzero::FtraceEvent::kTaskRenameFieldNumber: { |
| ParseTaskRename(data); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| // TODO(lalitm): maybe move this to the flush method in the trace processor |
| // once we have it. This may reduce performance in the ArgsTracker though so |
| // needs to be handled carefully. |
| context_->args_tracker->Flush(); |
| |
| PERFETTO_DCHECK(!decoder.bytes_left()); |
| } |
| |
| void ProtoTraceParser::ParseSignalDeliver(int64_t ts, |
| uint32_t pid, |
| ConstBytes blob) { |
| protos::pbzero::SignalDeliverFtraceEvent::Decoder sig(blob.data, blob.size); |
| UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid); |
| context_->event_tracker->PushInstant(ts, signal_deliver_id_, sig.sig(), utid, |
| RefType::kRefUtid); |
| } |
| |
| // This event has both the pid of the thread that sent the signal and the |
| // destination of the signal. Currently storing the pid of the destination. |
| void ProtoTraceParser::ParseSignalGenerate(int64_t ts, ConstBytes blob) { |
| protos::pbzero::SignalGenerateFtraceEvent::Decoder sig(blob.data, blob.size); |
| |
| UniqueTid utid = context_->process_tracker->GetOrCreateThread( |
| static_cast<uint32_t>(sig.pid())); |
| context_->event_tracker->PushInstant(ts, signal_generate_id_, sig.sig(), utid, |
| RefType::kRefUtid); |
| } |
| |
| void ProtoTraceParser::ParseLowmemoryKill(int64_t ts, ConstBytes blob) { |
| // TODO(taylori): Store the pagecache_size, pagecache_limit and free fields |
| // in an args table |
| protos::pbzero::LowmemoryKillFtraceEvent::Decoder lmk(blob.data, blob.size); |
| |
| // Store the pid of the event that is lmk-ed. |
| auto pid = static_cast<uint32_t>(lmk.pid()); |
| auto opt_utid = context_->process_tracker->GetThreadOrNull(pid); |
| |
| // Don't add LMK events for threads we've never seen before. This works around |
| // the case where we get an LMK event after a thread has already been killed. |
| if (!opt_utid) |
| return; |
| |
| auto row_id = context_->event_tracker->PushInstant( |
| ts, lmk_id_, 0, opt_utid.value(), RefType::kRefUtid, true); |
| |
| // Store the comm as an arg. |
| auto comm_id = context_->storage->InternString( |
| lmk.has_comm() ? lmk.comm() : base::StringView()); |
| context_->args_tracker->AddArg(row_id, comm_name_id_, comm_name_id_, |
| Variadic::String(comm_id)); |
| } |
| |
| void ProtoTraceParser::ParseRssStat(int64_t ts, uint32_t pid, ConstBytes blob) { |
| protos::pbzero::RssStatFtraceEvent::Decoder rss(blob.data, blob.size); |
| const auto kRssStatUnknown = static_cast<uint32_t>(rss_members_.size()) - 1; |
| auto member = static_cast<uint32_t>(rss.member()); |
| int64_t size = rss.size(); |
| if (member >= rss_members_.size()) { |
| context_->storage->IncrementStats(stats::rss_stat_unknown_keys); |
| member = kRssStatUnknown; |
| } |
| |
| if (size >= 0) { |
| UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid); |
| context_->event_tracker->PushCounter(ts, size, rss_members_[member], utid, |
| RefType::kRefUtid, true); |
| } else { |
| context_->storage->IncrementStats(stats::rss_stat_negative_size); |
| } |
| } |
| |
| void ProtoTraceParser::ParseIonHeapGrowOrShrink(int64_t ts, |
| uint32_t pid, |
| ConstBytes blob, |
| bool grow) { |
| protos::pbzero::IonHeapGrowFtraceEvent::Decoder ion(blob.data, blob.size); |
| int64_t change_bytes = static_cast<int64_t>(ion.len()) * (grow ? 1 : -1); |
| // The total_allocated ftrace event reports the value before the |
| // atomic_long_add / sub takes place. |
| int64_t total_bytes = ion.total_allocated() + change_bytes; |
| StringId global_name_id = ion_total_unknown_id_; |
| StringId change_name_id = ion_change_unknown_id_; |
| |
| if (ion.has_heap_name()) { |
| char counter_name[255]; |
| base::StringView heap_name = ion.heap_name(); |
| snprintf(counter_name, sizeof(counter_name), "mem.ion.%.*s", |
| int(heap_name.size()), heap_name.data()); |
| global_name_id = context_->storage->InternString(counter_name); |
| snprintf(counter_name, sizeof(counter_name), "mem.ion_change.%.*s", |
| int(heap_name.size()), heap_name.data()); |
| change_name_id = context_->storage->InternString(counter_name); |
| } |
| |
| // Push the global counter. |
| context_->event_tracker->PushCounter(ts, total_bytes, global_name_id, 0, |
| RefType::kRefNoRef); |
| |
| // Push the change counter. |
| // TODO(b/121331269): these should really be instant events. For now we |
| // manually reset them to 0 after 1ns. |
| UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid); |
| context_->event_tracker->PushCounter(ts, change_bytes, change_name_id, utid, |
| RefType::kRefUtid); |
| context_->event_tracker->PushCounter(ts + 1, 0, change_name_id, utid, |
| RefType::kRefUtid); |
| |
| // We are reusing the same function for ion_heap_grow and ion_heap_shrink. |
| // It is fine as the arguments are the same, but we need to be sure that the |
| // protobuf field id for both are the same. |
| static_assert( |
| static_cast<int>( |
| protos::pbzero::IonHeapGrowFtraceEvent::kTotalAllocatedFieldNumber) == |
| static_cast<int>(protos::pbzero::IonHeapShrinkFtraceEvent:: |
| kTotalAllocatedFieldNumber) && |
| static_cast<int>( |
| protos::pbzero::IonHeapGrowFtraceEvent::kLenFieldNumber) == |
| static_cast<int>( |
| protos::pbzero::IonHeapShrinkFtraceEvent::kLenFieldNumber) && |
| static_cast<int>( |
| protos::pbzero::IonHeapGrowFtraceEvent::kHeapNameFieldNumber) == |
| static_cast<int>(protos::pbzero::IonHeapShrinkFtraceEvent:: |
| kHeapNameFieldNumber), |
| "ION field mismatch"); |
| } |
| |
| void ProtoTraceParser::ParseCpuFreq(int64_t ts, ConstBytes blob) { |
| protos::pbzero::CpuFrequencyFtraceEvent::Decoder freq(blob.data, blob.size); |
| uint32_t cpu = freq.cpu_id(); |
| uint32_t new_freq = freq.state(); |
| context_->event_tracker->PushCounter(ts, new_freq, cpu_freq_name_id_, cpu, |
| RefType::kRefCpuId); |
| } |
| |
| void ProtoTraceParser::ParseCpuIdle(int64_t ts, ConstBytes blob) { |
| protos::pbzero::CpuIdleFtraceEvent::Decoder idle(blob.data, blob.size); |
| uint32_t cpu = idle.cpu_id(); |
| uint32_t new_state = idle.state(); |
| context_->event_tracker->PushCounter(ts, new_state, cpu_idle_name_id_, cpu, |
| RefType::kRefCpuId); |
| } |
| |
| void ProtoTraceParser::ParseGpuFreq(int64_t ts, ConstBytes blob) { |
| protos::pbzero::GpuFrequencyFtraceEvent::Decoder freq(blob.data, blob.size); |
| uint32_t gpu = freq.gpu_id(); |
| uint32_t new_freq = freq.state(); |
| context_->event_tracker->PushCounter(ts, new_freq, gpu_freq_name_id_, gpu, |
| RefType::kRefGpuId); |
| } |
| |
| PERFETTO_ALWAYS_INLINE |
| void ProtoTraceParser::ParseSchedSwitch(uint32_t cpu, |
| int64_t ts, |
| ConstBytes blob) { |
| protos::pbzero::SchedSwitchFtraceEvent::Decoder ss(blob.data, blob.size); |
| uint32_t prev_pid = static_cast<uint32_t>(ss.prev_pid()); |
| uint32_t next_pid = static_cast<uint32_t>(ss.next_pid()); |
| context_->event_tracker->PushSchedSwitch( |
| cpu, ts, prev_pid, ss.prev_comm(), ss.prev_prio(), ss.prev_state(), |
| next_pid, ss.next_comm(), ss.next_prio()); |
| } |
| |
| void ProtoTraceParser::ParseSchedWakeup(int64_t ts, ConstBytes blob) { |
| protos::pbzero::SchedWakeupFtraceEvent::Decoder sw(blob.data, blob.size); |
| uint32_t wakee_pid = static_cast<uint32_t>(sw.pid()); |
| StringId name_id = context_->storage->InternString(sw.comm()); |
| auto utid = context_->process_tracker->UpdateThreadName(wakee_pid, name_id); |
| context_->event_tracker->PushInstant(ts, sched_wakeup_name_id_, 0 /* value */, |
| utid, RefType::kRefUtid); |
| } |
| |
| void ProtoTraceParser::ParseSchedWaking(int64_t ts, ConstBytes blob) { |
| protos::pbzero::SchedWakingFtraceEvent::Decoder sw(blob.data, blob.size); |
| uint32_t wakee_pid = static_cast<uint32_t>(sw.pid()); |
| StringId name_id = context_->storage->InternString(sw.comm()); |
| auto utid = context_->process_tracker->UpdateThreadName(wakee_pid, name_id); |
| context_->event_tracker->PushInstant(ts, sched_waking_name_id_, 0 /* value */, |
| utid, RefType::kRefUtid); |
| } |
| |
| void ProtoTraceParser::ParseSchedProcessFree(int64_t ts, ConstBytes blob) { |
| protos::pbzero::SchedProcessFreeFtraceEvent::Decoder ex(blob.data, blob.size); |
| uint32_t pid = static_cast<uint32_t>(ex.pid()); |
| context_->process_tracker->EndThread(ts, pid); |
| } |
| |
| void ProtoTraceParser::ParseTaskNewTask(int64_t ts, |
| uint32_t source_tid, |
| ConstBytes blob) { |
| protos::pbzero::TaskNewtaskFtraceEvent::Decoder evt(blob.data, blob.size); |
| uint32_t clone_flags = static_cast<uint32_t>(evt.clone_flags()); |
| uint32_t new_tid = static_cast<uint32_t>(evt.pid()); |
| StringId new_comm = context_->storage->InternString(evt.comm()); |
| auto* proc_tracker = context_->process_tracker.get(); |
| |
| // task_newtask is raised both in the case of a new process creation (fork() |
| // family) and thread creation (clone(CLONE_THREAD, ...)). |
| static const uint32_t kCloneThread = 0x00010000; // From kernel's sched.h. |
| |
| // If the process is a fork, start a new process except if the source tid is |
| // kthreadd in which case just make it a new thread associated with kthreadd. |
| if ((clone_flags & kCloneThread) == 0 && source_tid != kKthreaddPid) { |
| // This is a plain-old fork() or equivalent. |
| proc_tracker->StartNewProcess(ts, source_tid, new_tid, new_comm); |
| return; |
| } |
| |
| if (source_tid == kKthreaddPid) { |
| context_->process_tracker->SetProcessMetadata(kKthreaddPid, base::nullopt, |
| kKthreaddName); |
| } |
| |
| // This is a pthread_create or similar. Bind the two threads together, so |
| // they get resolved to the same process. |
| auto source_utid = proc_tracker->GetOrCreateThread(source_tid); |
| auto new_utid = proc_tracker->StartNewThread(ts, new_tid, new_comm); |
| proc_tracker->AssociateThreads(source_utid, new_utid); |
| } |
| |
| void ProtoTraceParser::ParseTaskRename(ConstBytes blob) { |
| protos::pbzero::TaskRenameFtraceEvent::Decoder evt(blob.data, blob.size); |
| uint32_t tid = static_cast<uint32_t>(evt.pid()); |
| StringId comm = context_->storage->InternString(evt.newcomm()); |
| context_->process_tracker->UpdateThreadName(tid, comm); |
| context_->process_tracker->UpdateProcessNameFromThreadName(tid, comm); |
| } |
| |
| void ProtoTraceParser::ParsePrint(uint32_t, |
| int64_t ts, |
| uint32_t pid, |
| ConstBytes blob) { |
| protos::pbzero::PrintFtraceEvent::Decoder evt(blob.data, blob.size); |
| context_->systrace_parser->ParsePrintEvent(ts, pid, evt.buf()); |
| } |
| |
| void ProtoTraceParser::ParseZero(uint32_t, |
| int64_t ts, |
| uint32_t pid, |
| ConstBytes blob) { |
| protos::pbzero::ZeroFtraceEvent::Decoder evt(blob.data, blob.size); |
| uint32_t tgid = static_cast<uint32_t>(evt.pid()); |
| context_->systrace_parser->ParseZeroEvent(ts, pid, evt.flag(), evt.name(), |
| tgid, evt.value()); |
| } |
| |
| void ProtoTraceParser::ParseBatteryCounters(int64_t ts, ConstBytes blob) { |
| protos::pbzero::BatteryCounters::Decoder evt(blob.data, blob.size); |
| if (evt.has_charge_counter_uah()) { |
| context_->event_tracker->PushCounter( |
| ts, evt.charge_counter_uah(), batt_charge_id_, 0, RefType::kRefNoRef); |
| } |
| if (evt.has_capacity_percent()) { |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(evt.capacity_percent()), batt_capacity_id_, 0, |
| RefType::kRefNoRef); |
| } |
| if (evt.has_current_ua()) { |
| context_->event_tracker->PushCounter(ts, evt.current_ua(), batt_current_id_, |
| 0, RefType::kRefNoRef); |
| } |
| if (evt.has_current_avg_ua()) { |
| context_->event_tracker->PushCounter( |
| ts, evt.current_avg_ua(), batt_current_avg_id_, 0, RefType::kRefNoRef); |
| } |
| } |
| |
| void ProtoTraceParser::ParsePowerRails(ConstBytes blob) { |
| protos::pbzero::PowerRails::Decoder evt(blob.data, blob.size); |
| if (evt.has_rail_descriptor()) { |
| for (auto it = evt.rail_descriptor(); it; ++it) { |
| protos::pbzero::PowerRails::RailDescriptor::Decoder desc(it->data(), |
| it->size()); |
| uint32_t idx = desc.index(); |
| if (PERFETTO_UNLIKELY(idx > 256)) { |
| PERFETTO_DLOG("Skipping excessively large power_rail index %" PRIu32, |
| idx); |
| continue; |
| } |
| if (power_rails_strs_id_.size() <= idx) |
| power_rails_strs_id_.resize(idx + 1); |
| char counter_name[255]; |
| snprintf(counter_name, sizeof(counter_name), "power.%.*s_uws", |
| int(desc.rail_name().size), desc.rail_name().data); |
| power_rails_strs_id_[idx] = context_->storage->InternString(counter_name); |
| } |
| } |
| |
| if (evt.has_energy_data()) { |
| for (auto it = evt.energy_data(); it; ++it) { |
| protos::pbzero::PowerRails::EnergyData::Decoder desc(it->data(), |
| it->size()); |
| if (desc.index() < power_rails_strs_id_.size()) { |
| int64_t ts = static_cast<int64_t>(desc.timestamp_ms()) * 1000000; |
| context_->event_tracker->PushCounter(ts, desc.energy(), |
| power_rails_strs_id_[desc.index()], |
| 0, RefType::kRefNoRef); |
| } else { |
| context_->storage->IncrementStats(stats::power_rail_unknown_index); |
| } |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseOOMScoreAdjUpdate(int64_t ts, ConstBytes blob) { |
| protos::pbzero::OomScoreAdjUpdateFtraceEvent::Decoder evt(blob.data, |
| blob.size); |
| // The int16_t static cast is because older version of the on-device tracer |
| // had a bug on negative varint encoding (b/120618641). |
| int16_t oom_adj = static_cast<int16_t>(evt.oom_score_adj()); |
| uint32_t tid = static_cast<uint32_t>(evt.pid()); |
| UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid); |
| context_->event_tracker->PushCounter(ts, oom_adj, oom_score_adj_id_, utid, |
| RefType::kRefUtid, true); |
| } |
| |
| void ProtoTraceParser::ParseMmEventRecord(int64_t ts, |
| uint32_t pid, |
| ConstBytes blob) { |
| protos::pbzero::MmEventRecordFtraceEvent::Decoder evt(blob.data, blob.size); |
| uint32_t type = evt.type(); |
| UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid); |
| |
| if (type >= mm_event_counter_names_.size()) { |
| context_->storage->IncrementStats(stats::mm_unknown_type); |
| return; |
| } |
| |
| const auto& counter_names = mm_event_counter_names_[type]; |
| context_->event_tracker->PushCounter(ts, evt.count(), counter_names.count, |
| utid, RefType::kRefUtid, true); |
| context_->event_tracker->PushCounter(ts, evt.max_lat(), counter_names.max_lat, |
| utid, RefType::kRefUtid, true); |
| context_->event_tracker->PushCounter(ts, evt.avg_lat(), counter_names.avg_lat, |
| utid, RefType::kRefUtid, true); |
| } |
| |
| void ProtoTraceParser::ParseSysEvent(int64_t ts, |
| uint32_t pid, |
| bool is_enter, |
| ConstBytes blob) { |
| protos::pbzero::SysEnterFtraceEvent::Decoder evt(blob.data, blob.size); |
| uint32_t syscall_num = static_cast<uint32_t>(evt.id()); |
| UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid); |
| |
| if (is_enter) { |
| context_->syscall_tracker->Enter(ts, utid, syscall_num); |
| } else { |
| context_->syscall_tracker->Exit(ts, utid, syscall_num); |
| } |
| |
| // We are reusing the same function for sys_enter and sys_exit. |
| // It is fine as the arguments are the same, but we need to be sure that the |
| // protobuf field id for both are the same. |
| static_assert( |
| static_cast<int>(protos::pbzero::SysEnterFtraceEvent::kIdFieldNumber) == |
| static_cast<int>(protos::pbzero::SysExitFtraceEvent::kIdFieldNumber), |
| "field mismatch"); |
| } |
| |
| void ProtoTraceParser::ParseGenericFtrace(int64_t ts, |
| uint32_t cpu, |
| uint32_t tid, |
| ConstBytes blob) { |
| protos::pbzero::GenericFtraceEvent::Decoder evt(blob.data, blob.size); |
| StringId event_id = context_->storage->InternString(evt.event_name()); |
| UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid); |
| RowId row_id = context_->storage->mutable_raw_events()->AddRawEvent( |
| ts, event_id, cpu, utid); |
| |
| for (auto it = evt.field(); it; ++it) { |
| protos::pbzero::GenericFtraceEvent::Field::Decoder fld(it->data(), |
| it->size()); |
| auto field_name_id = context_->storage->InternString(fld.name()); |
| if (fld.has_int_value()) { |
| context_->args_tracker->AddArg(row_id, field_name_id, field_name_id, |
| Variadic::Integer(fld.int_value())); |
| } else if (fld.has_uint_value()) { |
| context_->args_tracker->AddArg( |
| row_id, field_name_id, field_name_id, |
| Variadic::Integer(static_cast<int64_t>(fld.uint_value()))); |
| } else if (fld.has_str_value()) { |
| StringId str_value = context_->storage->InternString(fld.str_value()); |
| context_->args_tracker->AddArg(row_id, field_name_id, field_name_id, |
| Variadic::String(str_value)); |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseTypedFtraceToRaw(uint32_t ftrace_id, |
| int64_t ts, |
| uint32_t cpu, |
| uint32_t tid, |
| ConstBytes blob) { |
| ProtoDecoder decoder(blob.data, blob.size); |
| if (ftrace_id >= GetDescriptorsSize()) { |
| PERFETTO_DLOG("Event with id: %d does not exist and cannot be parsed.", |
| ftrace_id); |
| return; |
| } |
| |
| MessageDescriptor* m = GetMessageDescriptorForId(ftrace_id); |
| const auto& message_strings = ftrace_message_strings_[ftrace_id]; |
| UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid); |
| RowId raw_event_id = context_->storage->mutable_raw_events()->AddRawEvent( |
| ts, message_strings.message_name_id, cpu, utid); |
| for (auto fld = decoder.ReadField(); fld.valid(); fld = decoder.ReadField()) { |
| if (PERFETTO_UNLIKELY(fld.id() >= kMaxFtraceEventFields)) { |
| PERFETTO_DLOG( |
| "Skipping ftrace arg - proto field id is too large (%" PRIu16 ")", |
| fld.id()); |
| continue; |
| } |
| ProtoSchemaType type = m->fields[fld.id()].type; |
| StringId name_id = message_strings.field_name_ids[fld.id()]; |
| switch (type) { |
| case ProtoSchemaType::kInt32: |
| case ProtoSchemaType::kInt64: |
| case ProtoSchemaType::kSfixed32: |
| case ProtoSchemaType::kSfixed64: |
| case ProtoSchemaType::kSint32: |
| case ProtoSchemaType::kSint64: |
| case ProtoSchemaType::kBool: |
| case ProtoSchemaType::kEnum: { |
| context_->args_tracker->AddArg(raw_event_id, name_id, name_id, |
| Variadic::Integer(fld.as_int64())); |
| break; |
| } |
| case ProtoSchemaType::kUint32: |
| case ProtoSchemaType::kUint64: |
| case ProtoSchemaType::kFixed32: |
| case ProtoSchemaType::kFixed64: { |
| // Note that SQLite functions will still treat unsigned values |
| // as a signed 64 bit integers (but the translation back to ftrace |
| // refers to this storage directly). |
| context_->args_tracker->AddArg( |
| raw_event_id, name_id, name_id, |
| Variadic::UnsignedInteger(fld.as_uint64())); |
| break; |
| } |
| case ProtoSchemaType::kString: |
| case ProtoSchemaType::kBytes: { |
| StringId value = context_->storage->InternString(fld.as_string()); |
| context_->args_tracker->AddArg(raw_event_id, name_id, name_id, |
| Variadic::String(value)); |
| break; |
| } |
| case ProtoSchemaType::kDouble: { |
| context_->args_tracker->AddArg(raw_event_id, name_id, name_id, |
| Variadic::Real(fld.as_double())); |
| break; |
| } |
| case ProtoSchemaType::kFloat: { |
| context_->args_tracker->AddArg( |
| raw_event_id, name_id, name_id, |
| Variadic::Real(static_cast<double>(fld.as_float()))); |
| break; |
| } |
| case ProtoSchemaType::kUnknown: |
| case ProtoSchemaType::kGroup: |
| case ProtoSchemaType::kMessage: |
| PERFETTO_DLOG("Could not store %s as a field in args table.", |
| ProtoSchemaToString(type)); |
| break; |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseAndroidLogPacket(ConstBytes blob) { |
| protos::pbzero::AndroidLogPacket::Decoder packet(blob.data, blob.size); |
| for (auto it = packet.events(); it; ++it) |
| ParseAndroidLogEvent(it->as_bytes()); |
| |
| if (packet.has_stats()) |
| ParseAndroidLogStats(packet.stats()); |
| } |
| |
| void ProtoTraceParser::ParseAndroidLogEvent(ConstBytes blob) { |
| // TODO(primiano): Add events and non-stringified fields to the "raw" table. |
| protos::pbzero::AndroidLogPacket::LogEvent::Decoder evt(blob.data, blob.size); |
| int64_t ts = static_cast<int64_t>(evt.timestamp()); |
| uint32_t pid = static_cast<uint32_t>(evt.pid()); |
| uint32_t tid = static_cast<uint32_t>(evt.tid()); |
| uint8_t prio = static_cast<uint8_t>(evt.prio()); |
| StringId tag_id = context_->storage->InternString( |
| evt.has_tag() ? evt.tag() : base::StringView()); |
| StringId msg_id = context_->storage->InternString( |
| evt.has_message() ? evt.message() : base::StringView()); |
| |
| char arg_msg[4096]; |
| char* arg_str = &arg_msg[0]; |
| *arg_str = '\0'; |
| auto arg_avail = [&arg_msg, &arg_str]() { |
| return sizeof(arg_msg) - static_cast<size_t>(arg_str - arg_msg); |
| }; |
| for (auto it = evt.args(); it; ++it) { |
| protos::pbzero::AndroidLogPacket::LogEvent::Arg::Decoder arg(it->data(), |
| it->size()); |
| if (!arg.has_name()) |
| continue; |
| arg_str += |
| snprintf(arg_str, arg_avail(), |
| " %.*s=", static_cast<int>(arg.name().size), arg.name().data); |
| if (arg.has_string_value()) { |
| arg_str += snprintf(arg_str, arg_avail(), "\"%.*s\"", |
| static_cast<int>(arg.string_value().size), |
| arg.string_value().data); |
| } else if (arg.has_int_value()) { |
| arg_str += snprintf(arg_str, arg_avail(), "%" PRId64, arg.int_value()); |
| } else if (arg.has_float_value()) { |
| arg_str += snprintf(arg_str, arg_avail(), "%f", |
| static_cast<double>(arg.float_value())); |
| } |
| } |
| |
| if (prio == 0) |
| prio = protos::pbzero::AndroidLogPriority::PRIO_INFO; |
| |
| if (arg_str != &arg_msg[0]) { |
| PERFETTO_DCHECK(msg_id.is_null()); |
| // Skip the first space char (" foo=1 bar=2" -> "foo=1 bar=2"). |
| msg_id = context_->storage->InternString(&arg_msg[1]); |
| } |
| UniquePid utid = tid ? context_->process_tracker->UpdateThread(tid, pid) : 0; |
| base::Optional<int64_t> opt_trace_time = context_->clock_tracker->ToTraceTime( |
| protos::pbzero::ClockSnapshot::Clock::REALTIME, ts); |
| if (!opt_trace_time) |
| return; |
| |
| // Log events are NOT required to be sorted by trace_time. The virtual table |
| // will take care of sorting on-demand. |
| context_->storage->mutable_android_log()->AddLogEvent( |
| opt_trace_time.value(), utid, prio, tag_id, msg_id); |
| } |
| |
| void ProtoTraceParser::ParseAndroidLogStats(ConstBytes blob) { |
| protos::pbzero::AndroidLogPacket::Stats::Decoder evt(blob.data, blob.size); |
| if (evt.has_num_failed()) { |
| context_->storage->SetStats(stats::android_log_num_failed, |
| static_cast<int64_t>(evt.num_failed())); |
| } |
| |
| if (evt.has_num_skipped()) { |
| context_->storage->SetStats(stats::android_log_num_skipped, |
| static_cast<int64_t>(evt.num_skipped())); |
| } |
| |
| if (evt.has_num_total()) { |
| context_->storage->SetStats(stats::android_log_num_total, |
| static_cast<int64_t>(evt.num_total())); |
| } |
| } |
| |
| void ProtoTraceParser::ParseTraceStats(ConstBytes blob) { |
| protos::pbzero::TraceStats::Decoder evt(blob.data, blob.size); |
| auto* storage = context_->storage.get(); |
| storage->SetStats(stats::traced_producers_connected, |
| static_cast<int64_t>(evt.producers_connected())); |
| storage->SetStats(stats::traced_data_sources_registered, |
| static_cast<int64_t>(evt.data_sources_registered())); |
| storage->SetStats(stats::traced_data_sources_seen, |
| static_cast<int64_t>(evt.data_sources_seen())); |
| storage->SetStats(stats::traced_tracing_sessions, |
| static_cast<int64_t>(evt.tracing_sessions())); |
| storage->SetStats(stats::traced_total_buffers, |
| static_cast<int64_t>(evt.total_buffers())); |
| storage->SetStats(stats::traced_chunks_discarded, |
| static_cast<int64_t>(evt.chunks_discarded())); |
| storage->SetStats(stats::traced_patches_discarded, |
| static_cast<int64_t>(evt.patches_discarded())); |
| |
| int buf_num = 0; |
| for (auto it = evt.buffer_stats(); it; ++it, ++buf_num) { |
| protos::pbzero::TraceStats::BufferStats::Decoder buf(it->data(), |
| it->size()); |
| storage->SetIndexedStats(stats::traced_buf_buffer_size, buf_num, |
| static_cast<int64_t>(buf.buffer_size())); |
| storage->SetIndexedStats(stats::traced_buf_bytes_written, buf_num, |
| static_cast<int64_t>(buf.bytes_written())); |
| storage->SetIndexedStats(stats::traced_buf_bytes_overwritten, buf_num, |
| static_cast<int64_t>(buf.bytes_overwritten())); |
| storage->SetIndexedStats(stats::traced_buf_bytes_read, buf_num, |
| static_cast<int64_t>(buf.bytes_read())); |
| storage->SetIndexedStats(stats::traced_buf_padding_bytes_written, buf_num, |
| static_cast<int64_t>(buf.padding_bytes_written())); |
| storage->SetIndexedStats(stats::traced_buf_padding_bytes_cleared, buf_num, |
| static_cast<int64_t>(buf.padding_bytes_cleared())); |
| storage->SetIndexedStats(stats::traced_buf_chunks_written, buf_num, |
| static_cast<int64_t>(buf.chunks_written())); |
| storage->SetIndexedStats(stats::traced_buf_chunks_rewritten, buf_num, |
| static_cast<int64_t>(buf.chunks_rewritten())); |
| storage->SetIndexedStats(stats::traced_buf_chunks_overwritten, buf_num, |
| static_cast<int64_t>(buf.chunks_overwritten())); |
| storage->SetIndexedStats(stats::traced_buf_chunks_discarded, buf_num, |
| static_cast<int64_t>(buf.chunks_discarded())); |
| storage->SetIndexedStats(stats::traced_buf_chunks_read, buf_num, |
| static_cast<int64_t>(buf.chunks_read())); |
| storage->SetIndexedStats( |
| stats::traced_buf_chunks_committed_out_of_order, buf_num, |
| static_cast<int64_t>(buf.chunks_committed_out_of_order())); |
| storage->SetIndexedStats(stats::traced_buf_write_wrap_count, buf_num, |
| static_cast<int64_t>(buf.write_wrap_count())); |
| storage->SetIndexedStats(stats::traced_buf_patches_succeeded, buf_num, |
| static_cast<int64_t>(buf.patches_succeeded())); |
| storage->SetIndexedStats(stats::traced_buf_patches_failed, buf_num, |
| static_cast<int64_t>(buf.patches_failed())); |
| storage->SetIndexedStats(stats::traced_buf_readaheads_succeeded, buf_num, |
| static_cast<int64_t>(buf.readaheads_succeeded())); |
| storage->SetIndexedStats(stats::traced_buf_readaheads_failed, buf_num, |
| static_cast<int64_t>(buf.readaheads_failed())); |
| storage->SetIndexedStats( |
| stats::traced_buf_trace_writer_packet_loss, buf_num, |
| static_cast<int64_t>(buf.trace_writer_packet_loss())); |
| } |
| } |
| |
| void ProtoTraceParser::ParseFtraceStats(ConstBytes blob) { |
| protos::pbzero::FtraceStats::Decoder evt(blob.data, blob.size); |
| size_t phase = |
| evt.phase() == protos::pbzero::FtraceStats_Phase_END_OF_TRACE ? 1 : 0; |
| |
| // This code relies on the fact that each ftrace_cpu_XXX_end event is |
| // just after the corresponding ftrace_cpu_XXX_begin event. |
| static_assert( |
| stats::ftrace_cpu_read_events_end - stats::ftrace_cpu_read_events_begin == |
| 1 && |
| stats::ftrace_cpu_entries_end - stats::ftrace_cpu_entries_begin == 1, |
| "ftrace_cpu_XXX stats definition are messed up"); |
| |
| auto* storage = context_->storage.get(); |
| for (auto it = evt.cpu_stats(); it; ++it) { |
| protos::pbzero::FtraceCpuStats::Decoder cpu_stats(it->data(), it->size()); |
| int cpu = static_cast<int>(cpu_stats.cpu()); |
| storage->SetIndexedStats(stats::ftrace_cpu_entries_begin + phase, cpu, |
| static_cast<int64_t>(cpu_stats.entries())); |
| storage->SetIndexedStats(stats::ftrace_cpu_overrun_begin + phase, cpu, |
| static_cast<int64_t>(cpu_stats.overrun())); |
| storage->SetIndexedStats(stats::ftrace_cpu_commit_overrun_begin + phase, |
| cpu, |
| static_cast<int64_t>(cpu_stats.commit_overrun())); |
| storage->SetIndexedStats(stats::ftrace_cpu_bytes_read_begin + phase, cpu, |
| static_cast<int64_t>(cpu_stats.bytes_read())); |
| |
| // oldest_event_ts can often be set to very high values, possibly because |
| // of wrapping. Ensure that we are not overflowing to avoid ubsan |
| // complaining. |
| double oldest_event_ts = cpu_stats.oldest_event_ts() * 1e9; |
| if (oldest_event_ts >= std::numeric_limits<int64_t>::max()) { |
| storage->SetIndexedStats(stats::ftrace_cpu_oldest_event_ts_begin + phase, |
| cpu, std::numeric_limits<int64_t>::max()); |
| } else { |
| storage->SetIndexedStats(stats::ftrace_cpu_oldest_event_ts_begin + phase, |
| cpu, static_cast<int64_t>(oldest_event_ts)); |
| } |
| |
| storage->SetIndexedStats(stats::ftrace_cpu_now_ts_begin + phase, cpu, |
| static_cast<int64_t>(cpu_stats.now_ts() * 1e9)); |
| storage->SetIndexedStats(stats::ftrace_cpu_dropped_events_begin + phase, |
| cpu, |
| static_cast<int64_t>(cpu_stats.dropped_events())); |
| storage->SetIndexedStats(stats::ftrace_cpu_read_events_begin + phase, cpu, |
| static_cast<int64_t>(cpu_stats.read_events())); |
| } |
| } |
| |
| void ProtoTraceParser::ParseProfilePacket( |
| int64_t, |
| ProtoIncrementalState::PacketSequenceState* sequence_state, |
| size_t sequence_state_generation, |
| ConstBytes blob) { |
| protos::pbzero::ProfilePacket::Decoder packet(blob.data, blob.size); |
| context_->heap_profile_tracker->SetProfilePacketIndex(packet.index()); |
| |
| for (auto it = packet.strings(); it; ++it) { |
| protos::pbzero::InternedString::Decoder entry(it->data(), it->size()); |
| |
| const char* str = reinterpret_cast<const char*>(entry.str().data); |
| auto str_view = base::StringView(str, entry.str().size); |
| sequence_state->stack_profile_tracker().AddString(entry.iid(), str_view); |
| } |
| |
| for (auto it = packet.mappings(); it; ++it) { |
| protos::pbzero::Mapping::Decoder entry(it->data(), it->size()); |
| StackProfileTracker::SourceMapping src_mapping = MakeSourceMapping(entry); |
| sequence_state->stack_profile_tracker().AddMapping(entry.iid(), |
| src_mapping); |
| } |
| |
| for (auto it = packet.frames(); it; ++it) { |
| protos::pbzero::Frame::Decoder entry(it->data(), it->size()); |
| StackProfileTracker::SourceFrame src_frame = MakeSourceFrame(entry); |
| sequence_state->stack_profile_tracker().AddFrame(entry.iid(), src_frame); |
| } |
| |
| for (auto it = packet.callstacks(); it; ++it) { |
| protos::pbzero::Callstack::Decoder entry(it->data(), it->size()); |
| StackProfileTracker::SourceCallstack src_callstack = |
| MakeSourceCallstack(entry); |
| sequence_state->stack_profile_tracker().AddCallstack(entry.iid(), |
| src_callstack); |
| } |
| |
| for (auto it = packet.process_dumps(); it; ++it) { |
| protos::pbzero::ProfilePacket::ProcessHeapSamples::Decoder entry( |
| it->data(), it->size()); |
| |
| int pid = static_cast<int>(entry.pid()); |
| |
| if (entry.buffer_corrupted()) |
| context_->storage->IncrementIndexedStats( |
| stats::heapprofd_buffer_corrupted, pid); |
| if (entry.buffer_overran()) |
| context_->storage->IncrementIndexedStats(stats::heapprofd_buffer_overran, |
| pid); |
| if (entry.rejected_concurrent()) |
| context_->storage->IncrementIndexedStats( |
| stats::heapprofd_rejected_concurrent, pid); |
| |
| for (auto sample_it = entry.samples(); sample_it; ++sample_it) { |
| protos::pbzero::ProfilePacket::HeapSample::Decoder sample( |
| sample_it->data(), sample_it->size()); |
| |
| HeapProfileTracker::SourceAllocation src_allocation; |
| src_allocation.pid = entry.pid(); |
| src_allocation.timestamp = static_cast<int64_t>(entry.timestamp()); |
| src_allocation.callstack_id = sample.callstack_id(); |
| src_allocation.self_allocated = sample.self_allocated(); |
| src_allocation.self_freed = sample.self_freed(); |
| src_allocation.alloc_count = sample.alloc_count(); |
| src_allocation.free_count = sample.free_count(); |
| |
| context_->heap_profile_tracker->StoreAllocation(src_allocation); |
| } |
| } |
| if (!packet.continued()) { |
| PERFETTO_CHECK(sequence_state); |
| ProfilePacketInternLookup intern_lookup(sequence_state, |
| sequence_state_generation); |
| context_->heap_profile_tracker->FinalizeProfile( |
| &sequence_state->stack_profile_tracker(), &intern_lookup); |
| } |
| } |
| |
| void ProtoTraceParser::ParseStreamingProfilePacket( |
| ProtoIncrementalState::PacketSequenceState* sequence_state, |
| size_t sequence_state_generation, |
| ConstBytes blob) { |
| protos::pbzero::StreamingProfilePacket::Decoder packet(blob.data, blob.size); |
| |
| ProcessTracker* procs = context_->process_tracker.get(); |
| TraceStorage* storage = context_->storage.get(); |
| StackProfileTracker& stack_profile_tracker = |
| sequence_state->stack_profile_tracker(); |
| ProfilePacketInternLookup intern_lookup(sequence_state, |
| sequence_state_generation); |
| |
| uint32_t pid = static_cast<uint32_t>(sequence_state->pid()); |
| uint32_t tid = static_cast<uint32_t>(sequence_state->tid()); |
| UniqueTid utid = procs->UpdateThread(tid, pid); |
| |
| auto timestamp_it = packet.timestamp_delta_us(); |
| for (auto callstack_it = packet.callstack_iid(); callstack_it; |
| ++callstack_it, ++timestamp_it) { |
| if (!timestamp_it) { |
| context_->storage->IncrementStats(stats::stackprofile_parser_error); |
| PERFETTO_ELOG( |
| "StreamingProfilePacket has less callstack IDs than timestamps!"); |
| break; |
| } |
| |
| auto maybe_callstack_id = stack_profile_tracker.FindCallstack( |
| callstack_it->as_uint64(), &intern_lookup); |
| if (!maybe_callstack_id) { |
| context_->storage->IncrementStats(stats::stackprofile_parser_error); |
| PERFETTO_ELOG("StreamingProfilePacket referencing invalid callstack!"); |
| continue; |
| } |
| |
| int64_t callstack_id = *maybe_callstack_id; |
| |
| TraceStorage::CpuProfileStackSamples::Row sample_row{ |
| sequence_state->IncrementAndGetTrackEventTimeNs( |
| timestamp_it->as_int64()), |
| callstack_id, utid}; |
| storage->mutable_cpu_profile_stack_samples()->Insert(sample_row); |
| } |
| } |
| |
| void ProtoTraceParser::ParseSystemInfo(ConstBytes blob) { |
| protos::pbzero::SystemInfo::Decoder packet(blob.data, blob.size); |
| if (packet.has_utsname()) { |
| ConstBytes utsname_blob = packet.utsname(); |
| protos::pbzero::Utsname::Decoder utsname(utsname_blob.data, |
| utsname_blob.size); |
| base::StringView machine = utsname.machine(); |
| if (machine == "aarch64" || machine == "armv8l") { |
| context_->syscall_tracker->SetArchitecture(kAarch64); |
| } else if (machine == "x86_64") { |
| context_->syscall_tracker->SetArchitecture(kX86_64); |
| } else { |
| PERFETTO_ELOG("Unknown architecture %s", machine.ToStdString().c_str()); |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseTrackEvent( |
| int64_t ts, |
| int64_t tts, |
| int64_t ticount, |
| ProtoIncrementalState::PacketSequenceState* sequence_state, |
| size_t sequence_state_generation, |
| ConstBytes blob) { |
| using LegacyEvent = protos::pbzero::TrackEvent::LegacyEvent; |
| |
| protos::pbzero::TrackEvent::Decoder event(blob.data, blob.size); |
| |
| const auto legacy_event_blob = event.legacy_event(); |
| LegacyEvent::Decoder legacy_event(legacy_event_blob.data, |
| legacy_event_blob.size); |
| |
| // TODO(eseckler): This legacy event field will eventually be replaced by |
| // fields in TrackEvent itself. |
| if (PERFETTO_UNLIKELY(!event.type() && !legacy_event.has_phase())) { |
| context_->storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG("TrackEvent without type or phase"); |
| return; |
| } |
| |
| ProcessTracker* procs = context_->process_tracker.get(); |
| TraceStorage* storage = context_->storage.get(); |
| TrackTracker* track_tracker = context_->track_tracker.get(); |
| SliceTracker* slice_tracker = context_->slice_tracker.get(); |
| |
| std::vector<uint64_t> category_iids; |
| for (auto it = event.category_iids(); it; ++it) { |
| category_iids.push_back(it->as_uint64()); |
| } |
| std::vector<protozero::ConstChars> category_strings; |
| for (auto it = event.categories(); it; ++it) { |
| category_strings.push_back(it->as_string()); |
| } |
| |
| StringId category_id = 0; |
| |
| // If there's a single category, we can avoid building a concatenated |
| // string. |
| if (PERFETTO_LIKELY(category_iids.size() == 1 && category_strings.empty())) { |
| auto* decoder = sequence_state->LookupInternedMessage< |
| protos::pbzero::InternedData::kEventCategoriesFieldNumber, |
| protos::pbzero::EventCategory>(sequence_state_generation, |
| category_iids[0]); |
| if (decoder) |
| category_id = storage->InternString(decoder->name()); |
| } else if (category_iids.empty() && category_strings.size() == 1) { |
| category_id = storage->InternString(category_strings[0]); |
| } else if (category_iids.size() + category_strings.size() > 1) { |
| // We concatenate the category strings together since we currently only |
| // support a single "cat" column. |
| // TODO(eseckler): Support multi-category events in the table schema. |
| std::string categories; |
| for (uint64_t iid : category_iids) { |
| auto* decoder = sequence_state->LookupInternedMessage< |
| protos::pbzero::InternedData::kEventCategoriesFieldNumber, |
| protos::pbzero::EventCategory>(sequence_state_generation, iid); |
| if (!decoder) |
| continue; |
| base::StringView name = decoder->name(); |
| if (!categories.empty()) |
| categories.append(","); |
| categories.append(name.data(), name.size()); |
| } |
| for (const protozero::ConstChars& cat : category_strings) { |
| if (!categories.empty()) |
| categories.append(","); |
| categories.append(cat.data, cat.size); |
| } |
| if (!categories.empty()) |
| category_id = storage->InternString(base::StringView(categories)); |
| } |
| |
| StringId name_id = 0; |
| |
| uint64_t name_iid = event.name_iid(); |
| if (!name_iid) |
| name_iid = legacy_event.name_iid(); |
| |
| if (PERFETTO_LIKELY(name_iid)) { |
| auto* decoder = sequence_state->LookupInternedMessage< |
| protos::pbzero::InternedData::kEventNamesFieldNumber, |
| protos::pbzero::EventName>(sequence_state_generation, name_iid); |
| if (decoder) |
| name_id = storage->InternString(decoder->name()); |
| } else if (event.has_name()) { |
| name_id = storage->InternString(event.name()); |
| } |
| |
| // TODO(eseckler): Also consider track_uuid from TrackEventDefaults. |
| // Fall back to the default descriptor track (uuid 0). |
| uint64_t track_uuid = event.has_track_uuid() ? event.track_uuid() : 0u; |
| TrackId track_id; |
| base::Optional<UniqueTid> utid; |
| base::Optional<UniqueTid> upid; |
| |
| // Determine track from track_uuid specified in either TrackEvent or |
| // TrackEventDefaults. If none is set, fall back to the track specified by the |
| // sequence's (or event's) pid + tid or a default track. |
| if (track_uuid) { |
| base::Optional<TrackId> opt_track_id = |
| track_tracker->GetDescriptorTrack(track_uuid); |
| if (!opt_track_id) { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG("TrackEvent with unknown track_uuid %" PRIu64, track_uuid); |
| return; |
| } |
| track_id = *opt_track_id; |
| |
| auto thread_track_row = |
| context_->storage->thread_track_table().id().IndexOf( |
| SqlValue::Long(track_id)); |
| if (thread_track_row) { |
| utid = storage->thread_track_table().utid()[*thread_track_row]; |
| upid = storage->GetThread(*utid).upid; |
| } else { |
| auto process_track_row = |
| context_->storage->process_track_table().id().IndexOf( |
| SqlValue::Long(track_id)); |
| if (process_track_row) |
| upid = storage->process_track_table().upid()[*process_track_row]; |
| } |
| } else if (sequence_state->pid_and_tid_valid() || |
| (legacy_event.has_pid_override() && |
| legacy_event.has_tid_override())) { |
| uint32_t pid = static_cast<uint32_t>(sequence_state->pid()); |
| uint32_t tid = static_cast<uint32_t>(sequence_state->tid()); |
| if (legacy_event.has_pid_override()) |
| pid = static_cast<uint32_t>(legacy_event.pid_override()); |
| if (legacy_event.has_tid_override()) |
| tid = static_cast<uint32_t>(legacy_event.tid_override()); |
| |
| utid = procs->UpdateThread(tid, pid); |
| upid = storage->GetThread(*utid).upid; |
| track_id = track_tracker->GetOrCreateDescriptorTrackForThread(*utid); |
| } else { |
| track_id = track_tracker->GetOrCreateDefaultDescriptorTrack(); |
| } |
| |
| // TODO(eseckler): Replace phase with type and remove handling of |
| // legacy_event.phase() once it is no longer used by producers. |
| int32_t phase = 0; |
| if (legacy_event.has_phase()) { |
| phase = legacy_event.phase(); |
| |
| switch (phase) { |
| case 'b': |
| case 'e': |
| case 'n': { |
| // Intern tracks for legacy async events based on legacy event ids. |
| base::Optional<UniquePid> event_upid; |
| int64_t source_id = 0; |
| if (legacy_event.has_unscoped_id()) { |
| source_id = static_cast<int64_t>(legacy_event.unscoped_id()); |
| } else if (legacy_event.has_global_id()) { |
| source_id = static_cast<int64_t>(legacy_event.global_id()); |
| } else if (legacy_event.has_local_id()) { |
| if (!upid) { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG( |
| "TrackEvent with local_id without process association"); |
| return; |
| } |
| |
| source_id = static_cast<int64_t>(legacy_event.local_id()); |
| event_upid = upid; |
| } else { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG("Async LegacyEvent without ID"); |
| return; |
| } |
| |
| StringId id_scope = 0; |
| if (legacy_event.has_id_scope()) { |
| id_scope = storage->InternString(legacy_event.id_scope()); |
| } |
| |
| track_id = context_->track_tracker->InternLegacyChromeAsyncTrack( |
| name_id, event_upid, source_id, id_scope); |
| break; |
| } |
| case 'i': |
| case 'I': { |
| // Intern tracks for global or process-scoped legacy instant events. |
| switch (legacy_event.instant_event_scope()) { |
| case LegacyEvent::SCOPE_UNSPECIFIED: |
| case LegacyEvent::SCOPE_THREAD: |
| // Thread-scoped legacy instant events already have the right track |
| // based on the tid/pid of the sequence. |
| if (!utid) { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG( |
| "Thread-scoped instant event without thread association"); |
| return; |
| } |
| break; |
| case LegacyEvent::SCOPE_GLOBAL: |
| track_id = context_->track_tracker |
| ->GetOrCreateLegacyChromeGlobalInstantTrack(); |
| break; |
| case LegacyEvent::SCOPE_PROCESS: |
| if (!upid) { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG( |
| "Process-scoped instant event without process association"); |
| return; |
| } |
| |
| track_id = |
| context_->track_tracker->InternLegacyChromeProcessInstantTrack( |
| *upid); |
| break; |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } else { |
| switch (event.type()) { |
| case protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN: |
| phase = utid ? 'B' : 'b'; |
| break; |
| case protos::pbzero::TrackEvent::TYPE_SLICE_END: |
| phase = utid ? 'E' : 'e'; |
| break; |
| case protos::pbzero::TrackEvent::TYPE_INSTANT: |
| phase = utid ? 'i' : 'n'; |
| break; |
| default: |
| PERFETTO_FATAL("unexpected event type %d", event.type()); |
| return; |
| } |
| } |
| |
| auto args_callback = [this, &event, &legacy_event, &sequence_state, |
| sequence_state_generation, ts, |
| utid](ArgsTracker* args_tracker, RowId row_id) { |
| for (auto it = event.debug_annotations(); it; ++it) { |
| ParseDebugAnnotationArgs(it->as_bytes(), sequence_state, |
| sequence_state_generation, args_tracker, row_id); |
| } |
| |
| if (event.has_task_execution()) { |
| ParseTaskExecutionArgs(event.task_execution(), sequence_state, |
| sequence_state_generation, args_tracker, row_id); |
| } |
| |
| if (event.has_log_message()) { |
| ParseLogMessage(event.log_message(), sequence_state, |
| sequence_state_generation, ts, utid, args_tracker, |
| row_id); |
| } |
| |
| // TODO(eseckler): Parse legacy flow events into flow events table once we |
| // have a design for it. |
| if (legacy_event.has_bind_id()) { |
| args_tracker->AddArg(row_id, legacy_event_bind_id_key_id_, |
| legacy_event_bind_id_key_id_, |
| Variadic::UnsignedInteger(legacy_event.bind_id())); |
| } |
| |
| if (legacy_event.bind_to_enclosing()) { |
| args_tracker->AddArg(row_id, legacy_event_bind_to_enclosing_key_id_, |
| legacy_event_bind_to_enclosing_key_id_, |
| Variadic::Boolean(true)); |
| } |
| |
| if (legacy_event.flow_direction()) { |
| StringId value; |
| switch (legacy_event.flow_direction()) { |
| case protos::pbzero::TrackEvent::LegacyEvent::FLOW_IN: |
| value = flow_direction_value_in_id_; |
| break; |
| case protos::pbzero::TrackEvent::LegacyEvent::FLOW_OUT: |
| value = flow_direction_value_out_id_; |
| break; |
| case protos::pbzero::TrackEvent::LegacyEvent::FLOW_INOUT: |
| value = flow_direction_value_inout_id_; |
| break; |
| default: |
| PERFETTO_FATAL("Unknown flow direction: %d", |
| legacy_event.flow_direction()); |
| break; |
| } |
| args_tracker->AddArg(row_id, legacy_event_flow_direction_key_id_, |
| legacy_event_flow_direction_key_id_, |
| Variadic::String(value)); |
| } |
| }; |
| |
| switch (static_cast<char>(phase)) { |
| case 'B': { // TRACE_EVENT_PHASE_BEGIN. |
| if (!utid) { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG("TrackEvent with phase B without thread association"); |
| return; |
| } |
| |
| auto opt_slice_id = |
| slice_tracker->Begin(ts, track_id, *utid, RefType::kRefUtid, |
| category_id, name_id, args_callback); |
| if (opt_slice_id.has_value()) { |
| auto* thread_slices = storage->mutable_thread_slices(); |
| PERFETTO_DCHECK(!thread_slices->slice_count() || |
| thread_slices->slice_ids().back() < |
| opt_slice_id.value()); |
| thread_slices->AddThreadSlice(opt_slice_id.value(), tts, |
| kPendingThreadDuration, ticount, |
| kPendingThreadInstructionDelta); |
| } |
| break; |
| } |
| case 'E': { // TRACE_EVENT_PHASE_END. |
| if (!utid) { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG("TrackEvent with phase E without thread association"); |
| return; |
| } |
| |
| auto opt_slice_id = |
| slice_tracker->End(ts, track_id, category_id, name_id, args_callback); |
| if (opt_slice_id.has_value()) { |
| auto* thread_slices = storage->mutable_thread_slices(); |
| thread_slices->UpdateThreadDeltasForSliceId(opt_slice_id.value(), tts, |
| ticount); |
| } |
| break; |
| } |
| case 'X': { // TRACE_EVENT_PHASE_COMPLETE. |
| if (!utid) { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG("TrackEvent with phase X without thread association"); |
| return; |
| } |
| |
| auto duration_ns = legacy_event.duration_us() * 1000; |
| if (duration_ns < 0) |
| return; |
| auto opt_slice_id = slice_tracker->Scoped( |
| ts, track_id, *utid, RefType::kRefUtid, category_id, name_id, |
| duration_ns, args_callback); |
| if (opt_slice_id.has_value()) { |
| auto* thread_slices = storage->mutable_thread_slices(); |
| PERFETTO_DCHECK(!thread_slices->slice_count() || |
| thread_slices->slice_ids().back() < |
| opt_slice_id.value()); |
| auto thread_duration_ns = legacy_event.thread_duration_us() * 1000; |
| thread_slices->AddThreadSlice(opt_slice_id.value(), tts, |
| thread_duration_ns, ticount, |
| legacy_event.thread_instruction_delta()); |
| } |
| break; |
| } |
| case 'i': |
| case 'I': { // TRACE_EVENT_PHASE_INSTANT. |
| // Handle instant events as slices with zero duration, so that they end |
| // up nested underneath their parent slices. |
| int64_t duration_ns = 0; |
| int64_t tidelta = 0; |
| |
| switch (legacy_event.instant_event_scope()) { |
| case LegacyEvent::SCOPE_UNSPECIFIED: |
| case LegacyEvent::SCOPE_THREAD: { |
| // TODO(lalitm): Associate thread slices with track instead. |
| auto opt_slice_id = slice_tracker->Scoped( |
| ts, track_id, *utid, RefType::kRefUtid, category_id, name_id, |
| duration_ns, args_callback); |
| if (opt_slice_id.has_value()) { |
| auto* thread_slices = storage->mutable_thread_slices(); |
| PERFETTO_DCHECK(!thread_slices->slice_count() || |
| thread_slices->slice_ids().back() < |
| opt_slice_id.value()); |
| thread_slices->AddThreadSlice(opt_slice_id.value(), tts, |
| duration_ns, ticount, tidelta); |
| } |
| break; |
| } |
| case LegacyEvent::SCOPE_GLOBAL: { |
| slice_tracker->Scoped(ts, track_id, /*ref=*/0, RefType::kRefNoRef, |
| category_id, name_id, duration_ns, |
| args_callback); |
| break; |
| } |
| case LegacyEvent::SCOPE_PROCESS: { |
| slice_tracker->Scoped(ts, track_id, *upid, RefType::kRefUpid, |
| category_id, name_id, duration_ns, |
| args_callback); |
| break; |
| } |
| default: { |
| PERFETTO_FATAL("Unknown instant event scope: %u", |
| legacy_event.instant_event_scope()); |
| break; |
| } |
| } |
| break; |
| } |
| case 'b': { // TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN |
| auto opt_slice_id = |
| slice_tracker->Begin(ts, track_id, track_id, RefType::kRefTrack, |
| category_id, name_id, args_callback); |
| // For the time beeing, we only create vtrack slice rows if we need to |
| // store thread timestamps/counters. |
| if (legacy_event.use_async_tts() && opt_slice_id.has_value()) { |
| auto* vtrack_slices = storage->mutable_virtual_track_slices(); |
| PERFETTO_DCHECK(!vtrack_slices->slice_count() || |
| vtrack_slices->slice_ids().back() < |
| opt_slice_id.value()); |
| vtrack_slices->AddVirtualTrackSlice(opt_slice_id.value(), tts, |
| kPendingThreadDuration, ticount, |
| kPendingThreadInstructionDelta); |
| } |
| break; |
| } |
| case 'e': { // TRACE_EVENT_PHASE_NESTABLE_ASYNC_END |
| auto opt_slice_id = |
| slice_tracker->End(ts, track_id, category_id, name_id, args_callback); |
| if (legacy_event.use_async_tts() && opt_slice_id.has_value()) { |
| auto* vtrack_slices = storage->mutable_virtual_track_slices(); |
| vtrack_slices->UpdateThreadDeltasForSliceId(opt_slice_id.value(), tts, |
| ticount); |
| } |
| break; |
| } |
| case 'n': { // TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT |
| // Handle instant events as slices with zero duration, so that they end up |
| // nested underneath their parent slices. |
| int64_t duration_ns = 0; |
| int64_t tidelta = 0; |
| auto opt_slice_id = slice_tracker->Scoped( |
| ts, track_id, track_id, RefType::kRefTrack, category_id, name_id, |
| duration_ns, args_callback); |
| if (legacy_event.use_async_tts() && opt_slice_id.has_value()) { |
| auto* vtrack_slices = storage->mutable_virtual_track_slices(); |
| PERFETTO_DCHECK(!vtrack_slices->slice_count() || |
| vtrack_slices->slice_ids().back() < |
| opt_slice_id.value()); |
| vtrack_slices->AddVirtualTrackSlice(opt_slice_id.value(), tts, |
| duration_ns, ticount, tidelta); |
| } |
| break; |
| } |
| case 'M': { // TRACE_EVENT_PHASE_METADATA (process and thread names). |
| // Parse process and thread names from correspondingly named events. |
| // TODO(eseckler): Also consider names from process/thread descriptors. |
| NullTermStringView event_name = storage->GetString(name_id); |
| PERFETTO_DCHECK(event_name.data()); |
| if (strcmp(event_name.c_str(), "thread_name") == 0) { |
| if (!utid) { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG( |
| "thread_name metadata event without thread association"); |
| return; |
| } |
| |
| auto it = event.debug_annotations(); |
| if (!it) |
| break; |
| protos::pbzero::DebugAnnotation::Decoder annotation(it->data(), |
| it->size()); |
| auto thread_name = annotation.string_value(); |
| if (!thread_name.size) |
| break; |
| auto thread_name_id = storage->InternString(thread_name); |
| procs->UpdateThreadName(storage->GetThread(*utid).tid, thread_name_id); |
| break; |
| } |
| if (strcmp(event_name.c_str(), "process_name") == 0) { |
| if (!upid) { |
| storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG( |
| "process_name metadata event without process association"); |
| return; |
| } |
| |
| auto it = event.debug_annotations(); |
| if (!it) |
| break; |
| protos::pbzero::DebugAnnotation::Decoder annotation(it->data(), |
| it->size()); |
| auto process_name = annotation.string_value(); |
| if (!process_name.size) |
| break; |
| procs->SetProcessMetadata(storage->GetProcess(*upid).pid, base::nullopt, |
| process_name); |
| break; |
| } |
| // Other metadata events are proxied via the raw table for JSON export. |
| ParseLegacyEventAsRawEvent(ts, tts, ticount, utid, category_id, name_id, |
| legacy_event, args_callback); |
| break; |
| } |
| default: { |
| // Other events are proxied via the raw table for JSON export. |
| ParseLegacyEventAsRawEvent(ts, tts, ticount, utid, category_id, name_id, |
| legacy_event, args_callback); |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseLegacyEventAsRawEvent( |
| int64_t ts, |
| int64_t tts, |
| int64_t ticount, |
| base::Optional<UniqueTid> utid, |
| StringId category_id, |
| StringId name_id, |
| const protos::pbzero::TrackEvent::LegacyEvent::Decoder& legacy_event, |
| SliceTracker::SetArgsCallback args_callback) { |
| if (!utid) { |
| context_->storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG("raw legacy event without thread association"); |
| return; |
| } |
| |
| RowId row_id = context_->storage->mutable_raw_events()->AddRawEvent( |
| ts, raw_legacy_event_id_, 0, *utid); |
| ArgsTracker args(context_); |
| args.AddArg(row_id, legacy_event_category_key_id_, |
| legacy_event_category_key_id_, Variadic::String(category_id)); |
| args.AddArg(row_id, legacy_event_name_key_id_, legacy_event_name_key_id_, |
| Variadic::String(name_id)); |
| |
| std::string phase_string(1, static_cast<char>(legacy_event.phase())); |
| StringId phase_id = context_->storage->InternString(phase_string.c_str()); |
| args.AddArg(row_id, legacy_event_phase_key_id_, legacy_event_phase_key_id_, |
| Variadic::String(phase_id)); |
| |
| if (legacy_event.has_duration_us()) { |
| args.AddArg(row_id, legacy_event_duration_ns_key_id_, |
| legacy_event_duration_ns_key_id_, |
| Variadic::Integer(legacy_event.duration_us() * 1000)); |
| } |
| |
| if (tts) { |
| args.AddArg(row_id, legacy_event_thread_timestamp_ns_key_id_, |
| legacy_event_thread_timestamp_ns_key_id_, |
| Variadic::Integer(tts)); |
| if (legacy_event.has_thread_duration_us()) { |
| args.AddArg(row_id, legacy_event_thread_duration_ns_key_id_, |
| legacy_event_thread_duration_ns_key_id_, |
| Variadic::Integer(legacy_event.thread_duration_us() * 1000)); |
| } |
| } |
| |
| if (ticount) { |
| args.AddArg(row_id, legacy_event_thread_instruction_count_key_id_, |
| legacy_event_thread_instruction_count_key_id_, |
| Variadic::Integer(tts)); |
| if (legacy_event.has_thread_instruction_delta()) { |
| args.AddArg(row_id, legacy_event_thread_instruction_delta_key_id_, |
| legacy_event_thread_instruction_delta_key_id_, |
| Variadic::Integer(legacy_event.thread_instruction_delta())); |
| } |
| } |
| |
| if (legacy_event.use_async_tts()) { |
| args.AddArg(row_id, legacy_event_use_async_tts_key_id_, |
| legacy_event_use_async_tts_key_id_, Variadic::Boolean(true)); |
| } |
| |
| bool has_id = false; |
| if (legacy_event.has_unscoped_id()) { |
| args.AddArg(row_id, legacy_event_global_id_key_id_, |
| legacy_event_global_id_key_id_, |
| Variadic::UnsignedInteger(legacy_event.unscoped_id())); |
| has_id = true; |
| } else if (legacy_event.has_global_id()) { |
| args.AddArg(row_id, legacy_event_global_id_key_id_, |
| legacy_event_global_id_key_id_, |
| Variadic::UnsignedInteger(legacy_event.global_id())); |
| has_id = true; |
| } else if (legacy_event.has_local_id()) { |
| args.AddArg(row_id, legacy_event_local_id_key_id_, |
| legacy_event_local_id_key_id_, |
| Variadic::UnsignedInteger(legacy_event.local_id())); |
| has_id = true; |
| } |
| |
| if (has_id && legacy_event.has_id_scope() && legacy_event.id_scope().size) { |
| args.AddArg(row_id, legacy_event_id_scope_key_id_, |
| legacy_event_id_scope_key_id_, |
| Variadic::String( |
| context_->storage->InternString(legacy_event.id_scope()))); |
| } |
| |
| // No need to parse legacy_event.instant_event_scope() because we import |
| // instant events into the slice table. |
| |
| args_callback(&args, row_id); |
| } |
| |
| void ProtoTraceParser::ParseDebugAnnotationArgs( |
| ConstBytes debug_annotation, |
| ProtoIncrementalState::PacketSequenceState* sequence_state, |
| size_t sequence_state_generation, |
| ArgsTracker* args_tracker, |
| RowId row_id) { |
| TraceStorage* storage = context_->storage.get(); |
| |
| protos::pbzero::DebugAnnotation::Decoder annotation(debug_annotation.data, |
| debug_annotation.size); |
| |
| |
| StringId name_id = 0; |
| |
| uint64_t name_iid = annotation.name_iid(); |
| if (PERFETTO_LIKELY(name_iid)) { |
| auto* decoder = sequence_state->LookupInternedMessage< |
| protos::pbzero::InternedData::kDebugAnnotationNamesFieldNumber, |
| protos::pbzero::DebugAnnotationName>(sequence_state_generation, |
| name_iid); |
| if (!decoder) |
| return; |
| |
| std::string name_prefixed = "debug." + decoder->name().ToStdString(); |
| name_id = storage->InternString(base::StringView(name_prefixed)); |
| } else if (annotation.has_name()) { |
| name_id = storage->InternString(annotation.name()); |
| } else { |
| context_->storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG("Debug annotation without name"); |
| return; |
| } |
| |
| if (annotation.has_bool_value()) { |
| args_tracker->AddArg(row_id, name_id, name_id, |
| Variadic::Boolean(annotation.bool_value())); |
| } else if (annotation.has_uint_value()) { |
| args_tracker->AddArg(row_id, name_id, name_id, |
| Variadic::UnsignedInteger(annotation.uint_value())); |
| } else if (annotation.has_int_value()) { |
| args_tracker->AddArg(row_id, name_id, name_id, |
| Variadic::Integer(annotation.int_value())); |
| } else if (annotation.has_double_value()) { |
| args_tracker->AddArg(row_id, name_id, name_id, |
| Variadic::Real(annotation.double_value())); |
| } else if (annotation.has_string_value()) { |
| args_tracker->AddArg( |
| row_id, name_id, name_id, |
| Variadic::String(storage->InternString(annotation.string_value()))); |
| } else if (annotation.has_pointer_value()) { |
| args_tracker->AddArg(row_id, name_id, name_id, |
| Variadic::Pointer(annotation.pointer_value())); |
| } else if (annotation.has_legacy_json_value()) { |
| args_tracker->AddArg( |
| row_id, name_id, name_id, |
| Variadic::Json(storage->InternString(annotation.legacy_json_value()))); |
| } else if (annotation.has_nested_value()) { |
| auto name = storage->GetString(name_id); |
| ParseNestedValueArgs(annotation.nested_value(), name, name, args_tracker, |
| row_id); |
| } |
| } |
| |
| void ProtoTraceParser::ParseNestedValueArgs(ConstBytes nested_value, |
| base::StringView flat_key, |
| base::StringView key, |
| ArgsTracker* args_tracker, |
| RowId row_id) { |
| protos::pbzero::DebugAnnotation::NestedValue::Decoder value( |
| nested_value.data, nested_value.size); |
| switch (value.nested_type()) { |
| case protos::pbzero::DebugAnnotation::NestedValue::UNSPECIFIED: { |
| auto flat_key_id = context_->storage->InternString(flat_key); |
| auto key_id = context_->storage->InternString(key); |
| // Leaf value. |
| if (value.has_bool_value()) { |
| args_tracker->AddArg(row_id, flat_key_id, key_id, |
| Variadic::Boolean(value.bool_value())); |
| } else if (value.has_int_value()) { |
| args_tracker->AddArg(row_id, flat_key_id, key_id, |
| Variadic::Integer(value.int_value())); |
| } else if (value.has_double_value()) { |
| args_tracker->AddArg(row_id, flat_key_id, key_id, |
| Variadic::Real(value.double_value())); |
| } else if (value.has_string_value()) { |
| args_tracker->AddArg(row_id, flat_key_id, key_id, |
| Variadic::String(context_->storage->InternString( |
| value.string_value()))); |
| } |
| break; |
| } |
| case protos::pbzero::DebugAnnotation::NestedValue::DICT: { |
| auto key_it = value.dict_keys(); |
| auto value_it = value.dict_values(); |
| for (; key_it && value_it; ++key_it, ++value_it) { |
| std::string child_name = key_it->as_std_string(); |
| std::string child_flat_key = flat_key.ToStdString() + "." + child_name; |
| std::string child_key = key.ToStdString() + "." + child_name; |
| ParseNestedValueArgs(value_it->as_bytes(), |
| base::StringView(child_flat_key), |
| base::StringView(child_key), args_tracker, row_id); |
| } |
| break; |
| } |
| case protos::pbzero::DebugAnnotation::NestedValue::ARRAY: { |
| int child_index = 0; |
| std::string child_flat_key = flat_key.ToStdString(); |
| for (auto value_it = value.array_values(); value_it; |
| ++value_it, ++child_index) { |
| std::string child_key = |
| key.ToStdString() + "[" + std::to_string(child_index) + "]"; |
| ParseNestedValueArgs(value_it->as_bytes(), |
| base::StringView(child_flat_key), |
| base::StringView(child_key), args_tracker, row_id); |
| } |
| break; |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseTaskExecutionArgs( |
| ConstBytes task_execution, |
| ProtoIncrementalState::PacketSequenceState* sequence_state, |
| size_t sequence_state_generation, |
| ArgsTracker* args_tracker, |
| RowId row) { |
| protos::pbzero::TaskExecution::Decoder task(task_execution.data, |
| task_execution.size); |
| uint64_t iid = task.posted_from_iid(); |
| if (!iid) |
| return; |
| |
| auto* decoder = sequence_state->LookupInternedMessage< |
| protos::pbzero::InternedData::kSourceLocationsFieldNumber, |
| protos::pbzero::SourceLocation>(sequence_state_generation, iid); |
| if (!decoder) |
| return; |
| |
| StringId file_name_id = 0; |
| StringId function_name_id = 0; |
| uint32_t line_number = 0; |
| |
| TraceStorage* storage = context_->storage.get(); |
| file_name_id = storage->InternString(decoder->file_name()); |
| function_name_id = storage->InternString(decoder->function_name()); |
| line_number = decoder->line_number(); |
| |
| args_tracker->AddArg(row, task_file_name_args_key_id_, |
| task_file_name_args_key_id_, |
| Variadic::String(file_name_id)); |
| args_tracker->AddArg(row, task_function_name_args_key_id_, |
| task_function_name_args_key_id_, |
| Variadic::String(function_name_id)); |
| |
| args_tracker->AddArg(row, task_line_number_args_key_id_, |
| task_line_number_args_key_id_, |
| Variadic::UnsignedInteger(line_number)); |
| } |
| |
| void ProtoTraceParser::ParseLogMessage( |
| ConstBytes blob, |
| ProtoIncrementalState::PacketSequenceState* sequence_state, |
| size_t sequence_state_generation, |
| int64_t ts, |
| base::Optional<UniqueTid> utid, |
| ArgsTracker* args_tracker, |
| RowId row) { |
| if (!utid) { |
| context_->storage->IncrementStats(stats::track_event_parser_errors); |
| PERFETTO_DLOG("LogMessage without thread association"); |
| return; |
| } |
| |
| protos::pbzero::LogMessage::Decoder message(blob.data, blob.size); |
| |
| TraceStorage* storage = context_->storage.get(); |
| |
| StringId log_message_id = 0; |
| |
| auto* decoder = sequence_state->LookupInternedMessage< |
| protos::pbzero::InternedData::kLogMessageBodyFieldNumber, |
| protos::pbzero::LogMessageBody>(sequence_state_generation, |
| message.body_iid()); |
| if (!decoder) |
| return; |
| |
| log_message_id = storage->InternString(decoder->body()); |
| |
| // TODO(nicomazz): LogMessage also contains the source of the message (file |
| // and line number). Android logs doesn't support this so far. |
| context_->storage->mutable_android_log()->AddLogEvent( |
| ts, *utid, |
| /*priority*/ 0, |
| /*tag_id*/ 0, // TODO(nicomazz): Abuse tag_id to display |
| // "file_name:line_number". |
| log_message_id); |
| |
| args_tracker->AddArg(row, log_message_body_key_id_, log_message_body_key_id_, |
| Variadic::String(log_message_id)); |
| // TODO(nicomazz): Add the source location as an argument. |
| } |
| |
| void ProtoTraceParser::ParseChromeBenchmarkMetadata(ConstBytes blob) { |
| TraceStorage* storage = context_->storage.get(); |
| protos::pbzero::ChromeBenchmarkMetadata::Decoder packet(blob.data, blob.size); |
| if (packet.has_benchmark_name()) { |
| auto benchmark_name_id = storage->InternString(packet.benchmark_name()); |
| storage->SetMetadata(metadata::benchmark_name, |
| Variadic::String(benchmark_name_id)); |
| } |
| if (packet.has_benchmark_description()) { |
| auto benchmark_description_id = |
| storage->InternString(packet.benchmark_description()); |
| storage->SetMetadata(metadata::benchmark_description, |
| Variadic::String(benchmark_description_id)); |
| } |
| if (packet.has_label()) { |
| auto label_id = storage->InternString(packet.label()); |
| storage->SetMetadata(metadata::benchmark_label, Variadic::String(label_id)); |
| } |
| if (packet.has_story_name()) { |
| auto story_name_id = storage->InternString(packet.story_name()); |
| storage->SetMetadata(metadata::benchmark_story_name, |
| Variadic::String(story_name_id)); |
| } |
| for (auto it = packet.story_tags(); it; ++it) { |
| auto story_tag_id = storage->InternString(it->as_string()); |
| storage->AppendMetadata(metadata::benchmark_story_tags, |
| Variadic::String(story_tag_id)); |
| } |
| if (packet.has_benchmark_start_time_us()) { |
| storage->SetMetadata(metadata::benchmark_start_time_us, |
| Variadic::Integer(packet.benchmark_start_time_us())); |
| } |
| if (packet.has_story_run_time_us()) { |
| storage->SetMetadata(metadata::benchmark_story_run_time_us, |
| Variadic::Integer(packet.story_run_time_us())); |
| } |
| if (packet.has_story_run_index()) { |
| storage->SetMetadata(metadata::benchmark_story_run_index, |
| Variadic::Integer(packet.story_run_index())); |
| } |
| if (packet.has_had_failures()) { |
| storage->SetMetadata(metadata::benchmark_had_failures, |
| Variadic::Integer(packet.had_failures())); |
| } |
| } |
| |
| void ProtoTraceParser::ParseChromeEvents(int64_t ts, ConstBytes blob) { |
| TraceStorage* storage = context_->storage.get(); |
| protos::pbzero::ChromeEventBundle::Decoder bundle(blob.data, blob.size); |
| ArgsTracker args(context_); |
| if (bundle.has_metadata()) { |
| RowId row_id = storage->mutable_raw_events()->AddRawEvent( |
| ts, raw_chrome_metadata_event_id_, 0, 0); |
| |
| // Metadata is proxied via a special event in the raw table to JSON export. |
| for (auto it = bundle.metadata(); it; ++it) { |
| protos::pbzero::ChromeMetadata::Decoder metadata(it->as_bytes().data, |
| it->as_bytes().size); |
| StringId name_id = storage->InternString(metadata.name()); |
| Variadic value; |
| if (metadata.has_string_value()) { |
| value = |
| Variadic::String(storage->InternString(metadata.string_value())); |
| } else if (metadata.has_int_value()) { |
| value = Variadic::Integer(metadata.int_value()); |
| } else if (metadata.has_bool_value()) { |
| value = Variadic::Integer(metadata.bool_value()); |
| } else if (metadata.has_json_value()) { |
| value = Variadic::Json(storage->InternString(metadata.json_value())); |
| } else { |
| PERFETTO_FATAL("Empty ChromeMetadata message"); |
| } |
| args.AddArg(row_id, name_id, name_id, value); |
| } |
| } |
| |
| if (bundle.has_legacy_ftrace_output()) { |
| RowId row_id = storage->mutable_raw_events()->AddRawEvent( |
| ts, raw_chrome_legacy_system_trace_event_id_, 0, 0); |
| |
| std::string data; |
| for (auto it = bundle.legacy_ftrace_output(); it; ++it) { |
| data += it->as_string().ToStdString(); |
| } |
| Variadic value = |
| Variadic::String(storage->InternString(base::StringView(data))); |
| args.AddArg(row_id, data_name_id_, data_name_id_, value); |
| } |
| |
| if (bundle.has_legacy_json_trace()) { |
| for (auto it = bundle.legacy_json_trace(); it; ++it) { |
| protos::pbzero::ChromeLegacyJsonTrace::Decoder legacy_trace( |
| it->as_bytes().data, it->as_bytes().size); |
| if (legacy_trace.type() != |
| protos::pbzero::ChromeLegacyJsonTrace::USER_TRACE) { |
| continue; |
| } |
| RowId row_id = storage->mutable_raw_events()->AddRawEvent( |
| ts, raw_chrome_legacy_user_trace_event_id_, 0, 0); |
| Variadic value = |
| Variadic::String(storage->InternString(legacy_trace.data())); |
| args.AddArg(row_id, data_name_id_, data_name_id_, value); |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseMetatraceEvent(int64_t ts, ConstBytes blob) { |
| protos::pbzero::PerfettoMetatrace::Decoder event(blob.data, blob.size); |
| auto utid = context_->process_tracker->GetOrCreateThread(event.thread_id()); |
| |
| StringId cat_id = metatrace_id_; |
| StringId name_id = 0; |
| char fallback[64]; |
| |
| if (event.has_event_id()) { |
| auto eid = event.event_id(); |
| if (eid < metatrace::EVENTS_MAX) { |
| name_id = context_->storage->InternString(metatrace::kEventNames[eid]); |
| } else { |
| sprintf(fallback, "Event %d", eid); |
| name_id = context_->storage->InternString(fallback); |
| } |
| TrackId track_id = context_->track_tracker->InternThreadTrack(utid); |
| context_->slice_tracker->Scoped(ts, track_id, utid, RefType::kRefUtid, |
| cat_id, name_id, event.event_duration_ns()); |
| } else if (event.has_counter_id()) { |
| auto cid = event.counter_id(); |
| if (cid < metatrace::COUNTERS_MAX) { |
| name_id = context_->storage->InternString(metatrace::kCounterNames[cid]); |
| } else { |
| sprintf(fallback, "Counter %d", cid); |
| name_id = context_->storage->InternString(fallback); |
| } |
| context_->event_tracker->PushCounter(ts, event.counter_value(), name_id, |
| utid, RefType::kRefUtid); |
| } |
| |
| if (event.has_overruns()) |
| context_->storage->IncrementStats(stats::metatrace_overruns); |
| } |
| |
| void ProtoTraceParser::ParseTraceConfig(ConstBytes blob) { |
| protos::pbzero::TraceConfig::Decoder trace_config(blob.data, blob.size); |
| if (trace_config.has_statsd_metadata()) { |
| ParseStatsdMetadata(trace_config.statsd_metadata()); |
| } |
| } |
| |
| void ProtoTraceParser::ParseStatsdMetadata(ConstBytes blob) { |
| protos::pbzero::TraceConfig::StatsdMetadata::Decoder metadata(blob.data, |
| blob.size); |
| if (metadata.has_triggering_subscription_id()) { |
| context_->storage->SetMetadata( |
| metadata::statsd_triggering_subscription_id, |
| Variadic::Integer(metadata.triggering_subscription_id())); |
| } |
| } |
| |
| void ProtoTraceParser::ParseAndroidPackagesList(ConstBytes blob) { |
| protos::pbzero::PackagesList::Decoder pkg_list(blob.data, blob.size); |
| context_->storage->SetStats(stats::packages_list_has_read_errors, |
| pkg_list.read_error()); |
| context_->storage->SetStats(stats::packages_list_has_parse_errors, |
| pkg_list.parse_error()); |
| |
| // Insert the package info into arg sets (one set per package), with the arg |
| // set ids collected in the Metadata table, under |
| // metadata::android_packages_list key type. |
| for (auto it = pkg_list.packages(); it; ++it) { |
| // Insert a placeholder metadata entry, which will be overwritten by the |
| // arg_set_id when the arg tracker is flushed. |
| RowId row_id = context_->storage->AppendMetadata( |
| metadata::android_packages_list, Variadic::Integer(0)); |
| |
| auto add_arg = [this, row_id](base::StringView name, Variadic value) { |
| StringId key_id = context_->storage->InternString(name); |
| context_->args_tracker->AddArg(row_id, key_id, key_id, value); |
| }; |
| protos::pbzero::PackagesList_PackageInfo::Decoder pkg(it->data(), |
| it->size()); |
| add_arg("name", |
| Variadic::String(context_->storage->InternString(pkg.name()))); |
| add_arg("uid", Variadic::UnsignedInteger(pkg.uid())); |
| add_arg("debuggable", Variadic::Boolean(pkg.debuggable())); |
| add_arg("profileable_from_shell", |
| Variadic::Boolean(pkg.profileable_from_shell())); |
| add_arg("version_code", Variadic::Integer(pkg.version_code())); |
| } |
| } |
| |
| void ProtoTraceParser::ParseModuleSymbols(ConstBytes blob) { |
| protos::pbzero::ModuleSymbols::Decoder module_symbols(blob.data, blob.size); |
| std::string hex_build_id = base::ToHex(module_symbols.build_id().data, |
| module_symbols.build_id().size); |
| auto mapping_rows = |
| context_->storage->stack_profile_mappings().FindMappingRow( |
| context_->storage->InternString(module_symbols.path()), |
| context_->storage->InternString(base::StringView(hex_build_id))); |
| if (mapping_rows.empty()) { |
| context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id); |
| return; |
| } |
| for (auto addr_it = module_symbols.address_symbols(); addr_it; ++addr_it) { |
| protos::pbzero::AddressSymbols::Decoder address_symbols(addr_it->data(), |
| addr_it->size()); |
| |
| ssize_t frame_row = -1; |
| for (int64_t mapping_row : mapping_rows) { |
| frame_row = context_->storage->stack_profile_frames().FindFrameRow( |
| static_cast<size_t>(mapping_row), address_symbols.address()); |
| if (frame_row != -1) |
| break; |
| } |
| if (frame_row == -1) { |
| context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id); |
| continue; |
| } |
| uint32_t symbol_set_id = context_->storage->symbol_table().size(); |
| context_->storage->mutable_stack_profile_frames()->SetSymbolSetId( |
| static_cast<size_t>(frame_row), symbol_set_id); |
| for (auto line_it = address_symbols.lines(); line_it; ++line_it) { |
| protos::pbzero::Line::Decoder line(line_it->data(), line_it->size()); |
| context_->storage->mutable_symbol_table()->Insert( |
| {symbol_set_id, context_->storage->InternString(line.function_name()), |
| context_->storage->InternString(line.source_file_name()), |
| line.line_number()}); |
| } |
| } |
| } |
| |
| void ProtoTraceParser::ParseHeapGraph(int64_t ts, ConstBytes blob) { |
| protos::pbzero::HeapGraph::Decoder heap_graph(blob.data, blob.size); |
| UniquePid upid = context_->process_tracker->GetOrCreateProcess( |
| static_cast<uint32_t>(heap_graph.pid())); |
| context_->heap_graph_tracker->SetPacketIndex(heap_graph.index()); |
| for (auto it = heap_graph.objects(); it; ++it) { |
| protos::pbzero::HeapGraphObject::Decoder object(it->data(), it->size()); |
| HeapGraphTracker::SourceObject obj; |
| obj.object_id = object.id(); |
| obj.self_size = object.self_size(); |
| obj.type_id = object.type_id(); |
| auto ref_field_ids_it = object.reference_field_id(); |
| auto ref_object_ids_it = object.reference_object_id(); |
| for (; ref_field_ids_it && ref_object_ids_it; |
| ++ref_field_ids_it, ++ref_object_ids_it) { |
| HeapGraphTracker::SourceObject::Reference ref; |
| ref.field_name_id = ref_field_ids_it->as_uint64(); |
| ref.owned_object_id = ref_object_ids_it->as_uint64(); |
| obj.references.emplace_back(std::move(ref)); |
| } |
| |
| if (ref_field_ids_it || ref_object_ids_it) { |
| context_->storage->IncrementIndexedStats(stats::heap_graph_missing_packet, |
| static_cast<int>(upid)); |
| continue; |
| } |
| context_->heap_graph_tracker->AddObject(upid, ts, std::move(obj)); |
| } |
| for (auto it = heap_graph.type_names(); it; ++it) { |
| protos::pbzero::InternedString::Decoder entry(it->data(), it->size()); |
| const char* str = reinterpret_cast<const char*>(entry.str().data); |
| auto str_view = base::StringView(str, entry.str().size); |
| |
| context_->heap_graph_tracker->AddInternedTypeName( |
| entry.iid(), context_->storage->InternString(str_view)); |
| } |
| for (auto it = heap_graph.field_names(); it; ++it) { |
| protos::pbzero::InternedString::Decoder entry(it->data(), it->size()); |
| const char* str = reinterpret_cast<const char*>(entry.str().data); |
| auto str_view = base::StringView(str, entry.str().size); |
| |
| context_->heap_graph_tracker->AddInternedFieldName( |
| entry.iid(), context_->storage->InternString(str_view)); |
| } |
| for (auto it = heap_graph.roots(); it; ++it) { |
| protos::pbzero::HeapGraphRoot::Decoder entry(it->data(), it->size()); |
| const char* str = HeapGraphRootTypeToString(entry.root_type()); |
| auto str_view = base::StringView(str); |
| |
| HeapGraphTracker::SourceRoot src_root; |
| src_root.root_type = context_->storage->InternString(str_view); |
| for (auto obj_it = entry.object_ids(); obj_it; ++obj_it) |
| src_root.object_ids.emplace_back(obj_it->as_uint64()); |
| context_->heap_graph_tracker->AddRoot(upid, ts, std::move(src_root)); |
| } |
| if (!heap_graph.continued()) { |
| context_->heap_graph_tracker->FinalizeProfile(); |
| } |
| } |
| |
| } // namespace trace_processor |
| } // namespace perfetto |