• 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 <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