• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/base/string_utils.h"
21 #include "perfetto/ext/base/string_view.h"
22 #include "perfetto/ext/traced/sys_stats_counters.h"
23 #include "perfetto/protozero/proto_decoder.h"
24 #include "src/trace_processor/importers/common/clock_tracker.h"
25 #include "src/trace_processor/importers/common/cpu_tracker.h"
26 #include "src/trace_processor/importers/common/event_tracker.h"
27 #include "src/trace_processor/importers/common/metadata_tracker.h"
28 #include "src/trace_processor/importers/common/process_tracker.h"
29 #include "src/trace_processor/importers/common/system_info_tracker.h"
30 #include "src/trace_processor/importers/syscalls/syscall_tracker.h"
31 #include "src/trace_processor/storage/metadata.h"
32 #include "src/trace_processor/types/trace_processor_context.h"
33 
34 #include "protos/perfetto/common/builtin_clock.pbzero.h"
35 #include "protos/perfetto/trace/ps/process_stats.pbzero.h"
36 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
37 #include "protos/perfetto/trace/system_info.pbzero.h"
38 #include "protos/perfetto/trace/system_info/cpu_info.pbzero.h"
39 
40 namespace {
41 
IsSupportedDiskStatDevice(const std::string & device_name)42 bool IsSupportedDiskStatDevice(const std::string& device_name) {
43   return device_name == "sda";  // Primary SCSI disk device name
44 }
45 
46 }  // namespace
47 
48 namespace perfetto {
49 namespace trace_processor {
50 
51 namespace {
52 
VersionStringToSdkVersion(const std::string & version)53 std::optional<int> VersionStringToSdkVersion(const std::string& version) {
54   // TODO(lalitm): remove this when the SDK version polling saturates
55   // S/T traces in practice.
56   if (base::StartsWith(version, "T") || base::StartsWith(version, "S")) {
57     return 31;
58   }
59 
60   // Documentation for this mapping can be found at
61   // https://source.android.com/compatibility/cdd.
62   if (version == "12") {
63     return 31;
64   } else if (version == "11") {
65     return 30;
66   } else if (version == "10") {
67     return 29;
68   } else if (version == "9") {
69     return 28;
70   } else if (version == "8.1") {
71     return 27;
72   } else if (version == "8.0") {
73     return 26;
74   } else if (version == "7.1") {
75     return 25;
76   } else if (version == "7.0") {
77     return 24;
78   } else if (version == "6.0") {
79     return 23;
80   } else if (version == "5.1" || version == "5.1.1") {
81     return 22;
82   } else if (version == "5.0" || version == "5.0.1" || version == "5.0.2") {
83     return 21;
84   }
85   // If we reached this point, we don't know how to parse this version
86   // so just return null.
87   return std::nullopt;
88 }
89 
FingerprintToSdkVersion(const std::string & fingerprint)90 std::optional<int> FingerprintToSdkVersion(const std::string& fingerprint) {
91   // Try to parse the SDK version from the fingerprint.
92   // Examples of fingerprints:
93   // google/shamu/shamu:7.0/NBD92F/3753956:userdebug/dev-keys
94   // google/coral/coral:12/SP1A.210812.015/7679548:userdebug/dev-keys
95   size_t colon = fingerprint.find(':');
96   if (colon == std::string::npos)
97     return std::nullopt;
98 
99   size_t slash = fingerprint.find('/', colon);
100   if (slash == std::string::npos)
101     return std::nullopt;
102 
103   std::string version = fingerprint.substr(colon + 1, slash - (colon + 1));
104   return VersionStringToSdkVersion(version);
105 }
106 }  // namespace
107 
SystemProbesParser(TraceProcessorContext * context)108 SystemProbesParser::SystemProbesParser(TraceProcessorContext* context)
109     : context_(context),
110       utid_name_id_(context->storage->InternString("utid")),
111       ns_unit_id_(context->storage->InternString("ns")),
112       bytes_unit_id_(context->storage->InternString("bytes")),
113       available_chunks_unit_id_(
114           context->storage->InternString("available chunks")),
115       num_forks_name_id_(context->storage->InternString("num_forks")),
116       num_irq_total_name_id_(context->storage->InternString("num_irq_total")),
117       num_softirq_total_name_id_(
118           context->storage->InternString("num_softirq_total")),
119       num_irq_name_id_(context->storage->InternString("num_irq")),
120       num_softirq_name_id_(context->storage->InternString("num_softirq")),
121       cpu_times_user_ns_id_(
122           context->storage->InternString("cpu.times.user_ns")),
123       cpu_times_user_nice_ns_id_(
124           context->storage->InternString("cpu.times.user_nice_ns")),
125       cpu_times_system_mode_ns_id_(
126           context->storage->InternString("cpu.times.system_mode_ns")),
127       cpu_times_idle_ns_id_(
128           context->storage->InternString("cpu.times.idle_ns")),
129       cpu_times_io_wait_ns_id_(
130           context->storage->InternString("cpu.times.io_wait_ns")),
131       cpu_times_irq_ns_id_(context->storage->InternString("cpu.times.irq_ns")),
132       cpu_times_softirq_ns_id_(
133           context->storage->InternString("cpu.times.softirq_ns")),
134       oom_score_adj_id_(context->storage->InternString("oom_score_adj")),
135       cpu_freq_id_(context_->storage->InternString("cpufreq")) {
136   for (const auto& name : BuildMeminfoCounterNames()) {
137     meminfo_strs_id_.emplace_back(context->storage->InternString(name));
138   }
139   for (const auto& name : BuildVmstatCounterNames()) {
140     vmstat_strs_id_.emplace_back(context->storage->InternString(name));
141   }
142 
143   using ProcessStats = protos::pbzero::ProcessStats;
144   proc_stats_process_names_[ProcessStats::Process::kVmSizeKbFieldNumber] =
145       context->storage->InternString("mem.virt");
146   proc_stats_process_names_[ProcessStats::Process::kVmRssKbFieldNumber] =
147       context->storage->InternString("mem.rss");
148   proc_stats_process_names_[ProcessStats::Process::kRssAnonKbFieldNumber] =
149       context->storage->InternString("mem.rss.anon");
150   proc_stats_process_names_[ProcessStats::Process::kRssFileKbFieldNumber] =
151       context->storage->InternString("mem.rss.file");
152   proc_stats_process_names_[ProcessStats::Process::kRssShmemKbFieldNumber] =
153       context->storage->InternString("mem.rss.shmem");
154   proc_stats_process_names_[ProcessStats::Process::kVmSwapKbFieldNumber] =
155       context->storage->InternString("mem.swap");
156   proc_stats_process_names_[ProcessStats::Process::kVmLockedKbFieldNumber] =
157       context->storage->InternString("mem.locked");
158   proc_stats_process_names_[ProcessStats::Process::kVmHwmKbFieldNumber] =
159       context->storage->InternString("mem.rss.watermark");
160   proc_stats_process_names_[ProcessStats::Process::kOomScoreAdjFieldNumber] =
161       oom_score_adj_id_;
162   proc_stats_process_names_[ProcessStats::Process::kSmrRssKbFieldNumber] =
163       context->storage->InternString("mem.smaps.rss");
164   proc_stats_process_names_[ProcessStats::Process::kSmrPssKbFieldNumber] =
165       context->storage->InternString("mem.smaps.pss");
166   proc_stats_process_names_[ProcessStats::Process::kSmrPssAnonKbFieldNumber] =
167       context->storage->InternString("mem.smaps.pss.anon");
168   proc_stats_process_names_[ProcessStats::Process::kSmrPssFileKbFieldNumber] =
169       context->storage->InternString("mem.smaps.pss.file");
170   proc_stats_process_names_[ProcessStats::Process::kSmrPssShmemKbFieldNumber] =
171       context->storage->InternString("mem.smaps.pss.shmem");
172   proc_stats_process_names_[ProcessStats::Process::kSmrSwapPssKbFieldNumber] =
173       context->storage->InternString("mem.smaps.swap.pss");
174   proc_stats_process_names_
175       [ProcessStats::Process::kRuntimeUserModeFieldNumber] =
176           context->storage->InternString("runtime.user_ns");
177   proc_stats_process_names_
178       [ProcessStats::Process::kRuntimeKernelModeFieldNumber] =
179           context->storage->InternString("runtime.kernel_ns");
180 
181   using PsiResource = protos::pbzero::SysStats::PsiSample::PsiResource;
182   sys_stats_psi_resource_names_[PsiResource::PSI_RESOURCE_UNSPECIFIED] =
183       context->storage->InternString("psi.resource.unspecified");
184   sys_stats_psi_resource_names_[PsiResource::PSI_RESOURCE_CPU_SOME] =
185       context->storage->InternString("psi.cpu.some");
186   sys_stats_psi_resource_names_[PsiResource::PSI_RESOURCE_CPU_FULL] =
187       context->storage->InternString("psi.cpu.full");
188   sys_stats_psi_resource_names_[PsiResource::PSI_RESOURCE_IO_SOME] =
189       context->storage->InternString("psi.io.some");
190   sys_stats_psi_resource_names_[PsiResource::PSI_RESOURCE_IO_FULL] =
191       context->storage->InternString("psi.io.full");
192   sys_stats_psi_resource_names_[PsiResource::PSI_RESOURCE_MEMORY_SOME] =
193       context->storage->InternString("psi.mem.some");
194   sys_stats_psi_resource_names_[PsiResource::PSI_RESOURCE_MEMORY_FULL] =
195       context->storage->InternString("psi.mem.full");
196 }
197 
ParseDiskStats(int64_t ts,ConstBytes blob)198 void SystemProbesParser::ParseDiskStats(int64_t ts, ConstBytes blob) {
199   protos::pbzero::SysStats::DiskStat::Decoder ds(blob.data, blob.size);
200   static constexpr double SECTORS_PER_MB = 2048.0;
201   static constexpr double MS_PER_SEC = 1000.0;
202   std::string device_name = ds.device_name().ToStdString();
203   if (!IsSupportedDiskStatDevice(device_name)) {
204     return;
205   }
206 
207   base::StackString<512> tag_prefix("diskstat.[%s]", device_name.c_str());
208   auto push_counter = [this, ts, tag_prefix](const char* counter_name,
209                                              double value) {
210     base::StackString<512> track_name("%s.%s", tag_prefix.c_str(),
211                                       counter_name);
212     StringId string_id = context_->storage->InternString(track_name.c_str());
213     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
214         TrackTracker::Group::kIo, string_id);
215     context_->event_tracker->PushCounter(ts, value, track);
216   };
217 
218   // TODO(rsavitski): with the UI now supporting rate mode for counter tracks,
219   // this is likely redundant.
220   auto calculate_throughput = [](double amount, int64_t diff) {
221     return diff == 0 ? 0 : amount * MS_PER_SEC / static_cast<double>(diff);
222   };
223 
224   int64_t cur_read_amount = static_cast<int64_t>(ds.read_sectors());
225   int64_t cur_write_amount = static_cast<int64_t>(ds.write_sectors());
226   int64_t cur_discard_amount = static_cast<int64_t>(ds.discard_sectors());
227   int64_t cur_flush_count = static_cast<int64_t>(ds.flush_count());
228   int64_t cur_read_time = static_cast<int64_t>(ds.read_time_ms());
229   int64_t cur_write_time = static_cast<int64_t>(ds.write_time_ms());
230   int64_t cur_discard_time = static_cast<int64_t>(ds.discard_time_ms());
231   int64_t cur_flush_time = static_cast<int64_t>(ds.flush_time_ms());
232 
233   if (prev_read_amount != -1) {
234     double read_amount =
235         static_cast<double>(cur_read_amount - prev_read_amount) /
236         SECTORS_PER_MB;
237     double write_amount =
238         static_cast<double>(cur_write_amount - prev_write_amount) /
239         SECTORS_PER_MB;
240     double discard_amount =
241         static_cast<double>(cur_discard_amount - prev_discard_amount) /
242         SECTORS_PER_MB;
243     double flush_count =
244         static_cast<double>(cur_flush_count - prev_flush_count);
245     int64_t read_time_diff = cur_read_time - prev_read_time;
246     int64_t write_time_diff = cur_write_time - prev_write_time;
247     int64_t discard_time_diff = cur_discard_time - prev_discard_time;
248     double flush_time_diff =
249         static_cast<double>(cur_flush_time - prev_flush_time);
250 
251     double read_thpt = calculate_throughput(read_amount, read_time_diff);
252     double write_thpt = calculate_throughput(write_amount, write_time_diff);
253     double discard_thpt =
254         calculate_throughput(discard_amount, discard_time_diff);
255 
256     push_counter("read_amount(mg)", read_amount);
257     push_counter("read_throughput(mg/s)", read_thpt);
258     push_counter("write_amount(mg)", write_amount);
259     push_counter("write_throughput(mg/s)", write_thpt);
260     push_counter("discard_amount(mg)", discard_amount);
261     push_counter("discard_throughput(mg/s)", discard_thpt);
262     push_counter("flush_amount(count)", flush_count);
263     push_counter("flush_time(ms)", flush_time_diff);
264   }
265 
266   prev_read_amount = cur_read_amount;
267   prev_write_amount = cur_write_amount;
268   prev_discard_amount = cur_discard_amount;
269   prev_flush_count = cur_flush_count;
270   prev_read_time = cur_read_time;
271   prev_write_time = cur_write_time;
272   prev_discard_time = cur_discard_time;
273   prev_flush_time = cur_flush_time;
274 }
275 
ParseSysStats(int64_t ts,ConstBytes blob)276 void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) {
277   protos::pbzero::SysStats::Decoder sys_stats(blob.data, blob.size);
278 
279   for (auto it = sys_stats.meminfo(); it; ++it) {
280     protos::pbzero::SysStats::MeminfoValue::Decoder mi(*it);
281     auto key = static_cast<size_t>(mi.key());
282     if (PERFETTO_UNLIKELY(key >= meminfo_strs_id_.size())) {
283       PERFETTO_ELOG("MemInfo key %zu is not recognized.", key);
284       context_->storage->IncrementStats(stats::meminfo_unknown_keys);
285       continue;
286     }
287     // /proc/meminfo counters are in kB, convert to bytes
288     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
289         TrackTracker::Group::kMemory, meminfo_strs_id_[key], {},
290         bytes_unit_id_);
291     context_->event_tracker->PushCounter(
292         ts, static_cast<double>(mi.value()) * 1024, track);
293   }
294 
295   for (auto it = sys_stats.devfreq(); it; ++it) {
296     protos::pbzero::SysStats::DevfreqValue::Decoder vm(*it);
297     auto key = static_cast<base::StringView>(vm.key());
298     // Append " Frequency" to align names with
299     // FtraceParser::ParseClockSetRate
300     base::StringView devfreq_subtitle("Frequency");
301     base::StackString<255> counter_name(
302         "%.*s %.*s", int(key.size()), key.data(), int(devfreq_subtitle.size()),
303         devfreq_subtitle.data());
304     StringId name = context_->storage->InternString(counter_name.string_view());
305     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
306         TrackTracker::Group::kClockFrequency, name);
307     context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()),
308                                          track);
309   }
310 
311   uint32_t c = 0;
312   for (auto it = sys_stats.cpufreq_khz(); it; ++it, ++c) {
313     TrackId track =
314         context_->track_tracker->InternCpuCounterTrack(cpu_freq_id_, c);
315     context_->event_tracker->PushCounter(ts, static_cast<double>(*it), track);
316   }
317 
318   for (auto it = sys_stats.vmstat(); it; ++it) {
319     protos::pbzero::SysStats::VmstatValue::Decoder vm(*it);
320     auto key = static_cast<size_t>(vm.key());
321     if (PERFETTO_UNLIKELY(key >= vmstat_strs_id_.size())) {
322       PERFETTO_ELOG("VmStat key %zu is not recognized.", key);
323       context_->storage->IncrementStats(stats::vmstat_unknown_keys);
324       continue;
325     }
326     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
327         TrackTracker::Group::kMemory, vmstat_strs_id_[key]);
328     context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()),
329                                          track);
330   }
331 
332   for (auto it = sys_stats.cpu_stat(); it; ++it) {
333     protos::pbzero::SysStats::CpuTimes::Decoder ct(*it);
334     if (PERFETTO_UNLIKELY(!ct.has_cpu_id())) {
335       PERFETTO_ELOG("CPU field not found in CpuTimes");
336       context_->storage->IncrementStats(stats::invalid_cpu_times);
337       continue;
338     }
339 
340     TrackId track = context_->track_tracker->InternCpuCounterTrack(
341         cpu_times_user_ns_id_, ct.cpu_id());
342     context_->event_tracker->PushCounter(ts, static_cast<double>(ct.user_ns()),
343                                          track);
344 
345     track = context_->track_tracker->InternCpuCounterTrack(
346         cpu_times_user_nice_ns_id_, ct.cpu_id());
347     context_->event_tracker->PushCounter(
348         ts, static_cast<double>(ct.user_nice_ns()), track);
349 
350     track = context_->track_tracker->InternCpuCounterTrack(
351         cpu_times_system_mode_ns_id_, ct.cpu_id());
352     context_->event_tracker->PushCounter(
353         ts, static_cast<double>(ct.system_mode_ns()), track);
354 
355     track = context_->track_tracker->InternCpuCounterTrack(
356         cpu_times_idle_ns_id_, ct.cpu_id());
357     context_->event_tracker->PushCounter(ts, static_cast<double>(ct.idle_ns()),
358                                          track);
359 
360     track = context_->track_tracker->InternCpuCounterTrack(
361         cpu_times_io_wait_ns_id_, ct.cpu_id());
362     context_->event_tracker->PushCounter(
363         ts, static_cast<double>(ct.io_wait_ns()), track);
364 
365     track = context_->track_tracker->InternCpuCounterTrack(cpu_times_irq_ns_id_,
366                                                            ct.cpu_id());
367     context_->event_tracker->PushCounter(ts, static_cast<double>(ct.irq_ns()),
368                                          track);
369 
370     track = context_->track_tracker->InternCpuCounterTrack(
371         cpu_times_softirq_ns_id_, ct.cpu_id());
372     context_->event_tracker->PushCounter(
373         ts, static_cast<double>(ct.softirq_ns()), track);
374   }
375 
376   for (auto it = sys_stats.num_irq(); it; ++it) {
377     protos::pbzero::SysStats::InterruptCount::Decoder ic(*it);
378 
379     TrackId track = context_->track_tracker->InternIrqCounterTrack(
380         num_irq_name_id_, ic.irq());
381     context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()),
382                                          track);
383   }
384 
385   for (auto it = sys_stats.num_softirq(); it; ++it) {
386     protos::pbzero::SysStats::InterruptCount::Decoder ic(*it);
387 
388     TrackId track = context_->track_tracker->InternSoftirqCounterTrack(
389         num_softirq_name_id_, ic.irq());
390     context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()),
391                                          track);
392   }
393 
394   if (sys_stats.has_num_forks()) {
395     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
396         TrackTracker::Group::kDeviceState, num_forks_name_id_);
397     context_->event_tracker->PushCounter(
398         ts, static_cast<double>(sys_stats.num_forks()), track);
399   }
400 
401   if (sys_stats.has_num_irq_total()) {
402     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
403         TrackTracker::Group::kDeviceState, num_irq_total_name_id_);
404     context_->event_tracker->PushCounter(
405         ts, static_cast<double>(sys_stats.num_irq_total()), track);
406   }
407 
408   if (sys_stats.has_num_softirq_total()) {
409     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
410         TrackTracker::Group::kDeviceState, num_softirq_total_name_id_);
411     context_->event_tracker->PushCounter(
412         ts, static_cast<double>(sys_stats.num_softirq_total()), track);
413   }
414 
415   // Fragmentation of the kernel binary buddy memory allocator.
416   // See /proc/buddyinfo in `man 5 proc`.
417   for (auto it = sys_stats.buddy_info(); it; ++it) {
418     protos::pbzero::SysStats::BuddyInfo::Decoder bi(*it);
419     int order = 0;
420     for (auto order_it = bi.order_pages(); order_it; ++order_it) {
421       std::string node = bi.node().ToStdString();
422       std::string zone = bi.zone().ToStdString();
423       uint32_t chunk_size_kb =
424           static_cast<uint32_t>(((1 << order) * page_size_) / 1024);
425       base::StackString<255> counter_name("mem.buddyinfo[%s][%s][%u kB]",
426                                           node.c_str(), zone.c_str(),
427                                           chunk_size_kb);
428       StringId name =
429           context_->storage->InternString(counter_name.string_view());
430       TrackId track = context_->track_tracker->InternGlobalCounterTrack(
431           TrackTracker::Group::kMemory, name, {}, available_chunks_unit_id_);
432       context_->event_tracker->PushCounter(ts, static_cast<double>(*order_it),
433                                            track);
434       order++;
435     }
436   }
437 
438   for (auto it = sys_stats.disk_stat(); it; ++it) {
439     ParseDiskStats(ts, *it);
440   }
441 
442   // Pressure Stall Information. See
443   // https://docs.kernel.org/accounting/psi.html.
444   for (auto it = sys_stats.psi(); it; ++it) {
445     protos::pbzero::SysStats::PsiSample::Decoder psi(*it);
446 
447     auto resource = static_cast<size_t>(psi.resource());
448     if (PERFETTO_UNLIKELY(resource >= sys_stats_psi_resource_names_.size())) {
449       PERFETTO_ELOG("PsiResource type %zu is not recognized.", resource);
450       context_->storage->IncrementStats(stats::psi_unknown_resource);
451       continue;
452     }
453 
454     // Unit = total blocked time on this resource in nanoseconds.
455     // TODO(b/315152880): Consider moving psi entries for cpu/io/memory into
456     // groups specific to that resource (e.g., `Group::kMemory`).
457     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
458         TrackTracker::Group::kDeviceState,
459         sys_stats_psi_resource_names_[resource], {}, ns_unit_id_);
460     context_->event_tracker->PushCounter(
461         ts, static_cast<double>(psi.total_ns()), track);
462   }
463 }
464 
ParseProcessTree(ConstBytes blob)465 void SystemProbesParser::ParseProcessTree(ConstBytes blob) {
466   protos::pbzero::ProcessTree::Decoder ps(blob.data, blob.size);
467 
468   for (auto it = ps.processes(); it; ++it) {
469     protos::pbzero::ProcessTree::Process::Decoder proc(*it);
470     if (!proc.has_cmdline())
471       continue;
472     auto pid = static_cast<uint32_t>(proc.pid());
473     auto ppid = static_cast<uint32_t>(proc.ppid());
474 
475     if (proc.has_nspid()) {
476       std::vector<uint32_t> nspid;
477       for (auto nspid_it = proc.nspid(); nspid_it; nspid_it++) {
478         nspid.emplace_back(static_cast<uint32_t>(*nspid_it));
479       }
480       context_->process_tracker->UpdateNamespacedProcess(pid, std::move(nspid));
481     }
482 
483     protozero::RepeatedFieldIterator<protozero::ConstChars> raw_cmdline =
484         proc.cmdline();
485     base::StringView argv0 = raw_cmdline ? *raw_cmdline : base::StringView();
486     base::StringView joined_cmdline{};
487 
488     // Special case: workqueue kernel threads (kworker). Worker threads are
489     // organised in pools, which can process work from different workqueues.
490     // When we read their thread name via procfs, the kernel takes a dedicated
491     // codepath that appends the name of the current/last workqueue that the
492     // worker processed. This is highly transient and therefore misleading to
493     // users if we keep using this name for the kernel thread.
494     // Example:
495     //   kworker/45:2-mm_percpu_wq
496     //   ^           ^
497     //   [worker id ][last queue ]
498     //
499     // Instead, use a truncated version of the process name that identifies just
500     // the worker itself. For the above example, this would be "kworker/45:2".
501     //
502     // https://github.com/torvalds/linux/blob/6d280f4d760e3bcb4a8df302afebf085b65ec982/kernel/workqueue.c#L5336
503     uint32_t kThreaddPid = 2;
504     if (ppid == kThreaddPid && argv0.StartsWith("kworker/")) {
505       size_t delim_loc = std::min(argv0.find('+', 8), argv0.find('-', 8));
506       if (delim_loc != base::StringView::npos) {
507         argv0 = argv0.substr(0, delim_loc);
508         joined_cmdline = argv0;
509       }
510     }
511 
512     // Special case: some processes rewrite their cmdline with spaces as a
513     // separator instead of a NUL byte. Assume that's the case if there's only a
514     // single cmdline element. This will be wrong for binaries that have spaces
515     // in their path and are invoked without additional arguments, but those are
516     // very rare. The full cmdline will still be correct either way.
517     if (bool(++proc.cmdline()) == false) {
518       size_t delim_pos = argv0.find(' ');
519       if (delim_pos != base::StringView::npos) {
520         argv0 = argv0.substr(0, delim_pos);
521       }
522     }
523 
524     std::string cmdline_str;
525     if (joined_cmdline.empty()) {
526       for (auto cmdline_it = raw_cmdline; cmdline_it;) {
527         auto cmdline_part = *cmdline_it;
528         cmdline_str.append(cmdline_part.data, cmdline_part.size);
529 
530         if (++cmdline_it)
531           cmdline_str.append(" ");
532       }
533       joined_cmdline = base::StringView(cmdline_str);
534     }
535     UniquePid upid = context_->process_tracker->SetProcessMetadata(
536         pid, ppid, argv0, joined_cmdline);
537 
538     if (proc.has_uid()) {
539       context_->process_tracker->SetProcessUid(
540           upid, static_cast<uint32_t>(proc.uid()));
541     }
542 
543     // note: early kernel threads can have an age of zero (at tick resolution)
544     if (proc.has_process_start_from_boot()) {
545       base::StatusOr<int64_t> start_ts = context_->clock_tracker->ToTraceTime(
546           protos::pbzero::BUILTIN_CLOCK_BOOTTIME,
547           static_cast<int64_t>(proc.process_start_from_boot()));
548       if (start_ts.ok()) {
549         context_->process_tracker->SetStartTsIfUnset(upid, *start_ts);
550       }
551     }
552   }
553 
554   for (auto it = ps.threads(); it; ++it) {
555     protos::pbzero::ProcessTree::Thread::Decoder thd(*it);
556     auto tid = static_cast<uint32_t>(thd.tid());
557     auto tgid = static_cast<uint32_t>(thd.tgid());
558     context_->process_tracker->UpdateThread(tid, tgid);
559 
560     if (thd.has_name()) {
561       StringId thread_name_id = context_->storage->InternString(thd.name());
562       context_->process_tracker->UpdateThreadName(
563           tid, thread_name_id, ThreadNamePriority::kProcessTree);
564     }
565 
566     if (thd.has_nstid()) {
567       std::vector<uint32_t> nstid;
568       for (auto nstid_it = thd.nstid(); nstid_it; nstid_it++) {
569         nstid.emplace_back(static_cast<uint32_t>(*nstid_it));
570       }
571       context_->process_tracker->UpdateNamespacedThread(tgid, tid,
572                                                         std::move(nstid));
573     }
574   }
575 }
576 
ParseProcessStats(int64_t ts,ConstBytes blob)577 void SystemProbesParser::ParseProcessStats(int64_t ts, ConstBytes blob) {
578   using Process = protos::pbzero::ProcessStats::Process;
579   protos::pbzero::ProcessStats::Decoder stats(blob.data, blob.size);
580   for (auto it = stats.processes(); it; ++it) {
581     // Maps a process counter field it to its value.
582     // E.g., 4 := 1024 -> "mem.rss.anon" := 1024.
583     std::array<int64_t, kProcStatsProcessSize> counter_values{};
584     std::array<bool, kProcStatsProcessSize> has_counter{};
585 
586     protozero::ProtoDecoder proc(*it);
587     uint32_t pid = 0;
588     for (auto fld = proc.ReadField(); fld.valid(); fld = proc.ReadField()) {
589       if (fld.id() == protos::pbzero::ProcessStats::Process::kPidFieldNumber) {
590         pid = fld.as_uint32();
591         continue;
592       }
593       if (fld.id() ==
594           protos::pbzero::ProcessStats::Process::kThreadsFieldNumber) {
595         ParseThreadStats(ts, pid, fld.as_bytes());
596         continue;
597       }
598       if (fld.id() == protos::pbzero::ProcessStats::Process::kFdsFieldNumber) {
599         ParseProcessFds(ts, pid, fld.as_bytes());
600         continue;
601       }
602       bool is_counter_field = fld.id() < proc_stats_process_names_.size() &&
603                               !proc_stats_process_names_[fld.id()].is_null();
604       if (is_counter_field) {
605         // Memory counters are in KB, keep values in bytes in the trace
606         // processor.
607         int64_t value = fld.as_int64();
608         if (fld.id() != Process::kOomScoreAdjFieldNumber &&
609             fld.id() != Process::kRuntimeUserModeFieldNumber &&
610             fld.id() != Process::kRuntimeKernelModeFieldNumber) {
611           value = value * 1024;  // KB -> B
612         }
613         counter_values[fld.id()] = value;
614         has_counter[fld.id()] = true;
615       } else {
616         // Chrome fields are processed by ChromeSystemProbesParser.
617         if (fld.id() == Process::kIsPeakRssResettableFieldNumber ||
618             fld.id() == Process::kChromePrivateFootprintKbFieldNumber ||
619             fld.id() == Process::kChromePrivateFootprintKbFieldNumber) {
620           continue;
621         }
622         context_->storage->IncrementStats(stats::proc_stat_unknown_counters);
623       }
624     }
625 
626     // Skip field_id 0 (invalid) and 1 (pid).
627     for (size_t field_id = 2; field_id < counter_values.size(); field_id++) {
628       if (!has_counter[field_id] || field_id ==
629                                         protos::pbzero::ProcessStats::Process::
630                                             kIsPeakRssResettableFieldNumber) {
631         continue;
632       }
633 
634       // Lookup the interned string id from the field name using the
635       // pre-cached |proc_stats_process_names_| map.
636       const StringId& name = proc_stats_process_names_[field_id];
637       UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
638       TrackId track =
639           context_->track_tracker->InternProcessCounterTrack(name, upid);
640       int64_t value = counter_values[field_id];
641       context_->event_tracker->PushCounter(ts, static_cast<double>(value),
642                                            track);
643     }
644   }
645 }
646 
ParseThreadStats(int64_t,uint32_t pid,ConstBytes blob)647 void SystemProbesParser::ParseThreadStats(int64_t,
648                                           uint32_t pid,
649                                           ConstBytes blob) {
650   protos::pbzero::ProcessStats::Thread::Decoder stats(blob.data, blob.size);
651   context_->process_tracker->UpdateThread(static_cast<uint32_t>(stats.tid()),
652                                           pid);
653 }
654 
ParseProcessFds(int64_t ts,uint32_t pid,ConstBytes blob)655 void SystemProbesParser::ParseProcessFds(int64_t ts,
656                                          uint32_t pid,
657                                          ConstBytes blob) {
658   protos::pbzero::ProcessStats::FDInfo::Decoder fd_info(blob.data, blob.size);
659 
660   tables::FiledescriptorTable::Row row;
661   row.fd = static_cast<int64_t>(fd_info.fd());
662   row.ts = ts;
663   row.path = context_->storage->InternString(fd_info.path());
664   row.upid = context_->process_tracker->GetOrCreateProcess(pid);
665 
666   auto* fd_table = context_->storage->mutable_filedescriptor_table();
667   fd_table->Insert(row);
668 }
669 
ParseSystemInfo(ConstBytes blob)670 void SystemProbesParser::ParseSystemInfo(ConstBytes blob) {
671   protos::pbzero::SystemInfo::Decoder packet(blob.data, blob.size);
672   SystemInfoTracker* system_info_tracker =
673       SystemInfoTracker::GetOrCreate(context_);
674   if (packet.has_utsname()) {
675     ConstBytes utsname_blob = packet.utsname();
676     protos::pbzero::Utsname::Decoder utsname(utsname_blob.data,
677                                              utsname_blob.size);
678     base::StringView machine = utsname.machine();
679     SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(context_);
680     Architecture arch = SyscallTable::ArchFromString(machine);
681     if (arch != Architecture::kUnknown) {
682       syscall_tracker->SetArchitecture(arch);
683     } else {
684       PERFETTO_ELOG("Unknown architecture %s. Syscall traces will not work.",
685                     machine.ToStdString().c_str());
686     }
687 
688     system_info_tracker->SetKernelVersion(utsname.sysname(), utsname.release());
689 
690     StringPool::Id sysname_id =
691         context_->storage->InternString(utsname.sysname());
692     StringPool::Id version_id =
693         context_->storage->InternString(utsname.version());
694     StringPool::Id release_id =
695         context_->storage->InternString(utsname.release());
696     StringPool::Id machine_id =
697         context_->storage->InternString(utsname.machine());
698 
699     MetadataTracker* metadata = context_->metadata_tracker.get();
700     metadata->SetMetadata(metadata::system_name, Variadic::String(sysname_id));
701     metadata->SetMetadata(metadata::system_version,
702                           Variadic::String(version_id));
703     metadata->SetMetadata(metadata::system_release,
704                           Variadic::String(release_id));
705     metadata->SetMetadata(metadata::system_machine,
706                           Variadic::String(machine_id));
707   }
708 
709   if (packet.has_timezone_off_mins()) {
710     context_->metadata_tracker->SetMetadata(
711         metadata::timezone_off_mins,
712         Variadic::Integer(packet.timezone_off_mins()));
713   }
714 
715   if (packet.has_android_build_fingerprint()) {
716     context_->metadata_tracker->SetMetadata(
717         metadata::android_build_fingerprint,
718         Variadic::String(context_->storage->InternString(
719             packet.android_build_fingerprint())));
720   }
721 
722   // If we have the SDK version in the trace directly just use that.
723   // Otherwise, try and parse it from the fingerprint.
724   std::optional<int64_t> opt_sdk_version;
725   if (packet.has_android_sdk_version()) {
726     opt_sdk_version = static_cast<int64_t>(packet.android_sdk_version());
727   } else if (packet.has_android_build_fingerprint()) {
728     opt_sdk_version = FingerprintToSdkVersion(
729         packet.android_build_fingerprint().ToStdString());
730   }
731 
732   if (opt_sdk_version) {
733     context_->metadata_tracker->SetMetadata(
734         metadata::android_sdk_version, Variadic::Integer(*opt_sdk_version));
735   }
736 
737   if (packet.has_android_soc_model()) {
738     context_->metadata_tracker->SetMetadata(
739         metadata::android_soc_model,
740         Variadic::String(
741 	    context_->storage->InternString(packet.android_soc_model())));
742   }
743 
744   page_size_ = packet.page_size();
745   if (!page_size_) {
746     page_size_ = 4096;
747   }
748 
749   if (packet.has_num_cpus()) {
750     system_info_tracker->SetNumCpus(packet.num_cpus());
751   }
752 }
753 
ParseCpuInfo(ConstBytes blob)754 void SystemProbesParser::ParseCpuInfo(ConstBytes blob) {
755   protos::pbzero::CpuInfo::Decoder packet(blob.data, blob.size);
756   uint32_t cluster_id = 0;
757   uint32_t cpu_id = 0;
758   std::vector<uint32_t> last_cpu_freqs;
759   for (auto it = packet.cpus(); it; it++, cpu_id++) {
760     protos::pbzero::CpuInfo::Cpu::Decoder cpu(*it);
761     std::vector<uint32_t> freqs;
762     for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) {
763       freqs.push_back(*freq_it);
764     }
765 
766     // Here we assume that cluster of CPUs are 'next' to each other.
767     if (freqs != last_cpu_freqs && !last_cpu_freqs.empty()) {
768       cluster_id++;
769     }
770 
771     last_cpu_freqs = freqs;
772 
773     tables::CpuTable::Id ucpu =
774         context_->cpu_tracker->SetCpuInfo(cpu_id, cpu.processor(), cluster_id);
775 
776     for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) {
777       uint32_t freq = *freq_it;
778       tables::CpuFreqTable::Row cpu_freq_row;
779       cpu_freq_row.ucpu = ucpu;
780       cpu_freq_row.freq = freq;
781       context_->storage->mutable_cpu_freq_table()->Insert(cpu_freq_row);
782     }
783   }
784 }
785 
786 }  // namespace trace_processor
787 }  // namespace perfetto
788