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