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