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 <set>
20
21 #include "perfetto/base/logging.h"
22 #include "perfetto/ext/base/string_utils.h"
23 #include "perfetto/ext/traced/sys_stats_counters.h"
24 #include "perfetto/protozero/proto_decoder.h"
25 #include "src/trace_processor/importers/common/event_tracker.h"
26 #include "src/trace_processor/importers/common/process_tracker.h"
27 #include "src/trace_processor/importers/common/system_info_tracker.h"
28 #include "src/trace_processor/importers/proto/metadata_tracker.h"
29 #include "src/trace_processor/importers/syscalls/syscall_tracker.h"
30 #include "src/trace_processor/storage/metadata.h"
31 #include "src/trace_processor/types/trace_processor_context.h"
32
33 #include "protos/perfetto/trace/ps/process_stats.pbzero.h"
34 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
35 #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
36 #include "protos/perfetto/trace/system_info.pbzero.h"
37 #include "protos/perfetto/trace/system_info/cpu_info.pbzero.h"
38
39 namespace perfetto {
40 namespace trace_processor {
41
42 namespace {
43 // kthreadd is the parent process for all kernel threads and always has
44 // pid == 2 on Linux and Android.
45 const uint32_t kKthreaddPid = 2;
46 const char kKthreaddName[] = "kthreadd";
47 } // namespace
48
SystemProbesParser(TraceProcessorContext * context)49 SystemProbesParser::SystemProbesParser(TraceProcessorContext* context)
50 : context_(context),
51 utid_name_id_(context->storage->InternString("utid")),
52 num_forks_name_id_(context->storage->InternString("num_forks")),
53 num_irq_total_name_id_(context->storage->InternString("num_irq_total")),
54 num_softirq_total_name_id_(
55 context->storage->InternString("num_softirq_total")),
56 num_irq_name_id_(context->storage->InternString("num_irq")),
57 num_softirq_name_id_(context->storage->InternString("num_softirq")),
58 cpu_times_user_ns_id_(
59 context->storage->InternString("cpu.times.user_ns")),
60 cpu_times_user_nice_ns_id_(
61 context->storage->InternString("cpu.times.user_nice_ns")),
62 cpu_times_system_mode_ns_id_(
63 context->storage->InternString("cpu.times.system_mode_ns")),
64 cpu_times_idle_ns_id_(
65 context->storage->InternString("cpu.times.idle_ns")),
66 cpu_times_io_wait_ns_id_(
67 context->storage->InternString("cpu.times.io_wait_ns")),
68 cpu_times_irq_ns_id_(context->storage->InternString("cpu.times.irq_ns")),
69 cpu_times_softirq_ns_id_(
70 context->storage->InternString("cpu.times.softirq_ns")),
71 oom_score_adj_id_(context->storage->InternString("oom_score_adj")),
72 thread_time_in_state_id_(context->storage->InternString("time_in_state")),
73 thread_time_in_state_cpu_id_(
74 context_->storage->InternString("time_in_state_cpu_id")),
75 cpu_freq_id_(context_->storage->InternString("freq")) {
76 for (const auto& name : BuildMeminfoCounterNames()) {
77 meminfo_strs_id_.emplace_back(context->storage->InternString(name));
78 }
79 for (const auto& name : BuildVmstatCounterNames()) {
80 vmstat_strs_id_.emplace_back(context->storage->InternString(name));
81 }
82
83 using ProcessStats = protos::pbzero::ProcessStats;
84 proc_stats_process_names_[ProcessStats::Process::kVmSizeKbFieldNumber] =
85 context->storage->InternString("mem.virt");
86 proc_stats_process_names_[ProcessStats::Process::kVmRssKbFieldNumber] =
87 context->storage->InternString("mem.rss");
88 proc_stats_process_names_[ProcessStats::Process::kRssAnonKbFieldNumber] =
89 context->storage->InternString("mem.rss.anon");
90 proc_stats_process_names_[ProcessStats::Process::kRssFileKbFieldNumber] =
91 context->storage->InternString("mem.rss.file");
92 proc_stats_process_names_[ProcessStats::Process::kRssShmemKbFieldNumber] =
93 context->storage->InternString("mem.rss.shmem");
94 proc_stats_process_names_[ProcessStats::Process::kVmSwapKbFieldNumber] =
95 context->storage->InternString("mem.swap");
96 proc_stats_process_names_[ProcessStats::Process::kVmLockedKbFieldNumber] =
97 context->storage->InternString("mem.locked");
98 proc_stats_process_names_[ProcessStats::Process::kVmHwmKbFieldNumber] =
99 context->storage->InternString("mem.rss.watermark");
100 proc_stats_process_names_[ProcessStats::Process::kOomScoreAdjFieldNumber] =
101 oom_score_adj_id_;
102 }
103
ParseSysStats(int64_t ts,ConstBytes blob)104 void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) {
105 protos::pbzero::SysStats::Decoder sys_stats(blob.data, blob.size);
106
107 for (auto it = sys_stats.meminfo(); it; ++it) {
108 protos::pbzero::SysStats::MeminfoValue::Decoder mi(*it);
109 auto key = static_cast<size_t>(mi.key());
110 if (PERFETTO_UNLIKELY(key >= meminfo_strs_id_.size())) {
111 PERFETTO_ELOG("MemInfo key %zu is not recognized.", key);
112 context_->storage->IncrementStats(stats::meminfo_unknown_keys);
113 continue;
114 }
115 // /proc/meminfo counters are in kB, convert to bytes
116 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
117 meminfo_strs_id_[key]);
118 context_->event_tracker->PushCounter(
119 ts, static_cast<double>(mi.value()) * 1024., track);
120 }
121
122 for (auto it = sys_stats.devfreq(); it; ++it) {
123 protos::pbzero::SysStats::DevfreqValue::Decoder vm(*it);
124 auto key = static_cast<base::StringView>(vm.key());
125 // Append " Frequency" to align names with FtraceParser::ParseClockSetRate
126 base::StringView devfreq_subtitle("Frequency");
127 char counter_name[255];
128 snprintf(counter_name, sizeof(counter_name), "%.*s %.*s", int(key.size()),
129 key.data(), int(devfreq_subtitle.size()), devfreq_subtitle.data());
130 StringId name = context_->storage->InternString(counter_name);
131 TrackId track = context_->track_tracker->InternGlobalCounterTrack(name);
132 context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()),
133 track);
134 }
135
136 for (auto it = sys_stats.vmstat(); it; ++it) {
137 protos::pbzero::SysStats::VmstatValue::Decoder vm(*it);
138 auto key = static_cast<size_t>(vm.key());
139 if (PERFETTO_UNLIKELY(key >= vmstat_strs_id_.size())) {
140 PERFETTO_ELOG("VmStat key %zu is not recognized.", key);
141 context_->storage->IncrementStats(stats::vmstat_unknown_keys);
142 continue;
143 }
144 TrackId track =
145 context_->track_tracker->InternGlobalCounterTrack(vmstat_strs_id_[key]);
146 context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()),
147 track);
148 }
149
150 for (auto it = sys_stats.cpu_stat(); it; ++it) {
151 protos::pbzero::SysStats::CpuTimes::Decoder ct(*it);
152 if (PERFETTO_UNLIKELY(!ct.has_cpu_id())) {
153 PERFETTO_ELOG("CPU field not found in CpuTimes");
154 context_->storage->IncrementStats(stats::invalid_cpu_times);
155 continue;
156 }
157
158 TrackId track = context_->track_tracker->InternCpuCounterTrack(
159 cpu_times_user_ns_id_, ct.cpu_id());
160 context_->event_tracker->PushCounter(ts, static_cast<double>(ct.user_ns()),
161 track);
162
163 track = context_->track_tracker->InternCpuCounterTrack(
164 cpu_times_user_nice_ns_id_, ct.cpu_id());
165 context_->event_tracker->PushCounter(
166 ts, static_cast<double>(ct.user_ice_ns()), track);
167
168 track = context_->track_tracker->InternCpuCounterTrack(
169 cpu_times_system_mode_ns_id_, ct.cpu_id());
170 context_->event_tracker->PushCounter(
171 ts, static_cast<double>(ct.system_mode_ns()), track);
172
173 track = context_->track_tracker->InternCpuCounterTrack(
174 cpu_times_idle_ns_id_, ct.cpu_id());
175 context_->event_tracker->PushCounter(ts, static_cast<double>(ct.idle_ns()),
176 track);
177
178 track = context_->track_tracker->InternCpuCounterTrack(
179 cpu_times_io_wait_ns_id_, ct.cpu_id());
180 context_->event_tracker->PushCounter(
181 ts, static_cast<double>(ct.io_wait_ns()), track);
182
183 track = context_->track_tracker->InternCpuCounterTrack(cpu_times_irq_ns_id_,
184 ct.cpu_id());
185 context_->event_tracker->PushCounter(ts, static_cast<double>(ct.irq_ns()),
186 track);
187
188 track = context_->track_tracker->InternCpuCounterTrack(
189 cpu_times_softirq_ns_id_, ct.cpu_id());
190 context_->event_tracker->PushCounter(
191 ts, static_cast<double>(ct.softirq_ns()), track);
192 }
193
194 for (auto it = sys_stats.num_irq(); it; ++it) {
195 protos::pbzero::SysStats::InterruptCount::Decoder ic(*it);
196
197 TrackId track = context_->track_tracker->InternIrqCounterTrack(
198 num_irq_name_id_, ic.irq());
199 context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()),
200 track);
201 }
202
203 for (auto it = sys_stats.num_softirq(); it; ++it) {
204 protos::pbzero::SysStats::InterruptCount::Decoder ic(*it);
205
206 TrackId track = context_->track_tracker->InternSoftirqCounterTrack(
207 num_softirq_name_id_, ic.irq());
208 context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()),
209 track);
210 }
211
212 if (sys_stats.has_num_forks()) {
213 TrackId track =
214 context_->track_tracker->InternGlobalCounterTrack(num_forks_name_id_);
215 context_->event_tracker->PushCounter(
216 ts, static_cast<double>(sys_stats.num_forks()), track);
217 }
218
219 if (sys_stats.has_num_irq_total()) {
220 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
221 num_irq_total_name_id_);
222 context_->event_tracker->PushCounter(
223 ts, static_cast<double>(sys_stats.num_irq_total()), track);
224 }
225
226 if (sys_stats.has_num_softirq_total()) {
227 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
228 num_softirq_total_name_id_);
229 context_->event_tracker->PushCounter(
230 ts, static_cast<double>(sys_stats.num_softirq_total()), track);
231 }
232 }
233
ParseProcessTree(ConstBytes blob)234 void SystemProbesParser::ParseProcessTree(ConstBytes blob) {
235 protos::pbzero::ProcessTree::Decoder ps(blob.data, blob.size);
236
237 for (auto it = ps.processes(); it; ++it) {
238 protos::pbzero::ProcessTree::Process::Decoder proc(*it);
239 if (!proc.has_cmdline())
240 continue;
241 auto pid = static_cast<uint32_t>(proc.pid());
242 auto ppid = static_cast<uint32_t>(proc.ppid());
243
244 // If the parent pid is kthreadd's pid, even though this pid is of a
245 // "process", we want to treat it as being a child thread of kthreadd.
246 if (ppid == kKthreaddPid) {
247 context_->process_tracker->SetProcessMetadata(
248 kKthreaddPid, base::nullopt, kKthreaddName, base::StringView());
249 context_->process_tracker->UpdateThread(pid, kKthreaddPid);
250 } else {
251 auto raw_cmdline = proc.cmdline();
252 base::StringView argv0 = raw_cmdline ? *raw_cmdline : base::StringView();
253
254 std::string cmdline_str;
255 for (auto cmdline_it = raw_cmdline; cmdline_it;) {
256 auto cmdline_part = *cmdline_it;
257 cmdline_str.append(cmdline_part.data, cmdline_part.size);
258
259 if (++cmdline_it)
260 cmdline_str.append(" ");
261 }
262 base::StringView cmdline = base::StringView(cmdline_str);
263 UniquePid upid = context_->process_tracker->SetProcessMetadata(
264 pid, ppid, argv0, cmdline);
265 if (proc.has_uid()) {
266 context_->process_tracker->SetProcessUid(
267 upid, static_cast<uint32_t>(proc.uid()));
268 }
269 }
270 }
271
272 for (auto it = ps.threads(); it; ++it) {
273 protos::pbzero::ProcessTree::Thread::Decoder thd(*it);
274 auto tid = static_cast<uint32_t>(thd.tid());
275 auto tgid = static_cast<uint32_t>(thd.tgid());
276 context_->process_tracker->UpdateThread(tid, tgid);
277
278 if (thd.has_name()) {
279 StringId thread_name_id = context_->storage->InternString(thd.name());
280 context_->process_tracker->UpdateThreadName(
281 tid, thread_name_id, ThreadNamePriority::kProcessTree);
282 }
283 }
284 }
285
ParseProcessStats(int64_t ts,ConstBytes blob)286 void SystemProbesParser::ParseProcessStats(int64_t ts, ConstBytes blob) {
287 using Process = protos::pbzero::ProcessStats::Process;
288 protos::pbzero::ProcessStats::Decoder stats(blob.data, blob.size);
289 const auto kOomScoreAdjFieldNumber =
290 protos::pbzero::ProcessStats::Process::kOomScoreAdjFieldNumber;
291 for (auto it = stats.processes(); it; ++it) {
292 // Maps a process counter field it to its value.
293 // E.g., 4 := 1024 -> "mem.rss.anon" := 1024.
294 std::array<int64_t, kProcStatsProcessSize> counter_values{};
295 std::array<bool, kProcStatsProcessSize> has_counter{};
296
297 protozero::ProtoDecoder proc(*it);
298 uint32_t pid = 0;
299 for (auto fld = proc.ReadField(); fld.valid(); fld = proc.ReadField()) {
300 if (fld.id() == protos::pbzero::ProcessStats::Process::kPidFieldNumber) {
301 pid = fld.as_uint32();
302 continue;
303 }
304 if (fld.id() ==
305 protos::pbzero::ProcessStats::Process::kThreadsFieldNumber) {
306 if (PERFETTO_UNLIKELY(ms_per_tick_ == 0 ||
307 thread_time_in_state_cpus_.empty())) {
308 context_->storage->IncrementStats(
309 stats::thread_time_in_state_out_of_order);
310 continue;
311 }
312 ParseThreadStats(ts, pid, fld.as_bytes());
313 continue;
314 }
315 bool is_counter_field = fld.id() < proc_stats_process_names_.size() &&
316 !proc_stats_process_names_[fld.id()].is_null();
317 if (is_counter_field) {
318 // Memory counters are in KB, keep values in bytes in the trace
319 // processor.
320 counter_values[fld.id()] = fld.id() == kOomScoreAdjFieldNumber
321 ? fld.as_int64()
322 : fld.as_int64() * 1024;
323 has_counter[fld.id()] = true;
324 } else {
325 // Chrome fields are processed by ChromeSystemProbesParser.
326 if (fld.id() == Process::kIsPeakRssResettableFieldNumber ||
327 fld.id() == Process::kChromePrivateFootprintKbFieldNumber ||
328 fld.id() == Process::kChromePrivateFootprintKbFieldNumber) {
329 continue;
330 }
331 context_->storage->IncrementStats(stats::proc_stat_unknown_counters);
332 }
333 }
334
335 // Skip field_id 0 (invalid) and 1 (pid).
336 for (size_t field_id = 2; field_id < counter_values.size(); field_id++) {
337 if (!has_counter[field_id] || field_id ==
338 protos::pbzero::ProcessStats::Process::
339 kIsPeakRssResettableFieldNumber) {
340 continue;
341 }
342
343 // Lookup the interned string id from the field name using the
344 // pre-cached |proc_stats_process_names_| map.
345 const StringId& name = proc_stats_process_names_[field_id];
346 UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
347 TrackId track =
348 context_->track_tracker->InternProcessCounterTrack(name, upid);
349 int64_t value = counter_values[field_id];
350 context_->event_tracker->PushCounter(ts, static_cast<double>(value),
351 track);
352 }
353 }
354 }
355
ParseThreadStats(int64_t ts,uint32_t pid,ConstBytes blob)356 void SystemProbesParser::ParseThreadStats(int64_t ts,
357 uint32_t pid,
358 ConstBytes blob) {
359 protos::pbzero::ProcessStats::Thread::Decoder stats(blob.data, blob.size);
360 UniqueTid utid = context_->process_tracker->UpdateThread(
361 static_cast<uint32_t>(stats.tid()), pid);
362 TrackId track_id = context_->track_tracker->InternThreadCounterTrack(
363 thread_time_in_state_id_, utid);
364
365 std::vector<uint64_t> ticks(thread_time_in_state_cpu_freqs_.size());
366 auto index_it = stats.cpu_freq_indices();
367 auto tick_it = stats.cpu_freq_ticks();
368 for (; index_it && tick_it; index_it++, tick_it++) {
369 auto freq_index = *index_it;
370 if (PERFETTO_UNLIKELY(!IsValidCpuFreqIndex(freq_index))) {
371 context_->storage->IncrementStats(
372 stats::thread_time_in_state_unknown_cpu_freq);
373 continue;
374 }
375 ticks[freq_index] = *tick_it;
376 }
377
378 for (uint32_t cpu : thread_time_in_state_cpus_) {
379 size_t start = thread_time_in_state_freq_index_[cpu];
380 size_t end = thread_time_in_state_freq_index_[cpu + 1];
381 for (size_t freq_index = start; freq_index < end; freq_index++) {
382 if (stats.cpu_freq_full() || ticks[freq_index] > 0) {
383 context_->event_tracker->PushCounter(
384 ts, static_cast<double>(ticks[freq_index] * ms_per_tick_), track_id,
385 [cpu, freq_index, this](ArgsTracker::BoundInserter* args_table) {
386 args_table->AddArg(thread_time_in_state_cpu_id_,
387 Variadic::UnsignedInteger(cpu));
388 args_table->AddArg(
389 cpu_freq_id_,
390 Variadic::UnsignedInteger(
391 thread_time_in_state_cpu_freqs_[freq_index]));
392 });
393 }
394 }
395 }
396 }
397
ParseSystemInfo(ConstBytes blob)398 void SystemProbesParser::ParseSystemInfo(ConstBytes blob) {
399 protos::pbzero::SystemInfo::Decoder packet(blob.data, blob.size);
400 if (packet.has_utsname()) {
401 ConstBytes utsname_blob = packet.utsname();
402 protos::pbzero::Utsname::Decoder utsname(utsname_blob.data,
403 utsname_blob.size);
404 base::StringView machine = utsname.machine();
405 SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(context_);
406 if (machine == "aarch64") {
407 syscall_tracker->SetArchitecture(kAarch64);
408 } else if (machine == "armv8l") {
409 syscall_tracker->SetArchitecture(kArmEabi);
410 } else if (machine == "armv7l") {
411 syscall_tracker->SetArchitecture(kAarch32);
412 } else if (machine == "x86_64") {
413 syscall_tracker->SetArchitecture(kX86_64);
414 } else if (machine == "i686") {
415 syscall_tracker->SetArchitecture(kX86);
416 } else {
417 PERFETTO_ELOG("Unknown architecture %s. Syscall traces will not work.",
418 machine.ToStdString().c_str());
419 }
420
421 SystemInfoTracker* system_info_tracker =
422 SystemInfoTracker::GetOrCreate(context_);
423 system_info_tracker->SetKernelVersion(utsname.sysname(), utsname.release());
424
425 StringPool::Id sysname_id =
426 context_->storage->InternString(utsname.sysname());
427 StringPool::Id version_id =
428 context_->storage->InternString(utsname.version());
429 StringPool::Id release_id =
430 context_->storage->InternString(utsname.release());
431 StringPool::Id machine_id =
432 context_->storage->InternString(utsname.machine());
433
434 MetadataTracker* metadata = context_->metadata_tracker.get();
435 metadata->SetMetadata(metadata::system_name, Variadic::String(sysname_id));
436 metadata->SetMetadata(metadata::system_version,
437 Variadic::String(version_id));
438 metadata->SetMetadata(metadata::system_release,
439 Variadic::String(release_id));
440 metadata->SetMetadata(metadata::system_machine,
441 Variadic::String(machine_id));
442 }
443
444 if (packet.has_android_build_fingerprint()) {
445 context_->metadata_tracker->SetMetadata(
446 metadata::android_build_fingerprint,
447 Variadic::String(context_->storage->InternString(
448 packet.android_build_fingerprint())));
449 }
450
451 int64_t hz = packet.hz();
452 if (hz > 0)
453 ms_per_tick_ = 1000u / static_cast<uint64_t>(hz);
454 }
455
ParseCpuInfo(ConstBytes blob)456 void SystemProbesParser::ParseCpuInfo(ConstBytes blob) {
457 // invalid_freq is used as the guard in thread_time_in_state_cpu_freq_ids_,
458 // see IsValidCpuFreqIndex.
459 uint32_t invalid_freq = 0;
460 thread_time_in_state_cpu_freqs_.push_back(invalid_freq);
461
462 protos::pbzero::CpuInfo::Decoder packet(blob.data, blob.size);
463 uint32_t cpu_index = 0;
464 uint32_t time_in_state_cpu_index = 0;
465 size_t freq_index = 1;
466 std::vector<uint32_t> last_cpu_freqs;
467 for (auto it = packet.cpus(); it; it++) {
468 thread_time_in_state_freq_index_.push_back(freq_index);
469
470 protos::pbzero::CpuInfo::Cpu::Decoder cpu(*it);
471 tables::CpuTable::Row cpu_row;
472 if (cpu.has_processor())
473 cpu_row.processor = context_->storage->InternString(cpu.processor());
474 std::vector<uint32_t> freqs;
475 for (auto freq_it = cpu.frequencies(); freq_it; freq_it++)
476 freqs.push_back(*freq_it);
477 if (freqs != last_cpu_freqs) {
478 time_in_state_cpu_index = cpu_index;
479 thread_time_in_state_cpus_.insert(cpu_index);
480 }
481 cpu_row.time_in_state_cpu_id = time_in_state_cpu_index;
482 last_cpu_freqs = freqs;
483 tables::CpuTable::Id cpu_row_id =
484 context_->storage->mutable_cpu_table()->Insert(cpu_row).id;
485
486 for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) {
487 uint32_t freq = *freq_it;
488 tables::CpuFreqTable::Row cpu_freq_row;
489 cpu_freq_row.cpu_id = cpu_row_id;
490 cpu_freq_row.freq = freq;
491 context_->storage->mutable_cpu_freq_table()->Insert(cpu_freq_row);
492 thread_time_in_state_cpu_freqs_.push_back(freq);
493 freq_index++;
494 }
495
496 cpu_index++;
497 }
498 thread_time_in_state_freq_index_.push_back(freq_index);
499 thread_time_in_state_cpu_freqs_.push_back(invalid_freq);
500 }
501
IsValidCpuFreqIndex(uint32_t freq_index) const502 bool SystemProbesParser::IsValidCpuFreqIndex(uint32_t freq_index) const {
503 // Frequency index 0 is invalid.
504 return freq_index > 0 && freq_index < thread_time_in_state_cpu_freqs_.size();
505 }
506
507 } // namespace trace_processor
508 } // namespace perfetto
509