| /* |
| * Copyright (C) 2019 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/importers/proto/system_probes_parser.h" |
| |
| #include <set> |
| |
| #include "perfetto/base/logging.h" |
| #include "perfetto/ext/base/string_utils.h" |
| #include "perfetto/ext/traced/sys_stats_counters.h" |
| #include "perfetto/protozero/proto_decoder.h" |
| #include "src/trace_processor/importers/common/event_tracker.h" |
| #include "src/trace_processor/importers/common/metadata_tracker.h" |
| #include "src/trace_processor/importers/common/process_tracker.h" |
| #include "src/trace_processor/importers/common/system_info_tracker.h" |
| #include "src/trace_processor/importers/syscalls/syscall_tracker.h" |
| #include "src/trace_processor/storage/metadata.h" |
| #include "src/trace_processor/types/trace_processor_context.h" |
| |
| #include "protos/perfetto/trace/ps/process_stats.pbzero.h" |
| #include "protos/perfetto/trace/ps/process_tree.pbzero.h" |
| #include "protos/perfetto/trace/system_info.pbzero.h" |
| #include "protos/perfetto/trace/system_info/cpu_info.pbzero.h" |
| |
| namespace { |
| |
| bool IsSupportedDiskStatDevice(const std::string& device_name) { |
| return device_name == "sda"; // Primary SCSI disk device name |
| } |
| |
| } // namespace |
| |
| namespace perfetto { |
| namespace trace_processor { |
| |
| namespace { |
| |
| std::optional<int> VersionStringToSdkVersion(const std::string& version) { |
| // TODO(lalitm): remove this when the SDK version polling saturates |
| // S/T traces in practice. |
| if (base::StartsWith(version, "T") || base::StartsWith(version, "S")) { |
| return 31; |
| } |
| |
| // Documentation for this mapping can be found at |
| // https://source.android.com/compatibility/cdd. |
| if (version == "12") { |
| return 31; |
| } else if (version == "11") { |
| return 30; |
| } else if (version == "10") { |
| return 29; |
| } else if (version == "9") { |
| return 28; |
| } else if (version == "8.1") { |
| return 27; |
| } else if (version == "8.0") { |
| return 26; |
| } else if (version == "7.1") { |
| return 25; |
| } else if (version == "7.0") { |
| return 24; |
| } else if (version == "6.0") { |
| return 23; |
| } else if (version == "5.1" || version == "5.1.1") { |
| return 22; |
| } else if (version == "5.0" || version == "5.0.1" || version == "5.0.2") { |
| return 21; |
| } |
| // If we reached this point, we don't know how to parse this version |
| // so just return null. |
| return std::nullopt; |
| } |
| |
| std::optional<int> FingerprintToSdkVersion(const std::string& fingerprint) { |
| // Try to parse the SDK version from the fingerprint. |
| // Examples of fingerprints: |
| // google/shamu/shamu:7.0/NBD92F/3753956:userdebug/dev-keys |
| // google/coral/coral:12/SP1A.210812.015/7679548:userdebug/dev-keys |
| size_t colon = fingerprint.find(':'); |
| if (colon == std::string::npos) |
| return std::nullopt; |
| |
| size_t slash = fingerprint.find('/', colon); |
| if (slash == std::string::npos) |
| return std::nullopt; |
| |
| std::string version = fingerprint.substr(colon + 1, slash - (colon + 1)); |
| return VersionStringToSdkVersion(version); |
| } |
| } // namespace |
| |
| SystemProbesParser::SystemProbesParser(TraceProcessorContext* context) |
| : context_(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")), |
| oom_score_adj_id_(context->storage->InternString("oom_score_adj")), |
| cpu_freq_id_(context_->storage->InternString("cpufreq")) { |
| 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_; |
| } |
| |
| void SystemProbesParser::ParseDiskStats(int64_t ts, ConstBytes blob) { |
| protos::pbzero::SysStats::DiskStat::Decoder ds(blob.data, blob.size); |
| static constexpr double SECTORS_PER_MB = 2048.0; |
| static constexpr double MS_PER_SEC = 1000.0; |
| std::string device_name = ds.device_name().ToStdString(); |
| if (!IsSupportedDiskStatDevice(device_name)) { |
| return; |
| } |
| |
| base::StackString<512> tag_prefix("diskstat.[%s]", device_name.c_str()); |
| auto push_counter = [this, ts, tag_prefix](const char* counter_name, |
| double value) { |
| base::StackString<512> track_name("%s.%s", tag_prefix.c_str(), |
| counter_name); |
| StringId string_id = context_->storage->InternString(track_name.c_str()); |
| TrackId track = |
| context_->track_tracker->InternGlobalCounterTrack(string_id); |
| context_->event_tracker->PushCounter(ts, value, track); |
| }; |
| |
| auto calculate_throughput = [](double amount, int64_t diff) { |
| return diff == 0 ? 0 : amount * MS_PER_SEC / static_cast<double>(diff); |
| }; |
| |
| int64_t cur_read_amount = static_cast<int64_t>(ds.read_sectors()); |
| int64_t cur_write_amount = static_cast<int64_t>(ds.write_sectors()); |
| int64_t cur_discard_amount = static_cast<int64_t>(ds.discard_sectors()); |
| int64_t cur_flush_count = static_cast<int64_t>(ds.flush_count()); |
| int64_t cur_read_time = static_cast<int64_t>(ds.read_time_ms()); |
| int64_t cur_write_time = static_cast<int64_t>(ds.write_time_ms()); |
| int64_t cur_discard_time = static_cast<int64_t>(ds.discard_time_ms()); |
| int64_t cur_flush_time = static_cast<int64_t>(ds.flush_time_ms()); |
| |
| if (prev_read_amount != -1) { |
| double read_amount = |
| static_cast<double>(cur_read_amount - prev_read_amount) / |
| SECTORS_PER_MB; |
| double write_amount = |
| static_cast<double>(cur_write_amount - prev_write_amount) / |
| SECTORS_PER_MB; |
| double discard_amount = |
| static_cast<double>(cur_discard_amount - prev_discard_amount) / |
| SECTORS_PER_MB; |
| double flush_count = |
| static_cast<double>(cur_flush_count - prev_flush_count); |
| int64_t read_time_diff = cur_read_time - prev_read_time; |
| int64_t write_time_diff = cur_write_time - prev_write_time; |
| int64_t discard_time_diff = cur_discard_time - prev_discard_time; |
| double flush_time_diff = |
| static_cast<double>(cur_flush_time - prev_flush_time); |
| |
| double read_thpt = calculate_throughput(read_amount, read_time_diff); |
| double write_thpt = calculate_throughput(write_amount, write_time_diff); |
| double discard_thpt = |
| calculate_throughput(discard_amount, discard_time_diff); |
| |
| push_counter("read_amount(mg)", read_amount); |
| push_counter("read_throughput(mg/s)", read_thpt); |
| push_counter("write_amount(mg)", write_amount); |
| push_counter("write_throughput(mg/s)", write_thpt); |
| push_counter("discard_amount(mg)", discard_amount); |
| push_counter("discard_throughput(mg/s)", discard_thpt); |
| push_counter("flush_amount(count)", flush_count); |
| push_counter("flush_time(ms)", flush_time_diff); |
| } |
| |
| prev_read_amount = cur_read_amount; |
| prev_write_amount = cur_write_amount; |
| prev_discard_amount = cur_discard_amount; |
| prev_flush_count = cur_flush_count; |
| prev_read_time = cur_read_time; |
| prev_write_time = cur_write_time; |
| prev_discard_time = cur_discard_time; |
| prev_flush_time = cur_flush_time; |
| } |
| |
| void SystemProbesParser::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 |
| TrackId track = context_->track_tracker->InternGlobalCounterTrack( |
| meminfo_strs_id_[key]); |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(mi.value()) * 1024., track); |
| } |
| |
| for (auto it = sys_stats.devfreq(); it; ++it) { |
| protos::pbzero::SysStats::DevfreqValue::Decoder vm(*it); |
| auto key = static_cast<base::StringView>(vm.key()); |
| // Append " Frequency" to align names with |
| // FtraceParser::ParseClockSetRate |
| base::StringView devfreq_subtitle("Frequency"); |
| base::StackString<255> counter_name( |
| "%.*s %.*s", int(key.size()), key.data(), int(devfreq_subtitle.size()), |
| devfreq_subtitle.data()); |
| StringId name = context_->storage->InternString(counter_name.string_view()); |
| TrackId track = context_->track_tracker->InternGlobalCounterTrack(name); |
| context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()), |
| track); |
| } |
| |
| uint32_t c = 0; |
| for (auto it = sys_stats.cpufreq_khz(); it; ++it, ++c) { |
| TrackId track = |
| context_->track_tracker->InternCpuCounterTrack(cpu_freq_id_, c); |
| context_->event_tracker->PushCounter(ts, static_cast<double>(*it), track); |
| } |
| |
| 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; |
| } |
| TrackId track = |
| context_->track_tracker->InternGlobalCounterTrack(vmstat_strs_id_[key]); |
| context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()), |
| track); |
| } |
| |
| 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; |
| } |
| |
| TrackId track = context_->track_tracker->InternCpuCounterTrack( |
| cpu_times_user_ns_id_, ct.cpu_id()); |
| context_->event_tracker->PushCounter(ts, static_cast<double>(ct.user_ns()), |
| track); |
| |
| track = context_->track_tracker->InternCpuCounterTrack( |
| cpu_times_user_nice_ns_id_, ct.cpu_id()); |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(ct.user_ice_ns()), track); |
| |
| track = context_->track_tracker->InternCpuCounterTrack( |
| cpu_times_system_mode_ns_id_, ct.cpu_id()); |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(ct.system_mode_ns()), track); |
| |
| track = context_->track_tracker->InternCpuCounterTrack( |
| cpu_times_idle_ns_id_, ct.cpu_id()); |
| context_->event_tracker->PushCounter(ts, static_cast<double>(ct.idle_ns()), |
| track); |
| |
| track = context_->track_tracker->InternCpuCounterTrack( |
| cpu_times_io_wait_ns_id_, ct.cpu_id()); |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(ct.io_wait_ns()), track); |
| |
| track = context_->track_tracker->InternCpuCounterTrack(cpu_times_irq_ns_id_, |
| ct.cpu_id()); |
| context_->event_tracker->PushCounter(ts, static_cast<double>(ct.irq_ns()), |
| track); |
| |
| track = context_->track_tracker->InternCpuCounterTrack( |
| cpu_times_softirq_ns_id_, ct.cpu_id()); |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(ct.softirq_ns()), track); |
| } |
| |
| for (auto it = sys_stats.num_irq(); it; ++it) { |
| protos::pbzero::SysStats::InterruptCount::Decoder ic(*it); |
| |
| TrackId track = context_->track_tracker->InternIrqCounterTrack( |
| num_irq_name_id_, ic.irq()); |
| context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()), |
| track); |
| } |
| |
| for (auto it = sys_stats.num_softirq(); it; ++it) { |
| protos::pbzero::SysStats::InterruptCount::Decoder ic(*it); |
| |
| TrackId track = context_->track_tracker->InternSoftirqCounterTrack( |
| num_softirq_name_id_, ic.irq()); |
| context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()), |
| track); |
| } |
| |
| if (sys_stats.has_num_forks()) { |
| TrackId track = |
| context_->track_tracker->InternGlobalCounterTrack(num_forks_name_id_); |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(sys_stats.num_forks()), track); |
| } |
| |
| if (sys_stats.has_num_irq_total()) { |
| TrackId track = context_->track_tracker->InternGlobalCounterTrack( |
| num_irq_total_name_id_); |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(sys_stats.num_irq_total()), track); |
| } |
| |
| if (sys_stats.has_num_softirq_total()) { |
| TrackId track = context_->track_tracker->InternGlobalCounterTrack( |
| num_softirq_total_name_id_); |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(sys_stats.num_softirq_total()), track); |
| } |
| |
| for (auto it = sys_stats.buddy_info(); it; ++it) { |
| protos::pbzero::SysStats::BuddyInfo::Decoder bi(*it); |
| int order = 0; |
| for (auto order_it = bi.order_pages(); order_it; ++order_it) { |
| std::string node = bi.node().ToStdString(); |
| std::string zone = bi.zone().ToStdString(); |
| uint32_t size_kb = |
| static_cast<uint32_t>(((1 << order) * page_size_) / 1024); |
| base::StackString<255> counter_name("mem.buddyinfo[%s][%s][%u kB]", |
| node.c_str(), zone.c_str(), size_kb); |
| StringId name = |
| context_->storage->InternString(counter_name.string_view()); |
| TrackId track = context_->track_tracker->InternGlobalCounterTrack(name); |
| context_->event_tracker->PushCounter(ts, static_cast<double>(*order_it), |
| track); |
| order++; |
| } |
| } |
| |
| for (auto it = sys_stats.disk_stat(); it; ++it) { |
| ParseDiskStats(ts, *it); |
| } |
| } |
| |
| void SystemProbesParser::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 (proc.has_nspid()) { |
| std::vector<uint32_t> nspid; |
| for (auto nspid_it = proc.nspid(); nspid_it; nspid_it++) { |
| nspid.emplace_back(static_cast<uint32_t>(*nspid_it)); |
| } |
| context_->process_tracker->UpdateNamespacedProcess(pid, std::move(nspid)); |
| } |
| |
| auto raw_cmdline = proc.cmdline(); |
| base::StringView argv0 = raw_cmdline ? *raw_cmdline : base::StringView(); |
| // Chrome child process overwrites /proc/self/cmdline and replaces all |
| // '\0' with ' '. This makes argv0 contain the full command line. Extract |
| // the actual argv0 if it's Chrome. |
| static const char kChromeBinary[] = "/chrome "; |
| auto pos = argv0.find(kChromeBinary); |
| if (pos != base::StringView::npos) { |
| argv0 = argv0.substr(0, pos + strlen(kChromeBinary) - 1); |
| } |
| |
| std::string cmdline_str; |
| for (auto cmdline_it = raw_cmdline; cmdline_it;) { |
| auto cmdline_part = *cmdline_it; |
| cmdline_str.append(cmdline_part.data, cmdline_part.size); |
| |
| if (++cmdline_it) |
| cmdline_str.append(" "); |
| } |
| base::StringView cmdline = base::StringView(cmdline_str); |
| UniquePid upid = context_->process_tracker->SetProcessMetadata( |
| pid, ppid, argv0, cmdline); |
| if (proc.has_uid()) { |
| context_->process_tracker->SetProcessUid( |
| upid, static_cast<uint32_t>(proc.uid())); |
| } |
| } |
| |
| 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 thread_name_id = context_->storage->InternString(thd.name()); |
| context_->process_tracker->UpdateThreadName( |
| tid, thread_name_id, ThreadNamePriority::kProcessTree); |
| } |
| |
| if (thd.has_nstid()) { |
| std::vector<uint32_t> nstid; |
| for (auto nstid_it = thd.nstid(); nstid_it; nstid_it++) { |
| nstid.emplace_back(static_cast<uint32_t>(*nstid_it)); |
| } |
| context_->process_tracker->UpdateNamespacedThread(tgid, tid, |
| std::move(nstid)); |
| } |
| } |
| } |
| |
| void SystemProbesParser::ParseProcessStats(int64_t ts, ConstBytes blob) { |
| using Process = protos::pbzero::ProcessStats::Process; |
| 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{}; |
| |
| protozero::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; |
| } |
| if (fld.id() == |
| protos::pbzero::ProcessStats::Process::kThreadsFieldNumber) { |
| ParseThreadStats(ts, pid, fld.as_bytes()); |
| continue; |
| } |
| if (fld.id() == protos::pbzero::ProcessStats::Process::kFdsFieldNumber) { |
| ParseProcessFds(ts, pid, fld.as_bytes()); |
| continue; |
| } |
| bool is_counter_field = fld.id() < proc_stats_process_names_.size() && |
| !proc_stats_process_names_[fld.id()].is_null(); |
| 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 { |
| // Chrome fields are processed by ChromeSystemProbesParser. |
| if (fld.id() == Process::kIsPeakRssResettableFieldNumber || |
| fld.id() == Process::kChromePrivateFootprintKbFieldNumber || |
| fld.id() == Process::kChromePrivateFootprintKbFieldNumber) { |
| continue; |
| } |
| 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] || field_id == |
| protos::pbzero::ProcessStats::Process:: |
| kIsPeakRssResettableFieldNumber) { |
| continue; |
| } |
| |
| // Lookup the interned string id from the field name using the |
| // pre-cached |proc_stats_process_names_| map. |
| const StringId& name = proc_stats_process_names_[field_id]; |
| UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid); |
| TrackId track = |
| context_->track_tracker->InternProcessCounterTrack(name, upid); |
| int64_t value = counter_values[field_id]; |
| context_->event_tracker->PushCounter(ts, static_cast<double>(value), |
| track); |
| } |
| } |
| } |
| |
| void SystemProbesParser::ParseThreadStats(int64_t, |
| uint32_t pid, |
| ConstBytes blob) { |
| protos::pbzero::ProcessStats::Thread::Decoder stats(blob.data, blob.size); |
| context_->process_tracker->UpdateThread(static_cast<uint32_t>(stats.tid()), |
| pid); |
| } |
| |
| void SystemProbesParser::ParseProcessFds(int64_t ts, |
| uint32_t pid, |
| ConstBytes blob) { |
| protos::pbzero::ProcessStats::FDInfo::Decoder fd_info(blob.data, blob.size); |
| |
| tables::FiledescriptorTable::Row row; |
| row.fd = static_cast<int64_t>(fd_info.fd()); |
| row.ts = ts; |
| row.path = context_->storage->InternString(fd_info.path()); |
| row.upid = context_->process_tracker->GetOrCreateProcess(pid); |
| |
| auto* fd_table = context_->storage->mutable_filedescriptor_table(); |
| fd_table->Insert(row); |
| } |
| |
| void SystemProbesParser::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(); |
| SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(context_); |
| Architecture arch = SyscallTable::ArchFromString(machine); |
| if (arch != kUnknown) { |
| syscall_tracker->SetArchitecture(arch); |
| } else { |
| PERFETTO_ELOG("Unknown architecture %s. Syscall traces will not work.", |
| machine.ToStdString().c_str()); |
| } |
| |
| SystemInfoTracker* system_info_tracker = |
| SystemInfoTracker::GetOrCreate(context_); |
| system_info_tracker->SetKernelVersion(utsname.sysname(), utsname.release()); |
| |
| StringPool::Id sysname_id = |
| context_->storage->InternString(utsname.sysname()); |
| StringPool::Id version_id = |
| context_->storage->InternString(utsname.version()); |
| StringPool::Id release_id = |
| context_->storage->InternString(utsname.release()); |
| StringPool::Id machine_id = |
| context_->storage->InternString(utsname.machine()); |
| |
| MetadataTracker* metadata = context_->metadata_tracker.get(); |
| metadata->SetMetadata(metadata::system_name, Variadic::String(sysname_id)); |
| metadata->SetMetadata(metadata::system_version, |
| Variadic::String(version_id)); |
| metadata->SetMetadata(metadata::system_release, |
| Variadic::String(release_id)); |
| metadata->SetMetadata(metadata::system_machine, |
| Variadic::String(machine_id)); |
| } |
| |
| if (packet.has_android_build_fingerprint()) { |
| context_->metadata_tracker->SetMetadata( |
| metadata::android_build_fingerprint, |
| Variadic::String(context_->storage->InternString( |
| packet.android_build_fingerprint()))); |
| } |
| |
| // If we have the SDK version in the trace directly just use that. |
| // Otherwise, try and parse it from the fingerprint. |
| std::optional<int64_t> opt_sdk_version; |
| if (packet.has_android_sdk_version()) { |
| opt_sdk_version = static_cast<int64_t>(packet.android_sdk_version()); |
| } else if (packet.has_android_build_fingerprint()) { |
| opt_sdk_version = FingerprintToSdkVersion( |
| packet.android_build_fingerprint().ToStdString()); |
| } |
| |
| if (opt_sdk_version) { |
| context_->metadata_tracker->SetMetadata( |
| metadata::android_sdk_version, Variadic::Integer(*opt_sdk_version)); |
| } |
| |
| int64_t hz = packet.hz(); |
| if (hz > 0) |
| ms_per_tick_ = 1000u / static_cast<uint64_t>(hz); |
| |
| page_size_ = packet.page_size(); |
| if (!page_size_) |
| page_size_ = 4096; |
| } |
| |
| void SystemProbesParser::ParseCpuInfo(ConstBytes blob) { |
| protos::pbzero::CpuInfo::Decoder packet(blob.data, blob.size); |
| uint32_t cluster_id = 0; |
| std::vector<uint32_t> last_cpu_freqs; |
| for (auto it = packet.cpus(); it; it++) { |
| protos::pbzero::CpuInfo::Cpu::Decoder cpu(*it); |
| tables::CpuTable::Row cpu_row; |
| if (cpu.has_processor()) { |
| cpu_row.processor = context_->storage->InternString(cpu.processor()); |
| } |
| std::vector<uint32_t> freqs; |
| for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) { |
| freqs.push_back(*freq_it); |
| } |
| |
| // Here we assume that cluster of CPUs are 'next' to each other. |
| if (freqs != last_cpu_freqs && !last_cpu_freqs.empty()) { |
| cluster_id++; |
| } |
| cpu_row.cluster_id = cluster_id; |
| |
| last_cpu_freqs = freqs; |
| tables::CpuTable::Id cpu_row_id = |
| context_->storage->mutable_cpu_table()->Insert(cpu_row).id; |
| |
| for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) { |
| uint32_t freq = *freq_it; |
| tables::CpuFreqTable::Row cpu_freq_row; |
| cpu_freq_row.cpu_id = cpu_row_id; |
| cpu_freq_row.freq = freq; |
| context_->storage->mutable_cpu_freq_table()->Insert(cpu_freq_row); |
| } |
| } |
| } |
| |
| } // namespace trace_processor |
| } // namespace perfetto |