blob: 98ea5f57cc39ef51957193e5f93c6cd16b5d4f84 [file] [log] [blame]
/*
* 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 "perfetto/trace_processor/status.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/heap_graph_tracker.h"
#include "src/trace_processor/heap_profile_tracker.h"
#include "src/trace_processor/importers/ftrace/ftrace_module.h"
#include "src/trace_processor/importers/proto/packet_sequence_state.h"
#include "src/trace_processor/importers/proto/track_event_module.h"
#include "src/trace_processor/importers/systrace/systrace_parser.h"
#include "src/trace_processor/metadata.h"
#include "src/trace_processor/process_tracker.h"
#include "src/trace_processor/slice_tracker.h"
#include "src/trace_processor/stack_profile_tracker.h"
#include "src/trace_processor/syscall_tracker.h"
#include "src/trace_processor/timestamped_trace_piece.h"
#include "src/trace_processor/trace_processor_context.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/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"
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);
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);
return src_callstack;
}
class ProfilePacketInternLookup : public StackProfileTracker::InternLookup {
public:
ProfilePacketInternLookup(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:
PacketSequenceState* seq_state_;
size_t seq_state_generation_;
};
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")),
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")),
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")),
oom_score_adj_id_(context->storage->InternString("oom_score_adj")),
metatrace_id_(context->storage->InternString("metatrace")),
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")) {
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));
}
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_;
// 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});
}
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());
ParseTracePacketImpl(ts, std::move(ttp), packet);
// 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::ParseTracePacketImpl(
int64_t ts,
TimestampedTracePiece ttp,
const protos::pbzero::TracePacket::Decoder& packet) {
// TODO(eseckler): Propagate statuses from modules.
if (!context_->ftrace_module->ParsePacket(packet, ttp).ignored())
return;
if (!context_->track_event_module->ParsePacket(packet, ttp).ignored())
return;
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(ts, packet.power_rails());
if (packet.has_trace_stats())
ParseTraceStats(packet.trace_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_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());
}
}
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);
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);
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);
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);
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);
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);
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 {
auto args = proc.cmdline();
base::StringView argv0 = args ? *args : base::StringView();
context_->process_tracker->SetProcessMetadata(pid, ppid, argv0);
}
}
for (auto it = ps.threads(); it; ++it) {
protos::pbzero::ProcessTree::Thread::Decoder thd(*it);
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);
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);
ModuleResult res = context_->ftrace_module->ParseFtracePacket(cpu, ttp);
PERFETTO_DCHECK(!res.ignored());
// TODO(eseckler): Propagate status.
if (!res.ok()) {
PERFETTO_ELOG("%s", res.message().c_str());
}
// 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();
}
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(int64_t ts, 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);
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);
if (desc.index() < power_rails_strs_id_.size()) {
int64_t actual_ts =
desc.has_timestamp_ms()
? static_cast<int64_t>(desc.timestamp_ms()) * 1000000
: ts;
context_->event_tracker->PushCounter(actual_ts, desc.energy(),
power_rails_strs_id_[desc.index()],
0, RefType::kRefNoRef);
} else {
context_->storage->IncrementStats(stats::power_rail_unknown_index);
}
}
}
}
void ProtoTraceParser::ParseAndroidLogPacket(ConstBytes blob) {
protos::pbzero::AndroidLogPacket::Decoder packet(blob.data, blob.size);
for (auto it = packet.events(); it; ++it)
ParseAndroidLogEvent(*it);
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);
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);
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::ParseProfilePacket(int64_t,
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);
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);
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);
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);
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);
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);
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(
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, &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),
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::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);
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);
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).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);
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);
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);
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);
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);
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;
ref.owned_object_id = *ref_object_ids_it;
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);
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);
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);
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);
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