blob: a84c3ab08bf74a5c980f3024a95c204e7da4e1e7 [file] [log] [blame]
// 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
#include <stdio.h>
#include <errno.h>
#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 false;
}
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), _cached{} {
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 false;
}
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 false;
}
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) {
static bool ever_printed_io_err = false;
if (!ever_printed_io_err) {
fprintf(stderr, "WARNING: Fail to open /proc/self/io, errno=%d. "
"I/O related bvars will be unavailable.\n", errno);
ever_printed_io_err = true;
}
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;
}