• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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/traced/probes/ps/process_stats_data_source.h"
18 
19 #include <stdlib.h>
20 
21 #include <algorithm>
22 #include <utility>
23 
24 #include "perfetto/base/task_runner.h"
25 #include "perfetto/base/time.h"
26 #include "perfetto/ext/base/file_utils.h"
27 #include "perfetto/ext/base/hash.h"
28 #include "perfetto/ext/base/metatrace.h"
29 #include "perfetto/ext/base/scoped_file.h"
30 #include "perfetto/ext/base/string_splitter.h"
31 #include "perfetto/ext/base/string_utils.h"
32 #include "perfetto/tracing/core/data_source_config.h"
33 
34 #include "protos/perfetto/config/process_stats/process_stats_config.pbzero.h"
35 #include "protos/perfetto/trace/ps/process_stats.pbzero.h"
36 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
37 #include "protos/perfetto/trace/trace_packet.pbzero.h"
38 
39 // TODO(primiano): the code in this file assumes that PIDs are never recycled
40 // and that processes/threads never change names. Neither is always true.
41 
42 // The notion of PID in the Linux kernel is a bit confusing.
43 // - PID: is really the thread id (for the main thread: PID == TID).
44 // - TGID (thread group ID): is the Unix Process ID (the actual PID).
45 // - PID == TGID for the main thread: the TID of the main thread is also the PID
46 //   of the process.
47 // So, in this file, |pid| might refer to either a process id or a thread id.
48 
49 namespace perfetto {
50 
51 namespace {
52 
53 // Default upper bound on the number of thread cpu frequency keys, used if none
54 // was provided in the config. The cache is trimmed if it exceeds this size.
55 const size_t kThreadTimeInStateCacheSize = 10000;
56 
ReadNextNumericDir(DIR * dirp)57 int32_t ReadNextNumericDir(DIR* dirp) {
58   while (struct dirent* dir_ent = readdir(dirp)) {
59     if (dir_ent->d_type != DT_DIR)
60       continue;
61     auto int_value = base::CStringToInt32(dir_ent->d_name);
62     if (int_value)
63       return *int_value;
64   }
65   return 0;
66 }
67 
ToInt(const std::string & str)68 inline int ToInt(const std::string& str) {
69   return atoi(str.c_str());
70 }
71 
ToU32(const char * str)72 inline uint32_t ToU32(const char* str) {
73   return static_cast<uint32_t>(strtol(str, nullptr, 10));
74 }
75 
76 }  // namespace
77 
78 // static
79 const ProbesDataSource::Descriptor ProcessStatsDataSource::descriptor = {
80     /*name*/ "linux.process_stats",
81     /*flags*/ Descriptor::kHandlesIncrementalState,
82 };
83 
ProcessStatsDataSource(base::TaskRunner * task_runner,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer,const DataSourceConfig & ds_config,std::unique_ptr<CpuFreqInfo> cpu_freq_info)84 ProcessStatsDataSource::ProcessStatsDataSource(
85     base::TaskRunner* task_runner,
86     TracingSessionID session_id,
87     std::unique_ptr<TraceWriter> writer,
88     const DataSourceConfig& ds_config,
89     std::unique_ptr<CpuFreqInfo> cpu_freq_info)
90     : ProbesDataSource(session_id, &descriptor),
91       task_runner_(task_runner),
92       writer_(std::move(writer)),
93       cpu_freq_info_(std::move(cpu_freq_info)),
94       weak_factory_(this) {
95   using protos::pbzero::ProcessStatsConfig;
96   ProcessStatsConfig::Decoder cfg(ds_config.process_stats_config_raw());
97   record_thread_names_ = cfg.record_thread_names();
98   dump_all_procs_on_start_ = cfg.scan_all_processes_on_start();
99 
100   enable_on_demand_dumps_ = true;
101   for (auto quirk = cfg.quirks(); quirk; ++quirk) {
102     if (*quirk == ProcessStatsConfig::DISABLE_ON_DEMAND)
103       enable_on_demand_dumps_ = false;
104   }
105 
106   poll_period_ms_ = cfg.proc_stats_poll_ms();
107   if (poll_period_ms_ > 0 && poll_period_ms_ < 100) {
108     PERFETTO_ILOG("proc_stats_poll_ms %" PRIu32
109                   " is less than minimum of 100ms. Increasing to 100ms.",
110                   poll_period_ms_);
111     poll_period_ms_ = 100;
112   }
113 
114   if (poll_period_ms_ > 0) {
115     auto proc_stats_ttl_ms = cfg.proc_stats_cache_ttl_ms();
116     process_stats_cache_ttl_ticks_ =
117         std::max(proc_stats_ttl_ms / poll_period_ms_, 1u);
118   }
119 
120   record_thread_time_in_state_ = cfg.record_thread_time_in_state();
121   thread_time_in_state_cache_size_ = cfg.thread_time_in_state_cache_size();
122   if (thread_time_in_state_cache_size_ == 0)
123     thread_time_in_state_cache_size_ = kThreadTimeInStateCacheSize;
124   thread_time_in_state_cache_.resize(thread_time_in_state_cache_size_);
125 }
126 
127 ProcessStatsDataSource::~ProcessStatsDataSource() = default;
128 
Start()129 void ProcessStatsDataSource::Start() {
130   if (dump_all_procs_on_start_)
131     WriteAllProcesses();
132 
133   if (poll_period_ms_) {
134     auto weak_this = GetWeakPtr();
135     task_runner_->PostTask(std::bind(&ProcessStatsDataSource::Tick, weak_this));
136   }
137 }
138 
GetWeakPtr() const139 base::WeakPtr<ProcessStatsDataSource> ProcessStatsDataSource::GetWeakPtr()
140     const {
141   return weak_factory_.GetWeakPtr();
142 }
143 
WriteAllProcesses()144 void ProcessStatsDataSource::WriteAllProcesses() {
145   PERFETTO_METATRACE_SCOPED(TAG_PROC_POLLERS, PS_WRITE_ALL_PROCESSES);
146   PERFETTO_DCHECK(!cur_ps_tree_);
147 
148   CacheProcFsScanStartTimestamp();
149 
150   base::ScopedDir proc_dir = OpenProcDir();
151   if (!proc_dir)
152     return;
153   while (int32_t pid = ReadNextNumericDir(*proc_dir)) {
154     WriteProcessOrThread(pid);
155     char task_path[255];
156     sprintf(task_path, "/proc/%d/task", pid);
157     base::ScopedDir task_dir(opendir(task_path));
158     if (!task_dir)
159       continue;
160 
161     while (int32_t tid = ReadNextNumericDir(*task_dir)) {
162       if (tid == pid)
163         continue;
164       if (record_thread_names_) {
165         WriteProcessOrThread(tid);
166       } else {
167         // If we are not interested in thread names, there is no need to open
168         // a proc file for each thread. We can save time and directly write the
169         // thread record.
170         WriteThread(tid, pid, /*optional_name=*/nullptr);
171       }
172     }
173   }
174   FinalizeCurPacket();
175 }
176 
OnPids(const base::FlatSet<int32_t> & pids)177 void ProcessStatsDataSource::OnPids(const base::FlatSet<int32_t>& pids) {
178   if (!enable_on_demand_dumps_)
179     return;
180   WriteProcessTree(pids);
181 }
182 
WriteProcessTree(const base::FlatSet<int32_t> & pids)183 void ProcessStatsDataSource::WriteProcessTree(
184     const base::FlatSet<int32_t>& pids) {
185   PERFETTO_METATRACE_SCOPED(TAG_PROC_POLLERS, PS_ON_PIDS);
186   PERFETTO_DCHECK(!cur_ps_tree_);
187   int pids_scanned = 0;
188   for (int32_t pid : pids) {
189     if (seen_pids_.count(pid) || pid == 0)
190       continue;
191     WriteProcessOrThread(pid);
192     pids_scanned++;
193   }
194   FinalizeCurPacket();
195   PERFETTO_METATRACE_COUNTER(TAG_PROC_POLLERS, PS_PIDS_SCANNED, pids_scanned);
196 }
197 
OnRenamePids(const base::FlatSet<int32_t> & pids)198 void ProcessStatsDataSource::OnRenamePids(const base::FlatSet<int32_t>& pids) {
199   PERFETTO_METATRACE_SCOPED(TAG_PROC_POLLERS, PS_ON_RENAME_PIDS);
200   if (!enable_on_demand_dumps_)
201     return;
202   PERFETTO_DCHECK(!cur_ps_tree_);
203   for (int32_t pid : pids)
204     seen_pids_.erase(pid);
205 }
206 
Flush(FlushRequestID,std::function<void ()> callback)207 void ProcessStatsDataSource::Flush(FlushRequestID,
208                                    std::function<void()> callback) {
209   // We shouldn't get this in the middle of WriteAllProcesses() or OnPids().
210   PERFETTO_DCHECK(!cur_ps_tree_);
211   PERFETTO_DCHECK(!cur_ps_stats_);
212   PERFETTO_DCHECK(!cur_ps_stats_process_);
213   writer_->Flush(callback);
214 }
215 
WriteProcessOrThread(int32_t pid)216 void ProcessStatsDataSource::WriteProcessOrThread(int32_t pid) {
217   // In case we're called from outside WriteAllProcesses()
218   CacheProcFsScanStartTimestamp();
219 
220   std::string proc_status = ReadProcPidFile(pid, "status");
221   if (proc_status.empty())
222     return;
223   int tgid = ToInt(ReadProcStatusEntry(proc_status, "Tgid:"));
224   if (tgid <= 0)
225     return;
226   if (!seen_pids_.count(tgid))
227     WriteProcess(tgid, proc_status);
228   if (pid != tgid) {
229     PERFETTO_DCHECK(!seen_pids_.count(pid));
230     std::string thread_name;
231     if (record_thread_names_)
232       thread_name = ReadProcStatusEntry(proc_status, "Name:");
233     WriteThread(pid, tgid, thread_name.empty() ? nullptr : thread_name.c_str());
234   }
235 }
236 
WriteProcess(int32_t pid,const std::string & proc_status)237 void ProcessStatsDataSource::WriteProcess(int32_t pid,
238                                           const std::string& proc_status) {
239   PERFETTO_DCHECK(ToInt(ReadProcStatusEntry(proc_status, "Tgid:")) == pid);
240   auto* proc = GetOrCreatePsTree()->add_processes();
241   proc->set_pid(pid);
242   proc->set_ppid(ToInt(ReadProcStatusEntry(proc_status, "PPid:")));
243   // Uid will have multiple entries, only return first (real uid).
244   proc->set_uid(ToInt(ReadProcStatusEntry(proc_status, "Uid:")));
245 
246   std::string cmdline = ReadProcPidFile(pid, "cmdline");
247   if (!cmdline.empty()) {
248     if (cmdline.back() != '\0') {
249       // Some kernels can miss the NUL terminator due to a bug. b/147438623.
250       cmdline.push_back('\0');
251     }
252     using base::StringSplitter;
253     for (StringSplitter ss(&cmdline[0], cmdline.size(), '\0'); ss.Next();)
254       proc->add_cmdline(ss.cur_token());
255   } else {
256     // Nothing in cmdline so use the thread name instead (which is == "comm").
257     proc->add_cmdline(ReadProcStatusEntry(proc_status, "Name:").c_str());
258   }
259   seen_pids_.insert(pid);
260 }
261 
WriteThread(int32_t tid,int32_t tgid,const char * optional_name)262 void ProcessStatsDataSource::WriteThread(int32_t tid,
263                                          int32_t tgid,
264                                          const char* optional_name) {
265   auto* thread = GetOrCreatePsTree()->add_threads();
266   thread->set_tid(tid);
267   thread->set_tgid(tgid);
268   if (optional_name)
269     thread->set_name(optional_name);
270   seen_pids_.insert(tid);
271 }
272 
OpenProcDir()273 base::ScopedDir ProcessStatsDataSource::OpenProcDir() {
274   base::ScopedDir proc_dir(opendir("/proc"));
275   if (!proc_dir)
276     PERFETTO_PLOG("Failed to opendir(/proc)");
277   return proc_dir;
278 }
279 
ReadProcPidFile(int32_t pid,const std::string & file)280 std::string ProcessStatsDataSource::ReadProcPidFile(int32_t pid,
281                                                     const std::string& file) {
282   std::string contents;
283   contents.reserve(4096);
284   if (!base::ReadFile("/proc/" + std::to_string(pid) + "/" + file, &contents))
285     return "";
286   return contents;
287 }
288 
OpenProcTaskDir(int32_t pid)289 base::ScopedDir ProcessStatsDataSource::OpenProcTaskDir(int32_t pid) {
290   char task_path[255];
291   sprintf(task_path, "/proc/%d/task", pid);
292   return base::ScopedDir(opendir(task_path));
293 }
294 
ReadProcStatusEntry(const std::string & buf,const char * key)295 std::string ProcessStatsDataSource::ReadProcStatusEntry(const std::string& buf,
296                                                         const char* key) {
297   auto begin = buf.find(key);
298   if (begin == std::string::npos)
299     return "";
300   begin = buf.find_first_not_of(" \t", begin + strlen(key));
301   if (begin == std::string::npos)
302     return "";
303   auto end = buf.find('\n', begin);
304   if (end == std::string::npos || end <= begin)
305     return "";
306   return buf.substr(begin, end - begin);
307 }
308 
StartNewPacketIfNeeded()309 void ProcessStatsDataSource::StartNewPacketIfNeeded() {
310   if (cur_packet_)
311     return;
312   cur_packet_ = writer_->NewTracePacket();
313   cur_packet_->set_timestamp(CacheProcFsScanStartTimestamp());
314 
315   if (did_clear_incremental_state_) {
316     cur_packet_->set_incremental_state_cleared(true);
317     did_clear_incremental_state_ = false;
318   }
319 }
320 
GetOrCreatePsTree()321 protos::pbzero::ProcessTree* ProcessStatsDataSource::GetOrCreatePsTree() {
322   StartNewPacketIfNeeded();
323   if (!cur_ps_tree_)
324     cur_ps_tree_ = cur_packet_->set_process_tree();
325   cur_ps_stats_ = nullptr;
326   cur_ps_stats_process_ = nullptr;
327   return cur_ps_tree_;
328 }
329 
GetOrCreateStats()330 protos::pbzero::ProcessStats* ProcessStatsDataSource::GetOrCreateStats() {
331   StartNewPacketIfNeeded();
332   if (!cur_ps_stats_)
333     cur_ps_stats_ = cur_packet_->set_process_stats();
334   cur_ps_tree_ = nullptr;
335   cur_ps_stats_process_ = nullptr;
336   return cur_ps_stats_;
337 }
338 
339 protos::pbzero::ProcessStats_Process*
GetOrCreateStatsProcess(int32_t pid)340 ProcessStatsDataSource::GetOrCreateStatsProcess(int32_t pid) {
341   if (cur_ps_stats_process_)
342     return cur_ps_stats_process_;
343   cur_ps_stats_process_ = GetOrCreateStats()->add_processes();
344   cur_ps_stats_process_->set_pid(pid);
345   return cur_ps_stats_process_;
346 }
347 
FinalizeCurPacket()348 void ProcessStatsDataSource::FinalizeCurPacket() {
349   PERFETTO_DCHECK(!cur_ps_tree_ || cur_packet_);
350   PERFETTO_DCHECK(!cur_ps_stats_ || cur_packet_);
351   uint64_t now = static_cast<uint64_t>(base::GetBootTimeNs().count());
352   if (cur_ps_tree_) {
353     cur_ps_tree_->set_collection_end_timestamp(now);
354     cur_ps_tree_ = nullptr;
355   }
356   if (cur_ps_stats_) {
357     cur_ps_stats_->set_collection_end_timestamp(now);
358     cur_ps_stats_ = nullptr;
359   }
360   cur_ps_stats_process_ = nullptr;
361   cur_procfs_scan_start_timestamp_ = 0;
362   cur_packet_ = TraceWriter::TracePacketHandle{};
363 }
364 
365 // static
Tick(base::WeakPtr<ProcessStatsDataSource> weak_this)366 void ProcessStatsDataSource::Tick(
367     base::WeakPtr<ProcessStatsDataSource> weak_this) {
368   if (!weak_this)
369     return;
370   ProcessStatsDataSource& thiz = *weak_this;
371   uint32_t period_ms = thiz.poll_period_ms_;
372   uint32_t delay_ms =
373       period_ms -
374       static_cast<uint32_t>(base::GetWallTimeMs().count() % period_ms);
375   thiz.task_runner_->PostDelayedTask(
376       std::bind(&ProcessStatsDataSource::Tick, weak_this), delay_ms);
377   thiz.WriteAllProcessStats();
378 
379   // We clear the cache every process_stats_cache_ttl_ticks_ ticks.
380   if (++thiz.cache_ticks_ == thiz.process_stats_cache_ttl_ticks_) {
381     thiz.cache_ticks_ = 0;
382     thiz.process_stats_cache_.clear();
383     thiz.thread_time_in_state_cache_.clear();
384     thiz.thread_time_in_state_cache_.resize(
385         thiz.thread_time_in_state_cache_size_);
386   }
387 }
388 
WriteAllProcessStats()389 void ProcessStatsDataSource::WriteAllProcessStats() {
390   // TODO(primiano): implement filtering of processes by names.
391   // TODO(primiano): Have a pid cache to avoid wasting cycles reading kthreads
392   // proc files over and over. Same for non-filtered processes (see above).
393 
394   CacheProcFsScanStartTimestamp();
395   PERFETTO_METATRACE_SCOPED(TAG_PROC_POLLERS, PS_WRITE_ALL_PROCESS_STATS);
396   base::ScopedDir proc_dir = OpenProcDir();
397   if (!proc_dir)
398     return;
399   base::FlatSet<int32_t> pids;
400   while (int32_t pid = ReadNextNumericDir(*proc_dir)) {
401     cur_ps_stats_process_ = nullptr;
402 
403     uint32_t pid_u = static_cast<uint32_t>(pid);
404     if (skip_stats_for_pids_.size() > pid_u && skip_stats_for_pids_[pid_u])
405       continue;
406 
407     std::string proc_status = ReadProcPidFile(pid, "status");
408     if (proc_status.empty())
409       continue;
410 
411     if (!WriteMemCounters(pid, proc_status)) {
412       // If WriteMemCounters() fails the pid is very likely a kernel thread
413       // that has a valid /proc/[pid]/status but no memory values. In this
414       // case avoid keep polling it over and over.
415       if (skip_stats_for_pids_.size() <= pid_u)
416         skip_stats_for_pids_.resize(pid_u + 1);
417       skip_stats_for_pids_[pid_u] = true;
418       continue;
419     }
420 
421     std::string oom_score_adj = ReadProcPidFile(pid, "oom_score_adj");
422     if (!oom_score_adj.empty()) {
423       CachedProcessStats& cached = process_stats_cache_[pid];
424       auto counter = ToInt(oom_score_adj);
425       if (counter != cached.oom_score_adj) {
426         GetOrCreateStatsProcess(pid)->set_oom_score_adj(counter);
427         cached.oom_score_adj = counter;
428       }
429     }
430 
431     if (record_thread_time_in_state_ && ShouldWriteThreadStats(pid)) {
432       if (auto task_dir = OpenProcTaskDir(pid)) {
433         while (int32_t tid = ReadNextNumericDir(*task_dir)) {
434           WriteThreadStats(pid, tid);
435           pids.insert(tid);
436         }
437       }
438     }
439 
440     pids.insert(pid);
441   }
442   FinalizeCurPacket();
443 
444   // Ensure that we write once long-term process info (e.g., name) for new pids
445   // that we haven't seen before.
446   WriteProcessTree(pids);
447 }
448 
449 // Returns true if the stats for the given |pid| have been written, false it
450 // it failed (e.g., |pid| was a kernel thread and, as such, didn't report any
451 // memory counters).
WriteMemCounters(int32_t pid,const std::string & proc_status)452 bool ProcessStatsDataSource::WriteMemCounters(int32_t pid,
453                                               const std::string& proc_status) {
454   bool proc_status_has_mem_counters = false;
455   CachedProcessStats& cached = process_stats_cache_[pid];
456 
457   // Parse /proc/[pid]/status, which looks like this:
458   // Name:   cat
459   // Umask:  0027
460   // State:  R (running)
461   // FDSize: 256
462   // Groups: 4 20 24 46 997
463   // VmPeak:     5992 kB
464   // VmSize:     5992 kB
465   // VmLck:         0 kB
466   // ...
467   std::vector<char> key;
468   std::vector<char> value;
469   enum { kKey, kSeparator, kValue } state = kKey;
470   for (char c : proc_status) {
471     if (c == '\n') {
472       key.push_back('\0');
473       value.push_back('\0');
474 
475       // |value| will contain "1234 KB". We rely on strtol() (in ToU32()) to
476       // stop parsing at the first non-numeric character.
477       if (strcmp(key.data(), "VmSize") == 0) {
478         // Assume that if we see VmSize we'll see also the others.
479         proc_status_has_mem_counters = true;
480 
481         auto counter = ToU32(value.data());
482         if (counter != cached.vm_size_kb) {
483           GetOrCreateStatsProcess(pid)->set_vm_size_kb(counter);
484           cached.vm_size_kb = counter;
485         }
486       } else if (strcmp(key.data(), "VmLck") == 0) {
487         auto counter = ToU32(value.data());
488         if (counter != cached.vm_locked_kb) {
489           GetOrCreateStatsProcess(pid)->set_vm_locked_kb(counter);
490           cached.vm_locked_kb = counter;
491         }
492       } else if (strcmp(key.data(), "VmHWM") == 0) {
493         auto counter = ToU32(value.data());
494         if (counter != cached.vm_hvm_kb) {
495           GetOrCreateStatsProcess(pid)->set_vm_hwm_kb(counter);
496           cached.vm_hvm_kb = counter;
497         }
498       } else if (strcmp(key.data(), "VmRSS") == 0) {
499         auto counter = ToU32(value.data());
500         if (counter != cached.vm_rss_kb) {
501           GetOrCreateStatsProcess(pid)->set_vm_rss_kb(counter);
502           cached.vm_rss_kb = counter;
503         }
504       } else if (strcmp(key.data(), "RssAnon") == 0) {
505         auto counter = ToU32(value.data());
506         if (counter != cached.rss_anon_kb) {
507           GetOrCreateStatsProcess(pid)->set_rss_anon_kb(counter);
508           cached.rss_anon_kb = counter;
509         }
510       } else if (strcmp(key.data(), "RssFile") == 0) {
511         auto counter = ToU32(value.data());
512         if (counter != cached.rss_file_kb) {
513           GetOrCreateStatsProcess(pid)->set_rss_file_kb(counter);
514           cached.rss_file_kb = counter;
515         }
516       } else if (strcmp(key.data(), "RssShmem") == 0) {
517         auto counter = ToU32(value.data());
518         if (counter != cached.rss_shmem_kb) {
519           GetOrCreateStatsProcess(pid)->set_rss_shmem_kb(counter);
520           cached.rss_shmem_kb = counter;
521         }
522       } else if (strcmp(key.data(), "VmSwap") == 0) {
523         auto counter = ToU32(value.data());
524         if (counter != cached.vm_swap_kb) {
525           GetOrCreateStatsProcess(pid)->set_vm_swap_kb(counter);
526           cached.vm_swap_kb = counter;
527         }
528       }
529 
530       key.clear();
531       state = kKey;
532       continue;
533     }
534 
535     if (state == kKey) {
536       if (c == ':') {
537         state = kSeparator;
538         continue;
539       }
540       key.push_back(c);
541       continue;
542     }
543 
544     if (state == kSeparator) {
545       if (isspace(c))
546         continue;
547       value.clear();
548       value.push_back(c);
549       state = kValue;
550       continue;
551     }
552 
553     if (state == kValue) {
554       value.push_back(c);
555     }
556   }
557   return proc_status_has_mem_counters;
558 }
559 
560 // Fast check to avoid reading information about all threads of a process.
561 // If the total process cpu time has not changed, we can skip reading
562 // time_in_state for all its threads.
ShouldWriteThreadStats(int32_t pid)563 bool ProcessStatsDataSource::ShouldWriteThreadStats(int32_t pid) {
564   std::string stat = ReadProcPidFile(pid, "stat");
565   // /proc/pid/stat may contain an additional space inside comm. For example:
566   // 1 (comm foo) 2 3 ...
567   // We strip the prefix including comm. So the result is: 2 3 ...
568   size_t comm_end = stat.rfind(") ");
569   if (comm_end == std::string::npos)
570     return false;
571   std::string stat_after_comm = stat.substr(comm_end + 2);
572 
573   // Indices of space separated fields in /proc/pid/stat offset by 2 to make
574   // up for fields removed by stripping the prefix including comm.
575   const uint32_t kStatCTimeIndex = 13 - 2;
576   const uint32_t kStatSTimeIndex = 14 - 2;
577 
578   auto stat_parts = base::SplitString(stat_after_comm, " ");
579   if (stat_parts.size() <= kStatSTimeIndex)
580     return false;
581   auto maybe_ctime = base::StringToUInt64(stat_parts[kStatCTimeIndex]);
582   if (!maybe_ctime.has_value())
583     return false;
584   auto maybe_stime = base::StringToUInt64(stat_parts[kStatSTimeIndex]);
585   if (!maybe_stime.has_value())
586     return false;
587   uint64_t current = maybe_ctime.value() + maybe_stime.value();
588   uint64_t& cached = process_stats_cache_[pid].cpu_time;
589   if (current != cached) {
590     cached = current;
591     return true;
592   }
593   return false;
594 }
595 
WriteThreadStats(int32_t pid,int32_t tid)596 void ProcessStatsDataSource::WriteThreadStats(int32_t pid, int32_t tid) {
597   // Reads /proc/tid/time_in_state, which looks like:
598   // cpu0
599   // 100 0
600   // 200 5
601   // ...
602   // cpu6
603   // 200 0
604   // 300 70
605   // ...
606   // Pairs of CPU frequency and the number of ticks at that frequency.
607   std::string time_in_state = ReadProcPidFile(tid, "time_in_state");
608   // Bail if time_in_state does not have cpuN headings. Parsing this data
609   // without them is more complicated and requires additional information.
610   if (!base::StartsWith(time_in_state, "cpu"))
611     return;
612   protos::pbzero::ProcessStats_Thread* thread = nullptr;
613   base::StringSplitter entries(std::move(time_in_state), '\n');
614   uint32_t last_cpu = 0;
615   // Whether all frequencies with non-zero ticks are added to cpu_freq_indices.
616   bool full = true;
617   while (entries.Next()) {
618     std::string line(entries.cur_token());
619     if (base::StartsWith(line, "cpu")) {
620       last_cpu = base::StringToUInt32(line.substr(3)).value();
621       continue;
622     }
623     base::StringSplitter key_value(&entries, ' ');
624     if (!key_value.Next())
625       continue;
626     uint32_t freq = ToU32(key_value.cur_token());
627     uint32_t freq_index = cpu_freq_info_->GetCpuFreqIndex(last_cpu, freq);
628     if (!key_value.Next())
629       continue;
630     auto maybe_ticks = base::CStringToUInt64(key_value.cur_token());
631     if (!maybe_ticks.has_value())
632       continue;
633     uint64_t ticks = maybe_ticks.value();
634     if (ticks == 0)
635       continue;
636     base::Hash key_hash;
637     key_hash.Update(tid);
638     key_hash.Update(freq_index);
639     size_t key = key_hash.digest() % thread_time_in_state_cache_size_;
640     PERFETTO_DCHECK(thread_time_in_state_cache_.size() ==
641                     thread_time_in_state_cache_size_);
642     TimeInStateCacheEntry& cached = thread_time_in_state_cache_[key];
643     TimeInStateCacheEntry current = {tid, freq_index, ticks};
644     if (current != cached) {
645       cached = current;
646       if (thread == nullptr) {
647         thread = GetOrCreateStatsProcess(pid)->add_threads();
648         thread->set_tid(tid);
649       }
650       thread->add_cpu_freq_indices(freq_index);
651       thread->add_cpu_freq_ticks(ticks);
652     } else {
653       full = false;
654     }
655   }
656   if (full && thread != nullptr) {
657     thread->set_cpu_freq_full(true);
658   }
659 }
660 
CacheProcFsScanStartTimestamp()661 uint64_t ProcessStatsDataSource::CacheProcFsScanStartTimestamp() {
662   if (!cur_procfs_scan_start_timestamp_)
663     cur_procfs_scan_start_timestamp_ =
664         static_cast<uint64_t>(base::GetBootTimeNs().count());
665   return cur_procfs_scan_start_timestamp_;
666 }
667 
ClearIncrementalState()668 void ProcessStatsDataSource::ClearIncrementalState() {
669   PERFETTO_DLOG("ProcessStatsDataSource clearing incremental state.");
670   seen_pids_.clear();
671   skip_stats_for_pids_.clear();
672 
673   cache_ticks_ = 0;
674   process_stats_cache_.clear();
675   thread_time_in_state_cache_.clear();
676   thread_time_in_state_cache_.resize(thread_time_in_state_cache_size_);
677 
678   // Set the relevant flag in the next packet.
679   did_clear_incremental_state_ = true;
680 }
681 
682 }  // namespace perfetto
683