text: // 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;
  }
  