Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "src/trace_processor/importers/proto/system_probes_parser.h" |
| 18 | |
| 19 | #include "perfetto/base/logging.h" |
| 20 | #include "perfetto/ext/traced/sys_stats_counters.h" |
| 21 | #include "perfetto/protozero/proto_decoder.h" |
| 22 | #include "src/trace_processor/event_tracker.h" |
Isabelle Taylor | 13a7585 | 2019-11-20 10:29:40 +0000 | [diff] [blame] | 23 | #include "src/trace_processor/metadata.h" |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 24 | #include "src/trace_processor/process_tracker.h" |
| 25 | #include "src/trace_processor/syscall_tracker.h" |
| 26 | #include "src/trace_processor/trace_processor_context.h" |
| 27 | |
| 28 | #include "protos/perfetto/trace/ps/process_stats.pbzero.h" |
| 29 | #include "protos/perfetto/trace/ps/process_tree.pbzero.h" |
| 30 | #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h" |
| 31 | #include "protos/perfetto/trace/system_info.pbzero.h" |
| 32 | |
| 33 | namespace perfetto { |
| 34 | namespace trace_processor { |
| 35 | |
| 36 | namespace { |
| 37 | // kthreadd is the parent process for all kernel threads and always has |
| 38 | // pid == 2 on Linux and Android. |
| 39 | const uint32_t kKthreaddPid = 2; |
| 40 | const char kKthreaddName[] = "kthreadd"; |
| 41 | } // namespace |
| 42 | |
Eric Seckler | 5fbcf6d | 2019-10-28 15:09:10 +0000 | [diff] [blame] | 43 | SystemProbesParser::SystemProbesParser(TraceProcessorContext* context) |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 44 | : context_(context), |
| 45 | utid_name_id_(context->storage->InternString("utid")), |
| 46 | num_forks_name_id_(context->storage->InternString("num_forks")), |
| 47 | num_irq_total_name_id_(context->storage->InternString("num_irq_total")), |
| 48 | num_softirq_total_name_id_( |
| 49 | context->storage->InternString("num_softirq_total")), |
| 50 | num_irq_name_id_(context->storage->InternString("num_irq")), |
| 51 | num_softirq_name_id_(context->storage->InternString("num_softirq")), |
| 52 | cpu_times_user_ns_id_( |
| 53 | context->storage->InternString("cpu.times.user_ns")), |
| 54 | cpu_times_user_nice_ns_id_( |
| 55 | context->storage->InternString("cpu.times.user_nice_ns")), |
| 56 | cpu_times_system_mode_ns_id_( |
| 57 | context->storage->InternString("cpu.times.system_mode_ns")), |
| 58 | cpu_times_idle_ns_id_( |
| 59 | context->storage->InternString("cpu.times.idle_ns")), |
| 60 | cpu_times_io_wait_ns_id_( |
| 61 | context->storage->InternString("cpu.times.io_wait_ns")), |
| 62 | cpu_times_irq_ns_id_(context->storage->InternString("cpu.times.irq_ns")), |
| 63 | cpu_times_softirq_ns_id_( |
| 64 | context->storage->InternString("cpu.times.softirq_ns")), |
| 65 | oom_score_adj_id_(context->storage->InternString("oom_score_adj")) { |
| 66 | for (const auto& name : BuildMeminfoCounterNames()) { |
| 67 | meminfo_strs_id_.emplace_back(context->storage->InternString(name)); |
| 68 | } |
| 69 | for (const auto& name : BuildVmstatCounterNames()) { |
| 70 | vmstat_strs_id_.emplace_back(context->storage->InternString(name)); |
| 71 | } |
| 72 | |
| 73 | using ProcessStats = protos::pbzero::ProcessStats; |
| 74 | proc_stats_process_names_[ProcessStats::Process::kVmSizeKbFieldNumber] = |
| 75 | context->storage->InternString("mem.virt"); |
| 76 | proc_stats_process_names_[ProcessStats::Process::kVmRssKbFieldNumber] = |
| 77 | context->storage->InternString("mem.rss"); |
| 78 | proc_stats_process_names_[ProcessStats::Process::kRssAnonKbFieldNumber] = |
| 79 | context->storage->InternString("mem.rss.anon"); |
| 80 | proc_stats_process_names_[ProcessStats::Process::kRssFileKbFieldNumber] = |
| 81 | context->storage->InternString("mem.rss.file"); |
| 82 | proc_stats_process_names_[ProcessStats::Process::kRssShmemKbFieldNumber] = |
| 83 | context->storage->InternString("mem.rss.shmem"); |
| 84 | proc_stats_process_names_[ProcessStats::Process::kVmSwapKbFieldNumber] = |
| 85 | context->storage->InternString("mem.swap"); |
| 86 | proc_stats_process_names_[ProcessStats::Process::kVmLockedKbFieldNumber] = |
| 87 | context->storage->InternString("mem.locked"); |
| 88 | proc_stats_process_names_[ProcessStats::Process::kVmHwmKbFieldNumber] = |
| 89 | context->storage->InternString("mem.rss.watermark"); |
| 90 | proc_stats_process_names_[ProcessStats::Process::kOomScoreAdjFieldNumber] = |
| 91 | oom_score_adj_id_; |
| 92 | } |
| 93 | |
Eric Seckler | 5fbcf6d | 2019-10-28 15:09:10 +0000 | [diff] [blame] | 94 | void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) { |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 95 | protos::pbzero::SysStats::Decoder sys_stats(blob.data, blob.size); |
| 96 | |
| 97 | for (auto it = sys_stats.meminfo(); it; ++it) { |
| 98 | protos::pbzero::SysStats::MeminfoValue::Decoder mi(*it); |
| 99 | auto key = static_cast<size_t>(mi.key()); |
| 100 | if (PERFETTO_UNLIKELY(key >= meminfo_strs_id_.size())) { |
| 101 | PERFETTO_ELOG("MemInfo key %zu is not recognized.", key); |
| 102 | context_->storage->IncrementStats(stats::meminfo_unknown_keys); |
| 103 | continue; |
| 104 | } |
| 105 | // /proc/meminfo counters are in kB, convert to bytes |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 106 | TrackId track = context_->track_tracker->InternGlobalCounterTrack( |
| 107 | meminfo_strs_id_[key]); |
| 108 | context_->event_tracker->PushCounter(ts, mi.value() * 1024L, track); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 109 | } |
| 110 | |
| 111 | for (auto it = sys_stats.vmstat(); it; ++it) { |
| 112 | protos::pbzero::SysStats::VmstatValue::Decoder vm(*it); |
| 113 | auto key = static_cast<size_t>(vm.key()); |
| 114 | if (PERFETTO_UNLIKELY(key >= vmstat_strs_id_.size())) { |
| 115 | PERFETTO_ELOG("VmStat key %zu is not recognized.", key); |
| 116 | context_->storage->IncrementStats(stats::vmstat_unknown_keys); |
| 117 | continue; |
| 118 | } |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 119 | TrackId track = |
| 120 | context_->track_tracker->InternGlobalCounterTrack(vmstat_strs_id_[key]); |
| 121 | context_->event_tracker->PushCounter(ts, vm.value(), track); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | for (auto it = sys_stats.cpu_stat(); it; ++it) { |
| 125 | protos::pbzero::SysStats::CpuTimes::Decoder ct(*it); |
| 126 | if (PERFETTO_UNLIKELY(!ct.has_cpu_id())) { |
| 127 | PERFETTO_ELOG("CPU field not found in CpuTimes"); |
| 128 | context_->storage->IncrementStats(stats::invalid_cpu_times); |
| 129 | continue; |
| 130 | } |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 131 | |
| 132 | TrackId track = context_->track_tracker->InternCpuCounterTrack( |
| 133 | cpu_times_user_ns_id_, ct.cpu_id()); |
| 134 | context_->event_tracker->PushCounter(ts, ct.user_ns(), track); |
| 135 | |
| 136 | track = context_->track_tracker->InternCpuCounterTrack( |
| 137 | cpu_times_user_nice_ns_id_, ct.cpu_id()); |
| 138 | context_->event_tracker->PushCounter(ts, ct.user_ice_ns(), track); |
| 139 | |
| 140 | track = context_->track_tracker->InternCpuCounterTrack( |
| 141 | cpu_times_system_mode_ns_id_, ct.cpu_id()); |
| 142 | context_->event_tracker->PushCounter(ts, ct.system_mode_ns(), track); |
| 143 | |
| 144 | track = context_->track_tracker->InternCpuCounterTrack( |
| 145 | cpu_times_idle_ns_id_, ct.cpu_id()); |
| 146 | context_->event_tracker->PushCounter(ts, ct.idle_ns(), track); |
| 147 | |
| 148 | track = context_->track_tracker->InternCpuCounterTrack( |
| 149 | cpu_times_io_wait_ns_id_, ct.cpu_id()); |
| 150 | context_->event_tracker->PushCounter(ts, ct.io_wait_ns(), track); |
| 151 | |
| 152 | track = context_->track_tracker->InternCpuCounterTrack(cpu_times_irq_ns_id_, |
| 153 | ct.cpu_id()); |
| 154 | context_->event_tracker->PushCounter(ts, ct.irq_ns(), track); |
| 155 | |
| 156 | track = context_->track_tracker->InternCpuCounterTrack( |
| 157 | cpu_times_softirq_ns_id_, ct.cpu_id()); |
| 158 | context_->event_tracker->PushCounter(ts, ct.softirq_ns(), track); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | for (auto it = sys_stats.num_irq(); it; ++it) { |
| 162 | protos::pbzero::SysStats::InterruptCount::Decoder ic(*it); |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 163 | |
| 164 | TrackId track = context_->track_tracker->InternIrqCounterTrack( |
| 165 | num_irq_name_id_, ic.irq()); |
| 166 | context_->event_tracker->PushCounter(ts, ic.count(), track); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | for (auto it = sys_stats.num_softirq(); it; ++it) { |
| 170 | protos::pbzero::SysStats::InterruptCount::Decoder ic(*it); |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 171 | |
| 172 | TrackId track = context_->track_tracker->InternSoftirqCounterTrack( |
| 173 | num_softirq_name_id_, ic.irq()); |
| 174 | context_->event_tracker->PushCounter(ts, ic.count(), track); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 175 | } |
| 176 | |
| 177 | if (sys_stats.has_num_forks()) { |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 178 | TrackId track = |
| 179 | context_->track_tracker->InternGlobalCounterTrack(num_forks_name_id_); |
| 180 | context_->event_tracker->PushCounter(ts, sys_stats.num_forks(), track); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | if (sys_stats.has_num_irq_total()) { |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 184 | TrackId track = context_->track_tracker->InternGlobalCounterTrack( |
| 185 | num_irq_total_name_id_); |
| 186 | context_->event_tracker->PushCounter(ts, sys_stats.num_irq_total(), track); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 187 | } |
| 188 | |
| 189 | if (sys_stats.has_num_softirq_total()) { |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 190 | TrackId track = context_->track_tracker->InternGlobalCounterTrack( |
| 191 | num_softirq_total_name_id_); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 192 | context_->event_tracker->PushCounter(ts, sys_stats.num_softirq_total(), |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 193 | track); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 194 | } |
| 195 | } |
| 196 | |
Eric Seckler | 5fbcf6d | 2019-10-28 15:09:10 +0000 | [diff] [blame] | 197 | void SystemProbesParser::ParseProcessTree(ConstBytes blob) { |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 198 | protos::pbzero::ProcessTree::Decoder ps(blob.data, blob.size); |
| 199 | |
| 200 | for (auto it = ps.processes(); it; ++it) { |
| 201 | protos::pbzero::ProcessTree::Process::Decoder proc(*it); |
| 202 | if (!proc.has_cmdline()) |
| 203 | continue; |
| 204 | auto pid = static_cast<uint32_t>(proc.pid()); |
| 205 | auto ppid = static_cast<uint32_t>(proc.ppid()); |
| 206 | |
| 207 | // If the parent pid is kthreadd's pid, even though this pid is of a |
| 208 | // "process", we want to treat it as being a child thread of kthreadd. |
| 209 | if (ppid == kKthreaddPid) { |
| 210 | context_->process_tracker->SetProcessMetadata(kKthreaddPid, base::nullopt, |
| 211 | kKthreaddName); |
| 212 | context_->process_tracker->UpdateThread(pid, kKthreaddPid); |
| 213 | } else { |
| 214 | auto args = proc.cmdline(); |
| 215 | base::StringView argv0 = args ? *args : base::StringView(); |
Ioannis Ilkos | fd554bc | 2019-11-16 01:14:57 +0000 | [diff] [blame] | 216 | UniquePid upid = |
| 217 | context_->process_tracker->SetProcessMetadata(pid, ppid, argv0); |
| 218 | if (proc.has_uid()) { |
| 219 | context_->process_tracker->SetProcessUid( |
| 220 | upid, static_cast<uint32_t>(proc.uid())); |
| 221 | } |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 222 | } |
| 223 | } |
| 224 | |
| 225 | for (auto it = ps.threads(); it; ++it) { |
| 226 | protos::pbzero::ProcessTree::Thread::Decoder thd(*it); |
| 227 | auto tid = static_cast<uint32_t>(thd.tid()); |
| 228 | auto tgid = static_cast<uint32_t>(thd.tgid()); |
| 229 | context_->process_tracker->UpdateThread(tid, tgid); |
| 230 | |
| 231 | if (thd.has_name()) { |
| 232 | StringId threadNameId = context_->storage->InternString(thd.name()); |
| 233 | context_->process_tracker->UpdateThreadName(tid, threadNameId); |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | |
Eric Seckler | 5fbcf6d | 2019-10-28 15:09:10 +0000 | [diff] [blame] | 238 | void SystemProbesParser::ParseProcessStats(int64_t ts, ConstBytes blob) { |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 239 | protos::pbzero::ProcessStats::Decoder stats(blob.data, blob.size); |
| 240 | const auto kOomScoreAdjFieldNumber = |
| 241 | protos::pbzero::ProcessStats::Process::kOomScoreAdjFieldNumber; |
| 242 | for (auto it = stats.processes(); it; ++it) { |
| 243 | // Maps a process counter field it to its value. |
| 244 | // E.g., 4 := 1024 -> "mem.rss.anon" := 1024. |
| 245 | std::array<int64_t, kProcStatsProcessSize> counter_values{}; |
| 246 | std::array<bool, kProcStatsProcessSize> has_counter{}; |
| 247 | |
| 248 | protozero::ProtoDecoder proc(*it); |
| 249 | uint32_t pid = 0; |
| 250 | for (auto fld = proc.ReadField(); fld.valid(); fld = proc.ReadField()) { |
| 251 | if (fld.id() == protos::pbzero::ProcessStats::Process::kPidFieldNumber) { |
| 252 | pid = fld.as_uint32(); |
| 253 | continue; |
| 254 | } |
| 255 | bool is_counter_field = fld.id() < proc_stats_process_names_.size() && |
| 256 | proc_stats_process_names_[fld.id()] != 0; |
| 257 | if (is_counter_field) { |
| 258 | // Memory counters are in KB, keep values in bytes in the trace |
| 259 | // processor. |
| 260 | counter_values[fld.id()] = fld.id() == kOomScoreAdjFieldNumber |
| 261 | ? fld.as_int64() |
| 262 | : fld.as_int64() * 1024; |
| 263 | has_counter[fld.id()] = true; |
| 264 | } else { |
| 265 | context_->storage->IncrementStats(stats::proc_stat_unknown_counters); |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | // Skip field_id 0 (invalid) and 1 (pid). |
| 270 | for (size_t field_id = 2; field_id < counter_values.size(); field_id++) { |
| 271 | if (!has_counter[field_id]) |
| 272 | continue; |
| 273 | |
| 274 | // Lookup the interned string id from the field name using the |
| 275 | // pre-cached |proc_stats_process_names_| map. |
| 276 | StringId name = proc_stats_process_names_[field_id]; |
| 277 | int64_t value = counter_values[field_id]; |
| 278 | UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid); |
Lalit Maganti | 809b2f9 | 2019-11-07 13:27:26 +0000 | [diff] [blame] | 279 | TrackId track = |
| 280 | context_->track_tracker->InternProcessCounterTrack(name, upid); |
| 281 | context_->event_tracker->PushCounter(ts, value, track); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 282 | } |
| 283 | } |
| 284 | } |
| 285 | |
Eric Seckler | 5fbcf6d | 2019-10-28 15:09:10 +0000 | [diff] [blame] | 286 | void SystemProbesParser::ParseSystemInfo(ConstBytes blob) { |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 287 | protos::pbzero::SystemInfo::Decoder packet(blob.data, blob.size); |
| 288 | if (packet.has_utsname()) { |
| 289 | ConstBytes utsname_blob = packet.utsname(); |
| 290 | protos::pbzero::Utsname::Decoder utsname(utsname_blob.data, |
| 291 | utsname_blob.size); |
| 292 | base::StringView machine = utsname.machine(); |
| 293 | if (machine == "aarch64" || machine == "armv8l") { |
| 294 | context_->syscall_tracker->SetArchitecture(kAarch64); |
| 295 | } else if (machine == "x86_64") { |
| 296 | context_->syscall_tracker->SetArchitecture(kX86_64); |
| 297 | } else { |
| 298 | PERFETTO_ELOG("Unknown architecture %s", machine.ToStdString().c_str()); |
| 299 | } |
Isabelle Taylor | 13a7585 | 2019-11-20 10:29:40 +0000 | [diff] [blame] | 300 | |
| 301 | StringPool::Id sysname_id = |
| 302 | context_->storage->InternString(utsname.sysname()); |
| 303 | StringPool::Id version_id = |
| 304 | context_->storage->InternString(utsname.version()); |
| 305 | StringPool::Id release_id = |
| 306 | context_->storage->InternString(utsname.release()); |
| 307 | StringPool::Id machine_id = |
| 308 | context_->storage->InternString(utsname.machine()); |
| 309 | |
| 310 | context_->storage->SetMetadata(metadata::system_name, |
| 311 | Variadic::String(sysname_id)); |
| 312 | context_->storage->SetMetadata(metadata::system_version, |
| 313 | Variadic::String(version_id)); |
| 314 | context_->storage->SetMetadata(metadata::system_release, |
| 315 | Variadic::String(release_id)); |
| 316 | context_->storage->SetMetadata(metadata::system_machine, |
| 317 | Variadic::String(machine_id)); |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 318 | } |
Ioannis Ilkos | 2f875b2 | 2019-11-27 19:26:02 +0000 | [diff] [blame] | 319 | |
| 320 | if (packet.has_android_build_fingerprint()) { |
| 321 | context_->storage->SetMetadata( |
| 322 | metadata::android_build_fingerprint, |
| 323 | Variadic::String(context_->storage->InternString( |
| 324 | packet.android_build_fingerprint()))); |
| 325 | } |
Eric Seckler | ab0604c | 2019-10-25 10:12:07 +0100 | [diff] [blame] | 326 | } |
| 327 | |
| 328 | } // namespace trace_processor |
| 329 | } // namespace perfetto |