// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // Date: Thu Jul 30 17:44:54 CST 2015 #include <unistd.h> // getpagesize #include <sys/types.h> #include <sys/resource.h> // getrusage #include <dirent.h> // dirent #include <iomanip> // setw #if defined(__APPLE__) #include <libproc.h> #include <sys/resource.h> #else #endif #include "butil/time.h" #include "butil/memory/singleton_on_pthread_once.h" #include "butil/scoped_lock.h" #include "butil/files/scoped_file.h" #include "butil/files/dir_reader_posix.h" #include "butil/file_util.h" #include "butil/process_util.h" // ReadCommandLine #include "butil/popen.h" // read_command_output #include "bvar/passive_status.h" namespace bvar { template <class T, class M> M get_member_type(M T::*); #define BVAR_MEMBER_TYPE(member) BAIDU_TYPEOF(bvar::get_member_type(member)) int do_link_default_variables = 0; const int64_t CACHED_INTERVAL_US = 100000L; // 100ms // ====================================== struct ProcStat { int pid; //std::string comm; char state; int ppid; int pgrp; int session; int tty_nr; int tpgid; unsigned flags; unsigned long minflt; unsigned long cminflt; unsigned long majflt; unsigned long cmajflt; unsigned long utime; unsigned long stime; unsigned long cutime; unsigned long cstime; long priority; long nice; long num_threads; }; static bool read_proc_status(ProcStat &stat) { stat = ProcStat(); errno = 0; #if defined(OS_LINUX) // Read status from /proc/self/stat. Information from `man proc' is out of date, // see http://man7.org/linux/man-pages/man5/proc.5.html butil::ScopedFILE fp("/proc/self/stat", "r"); if (NULL == fp) { PLOG_ONCE(WARNING) << "Fail to open /proc/self/stat"; return false; } if (fscanf(fp, "%d %*s %c " "%d %d %d %d %d " "%u %lu %lu %lu " "%lu %lu %lu %lu %lu " "%ld %ld %ld", &stat.pid, &stat.state, &stat.ppid, &stat.pgrp, &stat.session, &stat.tty_nr, &stat.tpgid, &stat.flags, &stat.minflt, &stat.cminflt, &stat.majflt, &stat.cmajflt, &stat.utime, &stat.stime, &stat.cutime, &stat.cstime, &stat.priority, &stat.nice, &stat.num_threads) != 19) { PLOG(WARNING) << "Fail to fscanf"; return false; } return true; #elif defined(OS_MACOSX) // TODO(zhujiashun): get remaining state in MacOS. memset(&stat, 0, sizeof(stat)); static pid_t pid = getpid(); std::ostringstream oss; char cmdbuf[128]; snprintf(cmdbuf, sizeof(cmdbuf), "ps -p %ld -o pid,ppid,pgid,sess" ",tpgid,flags,pri,nice | tail -n1", (long)pid); if (butil::read_command_output(oss, cmdbuf) != 0) { LOG(ERROR) << "Fail to read stat"; return -1; } const std::string& result = oss.str(); if (sscanf(result.c_str(), "%d %d %d %d" "%d %u %ld %ld", &stat.pid, &stat.ppid, &stat.pgrp, &stat.session, &stat.tpgid, &stat.flags, &stat.priority, &stat.nice) != 8) { PLOG(WARNING) << "Fail to sscanf"; return false; } return true; #else return false; #endif } // Reduce pressures to functions to get system metrics. template <typename T> class CachedReader { public: CachedReader() : _mtime_us(0) { CHECK_EQ(0, pthread_mutex_init(&_mutex, NULL)); } ~CachedReader() { pthread_mutex_destroy(&_mutex); } // NOTE: may return a volatile value that may be overwritten at any time. // This is acceptable right now. Both 32-bit and 64-bit numbers are atomic // to fetch in 64-bit machines(most of baidu machines) and the code inside // this .cpp utilizing this class generally return a struct with 32-bit // and 64-bit numbers. template <typename ReadFn> static const T& get_value(const ReadFn& fn) { CachedReader* p = butil::get_leaky_singleton<CachedReader>(); const int64_t now = butil::gettimeofday_us(); if (now > p->_mtime_us + CACHED_INTERVAL_US) { pthread_mutex_lock(&p->_mutex); if (now > p->_mtime_us + CACHED_INTERVAL_US) { p->_mtime_us = now; pthread_mutex_unlock(&p->_mutex); // don't run fn inside lock otherwise a slow fn may // block all concurrent bvar dumppers. (e.g. /vars) T result; if (fn(&result)) { pthread_mutex_lock(&p->_mutex); p->_cached = result; } else { pthread_mutex_lock(&p->_mutex); } } pthread_mutex_unlock(&p->_mutex); } return p->_cached; } private: int64_t _mtime_us; pthread_mutex_t _mutex; T _cached; }; class ProcStatReader { public: bool operator()(ProcStat* stat) const { return read_proc_status(*stat); } template <typename T, size_t offset> static T get_field(void*) { return *(T*)((char*)&CachedReader<ProcStat>::get_value( ProcStatReader()) + offset); } }; #define BVAR_DEFINE_PROC_STAT_FIELD(field) \ PassiveStatus<BVAR_MEMBER_TYPE(&ProcStat::field)> g_##field( \ ProcStatReader::get_field<BVAR_MEMBER_TYPE(&ProcStat::field), \ offsetof(ProcStat, field)>, NULL); #define BVAR_DEFINE_PROC_STAT_FIELD2(field, name) \ PassiveStatus<BVAR_MEMBER_TYPE(&ProcStat::field)> g_##field( \ name, \ ProcStatReader::get_field<BVAR_MEMBER_TYPE(&ProcStat::field), \ offsetof(ProcStat, field)>, NULL); // ================================================== struct ProcMemory { long size; // total program size long resident; // resident set size long share; // shared pages long trs; // text (code) long lrs; // library long drs; // data/stack long dt; // dirty pages }; static bool read_proc_memory(ProcMemory &m) { m = ProcMemory(); errno = 0; #if defined(OS_LINUX) butil::ScopedFILE fp("/proc/self/statm", "r"); if (NULL == fp) { PLOG_ONCE(WARNING) << "Fail to open /proc/self/statm"; return false; } if (fscanf(fp, "%ld %ld %ld %ld %ld %ld %ld", &m.size, &m.resident, &m.share, &m.trs, &m.lrs, &m.drs, &m.dt) != 7) { PLOG(WARNING) << "Fail to fscanf /proc/self/statm"; return false; } return true; #elif defined(OS_MACOSX) // TODO(zhujiashun): get remaining memory info in MacOS. memset(&m, 0, sizeof(m)); static pid_t pid = getpid(); static int64_t pagesize = getpagesize(); std::ostringstream oss; char cmdbuf[128]; snprintf(cmdbuf, sizeof(cmdbuf), "ps -p %ld -o rss=,vsz=", (long)pid); if (butil::read_command_output(oss, cmdbuf) != 0) { LOG(ERROR) << "Fail to read memory state"; return -1; } const std::string& result = oss.str(); if (sscanf(result.c_str(), "%ld %ld", &m.resident, &m.size) != 2) { PLOG(WARNING) << "Fail to sscanf"; return false; } // resident and size in Kbytes m.resident = m.resident * 1024 / pagesize; m.size = m.size * 1024 / pagesize; return true; #else return false; #endif } class ProcMemoryReader { public: bool operator()(ProcMemory* stat) const { return read_proc_memory(*stat); }; template <typename T, size_t offset> static T get_field(void*) { static int64_t pagesize = getpagesize(); return *(T*)((char*)&CachedReader<ProcMemory>::get_value( ProcMemoryReader()) + offset) * pagesize; } }; #define BVAR_DEFINE_PROC_MEMORY_FIELD(field, name) \ PassiveStatus<BVAR_MEMBER_TYPE(&ProcMemory::field)> g_##field( \ name, \ ProcMemoryReader::get_field<BVAR_MEMBER_TYPE(&ProcMemory::field), \ offsetof(ProcMemory, field)>, NULL); // ================================================== struct LoadAverage { double loadavg_1m; double loadavg_5m; double loadavg_15m; }; static bool read_load_average(LoadAverage &m) { #if defined(OS_LINUX) butil::ScopedFILE fp("/proc/loadavg", "r"); if (NULL == fp) { PLOG_ONCE(WARNING) << "Fail to open /proc/loadavg"; return false; } m = LoadAverage(); errno = 0; if (fscanf(fp, "%lf %lf %lf", &m.loadavg_1m, &m.loadavg_5m, &m.loadavg_15m) != 3) { PLOG(WARNING) << "Fail to fscanf"; return false; } return true; #elif defined(OS_MACOSX) std::ostringstream oss; if (butil::read_command_output(oss, "sysctl -n vm.loadavg") != 0) { LOG(ERROR) << "Fail to read loadavg"; return -1; } const std::string& result = oss.str(); if (sscanf(result.c_str(), "{ %lf %lf %lf }", &m.loadavg_1m, &m.loadavg_5m, &m.loadavg_15m) != 3) { PLOG(WARNING) << "Fail to sscanf"; return false; } return true; #else return false; #endif } class LoadAverageReader { public: bool operator()(LoadAverage* stat) const { return read_load_average(*stat); }; template <typename T, size_t offset> static T get_field(void*) { return *(T*)((char*)&CachedReader<LoadAverage>::get_value( LoadAverageReader()) + offset); } }; #define BVAR_DEFINE_LOAD_AVERAGE_FIELD(field, name) \ PassiveStatus<BVAR_MEMBER_TYPE(&LoadAverage::field)> g_##field( \ name, \ LoadAverageReader::get_field<BVAR_MEMBER_TYPE(&LoadAverage::field), \ offsetof(LoadAverage, field)>, NULL); // ================================================== static int get_fd_count(int limit) { #if defined(OS_LINUX) butil::DirReaderPosix dr("/proc/self/fd"); int count = 0; if (!dr.IsValid()) { PLOG(WARNING) << "Fail to open /proc/self/fd"; return -1; } // Have to limit the scaning which consumes a lot of CPU when #fd // are huge (100k+) for (; dr.Next() && count <= limit + 3; ++count) {} return count - 3 /* skipped ., .. and the fd in dr*/; #elif defined(OS_MACOSX) // TODO(zhujiashun): following code will cause core dump with some // probability under mac when program exits. Fix it. /* static pid_t pid = getpid(); std::ostringstream oss; char cmdbuf[128]; snprintf(cmdbuf, sizeof(cmdbuf), "lsof -p %ld | grep -v \"txt\" | wc -l", (long)pid); if (butil::read_command_output(oss, cmdbuf) != 0) { LOG(ERROR) << "Fail to read open files"; return -1; } const std::string& result = oss.str(); int count = 0; if (sscanf(result.c_str(), "%d", &count) != 1) { PLOG(WARNING) << "Fail to sscanf"; return -1; } // skipped . and first column line count = count - 2; return std::min(count, limit); */ return 0; #else return 0; #endif } extern PassiveStatus<int> g_fd_num; const int MAX_FD_SCAN_COUNT = 10003; static butil::static_atomic<bool> s_ever_reached_fd_scan_limit = BUTIL_STATIC_ATOMIC_INIT(false); class FdReader { public: bool operator()(int* stat) const { if (s_ever_reached_fd_scan_limit.load(butil::memory_order_relaxed)) { // Never update the count again. return false; } const int count = get_fd_count(MAX_FD_SCAN_COUNT); if (count < 0) { return false; } if (count == MAX_FD_SCAN_COUNT - 2 && s_ever_reached_fd_scan_limit.exchange( true, butil::memory_order_relaxed) == false) { // Rename the bvar to notify user. g_fd_num.hide(); g_fd_num.expose("process_fd_num_too_many"); } *stat = count; return true; } }; static int print_fd_count(void*) { return CachedReader<int>::get_value(FdReader()); } // ================================================== struct ProcIO { // number of bytes the process read, using any read-like system call (from // files, pipes, tty...). size_t rchar; // number of bytes the process wrote using any write-like system call. size_t wchar; // number of read-like system call invocations that the process performed. size_t syscr; // number of write-like system call invocations that the process performed. size_t syscw; // number of bytes the process directly read from disk. size_t read_bytes; // number of bytes the process originally dirtied in the page-cache // (assuming they will go to disk later). size_t write_bytes; // number of bytes the process "un-dirtied" - e.g. using an "ftruncate" // call that truncated pages from the page-cache. size_t cancelled_write_bytes; }; static bool read_proc_io(ProcIO* s) { #if defined(OS_LINUX) butil::ScopedFILE fp("/proc/self/io", "r"); if (NULL == fp) { PLOG_ONCE(WARNING) << "Fail to open /proc/self/io"; return false; } errno = 0; if (fscanf(fp, "%*s %lu %*s %lu %*s %lu %*s %lu %*s %lu %*s %lu %*s %lu", &s->rchar, &s->wchar, &s->syscr, &s->syscw, &s->read_bytes, &s->write_bytes, &s->cancelled_write_bytes) != 7) { PLOG(WARNING) << "Fail to fscanf"; return false; } return true; #elif defined(OS_MACOSX) // TODO(zhujiashun): get rchar, wchar, syscr, syscw, cancelled_write_bytes // in MacOS. memset(s, 0, sizeof(ProcIO)); static pid_t pid = getpid(); rusage_info_current rusage; if (proc_pid_rusage(pid, RUSAGE_INFO_CURRENT, (void**)&rusage) != 0) { PLOG(WARNING) << "Fail to proc_pid_rusage"; return false; } s->read_bytes = rusage.ri_diskio_bytesread; s->write_bytes = rusage.ri_diskio_byteswritten; return true; #else return false; #endif } class ProcIOReader { public: bool operator()(ProcIO* stat) const { return read_proc_io(stat); } template <typename T, size_t offset> static T get_field(void*) { return *(T*)((char*)&CachedReader<ProcIO>::get_value( ProcIOReader()) + offset); } }; #define BVAR_DEFINE_PROC_IO_FIELD(field) \ PassiveStatus<BVAR_MEMBER_TYPE(&ProcIO::field)> g_##field( \ ProcIOReader::get_field<BVAR_MEMBER_TYPE(&ProcIO::field), \ offsetof(ProcIO, field)>, NULL); // ================================================== // Refs: // https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats // https://www.kernel.org/doc/Documentation/iostats.txt // // The /proc/diskstats file displays the I/O statistics of block devices. // Each line contains the following 14 fields: struct DiskStat { long long major_number; long long minor_mumber; char device_name[64]; // The total number of reads completed successfully. long long reads_completed; // wMB/s wKB/s // Reads and writes which are adjacent to each other may be merged for // efficiency. Thus two 4K reads may become one 8K read before it is // ultimately handed to the disk, and so it will be counted (and queued) // as only one I/O. This field lets you know how often this was done. long long reads_merged; // rrqm/s // The total number of sectors read successfully. long long sectors_read; // rsec/s // The total number of milliseconds spent by all reads (as // measured from __make_request() to end_that_request_last()). long long time_spent_reading_ms; // The total number of writes completed successfully. long long writes_completed; // rKB/s rMB/s // See description of reads_merged long long writes_merged; // wrqm/s // The total number of sectors written successfully. long long sectors_written; // wsec/s // The total number of milliseconds spent by all writes (as // measured from __make_request() to end_that_request_last()). long long time_spent_writing_ms; // The only field that should go to zero. Incremented as requests are // given to appropriate struct request_queue and decremented as they finish. long long io_in_progress; // This field increases so long as `io_in_progress' is nonzero. long long time_spent_io_ms; // This field is incremented at each I/O start, I/O completion, I/O // merge, or read of these stats by the number of I/Os in progress // `io_in_progress' times the number of milliseconds spent doing // I/O since the last update of this field. This can provide an easy // measure of both I/O completion time and the backlog that may be // accumulating. long long weighted_time_spent_io_ms; }; static bool read_disk_stat(DiskStat* s) { #if defined(OS_LINUX) butil::ScopedFILE fp("/proc/diskstats", "r"); if (NULL == fp) { PLOG_ONCE(WARNING) << "Fail to open /proc/diskstats"; return false; } errno = 0; if (fscanf(fp, "%lld %lld %s %lld %lld %lld %lld %lld %lld %lld " "%lld %lld %lld %lld", &s->major_number, &s->minor_mumber, s->device_name, &s->reads_completed, &s->reads_merged, &s->sectors_read, &s->time_spent_reading_ms, &s->writes_completed, &s->writes_merged, &s->sectors_written, &s->time_spent_writing_ms, &s->io_in_progress, &s->time_spent_io_ms, &s->weighted_time_spent_io_ms) != 14) { PLOG(WARNING) << "Fail to fscanf"; return false; } return true; #elif defined(OS_MACOSX) // TODO(zhujiashun) return false; #else return false; #endif } class DiskStatReader { public: bool operator()(DiskStat* stat) const { return read_disk_stat(stat); } template <typename T, size_t offset> static T get_field(void*) { return *(T*)((char*)&CachedReader<DiskStat>::get_value( DiskStatReader()) + offset); } }; #define BVAR_DEFINE_DISK_STAT_FIELD(field) \ PassiveStatus<BVAR_MEMBER_TYPE(&DiskStat::field)> g_##field( \ DiskStatReader::get_field<BVAR_MEMBER_TYPE(&DiskStat::field), \ offsetof(DiskStat, field)>, NULL); // ===================================== struct ReadSelfCmdline { std::string content; ReadSelfCmdline() { char buf[1024]; const ssize_t nr = butil::ReadCommandLine(buf, sizeof(buf), true); content.append(buf, nr); } }; static void get_cmdline(std::ostream& os, void*) { os << butil::get_leaky_singleton<ReadSelfCmdline>()->content; } struct ReadVersion { std::string content; ReadVersion() { std::ostringstream oss; if (butil::read_command_output(oss, "uname -ap") != 0) { LOG(ERROR) << "Fail to read kernel version"; return; } content.append(oss.str()); } }; static void get_kernel_version(std::ostream& os, void*) { os << butil::get_leaky_singleton<ReadVersion>()->content; } // ====================================== static int64_t g_starting_time = butil::gettimeofday_us(); static timeval get_uptime(void*) { int64_t uptime_us = butil::gettimeofday_us() - g_starting_time; timeval tm; tm.tv_sec = uptime_us / 1000000L; tm.tv_usec = uptime_us - tm.tv_sec * 1000000L; return tm; } // ====================================== class RUsageReader { public: bool operator()(rusage* stat) const { const int rc = getrusage(RUSAGE_SELF, stat); if (rc < 0) { PLOG(WARNING) << "Fail to getrusage"; return false; } return true; } template <typename T, size_t offset> static T get_field(void*) { return *(T*)((char*)&CachedReader<rusage>::get_value( RUsageReader()) + offset); } }; #define BVAR_DEFINE_RUSAGE_FIELD(field) \ PassiveStatus<BVAR_MEMBER_TYPE(&rusage::field)> g_##field( \ RUsageReader::get_field<BVAR_MEMBER_TYPE(&rusage::field), \ offsetof(rusage, field)>, NULL); \ #define BVAR_DEFINE_RUSAGE_FIELD2(field, name) \ PassiveStatus<BVAR_MEMBER_TYPE(&rusage::field)> g_##field( \ name, \ RUsageReader::get_field<BVAR_MEMBER_TYPE(&rusage::field), \ offsetof(rusage, field)>, NULL); \ // ====================================== BVAR_DEFINE_PROC_STAT_FIELD2(pid, "pid"); BVAR_DEFINE_PROC_STAT_FIELD2(ppid, "ppid"); BVAR_DEFINE_PROC_STAT_FIELD2(pgrp, "pgrp"); static void get_username(std::ostream& os, void*) { char buf[32]; if (getlogin_r(buf, sizeof(buf)) == 0) { buf[sizeof(buf)-1] = '\0'; os << buf; } else { os << "unknown (" << berror() << ')' ; } } PassiveStatus<std::string> g_username( "process_username", get_username, NULL); BVAR_DEFINE_PROC_STAT_FIELD(minflt); PerSecond<PassiveStatus<unsigned long> > g_minflt_second( "process_faults_minor_second", &g_minflt); BVAR_DEFINE_PROC_STAT_FIELD2(majflt, "process_faults_major"); BVAR_DEFINE_PROC_STAT_FIELD2(priority, "process_priority"); BVAR_DEFINE_PROC_STAT_FIELD2(nice, "process_nice"); BVAR_DEFINE_PROC_STAT_FIELD2(num_threads, "process_thread_count"); PassiveStatus<int> g_fd_num("process_fd_count", print_fd_count, NULL); BVAR_DEFINE_PROC_MEMORY_FIELD(size, "process_memory_virtual"); BVAR_DEFINE_PROC_MEMORY_FIELD(resident, "process_memory_resident"); BVAR_DEFINE_PROC_MEMORY_FIELD(share, "process_memory_shared"); BVAR_DEFINE_PROC_MEMORY_FIELD(trs, "process_memory_text"); BVAR_DEFINE_PROC_MEMORY_FIELD(drs, "process_memory_data_and_stack"); BVAR_DEFINE_LOAD_AVERAGE_FIELD(loadavg_1m, "system_loadavg_1m"); BVAR_DEFINE_LOAD_AVERAGE_FIELD(loadavg_5m, "system_loadavg_5m"); BVAR_DEFINE_LOAD_AVERAGE_FIELD(loadavg_15m, "system_loadavg_15m"); BVAR_DEFINE_PROC_IO_FIELD(rchar); BVAR_DEFINE_PROC_IO_FIELD(wchar); PerSecond<PassiveStatus<size_t> > g_io_read_second( "process_io_read_bytes_second", &g_rchar); PerSecond<PassiveStatus<size_t> > g_io_write_second( "process_io_write_bytes_second", &g_wchar); BVAR_DEFINE_PROC_IO_FIELD(syscr); BVAR_DEFINE_PROC_IO_FIELD(syscw); PerSecond<PassiveStatus<size_t> > g_io_num_reads_second( "process_io_read_second", &g_syscr); PerSecond<PassiveStatus<size_t> > g_io_num_writes_second( "process_io_write_second", &g_syscw); BVAR_DEFINE_PROC_IO_FIELD(read_bytes); BVAR_DEFINE_PROC_IO_FIELD(write_bytes); PerSecond<PassiveStatus<size_t> > g_disk_read_second( "process_disk_read_bytes_second", &g_read_bytes); PerSecond<PassiveStatus<size_t> > g_disk_write_second( "process_disk_write_bytes_second", &g_write_bytes); BVAR_DEFINE_RUSAGE_FIELD(ru_utime); BVAR_DEFINE_RUSAGE_FIELD(ru_stime); PassiveStatus<timeval> g_uptime("process_uptime", get_uptime, NULL); static int get_core_num(void*) { return sysconf(_SC_NPROCESSORS_ONLN); } PassiveStatus<int> g_core_num("system_core_count", get_core_num, NULL); struct TimePercent { int64_t time_us; int64_t real_time_us; void operator-=(const TimePercent& rhs) { time_us -= rhs.time_us; real_time_us -= rhs.real_time_us; } void operator+=(const TimePercent& rhs) { time_us += rhs.time_us; real_time_us += rhs.real_time_us; } }; inline std::ostream& operator<<(std::ostream& os, const TimePercent& tp) { if (tp.real_time_us <= 0) { return os << "0"; } else { return os << std::fixed << std::setprecision(3) << (double)tp.time_us / tp.real_time_us; } } static TimePercent get_cputime_percent(void*) { TimePercent tp = { butil::timeval_to_microseconds(g_ru_stime.get_value()) + butil::timeval_to_microseconds(g_ru_utime.get_value()), butil::timeval_to_microseconds(g_uptime.get_value()) }; return tp; } PassiveStatus<TimePercent> g_cputime_percent(get_cputime_percent, NULL); Window<PassiveStatus<TimePercent>, SERIES_IN_SECOND> g_cputime_percent_second( "process_cpu_usage", &g_cputime_percent, FLAGS_bvar_dump_interval); static TimePercent get_stime_percent(void*) { TimePercent tp = { butil::timeval_to_microseconds(g_ru_stime.get_value()), butil::timeval_to_microseconds(g_uptime.get_value()) }; return tp; } PassiveStatus<TimePercent> g_stime_percent(get_stime_percent, NULL); Window<PassiveStatus<TimePercent>, SERIES_IN_SECOND> g_stime_percent_second( "process_cpu_usage_system", &g_stime_percent, FLAGS_bvar_dump_interval); static TimePercent get_utime_percent(void*) { TimePercent tp = { butil::timeval_to_microseconds(g_ru_utime.get_value()), butil::timeval_to_microseconds(g_uptime.get_value()) }; return tp; } PassiveStatus<TimePercent> g_utime_percent(get_utime_percent, NULL); Window<PassiveStatus<TimePercent>, SERIES_IN_SECOND> g_utime_percent_second( "process_cpu_usage_user", &g_utime_percent, FLAGS_bvar_dump_interval); // According to http://man7.org/linux/man-pages/man2/getrusage.2.html // Unsupported fields in linux: // ru_ixrss // ru_idrss // ru_isrss // ru_nswap // ru_nsignals BVAR_DEFINE_RUSAGE_FIELD(ru_inblock); BVAR_DEFINE_RUSAGE_FIELD(ru_oublock); BVAR_DEFINE_RUSAGE_FIELD(ru_nvcsw); BVAR_DEFINE_RUSAGE_FIELD(ru_nivcsw); PerSecond<PassiveStatus<long> > g_ru_inblock_second( "process_inblocks_second", &g_ru_inblock); PerSecond<PassiveStatus<long> > g_ru_oublock_second( "process_outblocks_second", &g_ru_oublock); PerSecond<PassiveStatus<long> > cs_vol_second( "process_context_switches_voluntary_second", &g_ru_nvcsw); PerSecond<PassiveStatus<long> > cs_invol_second( "process_context_switches_involuntary_second", &g_ru_nivcsw); PassiveStatus<std::string> g_cmdline("process_cmdline", get_cmdline, NULL); PassiveStatus<std::string> g_kernel_version( "kernel_version", get_kernel_version, NULL); static std::string* s_gcc_version = NULL; pthread_once_t g_gen_gcc_version_once = PTHREAD_ONCE_INIT; void gen_gcc_version() { #if defined(__GNUC__) const int gcc_major = __GNUC__; #else const int gcc_major = -1; #endif #if defined(__GNUC_MINOR__) const int gcc_minor = __GNUC_MINOR__; #else const int gcc_minor = -1; #endif #if defined(__GNUC_PATCHLEVEL__) const int gcc_patchlevel = __GNUC_PATCHLEVEL__; #else const int gcc_patchlevel = -1; #endif s_gcc_version = new std::string; if (gcc_major == -1) { *s_gcc_version = "unknown"; return; } std::ostringstream oss; oss << gcc_major; if (gcc_minor == -1) { return; } oss << '.' << gcc_minor; if (gcc_patchlevel == -1) { return; } oss << '.' << gcc_patchlevel; *s_gcc_version = oss.str(); } void get_gcc_version(std::ostream& os, void*) { pthread_once(&g_gen_gcc_version_once, gen_gcc_version); os << *s_gcc_version; } // ============================================= PassiveStatus<std::string> g_gcc_version("gcc_version", get_gcc_version, NULL); void get_work_dir(std::ostream& os, void*) { butil::FilePath path; const bool rc = butil::GetCurrentDirectory(&path); LOG_IF(WARNING, !rc) << "Fail to GetCurrentDirectory"; os << path.value(); } PassiveStatus<std::string> g_work_dir("process_work_dir", get_work_dir, NULL); #undef BVAR_MEMBER_TYPE #undef BVAR_DEFINE_PROC_STAT_FIELD #undef BVAR_DEFINE_PROC_STAT_FIELD2 #undef BVAR_DEFINE_PROC_MEMORY_FIELD #undef BVAR_DEFINE_RUSAGE_FIELD #undef BVAR_DEFINE_RUSAGE_FIELD2 } // namespace bvar // In the same scope where timeval is defined. Required by clang. inline std::ostream& operator<<(std::ostream& os, const timeval& tm) { return os << tm.tv_sec << '.' << std::setw(6) << std::setfill('0') << tm.tv_usec; }