Sleds/K2Daemon/LogMemstats.cpp

845 lines
27 KiB
C++

/*
Copyright (c) 2013 IOnU Security Inc. All rights reserved
Created October 2013 by Kendrick Webster
K2Daemon/LogMemstats.cpp - implementation for LogMemstats.h
*/
#ifdef __MACH__
#include <mach/task.h>
#include <mach/mach_init.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#else
#include <sys/sysinfo.h>
#include <malloc.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/times.h>
#include <chrono>
#include <string>
#include <cmath>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/algorithm/string.hpp>
#include "global.h"
#include "ClientMap.h"
#include "OfficeSubscriptions.h"
#include "Version.h"
#include "LogMemstats.h"
using namespace boost::property_tree;
// For debugging, un-comment (temporarilly) as needed:
//#define SHOW_RAW_MALLOC_INFO
//#define SHOW_PROC_SELF_STATUS
// Log severity for allocated memory and CPU time summary statistics
static const Log::severity_t stat_loglev = Log::informational;
// Format strings (to make columns line up in log text)
#define LABEL_FMT "%-40s" // Label field format
#define F1_WIDTH "%12" // Data field 1 width
static unsigned int getUptimeSeconds(void);
static Memstats::uint_t getHeapAllocated(void);
namespace Memstats // snapshot values and accessors
{
uint_t n_cpus; uint_t CpuCount(void) {return n_cpus;}
uint_t total_virtual_memory; uint_t TotalVirtualMemory(void) {return total_virtual_memory;}
uint_t total_memory; uint_t TotalMemory(void) {return total_memory;}
uint_t system_virtual_memory_used; uint_t SystemWideVirtualMemoryUsed(void) {return system_virtual_memory_used;}
uint_t system_memory_used; uint_t SystemWideMemoryUsed(void) {return system_memory_used;}
double system_cpu_load_percent; double SystemWideCpuLoadPercent(void) {return system_cpu_load_percent;}
uint_t UptimeSeconds(void) {return getUptimeSeconds();}
uint_t ConnectedClients(void) {return ClientMap::Count();}
uint_t SubscribedOffices(void) {return OfficeSubscriptions::OfficeCount();}
uint_t OfficeSubscriptions(void) {return OfficeSubscriptions::SubscriptionCount();}
uint_t virtual_memory_used; uint_t VirtualMemoryUsed(void) {return virtual_memory_used;}
uint_t memory_used; uint_t MemoryUsed(void) {return memory_used;}
uint_t HeapAllocated(void) {return getHeapAllocated();}
double cpu_load_percent; double CpuLoadPercent(void) {return cpu_load_percent;}
double cpu_user_percent; double CpuUserPercent(void) {return cpu_user_percent;}
double cpu_system_percent; double CpuSystemPercent(void) {return cpu_system_percent;}
double cpu_child_user_percent; double CpuChildUserPercent(void) {return cpu_child_user_percent;}
double cpu_child_system_percent; double CpuChildSystemPercent(void) {return cpu_child_system_percent;}
}
struct CUptimeInitializer
{
std::chrono::steady_clock::time_point t0;
CUptimeInitializer()
{
t0 = std::chrono::steady_clock::now();
}
};
static CUptimeInitializer uptimeInit;
static uint64_t getUptimeMicroseconds(void)
{
using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::steady_clock;
return duration_cast<microseconds>(steady_clock::now() - uptimeInit.t0).count();
}
static unsigned int getUptimeSeconds(void)
{
return static_cast<unsigned int>(getUptimeMicroseconds() / 1000000);
}
static Memstats::uint_t getHeapAllocated(void)
{
#ifndef __MACH__
struct mallinfo mi = mallinfo();
return static_cast<Memstats::uint_t>(mi.uordblks);
#else
// Don't know how to determine heap usage on OS X, but it isn't critical since the
// daemon is intended to run on Linux (with OS X only supported for developer use)
return 0;
#endif
}
/* ---------------------------------------------------------------------------
Utility functions
*/
static const char* kibi(uint64_t x)
{
constexpr unsigned int k = 1024;
constexpr const char* u = "KMGTPE";
static char v[16];
if (x < k)
{
snprintf(v, sizeof(v), "%d B", static_cast<int>(x));
}
else
{
int e = static_cast<int>(log(x) / log(k));
if ((e < 1) || (e > 6))
{
return "<ERROR>";
}
snprintf(v, sizeof(v), "%.1f %ciB", x / pow(k, e), u[e - 1]);
}
return v;
}
static const char* kibi(uint64_t x, char& c)
{
constexpr unsigned int k = 1024;
constexpr const char* u = "KMGTPE";
static char v[16];
if (x < k)
{
snprintf(v, sizeof(v), "%d", static_cast<int>(x));
c = 0;
}
else
{
int e = static_cast<int>(log(x) / log(k));
if ((e < 1) || (e > 6))
{
c = 0;
return "<ERROR>";
}
snprintf(v, sizeof(v), "%.1f", x / pow(k, e));
c = u[e - 1];
}
return v;
}
static double percent(long long n, long long d)
{
return 100.0 * (static_cast<double>(n) / static_cast<double>(d));
}
/* ---------------------------------------------------------------------------
Implementation
*/
static unsigned int get_num_cpus(void)
{
Memstats::uint_t &n = Memstats::n_cpus;
if (0 == n)
{
#ifndef __MACH__
int d;
char b[128];
FILE* f = fopen("/proc/cpuinfo", "rt");
while (NULL != fgets(b, sizeof(b), f))
{
if (1 == sscanf(b, "processor : %d", &d))
{
++n;
}
}
fclose(f);
#else
int mib[2], n1;
size_t len;
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
len = sizeof(n1);
sysctl(mib, 2, &n1, &len, NULL, 0);
n = n1;
#endif
}
return static_cast<unsigned int>(n);
}
// ----------------------
// malloc_info() parsing
#ifndef __MACH__ // everything related to malloc_info() is Linux-only, but is currently not used except for log output
#if 0
// Summing the sizes produces erratic data, can't get the same stats as what mallinfo() returns.
//
// Since mallinfo() is deprecated (not 64-bit compatible, will biff if 32 bits overflow), it would
// be preferable to get the total in-use stat from malloc_info() XML, but there doesn't appear to
// be any way to do so currently.
//
// Keeping this code around so it can be resurrected if a later version of malloc_info() gives us
// the stats we need.
static void logPtree(const ptree& t)
{
uint64_t freed = 0, current = 0;
const ptree& c = t.get_child("malloc");
const ptree& d = c.get_child("heap");
for (const ptree::value_type& e: d)
{
if (0 == e.first.compare("sizes"))
{
for (const ptree::value_type& f: e.second)
{
if ((0 == f.first.compare("size")) || (0 == f.first.compare("unsorted")))
{
const ptree& a = f.second.get_child("<xmlattr>");
std::string total = a.get<std::string>("total");
freed += strtoul(total.c_str(), NULL, 0);
}
}
}
else if (0 == e.first.compare("system"))
{
const ptree& a = e.second.get_child("<xmlattr>");
std::string type = a.get<std::string>("type");
if (0 == type.compare("current"))
{
std::string size = a.get<std::string>("size");
current = strtoul(size.c_str(), NULL, 0);
}
}
}
logprintf(Log::debug, LABEL_FMT F1_WIDTH "lu (%s)", "Current heap size:", current, kibi(current));
logprintf(Log::debug, LABEL_FMT F1_WIDTH "lu (%s)", "Heap freed:", freed, kibi(freed));
}
#else
static void logPtree(const ptree& t)
{
const ptree& c = t.get_child("malloc");
const ptree& d = c.get_child("heap");
for (const ptree::value_type& e: d)
{
if (0 == e.first.compare("system"))
{
const ptree& a = e.second.get_child("<xmlattr>");
std::string type = a.get<std::string>("type");
if (0 == type.compare("current"))
{
std::string size = a.get<std::string>("size");
uint64_t current = strtoul(size.c_str(), NULL, 0);
logprintf(Log::debug, LABEL_FMT F1_WIDTH "lu (%s)", "Current heap size:", current, kibi(current));
}
}
}
}
#endif
static void logMallocInfoSummary(const char* p)
{
using boost::property_tree::ptree;
ptree pt;
std::stringstream ss;
std::string str;
ss.str(p);
read_xml(ss, pt);
try
{
logPtree(pt);
}
catch (...)
{
logprintf(Log::error, "unable to parse malloc_info() xml");
}
}
#ifdef SHOW_RAW_MALLOC_INFO
static const char* indent(int d)
{
switch (d)
{
case 0: return "";
case 1: return " ";
case 2: return " ";
case 3: return " ";
case 4: return " ";
default: return " ";
}
}
static void logMallocInfoRaw(const char* p)
{
enum
{
START,
TAG1,
TAG2,
TAG3,
TAG4
}
e = START;
const char* q;
std::string s;
int d = 0, i = 0;
logprintf(Log::debug3, "malloc_info:");
while (char c = *p++)
{
switch (c)
{
case '\n':
q = strstr(s.c_str(), "size=\"");
if (q)
{
uint64_t x = strtoul (q + 6, NULL, 0);
logprintf(Log::debug3, "%s%-64s%s", indent(i), s.c_str(), kibi(x));
}
else
{
logprintf(Log::debug3, "%s%s", indent(i), s.c_str());
}
i = d;
s.clear();
break;
case '<':
e = TAG1;
s += c;
break;
case '/':
switch (e)
{
case TAG1:
e = TAG3;
break;
case TAG2:
e = TAG4;
break;
default: break;
}
s += c;
break;
case '>':
switch (e)
{
case TAG1:
case TAG2:
++d;
break;
case TAG3:
i = --d;
break;
default: break;
}
e = START;
s += c;
break;
default:
switch (e)
{
case TAG1:
e = TAG2;
break;
default: break;
}
s += c;
break;
}
}
}
#endif // SHOW_RAW_MALLOC_INFO
static void logMallocInfo(bool details)
{
if (details)
{
char *p;
size_t size;
FILE *f = open_memstream(&p, &size);
if (NULL == f)
{
logprintf(Log::error, "open_memstream: %s", strerror(errno));
return;
}
if (0 != malloc_info(0, f))
{
logprintf(Log::error, "malloc_info: %s", strerror(errno));
fclose(f);
return;
}
fclose(f);
#ifdef SHOW_RAW_MALLOC_INFO
logMallocInfoRaw(p);
#endif
logMallocInfoSummary(p);
free(p);
}
}
#else // __MACH__
static void logMallocInfo(bool details) {}
#endif
// END malloc_info() parsing
// --------------------------
#ifndef __MACH__
static void logMallinfo(bool details)
{
struct mallinfo mi = mallinfo();
if (details)
{
logprintf(Log::debug3, LABEL_FMT F1_WIDTH "d (%s)", "Total non-mmapped bytes (arena):", mi.arena, kibi(mi.arena));
logprintf(Log::debug3, LABEL_FMT F1_WIDTH "d", "# of free chunks (ordblks):", mi.ordblks);
logprintf(Log::debug3, LABEL_FMT F1_WIDTH "d", "# of free fastbin blocks (smblks):", mi.smblks);
logprintf(Log::debug3, LABEL_FMT F1_WIDTH "d", "# of mapped regions (hblks):", mi.hblks);
logprintf(Log::debug3, LABEL_FMT F1_WIDTH "d (%s)", "Bytes in mapped regions (hblkhd):", mi.hblkhd, kibi(mi.hblkhd));
logprintf(Log::debug3, LABEL_FMT F1_WIDTH "d (%s)", "Max. total allocated space (usmblks):", mi.usmblks, kibi(mi.usmblks));
logprintf(Log::debug3, LABEL_FMT F1_WIDTH "d (%s)", "Free bytes held in fastbins (fsmblks):", mi.fsmblks, kibi(mi.fsmblks));
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "d (%s)", "Total allocated space (uordblks):", mi.uordblks, kibi(mi.uordblks));
logprintf(Log::debug3, LABEL_FMT F1_WIDTH "d (%s)", "Total free space (fordblks):", mi.fordblks, kibi(mi.fordblks));
logprintf(Log::debug3, LABEL_FMT F1_WIDTH "d (%s)", "Topmost releasable block (keepcost):", mi.keepcost, kibi(mi.keepcost));
}
char c;
const char* s = kibi(mi.uordblks, c);
char p[4];
snprintf(p, sizeof(p), c ? "%ciB" : "B", c);
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "s %s (%d bytes)", "Heap allocated:", s, p, mi.uordblks);
}
#else
static void logMallinfo(bool details)
{
// no equivalent on OS X, skip
}
#endif
// ----------------------------
// Virtual and physical memory
#ifndef __MACH__
typedef struct
{
int VmSize;
int VmRSS;
}
proc_self_status_t;
static void getProcSelfVmSize(proc_self_status_t& out
#ifdef SHOW_PROC_SELF_STATUS
, bool details
#endif
)
{
memset(&out, 0, sizeof(out));
static const char* fname = "/proc/self/status";
FILE* f = fopen(fname, "rt");
if (NULL == f)
{
logprintf(Log::error, "fopen(\"%s\"): %s", fname, strerror(errno));
return;
}
char b[128];
#ifdef SHOW_PROC_SELF_STATUS
if (details)
{
logprintf(Log::debug3, "%s :", fname);
}
#endif
while (NULL != fgets(b, sizeof(b), f))
{
#ifdef SHOW_PROC_SELF_STATUS
if (details)
{
std::string s = b;
boost::algorithm::trim(s);
logprintf(Log::debug3, " %s", s.c_str());
}
#endif
if (0 == strncmp(b, "VmSize:", 7))
{
out.VmSize = atoi(b + 7);
}
else if (0 == strncmp(b, "VmRSS:", 6))
{
out.VmRSS = atoi(b + 6);
}
}
fclose(f);
}
static long long totalVirtualMem; // calculated by logMemoryInfo(), shared with logLocalMemoryInfo()
static long long totalRAM; // calculated by logMemoryInfo(), shared with logLocalMemoryInfo()
static void logMemoryInfo(bool details)
{
struct sysinfo memInfo;
if (0 != sysinfo(&memInfo))
{
logprintf(Log::error, "sysinfo(): %s", strerror(errno));
return;
}
// Virtual memory
totalVirtualMem = memInfo.totalram;
totalVirtualMem += memInfo.totalswap;
totalVirtualMem *= memInfo.mem_unit;
Memstats::total_virtual_memory = static_cast<Memstats::uint_t>(totalVirtualMem);
long long virtualMemUsed = memInfo.totalram - memInfo.freeram;
virtualMemUsed += memInfo.totalswap - memInfo.freeswap;
virtualMemUsed *= memInfo.mem_unit;
Memstats::system_virtual_memory_used = static_cast<Memstats::uint_t>(virtualMemUsed);
if (details)
{
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "lld (%s)", "Total virtual memory:", totalVirtualMem, kibi(totalVirtualMem));
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "lld (%s)", "Virtual memory used (system wide):", virtualMemUsed, kibi(virtualMemUsed));
}
logprintf(stat_loglev, LABEL_FMT F1_WIDTH ".1lf%% of %s", "Virtual memory used (system wide):",
percent(virtualMemUsed, totalVirtualMem), kibi(totalVirtualMem));
// RAM
totalRAM = memInfo.totalram;
totalRAM *= memInfo.mem_unit;
Memstats::total_memory = static_cast<Memstats::uint_t>(totalRAM);
long long ramUsed = memInfo.totalram - memInfo.freeram;
ramUsed *= memInfo.mem_unit;
Memstats::system_memory_used = static_cast<Memstats::uint_t>(ramUsed);
if (details)
{
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "lld (%s)", "Total RAM:", totalRAM, kibi(totalRAM));
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "lld (%s)", "RAM used (system wide):", ramUsed, kibi(ramUsed));
}
logprintf(stat_loglev, LABEL_FMT F1_WIDTH ".1lf%% of %s", "RAM used (system wide):",
percent(ramUsed, totalRAM), kibi(totalRAM));
}
static void logLocalMemoryInfo(bool details)
{
proc_self_status_t pss;
#ifdef SHOW_PROC_SELF_STATUS
getProcSelfVmSize(pss, details);
#else
getProcSelfVmSize(pss);
#endif
long long virtualMemThisProcess = 1024 * pss.VmSize;
Memstats::virtual_memory_used = static_cast<Memstats::uint_t>(virtualMemThisProcess);
if (details)
{
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "i (%s)", "Virtual memory used:", virtualMemThisProcess, kibi(virtualMemThisProcess));
logprintf(stat_loglev, LABEL_FMT F1_WIDTH ".1lf%%", "Virtual memory used:", percent(virtualMemThisProcess, totalVirtualMem));
}
long long ramThisProcess = 1024 * pss.VmRSS;
Memstats::memory_used = static_cast<Memstats::uint_t>(ramThisProcess);
if (details)
{
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "i (%s)", "RAM used:", ramThisProcess, kibi(ramThisProcess));
}
logprintf(stat_loglev, LABEL_FMT F1_WIDTH ".1lf%% (%s)", "RAM used:", percent(ramThisProcess, totalRAM), kibi(ramThisProcess));
}
#else // __MACH__
static long long totalRAM; // calculated by logMemoryInfo(), shared with logLocalMemoryInfo()
static void logMemoryInfo(bool details)
{
int mib[2];
uint64_t memsize;
size_t len;
mib[0] = CTL_HW;
mib[1] = HW_MEMSIZE;
len = sizeof(memsize);
sysctl(mib, 2, &memsize, &len, NULL, 0);
totalRAM = memsize;
Memstats::total_memory = static_cast<Memstats::uint_t>(totalRAM);
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "llu (%s)", "Total RAM:", memsize, kibi(memsize));
}
static void logLocalMemoryInfo(bool details)
{
struct task_basic_info s;
mach_msg_type_number_t n = TASK_BASIC_INFO_COUNT;
task_info(current_task(), TASK_BASIC_INFO, (task_info_t)&s, &n);
Memstats::virtual_memory_used = s.virtual_size;
Memstats::memory_used = s.resident_size;
if (details)
{
logprintf(stat_loglev, LABEL_FMT F1_WIDTH "u (%s)", "RAM used:", s.resident_size, kibi(s.resident_size));
}
logprintf(stat_loglev, LABEL_FMT F1_WIDTH ".1lf%% (%s)", "RAM used:", percent(s.resident_size, totalRAM), kibi(s.resident_size));
}
#endif // __MACH__
// END Virtual and physical memory
// --------------------------------
// ---------
// CPU load
#ifndef __MACH__
typedef unsigned long long cpu_stat_element_t;
typedef struct
{
cpu_stat_element_t user;
cpu_stat_element_t nice;
cpu_stat_element_t system;
cpu_stat_element_t idle;
cpu_stat_element_t iowait;
cpu_stat_element_t irq;
cpu_stat_element_t softirq;
cpu_stat_element_t steal;
cpu_stat_element_t guest;
cpu_stat_element_t guest_nice;
}
cpu_stats_t;
static void logCPU(bool details) // system-wide CPU stats
{
if (details)
{
logprintf(Log::informational, LABEL_FMT F1_WIDTH "d", "CPU count:", get_num_cpus());
}
FILE *f = fopen("/proc/stat", "rt");
if (NULL == f)
{
logprintf(Log::error, "fopen(\"/proc/stats\"): %s", strerror(errno));
return;
}
cpu_stats_t s = {0,0,0,0,0,0,0,0,0,0};
int n = fscanf(f, "cpu %Ld %Ld %Ld %Ld %Ld %Ld %Ld %Ld %Ld %Ld", &s.user, &s.nice, &s.system,
&s.idle, &s.iowait, &s.irq, &s.softirq, &s.steal, &s.guest, &s.guest_nice);
fclose(f);
if (n < 10)
{
if (n < 4)
{
logprintf(Log::error, "Unable to scan CPU usage statistics from /proc/stat, fscanf returned %d", n);
return;
}
else
{
logprintf(Log::informational, "scanned only %d (of 10) feilds from /proc/stat, the OS may be old", n);
}
}
static cpu_stats_t r;
if ((s.user < r.user) || (s.nice < r.nice) || (s.system < r.system) || (s.idle < r.idle) || (s.iowait < r.iowait) ||
(s.irq < r.irq) || (s.softirq < r.softirq) || (s.steal < r.steal) || (s.guest < r.guest) || (s.guest_nice < r.guest_nice))
{
logprintf(Log::debug, "skipping current CPU stats calculation due to count overflow");
}
else
{
cpu_stat_element_t d;
d = (r.user - s.user) + (r.nice - s.nice) + (r.system - s.system) + (r.iowait - s.iowait) + (r.irq < s.irq) +
(r.softirq - s.softirq) + (r.steal - s.steal) + (r.guest - s.guest) + (r.guest_nice - s.guest_nice);
unsigned int nc = get_num_cpus();
Memstats::system_cpu_load_percent = percent(d, d + (r.idle - s.idle));
logprintf(Log::informational, LABEL_FMT F1_WIDTH ".1lf%% of %d core%s", "CPU used (system wide):",
Memstats::system_cpu_load_percent, nc, (1 == nc) ? "" : "s");
}
r = s;
}
struct CTimesInitializer
{
clock_t t0;
struct tms s0;
CTimesInitializer()
{
t0 = times(&s0);
}
};
static CTimesInitializer timesInit;
static void logLocalCPU(bool details) // CPU stats for this process
{
static clock_t t0 = timesInit.t0;
static struct tms s0 = timesInit.s0;
struct tms s;
clock_t t = times(&s);
if ((t <= t0) || (s.tms_stime < s0.tms_stime) || (s.tms_utime < s0.tms_utime))
{
logprintf(Log::debug, "skipping current local CPU stats calculation due to count overflow");
}
else
{
long long n;
long long d = (t - t0);
#define LLCPU(field, label, stat) \
n = s.tms_##field - s0.tms_##field; \
Memstats::stat = percent(n, d); \
if (details) {logprintf(Log::debug, LABEL_FMT F1_WIDTH ".1lf%% (%d jiffies)", label ":", Memstats::stat, n);}
LLCPU(utime, "CPU (user)", cpu_user_percent)
LLCPU(stime, "CPU (system)", cpu_system_percent)
LLCPU(cutime, "CPU (children, user)", cpu_child_user_percent)
LLCPU(cstime, "CPU (children, system)", cpu_child_system_percent)
n = (s.tms_utime - s0.tms_utime) + (s.tms_stime - s0.tms_stime)
+ (s.tms_cutime - s0.tms_cutime) + (s.tms_cstime - s0.tms_cstime);
Memstats::cpu_load_percent = percent(n, d);
logprintf(Log::informational, LABEL_FMT F1_WIDTH ".1lf%% of 1 core (%d, %d, %d, %d jiffies u,s,cu,cs)", "CPU used:",
Memstats::cpu_load_percent,
(s.tms_utime - s0.tms_utime),
(s.tms_stime - s0.tms_stime),
(s.tms_cutime - s0.tms_cutime),
(s.tms_cstime - s0.tms_cstime));
}
t0 = t;
s0 = s;
}
#else // __MACH__
static void logCPU(bool details) // system-wide CPU stats
{
if (details)
{
logprintf(Log::informational, LABEL_FMT F1_WIDTH "d", "CPU count:", get_num_cpus());
}
// skipping system-wide CPU load on OS X (requires research)
}
static void logLocalCPU(bool details) // CPU stats for this process
{
struct rusage r_usage;
if (getrusage(RUSAGE_SELF, &r_usage))
{
logprintf(Log::error, "getrusage: %s", strerror(errno));
return;
}
double user_cpu_seconds = r_usage.ru_utime.tv_sec + (1E-6 * r_usage.ru_utime.tv_usec);
double system_cpu_seconds = r_usage.ru_stime.tv_sec + (1E-6 * r_usage.ru_stime.tv_usec);
if (details)
{
logprintf(Log::debug, LABEL_FMT F1_WIDTH ".6lf seconds", "User CPU", user_cpu_seconds);
logprintf(Log::debug, LABEL_FMT F1_WIDTH ".6lf seconds", "System CPU", system_cpu_seconds);
}
static uint64_t t0 = 0;
uint64_t t = getUptimeMicroseconds();
uint64_t elapsed = t - t0;
t0 = t;
Memstats::cpu_load_percent = 100.0 * ((user_cpu_seconds + system_cpu_seconds) / elapsed);
Memstats::cpu_user_percent = 100.0 * (user_cpu_seconds / elapsed);
Memstats::cpu_system_percent = 100.0 * (system_cpu_seconds / elapsed);
logprintf(Log::informational, LABEL_FMT F1_WIDTH ".1lf%% of 1 core (%.6lf, %.6lf seconds user,system)", "CPU used:",
Memstats::cpu_load_percent, user_cpu_seconds, system_cpu_seconds);
}
#endif // __MACH__
// END CPU load
// -------------
static void logUptime(void)
{
constexpr const char* label = "Uptime:";
constexpr const char* fmt = LABEL_FMT F1_WIDTH ".1lf %s";
unsigned int s = getUptimeSeconds();
double d = s / 86400.0;
if (d >= 0.5)
{
logprintf(Log::informational, fmt, label, "days", d);
}
else
{
double h = s / 3600.0;
if (h >= 0.5)
{
logprintf(Log::informational, fmt, label, "hours", h);
}
else
{
double m = s / 60.0;
logprintf(Log::informational, fmt, label, "minutes", m);
}
}
}
static void logStats(bool details)
{
logprintf(Log::informational, "------ Server load ------");
logMemoryInfo(details);
logCPU(details);
{
namespace vd = Version::Data;
logprintf(Log::informational, LABEL_FMT "%d.%d.%d %s", "------ This daemon ------", vd::major, vd::minor, vd::step, vd::timestamp);
}
logUptime();
logprintf(Log::informational, LABEL_FMT F1_WIDTH "u", "Connected clients:", ClientMap::Count());
logprintf(Log::informational, LABEL_FMT F1_WIDTH "u", "Subscribed offices:", OfficeSubscriptions::OfficeCount());
logprintf(Log::informational, LABEL_FMT F1_WIDTH "u", "Office subscriptions:", OfficeSubscriptions::SubscriptionCount());
logLocalMemoryInfo(details);
logMallocInfo(details);
logMallinfo(details);
logLocalCPU(details);
logprintf(Log::informational, "-------------------------");
}
/* ---------------------------------------------------------------------------
LogMemstats interface functions
*/
void LogMemstats::Timer(void)
{
time_t t = time(NULL);
static time_t t0s = t - (log_summary_interval_seconds - first_log_delay_seconds);
static time_t t0d = t - (log_details_interval_seconds - first_log_delay_seconds);
if ((t - t0s) >= log_summary_interval_seconds)
{
t0s = t;
bool details = false;
if ((t - t0d) >= log_details_interval_seconds)
{
t0d = t;
details = true;
}
logStats(details);
}
}