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 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 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 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 141 void SysStatsDataSource::Start() { 142 auto weak_this = GetWeakPtr(); 143 task_runner_->PostTask(std::bind(&SysStatsDataSource::Tick, weak_this)); 144 } 145 146 // static 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 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 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 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 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 299 base::WeakPtr<SysStatsDataSource> SysStatsDataSource::GetWeakPtr() const { 300 return weak_factory_.GetWeakPtr(); 301 } 302 303 void SysStatsDataSource::Flush(FlushRequestID, std::function<void()> callback) { 304 writer_->Flush(callback); 305 } 306 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