Home | History | Annotate | Download | only in sys_stats
      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