• 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/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 
VersionStringToSdkVersion(const std::string & version)48 base::Optional<int> VersionStringToSdkVersion(const std::string& version) {
49   // TODO(lalitm): remove this when the SDK version polling saturates
50   // S/T traces in practice.
51   if (base::StartsWith(version, "T") || base::StartsWith(version, "S")) {
52     return 31;
53   }
54 
55   // Documentation for this mapping can be found at
56   // https://source.android.com/compatibility/cdd.
57   if (version == "12") {
58     return 31;
59   } else if (version == "11") {
60     return 30;
61   } else if (version == "10") {
62     return 29;
63   } else if (version == "9") {
64     return 28;
65   } else if (version == "8.1") {
66     return 27;
67   } else if (version == "8.0") {
68     return 26;
69   } else if (version == "7.1") {
70     return 25;
71   } else if (version == "7.0") {
72     return 24;
73   } else if (version == "6.0") {
74     return 23;
75   } else if (version == "5.1" || version == "5.1.1") {
76     return 22;
77   } else if (version == "5.0" || version == "5.0.1" || version == "5.0.2") {
78     return 21;
79   }
80   // If we reached this point, we don't know how to parse this version
81   // so just return null.
82   return base::nullopt;
83 }
84 
FingerprintToSdkVersion(const std::string & fingerprint)85 base::Optional<int> FingerprintToSdkVersion(const std::string& fingerprint) {
86   // Try to parse the SDK version from the fingerprint.
87   // Examples of fingerprints:
88   // google/shamu/shamu:7.0/NBD92F/3753956:userdebug/dev-keys
89   // google/coral/coral:12/SP1A.210812.015/7679548:userdebug/dev-keys
90   size_t colon = fingerprint.find(':');
91   if (colon == std::string::npos)
92     return base::nullopt;
93 
94   size_t slash = fingerprint.find('/', colon);
95   if (slash == std::string::npos)
96     return base::nullopt;
97 
98   std::string version = fingerprint.substr(colon + 1, slash - (colon + 1));
99   return VersionStringToSdkVersion(version);
100 }
101 }  // namespace
102 
SystemProbesParser(TraceProcessorContext * context)103 SystemProbesParser::SystemProbesParser(TraceProcessorContext* context)
104     : context_(context),
105       utid_name_id_(context->storage->InternString("utid")),
106       num_forks_name_id_(context->storage->InternString("num_forks")),
107       num_irq_total_name_id_(context->storage->InternString("num_irq_total")),
108       num_softirq_total_name_id_(
109           context->storage->InternString("num_softirq_total")),
110       num_irq_name_id_(context->storage->InternString("num_irq")),
111       num_softirq_name_id_(context->storage->InternString("num_softirq")),
112       cpu_times_user_ns_id_(
113           context->storage->InternString("cpu.times.user_ns")),
114       cpu_times_user_nice_ns_id_(
115           context->storage->InternString("cpu.times.user_nice_ns")),
116       cpu_times_system_mode_ns_id_(
117           context->storage->InternString("cpu.times.system_mode_ns")),
118       cpu_times_idle_ns_id_(
119           context->storage->InternString("cpu.times.idle_ns")),
120       cpu_times_io_wait_ns_id_(
121           context->storage->InternString("cpu.times.io_wait_ns")),
122       cpu_times_irq_ns_id_(context->storage->InternString("cpu.times.irq_ns")),
123       cpu_times_softirq_ns_id_(
124           context->storage->InternString("cpu.times.softirq_ns")),
125       oom_score_adj_id_(context->storage->InternString("oom_score_adj")),
126       thread_time_in_state_id_(context->storage->InternString("time_in_state")),
127       thread_time_in_state_cpu_id_(
128           context_->storage->InternString("time_in_state_cpu_id")),
129       cpu_freq_id_(context_->storage->InternString("freq")) {
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 }
157 
ParseSysStats(int64_t ts,ConstBytes blob)158 void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) {
159   protos::pbzero::SysStats::Decoder sys_stats(blob.data, blob.size);
160 
161   for (auto it = sys_stats.meminfo(); it; ++it) {
162     protos::pbzero::SysStats::MeminfoValue::Decoder mi(*it);
163     auto key = static_cast<size_t>(mi.key());
164     if (PERFETTO_UNLIKELY(key >= meminfo_strs_id_.size())) {
165       PERFETTO_ELOG("MemInfo key %zu is not recognized.", key);
166       context_->storage->IncrementStats(stats::meminfo_unknown_keys);
167       continue;
168     }
169     // /proc/meminfo counters are in kB, convert to bytes
170     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
171         meminfo_strs_id_[key]);
172     context_->event_tracker->PushCounter(
173         ts, static_cast<double>(mi.value()) * 1024., track);
174   }
175 
176   for (auto it = sys_stats.devfreq(); it; ++it) {
177     protos::pbzero::SysStats::DevfreqValue::Decoder vm(*it);
178     auto key = static_cast<base::StringView>(vm.key());
179     // Append " Frequency" to align names with
180     // FtraceParser::ParseClockSetRate
181     base::StringView devfreq_subtitle("Frequency");
182     base::StackString<255> counter_name(
183         "%.*s %.*s", int(key.size()), key.data(), int(devfreq_subtitle.size()),
184         devfreq_subtitle.data());
185     StringId name = context_->storage->InternString(counter_name.string_view());
186     TrackId track = context_->track_tracker->InternGlobalCounterTrack(name);
187     context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()),
188                                          track);
189   }
190 
191   int c = 0;
192   for (auto it = sys_stats.cpufreq_khz(); it; ++it, ++c) {
193     base::StackString<255> counter_name("CPU %d Freq in kHz", c);
194     StringId name = context_->storage->InternString(counter_name.string_view());
195     TrackId track = context_->track_tracker->InternGlobalCounterTrack(name);
196     context_->event_tracker->PushCounter(ts, static_cast<double>(*it), track);
197   }
198 
199   for (auto it = sys_stats.vmstat(); it; ++it) {
200     protos::pbzero::SysStats::VmstatValue::Decoder vm(*it);
201     auto key = static_cast<size_t>(vm.key());
202     if (PERFETTO_UNLIKELY(key >= vmstat_strs_id_.size())) {
203       PERFETTO_ELOG("VmStat key %zu is not recognized.", key);
204       context_->storage->IncrementStats(stats::vmstat_unknown_keys);
205       continue;
206     }
207     TrackId track =
208         context_->track_tracker->InternGlobalCounterTrack(vmstat_strs_id_[key]);
209     context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()),
210                                          track);
211   }
212 
213   for (auto it = sys_stats.cpu_stat(); it; ++it) {
214     protos::pbzero::SysStats::CpuTimes::Decoder ct(*it);
215     if (PERFETTO_UNLIKELY(!ct.has_cpu_id())) {
216       PERFETTO_ELOG("CPU field not found in CpuTimes");
217       context_->storage->IncrementStats(stats::invalid_cpu_times);
218       continue;
219     }
220 
221     TrackId track = context_->track_tracker->InternCpuCounterTrack(
222         cpu_times_user_ns_id_, ct.cpu_id());
223     context_->event_tracker->PushCounter(ts, static_cast<double>(ct.user_ns()),
224                                          track);
225 
226     track = context_->track_tracker->InternCpuCounterTrack(
227         cpu_times_user_nice_ns_id_, ct.cpu_id());
228     context_->event_tracker->PushCounter(
229         ts, static_cast<double>(ct.user_ice_ns()), track);
230 
231     track = context_->track_tracker->InternCpuCounterTrack(
232         cpu_times_system_mode_ns_id_, ct.cpu_id());
233     context_->event_tracker->PushCounter(
234         ts, static_cast<double>(ct.system_mode_ns()), track);
235 
236     track = context_->track_tracker->InternCpuCounterTrack(
237         cpu_times_idle_ns_id_, ct.cpu_id());
238     context_->event_tracker->PushCounter(ts, static_cast<double>(ct.idle_ns()),
239                                          track);
240 
241     track = context_->track_tracker->InternCpuCounterTrack(
242         cpu_times_io_wait_ns_id_, ct.cpu_id());
243     context_->event_tracker->PushCounter(
244         ts, static_cast<double>(ct.io_wait_ns()), track);
245 
246     track = context_->track_tracker->InternCpuCounterTrack(cpu_times_irq_ns_id_,
247                                                            ct.cpu_id());
248     context_->event_tracker->PushCounter(ts, static_cast<double>(ct.irq_ns()),
249                                          track);
250 
251     track = context_->track_tracker->InternCpuCounterTrack(
252         cpu_times_softirq_ns_id_, ct.cpu_id());
253     context_->event_tracker->PushCounter(
254         ts, static_cast<double>(ct.softirq_ns()), track);
255   }
256 
257   for (auto it = sys_stats.num_irq(); it; ++it) {
258     protos::pbzero::SysStats::InterruptCount::Decoder ic(*it);
259 
260     TrackId track = context_->track_tracker->InternIrqCounterTrack(
261         num_irq_name_id_, ic.irq());
262     context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()),
263                                          track);
264   }
265 
266   for (auto it = sys_stats.num_softirq(); it; ++it) {
267     protos::pbzero::SysStats::InterruptCount::Decoder ic(*it);
268 
269     TrackId track = context_->track_tracker->InternSoftirqCounterTrack(
270         num_softirq_name_id_, ic.irq());
271     context_->event_tracker->PushCounter(ts, static_cast<double>(ic.count()),
272                                          track);
273   }
274 
275   if (sys_stats.has_num_forks()) {
276     TrackId track =
277         context_->track_tracker->InternGlobalCounterTrack(num_forks_name_id_);
278     context_->event_tracker->PushCounter(
279         ts, static_cast<double>(sys_stats.num_forks()), track);
280   }
281 
282   if (sys_stats.has_num_irq_total()) {
283     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
284         num_irq_total_name_id_);
285     context_->event_tracker->PushCounter(
286         ts, static_cast<double>(sys_stats.num_irq_total()), track);
287   }
288 
289   if (sys_stats.has_num_softirq_total()) {
290     TrackId track = context_->track_tracker->InternGlobalCounterTrack(
291         num_softirq_total_name_id_);
292     context_->event_tracker->PushCounter(
293         ts, static_cast<double>(sys_stats.num_softirq_total()), track);
294   }
295 }
296 
ParseProcessTree(ConstBytes blob)297 void SystemProbesParser::ParseProcessTree(ConstBytes blob) {
298   protos::pbzero::ProcessTree::Decoder ps(blob.data, blob.size);
299 
300   for (auto it = ps.processes(); it; ++it) {
301     protos::pbzero::ProcessTree::Process::Decoder proc(*it);
302     if (!proc.has_cmdline())
303       continue;
304     auto pid = static_cast<uint32_t>(proc.pid());
305     auto ppid = static_cast<uint32_t>(proc.ppid());
306 
307     if (proc.has_nspid()) {
308       std::vector<uint32_t> nspid;
309       for (auto nspid_it = proc.nspid(); nspid_it; nspid_it++) {
310         nspid.emplace_back(static_cast<uint32_t>(*nspid_it));
311       }
312       context_->process_tracker->UpdateNamespacedProcess(pid, std::move(nspid));
313     }
314 
315     // If the parent pid is kthreadd's pid, even though this pid is of a
316     // "process", we want to treat it as being a child thread of
317     // kthreadd.
318     if (ppid == kKthreaddPid) {
319       context_->process_tracker->SetProcessMetadata(
320           kKthreaddPid, base::nullopt, kKthreaddName, base::StringView());
321       context_->process_tracker->UpdateThread(pid, kKthreaddPid);
322     } else {
323       auto raw_cmdline = proc.cmdline();
324       base::StringView argv0 = raw_cmdline ? *raw_cmdline : base::StringView();
325       // Chrome child process overwrites /proc/self/cmdline and replaces all
326       // '\0' with ' '. This makes argv0 contain the full command line. Extract
327       // the actual argv0 if it's Chrome.
328       static const char kChromeBinary[] = "/chrome ";
329       auto pos = argv0.find(kChromeBinary);
330       if (pos != base::StringView::npos) {
331         argv0 = argv0.substr(0, pos + strlen(kChromeBinary) - 1);
332       }
333 
334       std::string cmdline_str;
335       for (auto cmdline_it = raw_cmdline; cmdline_it;) {
336         auto cmdline_part = *cmdline_it;
337         cmdline_str.append(cmdline_part.data, cmdline_part.size);
338 
339         if (++cmdline_it)
340           cmdline_str.append(" ");
341       }
342       base::StringView cmdline = base::StringView(cmdline_str);
343       UniquePid upid = context_->process_tracker->SetProcessMetadata(
344           pid, ppid, argv0, cmdline);
345       if (proc.has_uid()) {
346         context_->process_tracker->SetProcessUid(
347             upid, static_cast<uint32_t>(proc.uid()));
348       }
349     }
350   }
351 
352   for (auto it = ps.threads(); it; ++it) {
353     protos::pbzero::ProcessTree::Thread::Decoder thd(*it);
354     auto tid = static_cast<uint32_t>(thd.tid());
355     auto tgid = static_cast<uint32_t>(thd.tgid());
356     context_->process_tracker->UpdateThread(tid, tgid);
357 
358     if (thd.has_name()) {
359       StringId thread_name_id = context_->storage->InternString(thd.name());
360       context_->process_tracker->UpdateThreadName(
361           tid, thread_name_id, ThreadNamePriority::kProcessTree);
362     }
363 
364     if (thd.has_nstid()) {
365       std::vector<uint32_t> nstid;
366       for (auto nstid_it = thd.nstid(); nstid_it; nstid_it++) {
367         nstid.emplace_back(static_cast<uint32_t>(*nstid_it));
368       }
369       context_->process_tracker->UpdateNamespacedThread(tgid, tid,
370                                                         std::move(nstid));
371     }
372   }
373 }
374 
ParseProcessStats(int64_t ts,ConstBytes blob)375 void SystemProbesParser::ParseProcessStats(int64_t ts, ConstBytes blob) {
376   using Process = protos::pbzero::ProcessStats::Process;
377   protos::pbzero::ProcessStats::Decoder stats(blob.data, blob.size);
378   const auto kOomScoreAdjFieldNumber =
379       protos::pbzero::ProcessStats::Process::kOomScoreAdjFieldNumber;
380   for (auto it = stats.processes(); it; ++it) {
381     // Maps a process counter field it to its value.
382     // E.g., 4 := 1024 -> "mem.rss.anon" := 1024.
383     std::array<int64_t, kProcStatsProcessSize> counter_values{};
384     std::array<bool, kProcStatsProcessSize> has_counter{};
385 
386     protozero::ProtoDecoder proc(*it);
387     uint32_t pid = 0;
388     for (auto fld = proc.ReadField(); fld.valid(); fld = proc.ReadField()) {
389       if (fld.id() == protos::pbzero::ProcessStats::Process::kPidFieldNumber) {
390         pid = fld.as_uint32();
391         continue;
392       }
393       if (fld.id() ==
394           protos::pbzero::ProcessStats::Process::kThreadsFieldNumber) {
395         if (PERFETTO_UNLIKELY(ms_per_tick_ == 0 ||
396                               thread_time_in_state_cpus_.empty())) {
397           context_->storage->IncrementStats(
398               stats::thread_time_in_state_out_of_order);
399           continue;
400         }
401         ParseThreadStats(ts, pid, fld.as_bytes());
402         continue;
403       }
404       bool is_counter_field = fld.id() < proc_stats_process_names_.size() &&
405                               !proc_stats_process_names_[fld.id()].is_null();
406       if (is_counter_field) {
407         // Memory counters are in KB, keep values in bytes in the trace
408         // processor.
409         counter_values[fld.id()] = fld.id() == kOomScoreAdjFieldNumber
410                                        ? fld.as_int64()
411                                        : fld.as_int64() * 1024;
412         has_counter[fld.id()] = true;
413       } else {
414         // Chrome fields are processed by ChromeSystemProbesParser.
415         if (fld.id() == Process::kIsPeakRssResettableFieldNumber ||
416             fld.id() == Process::kChromePrivateFootprintKbFieldNumber ||
417             fld.id() == Process::kChromePrivateFootprintKbFieldNumber) {
418           continue;
419         }
420         context_->storage->IncrementStats(stats::proc_stat_unknown_counters);
421       }
422     }
423 
424     // Skip field_id 0 (invalid) and 1 (pid).
425     for (size_t field_id = 2; field_id < counter_values.size(); field_id++) {
426       if (!has_counter[field_id] || field_id ==
427                                         protos::pbzero::ProcessStats::Process::
428                                             kIsPeakRssResettableFieldNumber) {
429         continue;
430       }
431 
432       // Lookup the interned string id from the field name using the
433       // pre-cached |proc_stats_process_names_| map.
434       const StringId& name = proc_stats_process_names_[field_id];
435       UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
436       TrackId track =
437           context_->track_tracker->InternProcessCounterTrack(name, upid);
438       int64_t value = counter_values[field_id];
439       context_->event_tracker->PushCounter(ts, static_cast<double>(value),
440                                            track);
441     }
442   }
443 }
444 
ParseThreadStats(int64_t ts,uint32_t pid,ConstBytes blob)445 void SystemProbesParser::ParseThreadStats(int64_t ts,
446                                           uint32_t pid,
447                                           ConstBytes blob) {
448   protos::pbzero::ProcessStats::Thread::Decoder stats(blob.data, blob.size);
449   UniqueTid utid = context_->process_tracker->UpdateThread(
450       static_cast<uint32_t>(stats.tid()), pid);
451   TrackId track_id = context_->track_tracker->InternThreadCounterTrack(
452       thread_time_in_state_id_, utid);
453 
454   std::vector<uint64_t> ticks(thread_time_in_state_cpu_freqs_.size());
455   auto index_it = stats.cpu_freq_indices();
456   auto tick_it = stats.cpu_freq_ticks();
457   for (; index_it && tick_it; index_it++, tick_it++) {
458     auto freq_index = *index_it;
459     if (PERFETTO_UNLIKELY(!IsValidCpuFreqIndex(freq_index))) {
460       context_->storage->IncrementStats(
461           stats::thread_time_in_state_unknown_cpu_freq);
462       continue;
463     }
464     ticks[freq_index] = *tick_it;
465   }
466 
467   for (uint32_t cpu : thread_time_in_state_cpus_) {
468     size_t start = thread_time_in_state_freq_index_[cpu];
469     size_t end = thread_time_in_state_freq_index_[cpu + 1];
470     for (size_t freq_index = start; freq_index < end; freq_index++) {
471       if (stats.cpu_freq_full() || ticks[freq_index] > 0) {
472         context_->event_tracker->PushCounter(
473             ts, static_cast<double>(ticks[freq_index] * ms_per_tick_), track_id,
474             [cpu, freq_index, this](ArgsTracker::BoundInserter* args_table) {
475               args_table->AddArg(thread_time_in_state_cpu_id_,
476                                  Variadic::UnsignedInteger(cpu));
477               args_table->AddArg(
478                   cpu_freq_id_,
479                   Variadic::UnsignedInteger(
480                       thread_time_in_state_cpu_freqs_[freq_index]));
481             });
482       }
483     }
484   }
485 }
486 
ParseSystemInfo(ConstBytes blob)487 void SystemProbesParser::ParseSystemInfo(ConstBytes blob) {
488   protos::pbzero::SystemInfo::Decoder packet(blob.data, blob.size);
489   if (packet.has_utsname()) {
490     ConstBytes utsname_blob = packet.utsname();
491     protos::pbzero::Utsname::Decoder utsname(utsname_blob.data,
492                                              utsname_blob.size);
493     base::StringView machine = utsname.machine();
494     SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(context_);
495     if (machine == "aarch64") {
496       syscall_tracker->SetArchitecture(kAarch64);
497     } else if (machine == "armv8l") {
498       syscall_tracker->SetArchitecture(kArmEabi);
499     } else if (machine == "armv7l") {
500       syscall_tracker->SetArchitecture(kAarch32);
501     } else if (machine == "x86_64") {
502       syscall_tracker->SetArchitecture(kX86_64);
503     } else if (machine == "i686") {
504       syscall_tracker->SetArchitecture(kX86);
505     } else {
506       PERFETTO_ELOG("Unknown architecture %s. Syscall traces will not work.",
507                     machine.ToStdString().c_str());
508     }
509 
510     SystemInfoTracker* system_info_tracker =
511         SystemInfoTracker::GetOrCreate(context_);
512     system_info_tracker->SetKernelVersion(utsname.sysname(), utsname.release());
513 
514     StringPool::Id sysname_id =
515         context_->storage->InternString(utsname.sysname());
516     StringPool::Id version_id =
517         context_->storage->InternString(utsname.version());
518     StringPool::Id release_id =
519         context_->storage->InternString(utsname.release());
520     StringPool::Id machine_id =
521         context_->storage->InternString(utsname.machine());
522 
523     MetadataTracker* metadata = context_->metadata_tracker.get();
524     metadata->SetMetadata(metadata::system_name, Variadic::String(sysname_id));
525     metadata->SetMetadata(metadata::system_version,
526                           Variadic::String(version_id));
527     metadata->SetMetadata(metadata::system_release,
528                           Variadic::String(release_id));
529     metadata->SetMetadata(metadata::system_machine,
530                           Variadic::String(machine_id));
531   }
532 
533   if (packet.has_android_build_fingerprint()) {
534     context_->metadata_tracker->SetMetadata(
535         metadata::android_build_fingerprint,
536         Variadic::String(context_->storage->InternString(
537             packet.android_build_fingerprint())));
538   }
539 
540   // If we have the SDK version in the trace directly just use that.
541   // Otherwise, try and parse it from the fingerprint.
542   base::Optional<int64_t> opt_sdk_version;
543   if (packet.has_android_sdk_version()) {
544     opt_sdk_version = static_cast<int64_t>(packet.android_sdk_version());
545   } else if (packet.has_android_build_fingerprint()) {
546     opt_sdk_version = FingerprintToSdkVersion(
547         packet.android_build_fingerprint().ToStdString());
548   }
549 
550   if (opt_sdk_version) {
551     context_->metadata_tracker->SetMetadata(
552         metadata::android_sdk_version, Variadic::Integer(*opt_sdk_version));
553   }
554 
555   int64_t hz = packet.hz();
556   if (hz > 0)
557     ms_per_tick_ = 1000u / static_cast<uint64_t>(hz);
558 }
559 
ParseCpuInfo(ConstBytes blob)560 void SystemProbesParser::ParseCpuInfo(ConstBytes blob) {
561   // invalid_freq is used as the guard in
562   // thread_time_in_state_cpu_freq_ids_, see IsValidCpuFreqIndex.
563   uint32_t invalid_freq = 0;
564   thread_time_in_state_cpu_freqs_.push_back(invalid_freq);
565 
566   protos::pbzero::CpuInfo::Decoder packet(blob.data, blob.size);
567   uint32_t cpu_index = 0;
568   uint32_t time_in_state_cpu_index = 0;
569   size_t freq_index = 1;
570   std::vector<uint32_t> last_cpu_freqs;
571   for (auto it = packet.cpus(); it; it++) {
572     thread_time_in_state_freq_index_.push_back(freq_index);
573 
574     protos::pbzero::CpuInfo::Cpu::Decoder cpu(*it);
575     tables::CpuTable::Row cpu_row;
576     if (cpu.has_processor())
577       cpu_row.processor = context_->storage->InternString(cpu.processor());
578     std::vector<uint32_t> freqs;
579     for (auto freq_it = cpu.frequencies(); freq_it; freq_it++)
580       freqs.push_back(*freq_it);
581     if (freqs != last_cpu_freqs) {
582       time_in_state_cpu_index = cpu_index;
583       thread_time_in_state_cpus_.insert(cpu_index);
584     }
585     cpu_row.time_in_state_cpu_id = time_in_state_cpu_index;
586     last_cpu_freqs = freqs;
587     tables::CpuTable::Id cpu_row_id =
588         context_->storage->mutable_cpu_table()->Insert(cpu_row).id;
589 
590     for (auto freq_it = cpu.frequencies(); freq_it; freq_it++) {
591       uint32_t freq = *freq_it;
592       tables::CpuFreqTable::Row cpu_freq_row;
593       cpu_freq_row.cpu_id = cpu_row_id;
594       cpu_freq_row.freq = freq;
595       context_->storage->mutable_cpu_freq_table()->Insert(cpu_freq_row);
596       thread_time_in_state_cpu_freqs_.push_back(freq);
597       freq_index++;
598     }
599 
600     cpu_index++;
601   }
602   thread_time_in_state_freq_index_.push_back(freq_index);
603   thread_time_in_state_cpu_freqs_.push_back(invalid_freq);
604 }
605 
IsValidCpuFreqIndex(uint32_t freq_index) const606 bool SystemProbesParser::IsValidCpuFreqIndex(uint32_t freq_index) const {
607   // Frequency index 0 is invalid.
608   return freq_index > 0 && freq_index < thread_time_in_state_cpu_freqs_.size();
609 }
610 
611 }  // namespace trace_processor
612 }  // namespace perfetto
613