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/sys_stats/sys_stats_data_source.h"
18
19 #include <stdlib.h>
20 #include <unistd.h>
21
22 #include <algorithm>
23 #include <array>
24 #include <bitset>
25 #include <limits>
26 #include <utility>
27
28 #include "perfetto/base/task_runner.h"
29 #include "perfetto/base/time.h"
30 #include "perfetto/ext/base/file_utils.h"
31 #include "perfetto/ext/base/metatrace.h"
32 #include "perfetto/ext/base/scoped_file.h"
33 #include "perfetto/ext/base/string_splitter.h"
34 #include "perfetto/ext/base/string_utils.h"
35 #include "perfetto/ext/base/utils.h"
36 #include "perfetto/ext/traced/sys_stats_counters.h"
37
38 #include "protos/perfetto/common/sys_stats_counters.pbzero.h"
39 #include "protos/perfetto/config/sys_stats/sys_stats_config.pbzero.h"
40 #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
41 #include "protos/perfetto/trace/trace_packet.pbzero.h"
42
43 namespace perfetto {
44
45 using protos::pbzero::SysStatsConfig;
46
47 namespace {
48 constexpr size_t kReadBufSize = 1024 * 16;
49
OpenReadOnly(const char * path)50 base::ScopedFile OpenReadOnly(const char* path) {
51 base::ScopedFile fd(base::OpenFile(path, O_RDONLY));
52 if (!fd)
53 PERFETTO_PLOG("Failed opening %s", path);
54 return fd;
55 }
56
ClampTo10Ms(uint32_t period_ms,const char * counter_name)57 uint32_t ClampTo10Ms(uint32_t period_ms, const char* counter_name) {
58 if (period_ms > 0 && period_ms < 10) {
59 PERFETTO_ILOG("%s %" PRIu32
60 " is less than minimum of 10ms. Increasing to 10ms.",
61 counter_name, period_ms);
62 return 10;
63 }
64 return period_ms;
65 }
66
67 } // namespace
68
69 // static
70 const ProbesDataSource::Descriptor SysStatsDataSource::descriptor = {
71 /*name*/ "linux.sys_stats",
72 /*flags*/ Descriptor::kFlagsNone,
73 /*fill_descriptor_func*/ nullptr,
74 };
75
SysStatsDataSource(base::TaskRunner * task_runner,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer,const DataSourceConfig & ds_config,std::unique_ptr<CpuFreqInfo> cpu_freq_info,OpenFunction open_fn)76 SysStatsDataSource::SysStatsDataSource(
77 base::TaskRunner* task_runner,
78 TracingSessionID session_id,
79 std::unique_ptr<TraceWriter> writer,
80 const DataSourceConfig& ds_config,
81 std::unique_ptr<CpuFreqInfo> cpu_freq_info,
82 OpenFunction open_fn)
83 : ProbesDataSource(session_id, &descriptor),
84 task_runner_(task_runner),
85 writer_(std::move(writer)),
86 cpu_freq_info_(std::move(cpu_freq_info)),
87 weak_factory_(this) {
88 ns_per_user_hz_ = 1000000000ull / static_cast<uint64_t>(sysconf(_SC_CLK_TCK));
89
90 open_fn = open_fn ? open_fn : OpenReadOnly;
91 meminfo_fd_ = open_fn("/proc/meminfo");
92 vmstat_fd_ = open_fn("/proc/vmstat");
93 stat_fd_ = open_fn("/proc/stat");
94 buddy_fd_ = open_fn("/proc/buddyinfo");
95 diskstat_fd_ = open_fn("/proc/diskstats");
96 psi_cpu_fd_ = open_fn("/proc/pressure/cpu");
97 psi_io_fd_ = open_fn("/proc/pressure/io");
98 psi_memory_fd_ = open_fn("/proc/pressure/memory");
99
100 read_buf_ = base::PagedMemory::Allocate(kReadBufSize);
101
102 // Build a lookup map that allows to quickly translate strings like "MemTotal"
103 // into the corresponding enum value, only for the counters enabled in the
104 // config.
105
106 using protos::pbzero::SysStatsConfig;
107 SysStatsConfig::Decoder cfg(ds_config.sys_stats_config_raw());
108
109 constexpr size_t kMaxMeminfoEnum = protos::pbzero::MeminfoCounters_MAX;
110 std::bitset<kMaxMeminfoEnum + 1> meminfo_counters_enabled{};
111 if (!cfg.has_meminfo_counters())
112 meminfo_counters_enabled.set();
113 for (auto it = cfg.meminfo_counters(); it; ++it) {
114 uint32_t counter = static_cast<uint32_t>(*it);
115 if (counter > 0 && counter <= kMaxMeminfoEnum) {
116 meminfo_counters_enabled.set(counter);
117 } else {
118 PERFETTO_DFATAL("Meminfo counter out of bounds %u", counter);
119 }
120 }
121 for (size_t i = 0; i < base::ArraySize(kMeminfoKeys); i++) {
122 const auto& k = kMeminfoKeys[i];
123 if (meminfo_counters_enabled[static_cast<size_t>(k.id)])
124 meminfo_counters_.emplace(k.str, k.id);
125 }
126
127 constexpr size_t kMaxVmstatEnum = protos::pbzero::VmstatCounters_MAX;
128 std::bitset<kMaxVmstatEnum + 1> vmstat_counters_enabled{};
129 if (!cfg.has_vmstat_counters())
130 vmstat_counters_enabled.set();
131 for (auto it = cfg.vmstat_counters(); it; ++it) {
132 uint32_t counter = static_cast<uint32_t>(*it);
133 if (counter > 0 && counter <= kMaxVmstatEnum) {
134 vmstat_counters_enabled.set(counter);
135 } else {
136 PERFETTO_DFATAL("Vmstat counter out of bounds %u", counter);
137 }
138 }
139 for (size_t i = 0; i < base::ArraySize(kVmstatKeys); i++) {
140 const auto& k = kVmstatKeys[i];
141 if (vmstat_counters_enabled[static_cast<size_t>(k.id)])
142 vmstat_counters_.emplace(k.str, k.id);
143 }
144
145 if (!cfg.has_stat_counters())
146 stat_enabled_fields_ = ~0u;
147 for (auto counter = cfg.stat_counters(); counter; ++counter) {
148 stat_enabled_fields_ |= 1ul << static_cast<uint32_t>(*counter);
149 }
150
151 std::array<uint32_t, 8> periods_ms{};
152 std::array<uint32_t, 8> ticks{};
153 static_assert(periods_ms.size() == ticks.size(), "must have same size");
154
155 periods_ms[0] = ClampTo10Ms(cfg.meminfo_period_ms(), "meminfo_period_ms");
156 periods_ms[1] = ClampTo10Ms(cfg.vmstat_period_ms(), "vmstat_period_ms");
157 periods_ms[2] = ClampTo10Ms(cfg.stat_period_ms(), "stat_period_ms");
158 periods_ms[3] = ClampTo10Ms(cfg.devfreq_period_ms(), "devfreq_period_ms");
159 periods_ms[4] = ClampTo10Ms(cfg.cpufreq_period_ms(), "cpufreq_period_ms");
160 periods_ms[5] = ClampTo10Ms(cfg.buddyinfo_period_ms(), "buddyinfo_period_ms");
161 periods_ms[6] = ClampTo10Ms(cfg.diskstat_period_ms(), "diskstat_period_ms");
162 periods_ms[7] = ClampTo10Ms(cfg.psi_period_ms(), "psi_period_ms");
163
164 tick_period_ms_ = 0;
165 for (uint32_t ms : periods_ms) {
166 if (ms && (ms < tick_period_ms_ || tick_period_ms_ == 0))
167 tick_period_ms_ = ms;
168 }
169
170 if (tick_period_ms_ == 0)
171 return; // No polling configured.
172
173 for (size_t i = 0; i < periods_ms.size(); i++) {
174 auto ms = periods_ms[i];
175 if (ms && ms % tick_period_ms_ != 0) {
176 PERFETTO_ELOG("SysStat periods are not integer multiples of each other");
177 return;
178 }
179 ticks[i] = ms / tick_period_ms_;
180 }
181 meminfo_ticks_ = ticks[0];
182 vmstat_ticks_ = ticks[1];
183 stat_ticks_ = ticks[2];
184 devfreq_ticks_ = ticks[3];
185 cpufreq_ticks_ = ticks[4];
186 buddyinfo_ticks_ = ticks[5];
187 diskstat_ticks_ = ticks[6];
188 psi_ticks_ = ticks[7];
189 }
190
Start()191 void SysStatsDataSource::Start() {
192 auto weak_this = GetWeakPtr();
193 task_runner_->PostTask(std::bind(&SysStatsDataSource::Tick, weak_this));
194 }
195
196 // static
Tick(base::WeakPtr<SysStatsDataSource> weak_this)197 void SysStatsDataSource::Tick(base::WeakPtr<SysStatsDataSource> weak_this) {
198 if (!weak_this)
199 return;
200 SysStatsDataSource& thiz = *weak_this;
201
202 uint32_t period_ms = thiz.tick_period_ms_;
203 uint32_t delay_ms =
204 period_ms -
205 static_cast<uint32_t>(base::GetWallTimeMs().count() % period_ms);
206 thiz.task_runner_->PostDelayedTask(
207 std::bind(&SysStatsDataSource::Tick, weak_this), delay_ms);
208 thiz.ReadSysStats();
209 }
210
211 SysStatsDataSource::~SysStatsDataSource() = default;
212
ReadSysStats()213 void SysStatsDataSource::ReadSysStats() {
214 PERFETTO_METATRACE_SCOPED(TAG_PROC_POLLERS, READ_SYS_STATS);
215 auto packet = writer_->NewTracePacket();
216
217 packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
218 auto* sys_stats = packet->set_sys_stats();
219
220 if (meminfo_ticks_ && tick_ % meminfo_ticks_ == 0)
221 ReadMeminfo(sys_stats);
222
223 if (vmstat_ticks_ && tick_ % vmstat_ticks_ == 0)
224 ReadVmstat(sys_stats);
225
226 if (stat_ticks_ && tick_ % stat_ticks_ == 0)
227 ReadStat(sys_stats);
228
229 if (devfreq_ticks_ && tick_ % devfreq_ticks_ == 0)
230 ReadDevfreq(sys_stats);
231
232 if (cpufreq_ticks_ && tick_ % cpufreq_ticks_ == 0)
233 ReadCpufreq(sys_stats);
234
235 if (buddyinfo_ticks_ && tick_ % buddyinfo_ticks_ == 0)
236 ReadBuddyInfo(sys_stats);
237
238 if (diskstat_ticks_ && tick_ % diskstat_ticks_ == 0)
239 ReadDiskStat(sys_stats);
240
241 if (psi_ticks_ && tick_ % psi_ticks_ == 0)
242 ReadPsi(sys_stats);
243
244 sys_stats->set_collection_end_timestamp(
245 static_cast<uint64_t>(base::GetBootTimeNs().count()));
246
247 tick_++;
248 }
249
ReadDiskStat(protos::pbzero::SysStats * sys_stats)250 void SysStatsDataSource::ReadDiskStat(protos::pbzero::SysStats* sys_stats) {
251 size_t rsize = ReadFile(&diskstat_fd_, "/proc/diskstats");
252 if (!rsize) {
253 return;
254 }
255
256 char* buf = static_cast<char*>(read_buf_.Get());
257 for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
258 uint32_t index = 0;
259 auto* disk_stat = sys_stats->add_disk_stat();
260 for (base::StringSplitter words(&lines, ' '); words.Next(); index++) {
261 if (index == 2) { // index for device name (string)
262 disk_stat->set_device_name(words.cur_token());
263 } else if (index >= 5) { // integer values from index 5
264 std::optional<uint64_t> value_address =
265 base::CStringToUInt64(words.cur_token());
266 uint64_t value = value_address ? *value_address : 0;
267
268 switch (index) {
269 case 5:
270 disk_stat->set_read_sectors(value);
271 break;
272 case 6:
273 disk_stat->set_read_time_ms(value);
274 break;
275 case 9:
276 disk_stat->set_write_sectors(value);
277 break;
278 case 10:
279 disk_stat->set_write_time_ms(value);
280 break;
281 case 16:
282 disk_stat->set_discard_sectors(value);
283 break;
284 case 17:
285 disk_stat->set_discard_time_ms(value);
286 break;
287 case 18:
288 disk_stat->set_flush_count(value);
289 break;
290 case 19:
291 disk_stat->set_flush_time_ms(value);
292 break;
293 }
294
295 if (index == 19) {
296 break;
297 }
298 }
299 }
300 }
301 }
302
ReadPsi(protos::pbzero::SysStats * sys_stats)303 void SysStatsDataSource::ReadPsi(protos::pbzero::SysStats* sys_stats) {
304 using PsiSample = protos::pbzero::SysStats::PsiSample;
305
306 auto read_psi_resource = [this, sys_stats](
307 base::ScopedFile* file, const char* path,
308 PsiSample::PsiResource resource_some,
309 PsiSample::PsiResource resource_full) {
310 size_t rsize = ReadFile(file, path);
311 if (!rsize) {
312 return;
313 }
314
315 char* buf = static_cast<char*>(read_buf_.Get());
316 for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
317 uint32_t index = 0;
318 auto* psi = sys_stats->add_psi();
319 for (base::StringSplitter words(&lines, ' '); words.Next(); ++index) {
320 base::StringView token(words.cur_token(), words.cur_token_size());
321 // A single line is of the form (note we skip avg parsing, indexes 1-3):
322 // some avg10=0.00 avg60=0.00 avg300=0.00 total=0
323 if (index == 0) {
324 auto resource = token == "some" ? resource_some
325 : token == "full" ? resource_full
326 : PsiSample::PSI_RESOURCE_UNSPECIFIED;
327 psi->set_resource(resource);
328 } else if (index == 4) {
329 const base::StringView prefix("total=");
330 if (token.StartsWith(prefix)) {
331 token = token.substr(prefix.size());
332 }
333 // The raw PSI total readings are in micros, so convert accordingly.
334 std::optional<uint64_t> total_us =
335 base::CStringToUInt64(token.data());
336 uint64_t total_ns = total_us ? *total_us * 1000 : 0;
337 psi->set_total_ns(total_ns);
338 } else if (index > 4) {
339 break;
340 }
341 }
342 }
343 };
344
345 read_psi_resource(&psi_cpu_fd_, "/proc/pressure/cpu",
346 PsiSample::PSI_RESOURCE_CPU_SOME,
347 PsiSample::PSI_RESOURCE_CPU_FULL);
348 read_psi_resource(&psi_io_fd_, "/proc/pressure/io",
349 PsiSample::PSI_RESOURCE_IO_SOME,
350 PsiSample::PSI_RESOURCE_IO_FULL);
351 read_psi_resource(&psi_memory_fd_, "/proc/pressure/memory",
352 PsiSample::PSI_RESOURCE_MEMORY_SOME,
353 PsiSample::PSI_RESOURCE_MEMORY_FULL);
354 }
355
ReadBuddyInfo(protos::pbzero::SysStats * sys_stats)356 void SysStatsDataSource::ReadBuddyInfo(protos::pbzero::SysStats* sys_stats) {
357 size_t rsize = ReadFile(&buddy_fd_, "/proc/buddyinfo");
358 if (!rsize) {
359 return;
360 }
361
362 char* buf = static_cast<char*>(read_buf_.Get());
363 for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
364 uint32_t index = 0;
365 auto* buddy_info = sys_stats->add_buddy_info();
366 for (base::StringSplitter words(&lines, ' '); words.Next();) {
367 if (index == 1) {
368 std::string token = words.cur_token();
369 token = token.substr(0, token.find(","));
370 buddy_info->set_node(token);
371 } else if (index == 3) {
372 buddy_info->set_zone(words.cur_token());
373 } else if (index > 3) {
374 buddy_info->add_order_pages(
375 static_cast<uint32_t>(strtoul(words.cur_token(), nullptr, 0)));
376 }
377 index++;
378 }
379 }
380 }
381
ReadDevfreq(protos::pbzero::SysStats * sys_stats)382 void SysStatsDataSource::ReadDevfreq(protos::pbzero::SysStats* sys_stats) {
383 base::ScopedDir devfreq_dir = OpenDevfreqDir();
384 if (devfreq_dir) {
385 while (struct dirent* dir_ent = readdir(*devfreq_dir)) {
386 // Entries in /sys/class/devfreq are symlinks to /devices/platform
387 if (dir_ent->d_type != DT_LNK)
388 continue;
389 const char* name = dir_ent->d_name;
390 const char* file_content = ReadDevfreqCurFreq(name);
391 auto value = static_cast<uint64_t>(strtoll(file_content, nullptr, 10));
392 auto* devfreq = sys_stats->add_devfreq();
393 devfreq->set_key(name);
394 devfreq->set_value(value);
395 }
396 }
397 }
398
ReadCpufreq(protos::pbzero::SysStats * sys_stats)399 void SysStatsDataSource::ReadCpufreq(protos::pbzero::SysStats* sys_stats) {
400 const auto& cpufreq = cpu_freq_info_->ReadCpuCurrFreq();
401
402 for (const auto& c : cpufreq)
403 sys_stats->add_cpufreq_khz(c);
404 }
405
OpenDevfreqDir()406 base::ScopedDir SysStatsDataSource::OpenDevfreqDir() {
407 const char* base_dir = "/sys/class/devfreq/";
408 base::ScopedDir devfreq_dir(opendir(base_dir));
409 if (!devfreq_dir && !devfreq_error_logged_) {
410 devfreq_error_logged_ = true;
411 PERFETTO_PLOG("failed to opendir(/sys/class/devfreq)");
412 }
413 return devfreq_dir;
414 }
415
ReadDevfreqCurFreq(const std::string & deviceName)416 const char* SysStatsDataSource::ReadDevfreqCurFreq(
417 const std::string& deviceName) {
418 const char* devfreq_base_path = "/sys/class/devfreq";
419 const char* freq_file_name = "cur_freq";
420 base::StackString<256> cur_freq_path("%s/%s/%s", devfreq_base_path,
421 deviceName.c_str(), freq_file_name);
422 base::ScopedFile fd = OpenReadOnly(cur_freq_path.c_str());
423 if (!fd && !devfreq_error_logged_) {
424 devfreq_error_logged_ = true;
425 PERFETTO_PLOG("Failed to open %s", cur_freq_path.c_str());
426 return "";
427 }
428 size_t rsize = ReadFile(&fd, cur_freq_path.c_str());
429 if (!rsize)
430 return "";
431 return static_cast<char*>(read_buf_.Get());
432 }
433
ReadMeminfo(protos::pbzero::SysStats * sys_stats)434 void SysStatsDataSource::ReadMeminfo(protos::pbzero::SysStats* sys_stats) {
435 size_t rsize = ReadFile(&meminfo_fd_, "/proc/meminfo");
436 if (!rsize)
437 return;
438 char* buf = static_cast<char*>(read_buf_.Get());
439 for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
440 base::StringSplitter words(&lines, ' ');
441 if (!words.Next())
442 continue;
443 // Extract the meminfo key, dropping trailing ':' (e.g., "MemTotal: NN KB").
444 words.cur_token()[words.cur_token_size() - 1] = '\0';
445 auto it = meminfo_counters_.find(words.cur_token());
446 if (it == meminfo_counters_.end())
447 continue;
448 int counter_id = it->second;
449 if (!words.Next())
450 continue;
451 auto value = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
452 auto* meminfo = sys_stats->add_meminfo();
453 meminfo->set_key(static_cast<protos::pbzero::MeminfoCounters>(counter_id));
454 meminfo->set_value(value);
455 }
456 }
457
ReadVmstat(protos::pbzero::SysStats * sys_stats)458 void SysStatsDataSource::ReadVmstat(protos::pbzero::SysStats* sys_stats) {
459 size_t rsize = ReadFile(&vmstat_fd_, "/proc/vmstat");
460 if (!rsize)
461 return;
462 char* buf = static_cast<char*>(read_buf_.Get());
463 for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
464 base::StringSplitter words(&lines, ' ');
465 if (!words.Next())
466 continue;
467 auto it = vmstat_counters_.find(words.cur_token());
468 if (it == vmstat_counters_.end())
469 continue;
470 int counter_id = it->second;
471 if (!words.Next())
472 continue;
473 auto value = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
474 auto* vmstat = sys_stats->add_vmstat();
475 vmstat->set_key(static_cast<protos::pbzero::VmstatCounters>(counter_id));
476 vmstat->set_value(value);
477 }
478 }
479
ReadStat(protos::pbzero::SysStats * sys_stats)480 void SysStatsDataSource::ReadStat(protos::pbzero::SysStats* sys_stats) {
481 size_t rsize = ReadFile(&stat_fd_, "/proc/stat");
482 if (!rsize)
483 return;
484 char* buf = static_cast<char*>(read_buf_.Get());
485 for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
486 base::StringSplitter words(&lines, ' ');
487 if (!words.Next())
488 continue;
489
490 // Per-CPU stats.
491 if ((stat_enabled_fields_ & (1 << SysStatsConfig::STAT_CPU_TIMES)) &&
492 words.cur_token_size() > 3 && !strncmp(words.cur_token(), "cpu", 3)) {
493 long cpu_id = strtol(words.cur_token() + 3, nullptr, 10);
494 std::array<uint64_t, 7> cpu_times{};
495 for (size_t i = 0; i < cpu_times.size() && words.Next(); i++) {
496 cpu_times[i] =
497 static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
498 }
499 auto* cpu_stat = sys_stats->add_cpu_stat();
500 cpu_stat->set_cpu_id(static_cast<uint32_t>(cpu_id));
501 cpu_stat->set_user_ns(cpu_times[0] * ns_per_user_hz_);
502 cpu_stat->set_user_nice_ns(cpu_times[1] * ns_per_user_hz_);
503 cpu_stat->set_system_mode_ns(cpu_times[2] * ns_per_user_hz_);
504 cpu_stat->set_idle_ns(cpu_times[3] * ns_per_user_hz_);
505 cpu_stat->set_io_wait_ns(cpu_times[4] * ns_per_user_hz_);
506 cpu_stat->set_irq_ns(cpu_times[5] * ns_per_user_hz_);
507 cpu_stat->set_softirq_ns(cpu_times[6] * ns_per_user_hz_);
508 }
509 // IRQ counters
510 else if ((stat_enabled_fields_ & (1 << SysStatsConfig::STAT_IRQ_COUNTS)) &&
511 !strcmp(words.cur_token(), "intr")) {
512 for (size_t i = 0; words.Next(); i++) {
513 auto v = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
514 if (i == 0) {
515 sys_stats->set_num_irq_total(v);
516 } else if (v > 0) {
517 auto* irq_stat = sys_stats->add_num_irq();
518 irq_stat->set_irq(static_cast<int32_t>(i - 1));
519 irq_stat->set_count(v);
520 }
521 }
522 }
523 // Softirq counters.
524 else if ((stat_enabled_fields_ &
525 (1 << SysStatsConfig::STAT_SOFTIRQ_COUNTS)) &&
526 !strcmp(words.cur_token(), "softirq")) {
527 for (size_t i = 0; words.Next(); i++) {
528 auto v = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
529 if (i == 0) {
530 sys_stats->set_num_softirq_total(v);
531 } else {
532 auto* softirq_stat = sys_stats->add_num_softirq();
533 softirq_stat->set_irq(static_cast<int32_t>(i - 1));
534 softirq_stat->set_count(v);
535 }
536 }
537 }
538 // Number of forked processes since boot.
539 else if ((stat_enabled_fields_ & (1 << SysStatsConfig::STAT_FORK_COUNT)) &&
540 !strcmp(words.cur_token(), "processes")) {
541 if (words.Next()) {
542 sys_stats->set_num_forks(
543 static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10)));
544 }
545 }
546
547 } // for (line)
548 }
549
GetWeakPtr() const550 base::WeakPtr<SysStatsDataSource> SysStatsDataSource::GetWeakPtr() const {
551 return weak_factory_.GetWeakPtr();
552 }
553
Flush(FlushRequestID,std::function<void ()> callback)554 void SysStatsDataSource::Flush(FlushRequestID, std::function<void()> callback) {
555 writer_->Flush(callback);
556 }
557
ReadFile(base::ScopedFile * fd,const char * path)558 size_t SysStatsDataSource::ReadFile(base::ScopedFile* fd, const char* path) {
559 if (!*fd)
560 return 0;
561 ssize_t res = pread(**fd, read_buf_.Get(), kReadBufSize - 1, 0);
562 if (res <= 0) {
563 PERFETTO_PLOG("Failed reading %s", path);
564 fd->reset();
565 return 0;
566 }
567 size_t rsize = static_cast<size_t>(res);
568 static_cast<char*>(read_buf_.Get())[rsize] = '\0';
569 return rsize + 1; // Include null terminator in the count.
570 }
571
572 } // namespace perfetto
573