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/metadata_tracker.h"
27 #include "src/trace_processor/importers/common/process_tracker.h"
28 #include "src/trace_processor/importers/common/system_info_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/system_info.pbzero.h"
36 #include "protos/perfetto/trace/system_info/cpu_info.pbzero.h"
37
38 namespace {
39
IsSupportedDiskStatDevice(const std::string & device_name)40 bool IsSupportedDiskStatDevice(const std::string& device_name) {
41 return device_name == "sda"; // Primary SCSI disk device name
42 }
43
44 } // namespace
45
46 namespace perfetto {
47 namespace trace_processor {
48
49 namespace {
50
VersionStringToSdkVersion(const std::string & version)51 std::optional<int> VersionStringToSdkVersion(const std::string& version) {
52 // TODO(lalitm): remove this when the SDK version polling saturates
53 // S/T traces in practice.
54 if (base::StartsWith(version, "T") || base::StartsWith(version, "S")) {
55 return 31;
56 }
57
58 // Documentation for this mapping can be found at
59 // https://source.android.com/compatibility/cdd.
60 if (version == "12") {
61 return 31;
62 } else if (version == "11") {
63 return 30;
64 } else if (version == "10") {
65 return 29;
66 } else if (version == "9") {
67 return 28;
68 } else if (version == "8.1") {
69 return 27;
70 } else if (version == "8.0") {
71 return 26;
72 } else if (version == "7.1") {
73 return 25;
74 } else if (version == "7.0") {
75 return 24;
76 } else if (version == "6.0") {
77 return 23;
78 } else if (version == "5.1" || version == "5.1.1") {
79 return 22;
80 } else if (version == "5.0" || version == "5.0.1" || version == "5.0.2") {
81 return 21;
82 }
83 // If we reached this point, we don't know how to parse this version
84 // so just return null.
85 return std::nullopt;
86 }
87
FingerprintToSdkVersion(const std::string & fingerprint)88 std::optional<int> FingerprintToSdkVersion(const std::string& fingerprint) {
89 // Try to parse the SDK version from the fingerprint.
90 // Examples of fingerprints:
91 // google/shamu/shamu:7.0/NBD92F/3753956:userdebug/dev-keys
92 // google/coral/coral:12/SP1A.210812.015/7679548:userdebug/dev-keys
93 size_t colon = fingerprint.find(':');
94 if (colon == std::string::npos)
95 return std::nullopt;
96
97 size_t slash = fingerprint.find('/', colon);
98 if (slash == std::string::npos)
99 return std::nullopt;
100
101 std::string version = fingerprint.substr(colon + 1, slash - (colon + 1));
102 return VersionStringToSdkVersion(version);
103 }
104 } // namespace
105
SystemProbesParser(TraceProcessorContext * context)106 SystemProbesParser::SystemProbesParser(TraceProcessorContext* context)
107 : context_(context),
108 utid_name_id_(context->storage->InternString("utid")),
109 num_forks_name_id_(context->storage->InternString("num_forks")),
110 num_irq_total_name_id_(context->storage->InternString("num_irq_total")),
111 num_softirq_total_name_id_(
112 context->storage->InternString("num_softirq_total")),
113 num_irq_name_id_(context->storage->InternString("num_irq")),
114 num_softirq_name_id_(context->storage->InternString("num_softirq")),
115 cpu_times_user_ns_id_(
116 context->storage->InternString("cpu.times.user_ns")),
117 cpu_times_user_nice_ns_id_(
118 context->storage->InternString("cpu.times.user_nice_ns")),
119 cpu_times_system_mode_ns_id_(
120 context->storage->InternString("cpu.times.system_mode_ns")),
121 cpu_times_idle_ns_id_(
122 context->storage->InternString("cpu.times.idle_ns")),
123 cpu_times_io_wait_ns_id_(
124 context->storage->InternString("cpu.times.io_wait_ns")),
125 cpu_times_irq_ns_id_(context->storage->InternString("cpu.times.irq_ns")),
126 cpu_times_softirq_ns_id_(
127 context->storage->InternString("cpu.times.softirq_ns")),
128 oom_score_adj_id_(context->storage->InternString("oom_score_adj")),
129 cpu_freq_id_(context_->storage->InternString("cpufreq")) {
130 for (const auto& name : BuildMeminfoCounterNames()) {
131 meminfo_strs_id_.emplace_back(context->storage->InternString(name));
132 }
133 for (const auto& name : BuildVmstatCounterNames()) {
134 vmstat_strs_id_.emplace_back(context->storage->InternString(name));
135 }
136
137 using ProcessStats = protos::pbzero::ProcessStats;
138 proc_stats_process_names_[ProcessStats::Process::kVmSizeKbFieldNumber] =
139 context->storage->InternString("mem.virt");
140 proc_stats_process_names_[ProcessStats::Process::kVmRssKbFieldNumber] =
141 context->storage->InternString("mem.rss");
142 proc_stats_process_names_[ProcessStats::Process::kRssAnonKbFieldNumber] =
143 context->storage->InternString("mem.rss.anon");
144 proc_stats_process_names_[ProcessStats::Process::kRssFileKbFieldNumber] =
145 context->storage->InternString("mem.rss.file");
146 proc_stats_process_names_[ProcessStats::Process::kRssShmemKbFieldNumber] =
147 context->storage->InternString("mem.rss.shmem");
148 proc_stats_process_names_[ProcessStats::Process::kVmSwapKbFieldNumber] =
149 context->storage->InternString("mem.swap");
150 proc_stats_process_names_[ProcessStats::Process::kVmLockedKbFieldNumber] =
151 context->storage->InternString("mem.locked");
152 proc_stats_process_names_[ProcessStats::Process::kVmHwmKbFieldNumber] =
153 context->storage->InternString("mem.rss.watermark");
154 proc_stats_process_names_[ProcessStats::Process::kOomScoreAdjFieldNumber] =
155 oom_score_adj_id_;
156 proc_stats_process_names_[ProcessStats::Process::kSmrRssKbFieldNumber] =
157 context->storage->InternString("mem.smaps.rss");
158 proc_stats_process_names_[ProcessStats::Process::kSmrPssKbFieldNumber] =
159 context->storage->InternString("mem.smaps.pss");
160 proc_stats_process_names_[ProcessStats::Process::kSmrPssAnonKbFieldNumber] =
161 context->storage->InternString("mem.smaps.pss.anon");
162 proc_stats_process_names_[ProcessStats::Process::kSmrPssFileKbFieldNumber] =
163 context->storage->InternString("mem.smaps.pss.file");
164 proc_stats_process_names_[ProcessStats::Process::kSmrPssShmemKbFieldNumber] =
165 context->storage->InternString("mem.smaps.pss.shmem");
166 }
167
ParseDiskStats(int64_t ts,ConstBytes blob)168 void SystemProbesParser::ParseDiskStats(int64_t ts, ConstBytes blob) {
169 protos::pbzero::SysStats::DiskStat::Decoder ds(blob.data, blob.size);
170 static constexpr double SECTORS_PER_MB = 2048.0;
171 static constexpr double MS_PER_SEC = 1000.0;
172 std::string device_name = ds.device_name().ToStdString();
173 if (!IsSupportedDiskStatDevice(device_name)) {
174 return;
175 }
176
177 base::StackString<512> tag_prefix("diskstat.[%s]", device_name.c_str());
178 auto push_counter = [this, ts, tag_prefix](const char* counter_name,
179 double value) {
180 base::StackString<512> track_name("%s.%s", tag_prefix.c_str(),
181 counter_name);
182 StringId string_id = context_->storage->InternString(track_name.c_str());
183 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
184 TrackTracker::Group::kIo, string_id);
185 context_->event_tracker->PushCounter(ts, value, track);
186 };
187
188 auto calculate_throughput = [](double amount, int64_t diff) {
189 return diff == 0 ? 0 : amount * MS_PER_SEC / static_cast<double>(diff);
190 };
191
192 int64_t cur_read_amount = static_cast<int64_t>(ds.read_sectors());
193 int64_t cur_write_amount = static_cast<int64_t>(ds.write_sectors());
194 int64_t cur_discard_amount = static_cast<int64_t>(ds.discard_sectors());
195 int64_t cur_flush_count = static_cast<int64_t>(ds.flush_count());
196 int64_t cur_read_time = static_cast<int64_t>(ds.read_time_ms());
197 int64_t cur_write_time = static_cast<int64_t>(ds.write_time_ms());
198 int64_t cur_discard_time = static_cast<int64_t>(ds.discard_time_ms());
199 int64_t cur_flush_time = static_cast<int64_t>(ds.flush_time_ms());
200
201 if (prev_read_amount != -1) {
202 double read_amount =
203 static_cast<double>(cur_read_amount - prev_read_amount) /
204 SECTORS_PER_MB;
205 double write_amount =
206 static_cast<double>(cur_write_amount - prev_write_amount) /
207 SECTORS_PER_MB;
208 double discard_amount =
209 static_cast<double>(cur_discard_amount - prev_discard_amount) /
210 SECTORS_PER_MB;
211 double flush_count =
212 static_cast<double>(cur_flush_count - prev_flush_count);
213 int64_t read_time_diff = cur_read_time - prev_read_time;
214 int64_t write_time_diff = cur_write_time - prev_write_time;
215 int64_t discard_time_diff = cur_discard_time - prev_discard_time;
216 double flush_time_diff =
217 static_cast<double>(cur_flush_time - prev_flush_time);
218
219 double read_thpt = calculate_throughput(read_amount, read_time_diff);
220 double write_thpt = calculate_throughput(write_amount, write_time_diff);
221 double discard_thpt =
222 calculate_throughput(discard_amount, discard_time_diff);
223
224 push_counter("read_amount(mg)", read_amount);
225 push_counter("read_throughput(mg/s)", read_thpt);
226 push_counter("write_amount(mg)", write_amount);
227 push_counter("write_throughput(mg/s)", write_thpt);
228 push_counter("discard_amount(mg)", discard_amount);
229 push_counter("discard_throughput(mg/s)", discard_thpt);
230 push_counter("flush_amount(count)", flush_count);
231 push_counter("flush_time(ms)", flush_time_diff);
232 }
233
234 prev_read_amount = cur_read_amount;
235 prev_write_amount = cur_write_amount;
236 prev_discard_amount = cur_discard_amount;
237 prev_flush_count = cur_flush_count;
238 prev_read_time = cur_read_time;
239 prev_write_time = cur_write_time;
240 prev_discard_time = cur_discard_time;
241 prev_flush_time = cur_flush_time;
242 }
243
ParseSysStats(int64_t ts,ConstBytes blob)244 void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) {
245 protos::pbzero::SysStats::Decoder sys_stats(blob.data, blob.size);
246
247 for (auto it = sys_stats.meminfo(); it; ++it) {
248 protos::pbzero::SysStats::MeminfoValue::Decoder mi(*it);
249 auto key = static_cast<size_t>(mi.key());
250 if (PERFETTO_UNLIKELY(key >= meminfo_strs_id_.size())) {
251 PERFETTO_ELOG("MemInfo key %zu is not recognized.", key);
252 context_->storage->IncrementStats(stats::meminfo_unknown_keys);
253 continue;
254 }
255 // /proc/meminfo counters are in kB, convert to bytes
256 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
257 TrackTracker::Group::kMemory, meminfo_strs_id_[key]);
258 context_->event_tracker->PushCounter(
259 ts, static_cast<double>(mi.value()) * 1024., track);
260 }
261
262 for (auto it = sys_stats.devfreq(); it; ++it) {
263 protos::pbzero::SysStats::DevfreqValue::Decoder vm(*it);
264 auto key = static_cast<base::StringView>(vm.key());
265 // Append " Frequency" to align names with
266 // FtraceParser::ParseClockSetRate
267 base::StringView devfreq_subtitle("Frequency");
268 base::StackString<255> counter_name(
269 "%.*s %.*s", int(key.size()), key.data(), int(devfreq_subtitle.size()),
270 devfreq_subtitle.data());
271 StringId name = context_->storage->InternString(counter_name.string_view());
272 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
273 TrackTracker::Group::kClockFrequency, name);
274 context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()),
275 track);
276 }
277
278 uint32_t c = 0;
279 for (auto it = sys_stats.cpufreq_khz(); it; ++it, ++c) {
280 TrackId track =
281 context_->track_tracker->InternCpuCounterTrack(cpu_freq_id_, c);
282 context_->event_tracker->PushCounter(ts, static_cast<double>(*it), track);
283 }
284
285 for (auto it = sys_stats.vmstat(); it; ++it) {
286 protos::pbzero::SysStats::VmstatValue::Decoder vm(*it);
287 auto key = static_cast<size_t>(vm.key());
288 if (PERFETTO_UNLIKELY(key >= vmstat_strs_id_.size())) {
289 PERFETTO_ELOG("VmStat key %zu is not recognized.", key);
290 context_->storage->IncrementStats(stats::vmstat_unknown_keys);
291 continue;
292 }
293 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
294 TrackTracker::Group::kMemory, vmstat_strs_id_[key]);
295 context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()),
296 track);
297 }
298
299 for (auto it = sys_stats.cpu_stat(); it; ++it) {
300 protos::pbzero::SysStats::CpuTimes::Decoder ct(*it);
301 if (PERFETTO_UNLIKELY(!ct.has_cpu_id())) {
302 PERFETTO_ELOG("CPU field not found in CpuTimes");
303 context_->storage->IncrementStats(stats::invalid_cpu_times);
304 continue;
305 }
306
307 TrackId track = context_->track_tracker->InternCpuCounterTrack(
308 cpu_times_user_ns_id_, ct.cpu_id());
309 context_->event_tracker->PushCounter(ts, static_cast<double>(ct.user_ns()),
310 track);
311
312 track = context_->track_tracker->InternCpuCounterTrack(
313 cpu_times_user_nice_ns_id_, ct.cpu_id());
314 context_->event_tracker->PushCounter(
315 ts, static_cast<double>(ct.user_ice_ns()), track);
316
317 track = context_->track_tracker->InternCpuCounterTrack(
318 cpu_times_system_mode_ns_id_, ct.cpu_id());
319 context_->event_tracker->PushCounter(
320 ts, static_cast<double>(ct.system_mode_ns()), track);
321
322 track = context_->track_tracker->InternCpuCounterTrack(
323 cpu_times_idle_ns_id_, ct.cpu_id());
324 context_->event_tracker->PushCounter(ts, static_cast<double>(ct.idle_ns()),
325 track);
326
327 track = context_->track_tracker->InternCpuCounterTrack(
328 cpu_times_io_wait_ns_id_, ct.cpu_id());
329 context_->event_tracker->PushCounter(
330 ts, static_cast<double>(ct.io_wait_ns()), track);
331
332 track = context_->track_tracker->InternCpuCounterTrack(cpu_times_irq_ns_id_,
333 ct.cpu_id());
334 context_->event_tracker->PushCounter(ts, static_cast<double>(ct.irq_ns()),
335 track);
336
337 track = context_->track_tracker->InternCpuCounterTrack(
338 cpu_times_softirq_ns_id_, ct.cpu_id());
339 context_->event_tracker->PushCounter(
340 ts, static_cast<double>(ct.softirq_ns()), track);
341 }
342
343 for (auto it = sys_stats.num_irq(); it; ++it) {
344 protos::pbzero::SysStats::InterruptCount::Decoder ic(*it);
345
346 TrackId track = context_->track_tracker->InternIrqCounterTrack(
347 num_irq_name_id_, ic.irq());
348 context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()),
349 track);
350 }
351
352 for (auto it = sys_stats.num_softirq(); it; ++it) {
353 protos::pbzero::SysStats::InterruptCount::Decoder ic(*it);
354
355 TrackId track = context_->track_tracker->InternSoftirqCounterTrack(
356 num_softirq_name_id_, ic.irq());
357 context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()),
358 track);
359 }
360
361 if (sys_stats.has_num_forks()) {
362 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
363 TrackTracker::Group::kDeviceState, num_forks_name_id_);
364 context_->event_tracker->PushCounter(
365 ts, static_cast<double>(sys_stats.num_forks()), track);
366 }
367
368 if (sys_stats.has_num_irq_total()) {
369 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
370 TrackTracker::Group::kDeviceState, num_irq_total_name_id_);
371 context_->event_tracker->PushCounter(
372 ts, static_cast<double>(sys_stats.num_irq_total()), track);
373 }
374
375 if (sys_stats.has_num_softirq_total()) {
376 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
377 TrackTracker::Group::kDeviceState, num_softirq_total_name_id_);
378 context_->event_tracker->PushCounter(
379 ts, static_cast<double>(sys_stats.num_softirq_total()), track);
380 }
381
382 for (auto it = sys_stats.buddy_info(); it; ++it) {
383 protos::pbzero::SysStats::BuddyInfo::Decoder bi(*it);
384 int order = 0;
385 for (auto order_it = bi.order_pages(); order_it; ++order_it) {
386 std::string node = bi.node().ToStdString();
387 std::string zone = bi.zone().ToStdString();
388 uint32_t size_kb =
389 static_cast<uint32_t>(((1 << order) * page_size_) / 1024);
390 base::StackString<255> counter_name("mem.buddyinfo[%s][%s][%u kB]",
391 node.c_str(), zone.c_str(), size_kb);
392 StringId name =
393 context_->storage->InternString(counter_name.string_view());
394 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
395 TrackTracker::Group::kMemory, name);
396 context_->event_tracker->PushCounter(ts, static_cast<double>(*order_it),
397 track);
398 order++;
399 }
400 }
401
402 for (auto it = sys_stats.disk_stat(); it; ++it) {
403 ParseDiskStats(ts, *it);
404 }
405 }
406
ParseProcessTree(ConstBytes blob)407 void SystemProbesParser::ParseProcessTree(ConstBytes blob) {
408 protos::pbzero::ProcessTree::Decoder ps(blob.data, blob.size);
409
410 for (auto it = ps.processes(); it; ++it) {
411 protos::pbzero::ProcessTree::Process::Decoder proc(*it);
412 if (!proc.has_cmdline())
413 continue;
414 auto pid = static_cast<uint32_t>(proc.pid());
415 auto ppid = static_cast<uint32_t>(proc.ppid());
416
417 if (proc.has_nspid()) {
418 std::vector<uint32_t> nspid;
419 for (auto nspid_it = proc.nspid(); nspid_it; nspid_it++) {
420 nspid.emplace_back(static_cast<uint32_t>(*nspid_it));
421 }
422 context_->process_tracker->UpdateNamespacedProcess(pid, std::move(nspid));
423 }
424
425 auto raw_cmdline = proc.cmdline();
426 base::StringView argv0 = raw_cmdline ? *raw_cmdline : base::StringView();
427 // Chrome child process overwrites /proc/self/cmdline and replaces all
428 // '\0' with ' '. This makes argv0 contain the full command line. Extract
429 // the actual argv0 if it's Chrome.
430 static const char kChromeBinary[] = "/chrome ";
431 auto pos = argv0.find(kChromeBinary);
432 if (pos != base::StringView::npos) {
433 argv0 = argv0.substr(0, pos + strlen(kChromeBinary) - 1);
434 }
435
436 std::string cmdline_str;
437 for (auto cmdline_it = raw_cmdline; cmdline_it;) {
438 auto cmdline_part = *cmdline_it;
439 cmdline_str.append(cmdline_part.data, cmdline_part.size);
440
441 if (++cmdline_it)
442 cmdline_str.append(" ");
443 }
444 base::StringView cmdline = base::StringView(cmdline_str);
445 UniquePid upid = context_->process_tracker->SetProcessMetadata(
446 pid, ppid, argv0, cmdline);
447 if (proc.has_uid()) {
448 context_->process_tracker->SetProcessUid(
449 upid, static_cast<uint32_t>(proc.uid()));
450 }
451 }
452
453 for (auto it = ps.threads(); it; ++it) {
454 protos::pbzero::ProcessTree::Thread::Decoder thd(*it);
455 auto tid = static_cast<uint32_t>(thd.tid());
456 auto tgid = static_cast<uint32_t>(thd.tgid());
457 context_->process_tracker->UpdateThread(tid, tgid);
458
459 if (thd.has_name()) {
460 StringId thread_name_id = context_->storage->InternString(thd.name());
461 context_->process_tracker->UpdateThreadName(
462 tid, thread_name_id, ThreadNamePriority::kProcessTree);
463 }
464
465 if (thd.has_nstid()) {
466 std::vector<uint32_t> nstid;
467 for (auto nstid_it = thd.nstid(); nstid_it; nstid_it++) {
468 nstid.emplace_back(static_cast<uint32_t>(*nstid_it));
469 }
470 context_->process_tracker->UpdateNamespacedThread(tgid, tid,
471 std::move(nstid));
472 }
473 }
474 }
475
ParseProcessStats(int64_t ts,ConstBytes blob)476 void SystemProbesParser::ParseProcessStats(int64_t ts, ConstBytes blob) {
477 using Process = protos::pbzero::ProcessStats::Process;
478 protos::pbzero::ProcessStats::Decoder stats(blob.data, blob.size);
479 const auto kOomScoreAdjFieldNumber =
480 protos::pbzero::ProcessStats::Process::kOomScoreAdjFieldNumber;
481 for (auto it = stats.processes(); it; ++it) {
482 // Maps a process counter field it to its value.
483 // E.g., 4 := 1024 -> "mem.rss.anon" := 1024.
484 std::array<int64_t, kProcStatsProcessSize> counter_values{};
485 std::array<bool, kProcStatsProcessSize> has_counter{};
486
487 protozero::ProtoDecoder proc(*it);
488 uint32_t pid = 0;
489 for (auto fld = proc.ReadField(); fld.valid(); fld = proc.ReadField()) {
490 if (fld.id() == protos::pbzero::ProcessStats::Process::kPidFieldNumber) {
491 pid = fld.as_uint32();
492 continue;
493 }
494 if (fld.id() ==
495 protos::pbzero::ProcessStats::Process::kThreadsFieldNumber) {
496 ParseThreadStats(ts, pid, fld.as_bytes());
497 continue;
498 }
499 if (fld.id() == protos::pbzero::ProcessStats::Process::kFdsFieldNumber) {
500 ParseProcessFds(ts, pid, fld.as_bytes());
501 continue;
502 }
503 bool is_counter_field = fld.id() < proc_stats_process_names_.size() &&
504 !proc_stats_process_names_[fld.id()].is_null();
505 if (is_counter_field) {
506 // Memory counters are in KB, keep values in bytes in the trace
507 // processor.
508 counter_values[fld.id()] = fld.id() == kOomScoreAdjFieldNumber
509 ? fld.as_int64()
510 : fld.as_int64() * 1024;
511 has_counter[fld.id()] = true;
512 } else {
513 // Chrome fields are processed by ChromeSystemProbesParser.
514 if (fld.id() == Process::kIsPeakRssResettableFieldNumber ||
515 fld.id() == Process::kChromePrivateFootprintKbFieldNumber ||
516 fld.id() == Process::kChromePrivateFootprintKbFieldNumber) {
517 continue;
518 }
519 context_->storage->IncrementStats(stats::proc_stat_unknown_counters);
520 }
521 }
522
523 // Skip field_id 0 (invalid) and 1 (pid).
524 for (size_t field_id = 2; field_id < counter_values.size(); field_id++) {
525 if (!has_counter[field_id] || field_id ==
526 protos::pbzero::ProcessStats::Process::
527 kIsPeakRssResettableFieldNumber) {
528 continue;
529 }
530
531 // Lookup the interned string id from the field name using the
532 // pre-cached |proc_stats_process_names_| map.
533 const StringId& name = proc_stats_process_names_[field_id];
534 UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
535 TrackId track =
536 context_->track_tracker->InternProcessCounterTrack(name, upid);
537 int64_t value = counter_values[field_id];
538 context_->event_tracker->PushCounter(ts, static_cast<double>(value),
539 track);
540 }
541 }
542 }
543
ParseThreadStats(int64_t,uint32_t pid,ConstBytes blob)544 void SystemProbesParser::ParseThreadStats(int64_t,
545 uint32_t pid,
546 ConstBytes blob) {
547 protos::pbzero::ProcessStats::Thread::Decoder stats(blob.data, blob.size);
548 context_->process_tracker->UpdateThread(static_cast<uint32_t>(stats.tid()),
549 pid);
550 }
551
ParseProcessFds(int64_t ts,uint32_t pid,ConstBytes blob)552 void SystemProbesParser::ParseProcessFds(int64_t ts,
553 uint32_t pid,
554 ConstBytes blob) {
555 protos::pbzero::ProcessStats::FDInfo::Decoder fd_info(blob.data, blob.size);
556
557 tables::FiledescriptorTable::Row row;
558 row.fd = static_cast<int64_t>(fd_info.fd());
559 row.ts = ts;
560 row.path = context_->storage->InternString(fd_info.path());
561 row.upid = context_->process_tracker->GetOrCreateProcess(pid);
562
563 auto* fd_table = context_->storage->mutable_filedescriptor_table();
564 fd_table->Insert(row);
565 }
566
ParseSystemInfo(ConstBytes blob)567 void SystemProbesParser::ParseSystemInfo(ConstBytes blob) {
568 protos::pbzero::SystemInfo::Decoder packet(blob.data, blob.size);
569 if (packet.has_utsname()) {
570 ConstBytes utsname_blob = packet.utsname();
571 protos::pbzero::Utsname::Decoder utsname(utsname_blob.data,
572 utsname_blob.size);
573 base::StringView machine = utsname.machine();
574 SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(context_);
575 Architecture arch = SyscallTable::ArchFromString(machine);
576 if (arch != kUnknown) {
577 syscall_tracker->SetArchitecture(arch);
578 } else {
579 PERFETTO_ELOG("Unknown architecture %s. Syscall traces will not work.",
580 machine.ToStdString().c_str());
581 }
582
583 SystemInfoTracker* system_info_tracker =
584 SystemInfoTracker::GetOrCreate(context_);
585 system_info_tracker->SetKernelVersion(utsname.sysname(), utsname.release());
586
587 StringPool::Id sysname_id =
588 context_->storage->InternString(utsname.sysname());
589 StringPool::Id version_id =
590 context_->storage->InternString(utsname.version());
591 StringPool::Id release_id =
592 context_->storage->InternString(utsname.release());
593 StringPool::Id machine_id =
594 context_->storage->InternString(utsname.machine());
595
596 MetadataTracker* metadata = context_->metadata_tracker.get();
597 metadata->SetMetadata(metadata::system_name, Variadic::String(sysname_id));
598 metadata->SetMetadata(metadata::system_version,
599 Variadic::String(version_id));
600 metadata->SetMetadata(metadata::system_release,
601 Variadic::String(release_id));
602 metadata->SetMetadata(metadata::system_machine,
603 Variadic::String(machine_id));
604 }
605
606 if (packet.has_android_build_fingerprint()) {
607 context_->metadata_tracker->SetMetadata(
608 metadata::android_build_fingerprint,
609 Variadic::String(context_->storage->InternString(
610 packet.android_build_fingerprint())));
611 }
612
613 // If we have the SDK version in the trace directly just use that.
614 // Otherwise, try and parse it from the fingerprint.
615 std::optional<int64_t> opt_sdk_version;
616 if (packet.has_android_sdk_version()) {
617 opt_sdk_version = static_cast<int64_t>(packet.android_sdk_version());
618 } else if (packet.has_android_build_fingerprint()) {
619 opt_sdk_version = FingerprintToSdkVersion(
620 packet.android_build_fingerprint().ToStdString());
621 }
622
623 if (opt_sdk_version) {
624 context_->metadata_tracker->SetMetadata(
625 metadata::android_sdk_version, Variadic::Integer(*opt_sdk_version));
626 }
627
628 int64_t hz = packet.hz();
629 if (hz > 0)
630 ms_per_tick_ = 1000u / static_cast<uint64_t>(hz);
631
632 page_size_ = packet.page_size();
633 if (!page_size_)
634 page_size_ = 4096;
635 }
636
ParseCpuInfo(ConstBytes blob)637 void SystemProbesParser::ParseCpuInfo(ConstBytes blob) {
638 protos::pbzero::CpuInfo::Decoder packet(blob.data, blob.size);
639 uint32_t cluster_id = 0;
640 std::vector<uint32_t> last_cpu_freqs;
641 for (auto it = packet.cpus(); it; it++) {
642 protos::pbzero::CpuInfo::Cpu::Decoder cpu(*it);
643 tables::CpuTable::Row cpu_row;
644 if (cpu.has_processor()) {
645 cpu_row.processor = context_->storage->InternString(cpu.processor());
646 }
647 std::vector<uint32_t> freqs;
648 for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) {
649 freqs.push_back(*freq_it);
650 }
651
652 // Here we assume that cluster of CPUs are 'next' to each other.
653 if (freqs != last_cpu_freqs && !last_cpu_freqs.empty()) {
654 cluster_id++;
655 }
656 cpu_row.cluster_id = cluster_id;
657
658 last_cpu_freqs = freqs;
659 tables::CpuTable::Id cpu_row_id =
660 context_->storage->mutable_cpu_table()->Insert(cpu_row).id;
661
662 for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) {
663 uint32_t freq = *freq_it;
664 tables::CpuFreqTable::Row cpu_freq_row;
665 cpu_freq_row.cpu_id = cpu_row_id;
666 cpu_freq_row.freq = freq;
667 context_->storage->mutable_cpu_freq_table()->Insert(cpu_freq_row);
668 }
669 }
670 }
671
672 } // namespace trace_processor
673 } // namespace perfetto
674