• 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/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 <limits>
25 #include <utility>
26 
27 #include "perfetto/base/file_utils.h"
28 #include "perfetto/base/metatrace.h"
29 #include "perfetto/base/scoped_file.h"
30 #include "perfetto/base/string_splitter.h"
31 #include "perfetto/base/task_runner.h"
32 #include "perfetto/base/time.h"
33 #include "perfetto/base/utils.h"
34 #include "perfetto/traced/sys_stats_counters.h"
35 #include "perfetto/tracing/core/sys_stats_config.h"
36 
37 #include "perfetto/common/sys_stats_counters.pb.h"
38 #include "perfetto/config/sys_stats/sys_stats_config.pb.h"
39 #include "perfetto/trace/sys_stats/sys_stats.pbzero.h"
40 #include "perfetto/trace/trace_packet.pbzero.h"
41 
42 namespace perfetto {
43 
44 namespace {
45 constexpr size_t kReadBufSize = 1024 * 16;
46 
OpenReadOnly(const char * path)47 base::ScopedFile OpenReadOnly(const char* path) {
48   base::ScopedFile fd(base::OpenFile(path, O_RDONLY));
49   if (!fd)
50     PERFETTO_PLOG("Failed opening %s", path);
51   return fd;
52 }
53 
ClampTo10Ms(uint32_t period_ms,const char * counter_name)54 uint32_t ClampTo10Ms(uint32_t period_ms, const char* counter_name) {
55   if (period_ms > 0 && period_ms < 10) {
56     PERFETTO_ILOG("%s %" PRIu32
57                   " is less than minimum of 10ms. Increasing to 10ms.",
58                   counter_name, period_ms);
59     return 10;
60   }
61   return period_ms;
62 }
63 
64 }  // namespace
65 
66 // static
67 constexpr int SysStatsDataSource::kTypeId;
68 
SysStatsDataSource(base::TaskRunner * task_runner,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer,const DataSourceConfig & ds_config,OpenFunction open_fn)69 SysStatsDataSource::SysStatsDataSource(base::TaskRunner* task_runner,
70                                        TracingSessionID session_id,
71                                        std::unique_ptr<TraceWriter> writer,
72                                        const DataSourceConfig& ds_config,
73                                        OpenFunction open_fn)
74     : ProbesDataSource(session_id, kTypeId),
75       task_runner_(task_runner),
76       writer_(std::move(writer)),
77       weak_factory_(this) {
78   const auto& config = ds_config.sys_stats_config();
79 
80   ns_per_user_hz_ = 1000000000ull / static_cast<uint64_t>(sysconf(_SC_CLK_TCK));
81 
82   open_fn = open_fn ? open_fn : OpenReadOnly;
83   meminfo_fd_ = open_fn("/proc/meminfo");
84   vmstat_fd_ = open_fn("/proc/vmstat");
85   stat_fd_ = open_fn("/proc/stat");
86 
87   read_buf_ = base::PagedMemory::Allocate(kReadBufSize);
88 
89   // Build a lookup map that allows to quickly translate strings like "MemTotal"
90   // into the corresponding enum value, only for the counters enabled in the
91   // config.
92   for (const auto& counter_id : config.meminfo_counters()) {
93     for (size_t i = 0; i < base::ArraySize(kMeminfoKeys); i++) {
94       const auto& k = kMeminfoKeys[i];
95       if (static_cast<int>(k.id) == static_cast<int>(counter_id))
96         meminfo_counters_.emplace(k.str, k.id);
97     }
98   }
99 
100   for (const auto& counter_id : config.vmstat_counters()) {
101     for (size_t i = 0; i < base::ArraySize(kVmstatKeys); i++) {
102       const auto& k = kVmstatKeys[i];
103       if (static_cast<int>(k.id) == static_cast<int>(counter_id))
104         vmstat_counters_.emplace(k.str, k.id);
105     }
106   }
107 
108   for (const auto& counter_id : config.stat_counters()) {
109     stat_enabled_fields_ |= 1 << counter_id;
110   }
111 
112   std::array<uint32_t, 3> periods_ms{};
113   std::array<uint32_t, 3> ticks{};
114   static_assert(periods_ms.size() == ticks.size(), "must have same size");
115 
116   periods_ms[0] = ClampTo10Ms(config.meminfo_period_ms(), "meminfo_period_ms");
117   periods_ms[1] = ClampTo10Ms(config.vmstat_period_ms(), "vmstat_period_ms");
118   periods_ms[2] = ClampTo10Ms(config.stat_period_ms(), "stat_period_ms");
119 
120   tick_period_ms_ = 0;
121   for (uint32_t ms : periods_ms) {
122     if (ms && (ms < tick_period_ms_ || tick_period_ms_ == 0))
123       tick_period_ms_ = ms;
124   }
125   if (tick_period_ms_ == 0)
126     return;  // No polling configured.
127 
128   for (size_t i = 0; i < periods_ms.size(); i++) {
129     auto ms = periods_ms[i];
130     if (ms && ms % tick_period_ms_ != 0) {
131       PERFETTO_ELOG("SysStat periods are not integer multiples of each other");
132       return;
133     }
134     ticks[i] = ms / tick_period_ms_;
135   }
136   meminfo_ticks_ = ticks[0];
137   vmstat_ticks_ = ticks[1];
138   stat_ticks_ = ticks[2];
139 }
140 
Start()141 void SysStatsDataSource::Start() {
142   auto weak_this = GetWeakPtr();
143   task_runner_->PostTask(std::bind(&SysStatsDataSource::Tick, weak_this));
144 }
145 
146 // static
Tick(base::WeakPtr<SysStatsDataSource> weak_this)147 void SysStatsDataSource::Tick(base::WeakPtr<SysStatsDataSource> weak_this) {
148   if (!weak_this)
149     return;
150   SysStatsDataSource& thiz = *weak_this;
151 
152   uint32_t period_ms = thiz.tick_period_ms_;
153   uint32_t delay_ms = period_ms - (base::GetWallTimeMs().count() % period_ms);
154   thiz.task_runner_->PostDelayedTask(
155       std::bind(&SysStatsDataSource::Tick, weak_this), delay_ms);
156   thiz.ReadSysStats();
157 }
158 
159 SysStatsDataSource::~SysStatsDataSource() = default;
160 
ReadSysStats()161 void SysStatsDataSource::ReadSysStats() {
162   PERFETTO_METATRACE("ReadSysStats", 0);
163   auto packet = writer_->NewTracePacket();
164 
165   packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
166   auto* sys_stats = packet->set_sys_stats();
167 
168   if (meminfo_ticks_ && tick_ % meminfo_ticks_ == 0)
169     ReadMeminfo(sys_stats);
170 
171   if (vmstat_ticks_ && tick_ % vmstat_ticks_ == 0)
172     ReadVmstat(sys_stats);
173 
174   if (stat_ticks_ && tick_ % stat_ticks_ == 0)
175     ReadStat(sys_stats);
176 
177   sys_stats->set_collection_end_timestamp(
178       static_cast<uint64_t>(base::GetBootTimeNs().count()));
179 
180   tick_++;
181 }
182 
ReadMeminfo(protos::pbzero::SysStats * sys_stats)183 void SysStatsDataSource::ReadMeminfo(protos::pbzero::SysStats* sys_stats) {
184   size_t rsize = ReadFile(&meminfo_fd_, "/proc/meminfo");
185   if (!rsize)
186     return;
187   char* buf = static_cast<char*>(read_buf_.Get());
188   for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
189     base::StringSplitter words(&lines, ' ');
190     if (!words.Next())
191       continue;
192     // Extract the meminfo key, dropping trailing ':' (e.g., "MemTotal: NN KB").
193     words.cur_token()[words.cur_token_size() - 1] = '\0';
194     auto it = meminfo_counters_.find(words.cur_token());
195     if (it == meminfo_counters_.end())
196       continue;
197     int counter_id = it->second;
198     if (!words.Next())
199       continue;
200     auto value = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
201     auto* meminfo = sys_stats->add_meminfo();
202     meminfo->set_key(static_cast<protos::pbzero::MeminfoCounters>(counter_id));
203     meminfo->set_value(value);
204   }
205 }
206 
ReadVmstat(protos::pbzero::SysStats * sys_stats)207 void SysStatsDataSource::ReadVmstat(protos::pbzero::SysStats* sys_stats) {
208   size_t rsize = ReadFile(&vmstat_fd_, "/proc/vmstat");
209   if (!rsize)
210     return;
211   char* buf = static_cast<char*>(read_buf_.Get());
212   for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
213     base::StringSplitter words(&lines, ' ');
214     if (!words.Next())
215       continue;
216     auto it = vmstat_counters_.find(words.cur_token());
217     if (it == vmstat_counters_.end())
218       continue;
219     int counter_id = it->second;
220     if (!words.Next())
221       continue;
222     auto value = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
223     auto* vmstat = sys_stats->add_vmstat();
224     vmstat->set_key(static_cast<protos::pbzero::VmstatCounters>(counter_id));
225     vmstat->set_value(value);
226   }
227 }
228 
ReadStat(protos::pbzero::SysStats * sys_stats)229 void SysStatsDataSource::ReadStat(protos::pbzero::SysStats* sys_stats) {
230   size_t rsize = ReadFile(&stat_fd_, "/proc/stat");
231   if (!rsize)
232     return;
233   char* buf = static_cast<char*>(read_buf_.Get());
234   for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
235     base::StringSplitter words(&lines, ' ');
236     if (!words.Next())
237       continue;
238 
239     // Per-CPU stats.
240     if ((stat_enabled_fields_ & (1 << SysStatsConfig::STAT_CPU_TIMES)) &&
241         words.cur_token_size() > 3 && !strncmp(words.cur_token(), "cpu", 3)) {
242       long cpu_id = strtol(words.cur_token() + 3, nullptr, 10);
243       std::array<uint64_t, 7> cpu_times{};
244       for (size_t i = 0; i < cpu_times.size() && words.Next(); i++) {
245         cpu_times[i] =
246             static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
247       }
248       auto* cpu_stat = sys_stats->add_cpu_stat();
249       cpu_stat->set_cpu_id(static_cast<uint32_t>(cpu_id));
250       cpu_stat->set_user_ns(cpu_times[0] * ns_per_user_hz_);
251       cpu_stat->set_user_ice_ns(cpu_times[1] * ns_per_user_hz_);
252       cpu_stat->set_system_mode_ns(cpu_times[2] * ns_per_user_hz_);
253       cpu_stat->set_idle_ns(cpu_times[3] * ns_per_user_hz_);
254       cpu_stat->set_io_wait_ns(cpu_times[4] * ns_per_user_hz_);
255       cpu_stat->set_irq_ns(cpu_times[5] * ns_per_user_hz_);
256       cpu_stat->set_softirq_ns(cpu_times[6] * ns_per_user_hz_);
257     }
258     // IRQ counters
259     else if ((stat_enabled_fields_ & (1 << SysStatsConfig::STAT_IRQ_COUNTS)) &&
260              !strcmp(words.cur_token(), "intr")) {
261       for (size_t i = 0; words.Next(); i++) {
262         auto v = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
263         if (i == 0) {
264           sys_stats->set_num_irq_total(v);
265         } else {
266           auto* irq_stat = sys_stats->add_num_irq();
267           irq_stat->set_irq(static_cast<int32_t>(i - 1));
268           irq_stat->set_count(v);
269         }
270       }
271     }
272     // Softirq counters.
273     else if ((stat_enabled_fields_ &
274               (1 << SysStatsConfig::STAT_SOFTIRQ_COUNTS)) &&
275              !strcmp(words.cur_token(), "softirq")) {
276       for (size_t i = 0; words.Next(); i++) {
277         auto v = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
278         if (i == 0) {
279           sys_stats->set_num_softirq_total(v);
280         } else {
281           auto* softirq_stat = sys_stats->add_num_softirq();
282           softirq_stat->set_irq(static_cast<int32_t>(i - 1));
283           softirq_stat->set_count(v);
284         }
285       }
286     }
287     // Number of forked processes since boot.
288     else if ((stat_enabled_fields_ & (1 << SysStatsConfig::STAT_FORK_COUNT)) &&
289              !strcmp(words.cur_token(), "processes")) {
290       if (words.Next()) {
291         sys_stats->set_num_forks(
292             static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10)));
293       }
294     }
295 
296   }  // for (line)
297 }
298 
GetWeakPtr() const299 base::WeakPtr<SysStatsDataSource> SysStatsDataSource::GetWeakPtr() const {
300   return weak_factory_.GetWeakPtr();
301 }
302 
Flush(FlushRequestID,std::function<void ()> callback)303 void SysStatsDataSource::Flush(FlushRequestID, std::function<void()> callback) {
304   writer_->Flush(callback);
305 }
306 
ReadFile(base::ScopedFile * fd,const char * path)307 size_t SysStatsDataSource::ReadFile(base::ScopedFile* fd, const char* path) {
308   if (!*fd)
309     return 0;
310   ssize_t res = pread(**fd, read_buf_.Get(), kReadBufSize - 1, 0);
311   if (res <= 0) {
312     PERFETTO_PLOG("Failed reading %s", path);
313     fd->reset();
314     return 0;
315   }
316   size_t rsize = static_cast<size_t>(res);
317   static_cast<char*>(read_buf_.Get())[rsize] = '\0';
318   return rsize + 1;  // Include null terminator in the count.
319 }
320 
321 }  // namespace perfetto
322