mirror of
https://github.com/bjango/istatserverlinux.git
synced 2025-10-25 08:50:46 +00:00
Initial commit
This commit is contained in:
160
src/stats/StatBase.cpp
Normal file
160
src/stats/StatBase.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void StatsBase::tickSample()
|
||||
{
|
||||
sampleIndex[0].sampleID = sampleIndex[0].sampleID + 1;
|
||||
}
|
||||
|
||||
void StatsBase::tick()
|
||||
{
|
||||
sampleIndex[0].time = sampleIndex[0].nextTime;
|
||||
}
|
||||
|
||||
void StatsBase::prepareUpdate()
|
||||
{
|
||||
sampleIndex[0].time = sampleIndex[0].nextTime;
|
||||
sampleIndex[0].sampleID = sampleIndex[0].sampleID + 1;
|
||||
}
|
||||
|
||||
void StatsBase::initShared()
|
||||
{
|
||||
int x;
|
||||
int storageIndexes[9] = {0, 0, 1, 1, 2, 2, 2, 2, 0};
|
||||
int intervals[9] = {1, 6, 144, 1008, 4320, 12960, 25920, 52560, 300};
|
||||
for(x=0;x<8;x++){
|
||||
sampleIndex[x].time = 0;
|
||||
sampleIndex[x].nextTime = 0;
|
||||
sampleIndex[x].sampleID = 1;
|
||||
sampleIndex[x].interval = intervals[x];
|
||||
sampleIndex[x].historyIndex = storageIndexes[x];
|
||||
}
|
||||
ready = 1;
|
||||
session = 0;
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
string StatsBase::tableAtIndex(int index)
|
||||
{
|
||||
string tables[8] = {"", "hour", "day", "week", "month", "threemonth", "sixmonth", "year" };
|
||||
return tables[index];
|
||||
}
|
||||
|
||||
double StatsBase::sampleIdForTable(string table)
|
||||
{
|
||||
string sql = "select sample from " + table + " order by sample desc limit 1";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
|
||||
if(query.next())
|
||||
{
|
||||
double value = sqlite3_column_double(query._statement, 0);
|
||||
query.finalize();
|
||||
return value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StatsBase::fillGaps()
|
||||
{
|
||||
fillGapsAtIndex(1);
|
||||
fillGapsAtIndex(2);
|
||||
fillGapsAtIndex(3);
|
||||
fillGapsAtIndex(4);
|
||||
fillGapsAtIndex(5);
|
||||
fillGapsAtIndex(6);
|
||||
fillGapsAtIndex(7);
|
||||
}
|
||||
|
||||
void StatsBase::fillGapsAtIndex(int index)
|
||||
{
|
||||
double now = get_current_time();
|
||||
|
||||
if (sampleIndex[index].nextTime == 0) {
|
||||
sampleIndex[index].nextTime = ceil(now) + sampleIndex[index].interval;
|
||||
sampleIndex[index].time = sampleIndex[index].nextTime - sampleIndex[index].interval;
|
||||
|
||||
InsertInitialSample(index, sampleIndex[index].time, sampleIndex[index].sampleID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sampleIndex[index].nextTime < now)
|
||||
{
|
||||
while (sampleIndex[index].nextTime < now)
|
||||
{
|
||||
sampleIndex[index].nextTime = sampleIndex[index].nextTime + sampleIndex[index].interval;
|
||||
sampleIndex[index].sampleID = sampleIndex[index].sampleID + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int StatsBase::InsertInitialSample(int index, double t, long long sampleID)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(index);
|
||||
if (databaseType == 1)
|
||||
table += "_id";
|
||||
|
||||
string sql = "insert into " + table + " (sample, time, empty) values(?, ?, 1)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(dbItem._statement, 1, (double)sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 2, t);
|
||||
dbItem.executeUpdate();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StatsBase::removeOldSamples()
|
||||
{
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
if (databaseType == 1)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x) + "_id";
|
||||
string sql = "delete from " + table + " WHERE sample < ?";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(dbItem._statement, 1, sampleIndex[x].sampleID - 600);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
string sql = "delete from " + table + " WHERE sample < ?";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(dbItem._statement, 1, sampleIndex[x].sampleID - 600);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
303
src/stats/StatBase.h
Normal file
303
src/stats/StatBase.h
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _STATBASE_H
|
||||
#define _STATBASE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <limits.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
#include "System.h"
|
||||
#include "Utility.h"
|
||||
|
||||
// needed for solaris
|
||||
#define _STRUCTURED_PROC 1
|
||||
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
# include <sensors/sensors.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PROCFS_H
|
||||
#include <sys/procfs.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DIRENT_H
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PROCFS_H
|
||||
#include <procfs.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PROCINFO_H
|
||||
#include <procinfo.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KVM_H
|
||||
# include <kvm.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_USER_H
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NET_IF_H
|
||||
# include <net/if.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NET_IF_MIB_H
|
||||
# include <net/if_mib.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETIFADDRS
|
||||
#include <ifaddrs.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#ifndef AF_LINK
|
||||
#define AF_LINK AF_PACKET
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INET_COMMON_H
|
||||
#include <inet/common.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SOCKIO_H
|
||||
#include <sys/sockio.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SWAP_H
|
||||
# include <sys/swap.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UVM_UVM_EXTERN_H
|
||||
# include <uvm/uvm_extern.h>
|
||||
#endif
|
||||
|
||||
#ifdef TIME_WITH_SYS_TIME
|
||||
# include <sys/time.h>
|
||||
# include <time.h>
|
||||
#elif defined(HAVE_SYS_TIME_H)
|
||||
# include <sys/time.h>
|
||||
#else
|
||||
# include <time.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_VMMETER_H
|
||||
# include <sys/vmmeter.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_MNTTAB_H
|
||||
# include <sys/mnttab.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_STATVFS_H
|
||||
# include <sys/statvfs.h>
|
||||
#elif defined(HAVE_SYS_STATFS_H)
|
||||
# include <sys/statfs.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_MOUNT_H
|
||||
# include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MNTENT_H
|
||||
# include <mntent.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PATHS_H
|
||||
# include <paths.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_RESOURCE_H
|
||||
# include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SCHED_H
|
||||
# include <sys/sched.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PROCESSOR_H
|
||||
# include <sys/processor.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SYSINFO_H
|
||||
# include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DIRENT_H
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MACHINE_APMVAR_H
|
||||
#include <machine/apmvar.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DEV_ACPICA_ACPIIO_H
|
||||
#include <dev/acpica/acpiio.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SYSCTL_H
|
||||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DEVSTAT) || defined(HAVE_DEVSTAT_ALT)
|
||||
#include <devstat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KSTAT_H
|
||||
# include <kstat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PATHS_H
|
||||
# include <paths.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_LOADAVG_H
|
||||
# include <sys/loadavg.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PROC_H
|
||||
# include <sys/proc.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBPERFSTAT_H
|
||||
# include <libperfstat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PSTAT_H
|
||||
# include <sys/pstat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_DK_H
|
||||
# include <sys/dk.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_DLPI_H
|
||||
# include <sys/dlpi.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_DLPI_EXT_H
|
||||
# include <sys/dlpi_ext.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_MIB_H
|
||||
# include <sys/mib.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_STROPTS_H
|
||||
# include <sys/stropts.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_DISK_H
|
||||
# include <sys/disk.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_DKSTAT_H
|
||||
# include <sys/dkstat.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
#include "Database.h"
|
||||
#endif
|
||||
|
||||
class StatsBase
|
||||
{
|
||||
typedef struct sampleindexconfig {
|
||||
long long sampleID;
|
||||
double time;
|
||||
double nextTime;
|
||||
double interval;
|
||||
int historyIndex;
|
||||
} sampleindexconfig_t;
|
||||
|
||||
public:
|
||||
void prepareUpdate();
|
||||
void tick();
|
||||
void tickSample();
|
||||
struct sampleindexconfig sampleIndex[8];
|
||||
void initShared();
|
||||
int ready;
|
||||
long session;
|
||||
bool historyEnabled;
|
||||
bool debugLogging;
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
std::vector<DatabaseItem> databaseQueue;
|
||||
Database _database;
|
||||
int databaseType;
|
||||
std::string databasePrefix;
|
||||
void fillGaps();
|
||||
void fillGapsAtIndex(int index);
|
||||
std::string tableAtIndex(int index);
|
||||
int InsertInitialSample(int index, double time, long long sampleID);
|
||||
double sampleIdForTable(std::string table);
|
||||
void loadPreviousSamples();
|
||||
void removeOldSamples();
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
672
src/stats/StatsActivity.cpp
Normal file
672
src/stats/StatsActivity.cpp
Normal file
@@ -0,0 +1,672 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsActivity.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if defined(USE_ACTIVITY_HPUX)
|
||||
|
||||
void StatsActivity::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsActivity::update(long long sampleID)
|
||||
{
|
||||
struct pst_diskinfo pstat_disk;
|
||||
int i = 0;
|
||||
|
||||
while (pstat_getdisk(pstat_diskinfo, sizeof pstat_disk, 1, i) != -1) {
|
||||
i++;
|
||||
if(pstat_disk.psd_status == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
processDisk(string(pstat_disk.psd_hw_path.psh_name), sampleID, pstat_disk.psd_dkbyteread, pstat_disk.psd_dkbytewrite, pstat_disk.psd_dkread, pstat_disk.psd_dkwrite);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(USE_ACTIVITY_HWDS_NET)
|
||||
|
||||
#ifdef HW_IOSTATS
|
||||
#define HW_DISKSTATS HW_IOSTATS
|
||||
#define disk_sysctl io_sysctl
|
||||
#define dk_rbytes rbytes
|
||||
#define dk_wbytes wbytes
|
||||
#define dk_name name
|
||||
#define dk_rxfer rxfer
|
||||
#define dk_wxfer wxfer
|
||||
#endif
|
||||
|
||||
void StatsActivity::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsActivity::update(long long sampleID)
|
||||
{
|
||||
int mib[3];
|
||||
int x;
|
||||
int diskn;
|
||||
size_t len;
|
||||
struct disk_sysctl *ds;
|
||||
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_DISKSTATS;
|
||||
mib[2] = sizeof(struct disk_sysctl);
|
||||
if (sysctl(mib, 3, NULL, &len, NULL, 0) == -1)
|
||||
return;
|
||||
|
||||
diskn = len / sizeof(struct disk_sysctl);
|
||||
ds = (struct disk_sysctl *)malloc(len);
|
||||
if (sysctl(mib, 3, ds, &len, NULL, 0) == -1)
|
||||
return;
|
||||
|
||||
for (x = 0; x < diskn; x++){
|
||||
if(!strncmp(ds[x].dk_name, "cd", 2))
|
||||
continue;
|
||||
if(!strncmp(ds[x].dk_name, "fd", 2))
|
||||
continue;
|
||||
processDisk(string(ds[x].dk_name), sampleID, ds[x].dk_rbytes, ds[x].dk_wbytes, ds[x].dk_rxfer, ds[x].dk_wxfer);
|
||||
}
|
||||
|
||||
free(ds);
|
||||
}
|
||||
|
||||
#elif defined(USE_ACTIVITY_HWDS_OPEN)
|
||||
|
||||
void StatsActivity::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsActivity::update(long long sampleID)
|
||||
{
|
||||
int mib[3];
|
||||
int x;
|
||||
int diskn;
|
||||
size_t len;
|
||||
struct diskstats *ds;
|
||||
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_DISKCOUNT;
|
||||
len = sizeof(diskn);
|
||||
|
||||
if (sysctl(mib, 2, &diskn, &len, NULL, 0) < 0)
|
||||
return;
|
||||
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_DISKSTATS;
|
||||
len = diskn * sizeof(struct diskstats);
|
||||
|
||||
ds = (struct diskstats *)malloc(len);
|
||||
if (ds == NULL)
|
||||
return;
|
||||
|
||||
if (sysctl(mib, 2, ds, &len, NULL, 0) < 0) {
|
||||
free(ds);
|
||||
return;
|
||||
}
|
||||
|
||||
for (x = 0; x < diskn; x++){
|
||||
if(!strncmp(ds[x].ds_name, "cd", 2))
|
||||
continue;
|
||||
if(!strncmp(ds[x].ds_name, "fd", 2))
|
||||
continue;
|
||||
processDisk(string(ds[x].ds_name), sampleID, ds[x].ds_rbytes, ds[x].ds_wbytes, ds[x].ds_rxfer, ds[x].ds_wxfer);
|
||||
}
|
||||
|
||||
free(ds);
|
||||
}
|
||||
|
||||
#elif defined(USE_ACTIVITY_KSTAT)
|
||||
|
||||
void StatsActivity::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsActivity::update(long long sampleID)
|
||||
{
|
||||
kstat_t *ksp;
|
||||
kstat_io_t kios;
|
||||
|
||||
for (ksp = ksh->kc_chain; ksp; ksp = ksp->ks_next) {
|
||||
if (ksp->ks_type != KSTAT_TYPE_IO)
|
||||
continue;
|
||||
if (strncmp(ksp->ks_class, "disk", 4) || !strcmp(ksp->ks_module, "fd"))
|
||||
continue;
|
||||
|
||||
memset((void *)&kios, 0, sizeof(kstat_io_t));
|
||||
kstat_read(ksh, ksp, &kios);
|
||||
|
||||
processDisk(string(ksp->ks_name), sampleID, kios.nread, kios.nwritten, kios.reads, kios.writes);
|
||||
}
|
||||
}
|
||||
|
||||
#elif (defined(HAVE_DEVSTAT) || defined(HAVE_DEVSTAT_ALT)) && defined(USE_ACTIVITY_DEVSTAT)
|
||||
|
||||
void StatsActivity::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsActivity::update(long long sampleID)
|
||||
{
|
||||
int devs_count, num_selected, num_selections, dn;
|
||||
struct device_selection *dev_select = NULL;
|
||||
long select_generation;
|
||||
static struct statinfo statinfo_cur;
|
||||
|
||||
memset(&statinfo_cur, 0, sizeof(statinfo_cur));
|
||||
statinfo_cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
|
||||
|
||||
#ifdef HAVE_DEVSTAT_ALT
|
||||
if (getdevs(&statinfo_cur) < 0) {
|
||||
#else
|
||||
if (devstat_getdevs(NULL, &statinfo_cur) < 0) {
|
||||
#endif
|
||||
free(statinfo_cur.dinfo);
|
||||
return;
|
||||
}
|
||||
|
||||
devs_count = statinfo_cur.dinfo->numdevs;
|
||||
#ifdef HAVE_DEVSTAT_ALT
|
||||
if (selectdevs(&dev_select, &num_selected, &num_selections,
|
||||
#else
|
||||
if (devstat_selectdevs(&dev_select, &num_selected, &num_selections,
|
||||
#endif
|
||||
&select_generation, statinfo_cur.dinfo->generation,
|
||||
statinfo_cur.dinfo->devices, devs_count, NULL, 0, NULL, 0,
|
||||
DS_SELECT_ONLY, 16, 1) >= 0) {
|
||||
for (dn = 0; dn < devs_count; dn++) {
|
||||
int di;
|
||||
struct devstat *dev;
|
||||
|
||||
di = dev_select[dn].position;
|
||||
dev = &statinfo_cur.dinfo->devices[di];
|
||||
|
||||
if (((dev->device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) && ((dev->device_type & DEVSTAT_TYPE_PASS) == 0) && ((dev->device_name[0] != '\0')))
|
||||
{
|
||||
stringstream n;
|
||||
n << "/dev/" << dev->device_name << dev->unit_number;
|
||||
|
||||
#ifdef HAVE_DEVSTAT_ALT
|
||||
processDisk(n.str(), sampleID, dev->bytes_read, dev->bytes_written, dev->num_reads, dev->num_writes);
|
||||
#else
|
||||
processDisk(n.str(), sampleID, dev->bytes[DEVSTAT_READ], dev->bytes[DEVSTAT_WRITE], dev->operations[DEVSTAT_READ], dev->operations[DEVSTAT_WRITE]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
free(dev_select);
|
||||
}
|
||||
|
||||
free(statinfo_cur.dinfo);
|
||||
}
|
||||
|
||||
#elif defined(USE_ACTIVITY_PROCFS)
|
||||
|
||||
void StatsActivity::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsActivity::update(long long sampleID)
|
||||
{
|
||||
FILE * fp = NULL;
|
||||
|
||||
if (!(fp = fopen("/proc/diskstats", "r")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (!feof(fp))
|
||||
{
|
||||
unsigned long long read;
|
||||
unsigned long long write;
|
||||
unsigned long long reads;
|
||||
unsigned long long writes;
|
||||
unsigned int major;
|
||||
unsigned int minor;
|
||||
char dev[32];
|
||||
|
||||
if(fscanf(fp, "%u %u %s %llu %*u %llu %*u %llu %*u %llu %*[^\n]", &major, &minor, dev, &reads, &read, &writes, &write) > 0)
|
||||
{
|
||||
if(major == 1 || major == 58 || major == 43 || major == 7 || major == 253 || major == 11)
|
||||
continue;
|
||||
|
||||
processDisk(string(dev), sampleID, read * 512, write * 512, reads, writes);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(fscanf(fp, "%*[^\n]")){}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_LIBPERFSTAT) && defined(USE_ACTIVITY_PERFSTAT)
|
||||
|
||||
void StatsActivity::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsActivity::update(long long sampleID)
|
||||
{
|
||||
int disks = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0);
|
||||
if(disks < 0)
|
||||
return;
|
||||
|
||||
perfstat_disk_t *storage = (perfstat_disk_t *)calloc(disks, sizeof(perfstat_disk_t));
|
||||
|
||||
perfstat_id_t firstpath;
|
||||
disks = perfstat_disk(&firstpath, storage, sizeof(perfstat_disk_t), disks);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < disks; i++)
|
||||
{
|
||||
processDisk(string(storage[i].name), sampleID, storage[i].rblks * storage[i].bsize, storage[i].wblks * storage[i].bsize, storage[i].__rxfers, storage[i].xfers - storage[i].__rxfers);
|
||||
for (vector<activity_info>::iterator curdisk = _items.begin(); curdisk != _items.end(); ++curdisk)
|
||||
{
|
||||
if(string(storage[i].name) == (*curdisk).device)
|
||||
{
|
||||
if((*curdisk).is_new == true)
|
||||
{
|
||||
(*curdisk).is_new = false;
|
||||
|
||||
char path[2048];
|
||||
|
||||
stringstream cmd;
|
||||
cmd << "/usr/sbin/lspv -l ";
|
||||
cmd << storage[i].name;
|
||||
|
||||
FILE *fp = popen(cmd.str().c_str(), "r");
|
||||
if (fp != NULL) {
|
||||
stringstream output;
|
||||
while (fgets(path, sizeof(path)-1, fp) != NULL) {
|
||||
output << string(path) << endl;
|
||||
}
|
||||
vector<string> lines = explode(output.str(), "\n");
|
||||
|
||||
unsigned int x;
|
||||
for(x=0;x<lines.size();x++){
|
||||
string line = lines[x];
|
||||
vector<string> segments = explode(line, " ");
|
||||
if(segments.size() == 5){
|
||||
string mount = string(segments[4]);
|
||||
if(mount.compare("N/A") != 0)
|
||||
{
|
||||
(*curdisk).mounts.push_back(mount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(storage);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void StatsActivity::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsActivity::update(long long sampleID)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void StatsActivity::createDisk(string key)
|
||||
{
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<activity_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
activity_info interface = *cur;
|
||||
if (interface.device == key)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activity_info item;
|
||||
item.last_r = 0;
|
||||
item.last_w = 0;
|
||||
item.last_rIOPS = 0;
|
||||
item.last_wIOPS = 0;
|
||||
item.active = false;
|
||||
item.is_new = true;
|
||||
|
||||
item.device = key;
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
double sampleID = 0;
|
||||
if(samples[x].size() > 0)
|
||||
sampleID = samples[x][0].sampleID;
|
||||
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample AND uuid = ? order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
sqlite3_bind_text(query._statement, 2, key.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
activity_data sample;
|
||||
sample.r = query.doubleForColumn("read");
|
||||
sample.w = query.doubleForColumn("write");
|
||||
sample.rIOPS = query.doubleForColumn("readiops");
|
||||
sample.wIOPS = query.doubleForColumn("writeiops");
|
||||
sample.sampleID = query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
item.samples[x].push_front(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
session++;
|
||||
_items.insert(_items.begin(), item);
|
||||
}
|
||||
|
||||
void StatsActivity::prepareUpdate()
|
||||
{
|
||||
if(ready == 0)
|
||||
return;
|
||||
|
||||
StatsBase::prepareUpdate();
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<activity_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
(*cur).active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsActivity::processDisk(string key, long long sampleID, unsigned long long read, unsigned long long write, unsigned long long reads, unsigned long long writes)
|
||||
{
|
||||
if(key == "pass" || key == "cd")
|
||||
return;
|
||||
|
||||
createDisk(key);
|
||||
|
||||
for (vector<activity_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if (key == (*cur).device)
|
||||
{
|
||||
(*cur).active = true;
|
||||
|
||||
if(ready == 0)
|
||||
continue;
|
||||
|
||||
activity_data data;
|
||||
|
||||
if((*cur).last_r == 0)
|
||||
(*cur).last_r = read;
|
||||
if((*cur).last_w == 0)
|
||||
(*cur).last_w = write;
|
||||
if((*cur).last_wIOPS == 0)
|
||||
(*cur).last_wIOPS = writes;
|
||||
if((*cur).last_rIOPS == 0)
|
||||
(*cur).last_rIOPS = reads;
|
||||
|
||||
double w = (double)(write - (*cur).last_w);
|
||||
double r = (double)(read - (*cur).last_r);
|
||||
double wIOPS = (double)(writes - (*cur).last_wIOPS);
|
||||
double rIOPS = (double)(reads - (*cur).last_rIOPS);
|
||||
|
||||
(*cur).last_w = write;
|
||||
(*cur).last_r = read;
|
||||
(*cur).last_wIOPS = writes;
|
||||
(*cur).last_rIOPS = reads;
|
||||
|
||||
data.r = r;
|
||||
data.w = w;
|
||||
data.rIOPS = rIOPS;
|
||||
data.wIOPS = wIOPS;
|
||||
data.sampleID = sampleIndex[0].sampleID;
|
||||
data.time = sampleIndex[0].time;
|
||||
|
||||
(*cur).samples[0].push_front(data);
|
||||
if ((*cur).samples[0].size() > HISTORY_SIZE)
|
||||
(*cur).samples[0].pop_back();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsActivity::_init()
|
||||
{
|
||||
initShared();
|
||||
ready = 0;
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
databaseType = 1;
|
||||
databasePrefix = "activity_";
|
||||
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x) + "_id";
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double PRIMARY KEY NOT NULL, time double NOT NULL DEFAULT 0, empty integer NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
|
||||
table = databasePrefix + tableAtIndex(x);
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double NOT NULL, time double NOT NULL DEFAULT 0, uuid varchar(255) NOT NULL, read double NOT NULL DEFAULT 0, write double NOT NULL DEFAULT 0, readiops double NOT NULL DEFAULT 0, writeiops double NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
}
|
||||
loadPreviousSamples();
|
||||
fillGaps();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void StatsActivity::loadPreviousSamples()
|
||||
{
|
||||
loadPreviousSamplesAtIndex(1);
|
||||
loadPreviousSamplesAtIndex(2);
|
||||
loadPreviousSamplesAtIndex(3);
|
||||
loadPreviousSamplesAtIndex(4);
|
||||
loadPreviousSamplesAtIndex(5);
|
||||
loadPreviousSamplesAtIndex(6);
|
||||
loadPreviousSamplesAtIndex(7);
|
||||
}
|
||||
|
||||
void StatsActivity::loadPreviousSamplesAtIndex(int index)
|
||||
{
|
||||
|
||||
string table = databasePrefix + tableAtIndex(index) + "_id";
|
||||
double sampleID = sampleIdForTable(table);
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
sample_data sample;
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
samples[index].insert(samples[index].begin(), sample);
|
||||
}
|
||||
if(samples[index].size() > 0)
|
||||
{
|
||||
sampleIndex[index].sampleID = samples[index][0].sampleID;
|
||||
sampleIndex[index].time = samples[index][0].time;
|
||||
sampleIndex[index].nextTime = sampleIndex[index].time + sampleIndex[index].interval;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsActivity::updateHistory()
|
||||
{
|
||||
int x;
|
||||
for (x = 1; x < 8; x++)
|
||||
{
|
||||
if(sampleIndex[0].time >= sampleIndex[x].nextTime)
|
||||
{
|
||||
double now = get_current_time();
|
||||
double earlistTime = now - (HISTORY_SIZE * sampleIndex[x].interval);
|
||||
while(sampleIndex[x].nextTime < now)
|
||||
{
|
||||
sampleIndex[x].sampleID = sampleIndex[x].sampleID + 1;
|
||||
sampleIndex[x].time = sampleIndex[x].nextTime;
|
||||
sampleIndex[x].nextTime = sampleIndex[x].nextTime + sampleIndex[x].interval;
|
||||
|
||||
if(sampleIndex[x].time < earlistTime)
|
||||
continue;
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<activity_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
activity_data sample = historyItemAtIndex(x, (*cur));
|
||||
|
||||
(*cur).samples[x].push_front(sample);
|
||||
if ((*cur).samples[x].size() > HISTORY_SIZE) (*cur).samples[x].pop_back();
|
||||
|
||||
if(sample.empty)
|
||||
continue;
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
string sql = "insert into " + table + " (sample, time, read, write, readiops, writeiops, uuid) values(?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(dbItem._statement, 1, (double)sample.sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 2, sample.time);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sample.r);
|
||||
sqlite3_bind_double(dbItem._statement, 4, sample.w);
|
||||
sqlite3_bind_double(dbItem._statement, 5, sample.rIOPS);
|
||||
sqlite3_bind_double(dbItem._statement, 6, sample.wIOPS);
|
||||
sqlite3_bind_text(dbItem._statement, 7, (*cur).device.c_str(), -1, SQLITE_STATIC);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x) + "_id";
|
||||
string sql = "insert into " + table + " (empty, sample, time) values(?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_int(dbItem._statement, 1, 0);
|
||||
sqlite3_bind_double(dbItem._statement, 2, (double)sampleIndex[x].sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sampleIndex[x].time);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activity_data StatsActivity::historyItemAtIndex(int index, activity_info item)
|
||||
{
|
||||
activity_data sample;
|
||||
double r = 0;
|
||||
double w = 0;
|
||||
double rIOPS = 0;
|
||||
double wIOPS = 0;
|
||||
|
||||
std::deque<activity_data> from = item.samples[sampleIndex[index].historyIndex];
|
||||
double minimumTime = sampleIndex[index].time - sampleIndex[index].interval;
|
||||
double maximumTime = sampleIndex[index].time;
|
||||
if(sampleIndex[index].historyIndex == 0)
|
||||
maximumTime += 0.99;
|
||||
|
||||
int count = 0;
|
||||
if(from.size() > 0)
|
||||
{
|
||||
for (deque<activity_data>::iterator cursample = from.begin(); cursample != from.end(); ++cursample)
|
||||
{
|
||||
if ((*cursample).time > maximumTime)
|
||||
continue;
|
||||
|
||||
if ((*cursample).time < minimumTime)
|
||||
break;
|
||||
|
||||
r += (*cursample).r;
|
||||
w += (*cursample).w;
|
||||
rIOPS += (*cursample).rIOPS;
|
||||
wIOPS += (*cursample).wIOPS;
|
||||
count++;
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
r /= count;
|
||||
w /= count;
|
||||
rIOPS /= count;
|
||||
wIOPS /= count;
|
||||
}
|
||||
}
|
||||
|
||||
sample.r = r;
|
||||
sample.w = w;
|
||||
sample.rIOPS = rIOPS;
|
||||
sample.wIOPS = wIOPS;
|
||||
|
||||
sample.sampleID = sampleIndex[index].sampleID;
|
||||
sample.time = sampleIndex[index].time;
|
||||
sample.empty = false;
|
||||
if(count == 0)
|
||||
sample.empty = true;
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
79
src/stats/StatsActivity.h
Normal file
79
src/stats/StatsActivity.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
# include "config.h"
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _STATSACTIVITY_H
|
||||
#define _STATSACTIVITY_H
|
||||
|
||||
class activity_info
|
||||
{
|
||||
public:
|
||||
bool active;
|
||||
bool is_new;
|
||||
unsigned int id;
|
||||
unsigned long long last_r;
|
||||
unsigned long long last_w;
|
||||
unsigned long long last_rIOPS;
|
||||
unsigned long long last_wIOPS;
|
||||
|
||||
std::string device;
|
||||
std::vector<std::string> mounts;
|
||||
std::deque<activity_data> samples[8];
|
||||
};
|
||||
|
||||
class StatsActivity : public StatsBase
|
||||
{
|
||||
public:
|
||||
void update(long long sampleID);
|
||||
void init();
|
||||
void prepareUpdate();
|
||||
void createDisk(std::string key);
|
||||
void processDisk(std::string key, long long sampleID, unsigned long long read, unsigned long long write, unsigned long long reads, unsigned long long writes);
|
||||
|
||||
void _init();
|
||||
#ifdef USE_SQLITE
|
||||
void updateHistory();
|
||||
activity_data historyItemAtIndex(int index, activity_info item);
|
||||
void loadPreviousSamples();
|
||||
void loadPreviousSamplesAtIndex(int index);
|
||||
#endif
|
||||
|
||||
std::vector<activity_info> _items;
|
||||
std::deque<sample_data> samples[8];
|
||||
|
||||
#ifdef HAVE_LIBKSTAT
|
||||
kstat_ctl_t *ksh;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
510
src/stats/StatsBattery.cpp
Normal file
510
src/stats/StatsBattery.cpp
Normal file
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsBattery.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if defined(USE_BATTERY_APM)
|
||||
|
||||
#ifndef APM_BATT_ABSENT
|
||||
#define APM_BATT_ABSENT APM_BATTERY_ABSENT
|
||||
#endif
|
||||
|
||||
void StatsBattery::init()
|
||||
{
|
||||
_init();
|
||||
|
||||
int fd = open("/dev/apm", O_RDONLY);
|
||||
if(fd == -1)
|
||||
return;
|
||||
createBattery("apm", "", "");
|
||||
}
|
||||
|
||||
void StatsBattery::update(long long sampleID)
|
||||
{
|
||||
if(_items.size() == 0)
|
||||
return;
|
||||
|
||||
int fd = open("/dev/apm", O_RDONLY);
|
||||
if(fd == -1)
|
||||
return;
|
||||
|
||||
int state = BatteryStateUnknown;
|
||||
int source = BatterySourceAC;
|
||||
int present = 1;
|
||||
int percentage = -1;
|
||||
float t = 0;
|
||||
|
||||
struct apm_power_info info;
|
||||
if (ioctl(fd, APM_IOC_GETPOWER, &info) == 0)
|
||||
{
|
||||
if(info.battery_life == 255)
|
||||
percentage = 100;
|
||||
else
|
||||
percentage = info.battery_life;
|
||||
|
||||
if(info.battery_state == APM_BATT_CHARGING)
|
||||
{
|
||||
if(percentage == 100)
|
||||
state = BatteryStateCharged;
|
||||
else
|
||||
state = BatteryStateCharging;
|
||||
}
|
||||
else if(info.battery_state == APM_BATT_UNKNOWN || info.battery_state == APM_BATT_ABSENT)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
state = BatteryStateDraining;
|
||||
}
|
||||
|
||||
if(info.ac_state == APM_AC_ON)
|
||||
{
|
||||
if(percentage == 100)
|
||||
state = BatteryStateCharged;
|
||||
else
|
||||
state = BatteryStateCharging;
|
||||
source = BatterySourceAC;
|
||||
}
|
||||
else
|
||||
source = BatterySourceBattery;
|
||||
|
||||
if(info.minutes_left > 0 && info.minutes_left < 0xFFFF)
|
||||
t = info.minutes_left;
|
||||
|
||||
int timeRemaining = t;
|
||||
|
||||
for (vector<battery_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
(*cur).state = state;
|
||||
(*cur).source = source;
|
||||
(*cur).present = present;
|
||||
(*cur).percentage = percentage;
|
||||
(*cur).timeRemaining = timeRemaining;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
#elif defined(USE_BATTERY_ACPI) && defined(HAVE_SYSCTLBYNAME)
|
||||
|
||||
#ifndef UNKNOWN_CAP
|
||||
#define UNKNOWN_CAP 0xffffffff
|
||||
#endif
|
||||
|
||||
#ifndef UNKNOWN_VOLTAGE
|
||||
#define UNKNOWN_VOLTAGE 0xffffffff
|
||||
#endif
|
||||
|
||||
void StatsBattery::init()
|
||||
{
|
||||
_init();
|
||||
|
||||
int batteries;
|
||||
size_t len = sizeof(batteries);
|
||||
|
||||
if (sysctlbyname("hw.acpi.battery.units", &batteries, &len, NULL, 0) >= 0)
|
||||
{
|
||||
int x;
|
||||
for(x=0;x<batteries;x++)
|
||||
{
|
||||
stringstream key;
|
||||
key << x;
|
||||
createBattery(key.str(), "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsBattery::update(long long sampleID)
|
||||
{
|
||||
if(_items.size() == 0)
|
||||
return;
|
||||
|
||||
int acpifd = open("/dev/acpi", O_RDONLY);
|
||||
|
||||
for (vector<battery_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
union acpi_battery_ioctl_arg battio;
|
||||
battio.unit = atoi((*cur).key.c_str());
|
||||
if (ioctl(acpifd, ACPIIO_BATT_GET_BIF, &battio) == -1)
|
||||
{
|
||||
(*cur).active = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
int state = BatteryStateUnknown;
|
||||
int source = BatterySourceAC;
|
||||
int present = 1;
|
||||
int cycles = 0;
|
||||
int percentage = -1;
|
||||
int capacityDesign = 0;
|
||||
int capacityFull = 0;
|
||||
int capacityNow = 0;
|
||||
int capacityRate = 0;
|
||||
int voltage = 0;
|
||||
float health = 0;
|
||||
float t = 0;
|
||||
|
||||
if (battio.bif.dcap != UNKNOWN_CAP)
|
||||
capacityDesign = battio.bif.dcap;
|
||||
|
||||
if (battio.bif.lfcap != UNKNOWN_CAP)
|
||||
capacityFull = battio.bif.lfcap;
|
||||
|
||||
|
||||
battio.unit = atoi((*cur).key.c_str());
|
||||
if (ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &battio) == -1)
|
||||
{
|
||||
(*cur).active = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
(*cur).active = true;
|
||||
|
||||
int acline = 0;
|
||||
ioctl(acpifd, ACPIIO_ACAD_GET_STATUS, &acline);
|
||||
|
||||
if (battio.battinfo.state != ACPI_BATT_STAT_NOT_PRESENT)
|
||||
{
|
||||
if (battio.battinfo.cap != -1)
|
||||
percentage = battio.battinfo.cap;
|
||||
|
||||
if(acline == 1)
|
||||
{
|
||||
source = BatterySourceAC;
|
||||
|
||||
if (battio.battinfo.state & ACPI_BATT_STAT_CHARGING)
|
||||
state = BatteryStateCharging;
|
||||
else
|
||||
{
|
||||
if(percentage == 100)
|
||||
state = BatteryStateCharged;
|
||||
else
|
||||
state = BatteryStateCharging;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
source = BatterySourceBattery;
|
||||
state = BatteryStateDraining;
|
||||
}
|
||||
|
||||
if (battio.battinfo.min != -1)
|
||||
t = battio.battinfo.min;
|
||||
}
|
||||
|
||||
battio.unit = atoi((*cur).key.c_str());
|
||||
if (ioctl(acpifd, ACPIIO_BATT_GET_BST, &battio) != -1)
|
||||
{
|
||||
if (battio.bst.state != ACPI_BATT_STAT_NOT_PRESENT) {
|
||||
if (battio.bst.volt != UNKNOWN_VOLTAGE)
|
||||
voltage = battio.bst.volt;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(percentage == -1)
|
||||
{
|
||||
if(capacityFull > 0 && capacityNow > 0)
|
||||
percentage = (capacityNow / capacityFull) * 100;
|
||||
else
|
||||
percentage = 0;
|
||||
}*/
|
||||
|
||||
if(capacityDesign > 0 && capacityFull > 0)
|
||||
health = ((float)capacityFull / (float)capacityDesign) * 100;
|
||||
|
||||
int timeRemaining = t;
|
||||
|
||||
(*cur).state = state;
|
||||
(*cur).source = source;
|
||||
(*cur).present = present;
|
||||
(*cur).cycles = cycles;
|
||||
(*cur).percentage = percentage;
|
||||
(*cur).capacityDesign = capacityDesign;
|
||||
(*cur).capacityFull = capacityFull;
|
||||
(*cur).capacityNow = capacityNow;
|
||||
(*cur).capacityRate = capacityRate;
|
||||
(*cur).voltage = voltage;
|
||||
(*cur).timeRemaining = timeRemaining;
|
||||
(*cur).health = health;
|
||||
}
|
||||
|
||||
close(acpifd);
|
||||
}
|
||||
|
||||
#elif defined(USE_BATTERY_PROCFS)
|
||||
|
||||
bool StatsBattery::fileExists(string path)
|
||||
{
|
||||
FILE * fp = NULL;
|
||||
|
||||
if (!(fp = fopen(path.c_str(), "r")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StatsBattery::init()
|
||||
{
|
||||
_init();
|
||||
|
||||
DIR *dpdf;
|
||||
struct dirent *epdf;
|
||||
|
||||
vector<string> adapters;
|
||||
vector<string> batteries;
|
||||
string directory = string("/sys/class/power_supply/");
|
||||
|
||||
dpdf = opendir(directory.c_str());
|
||||
if (dpdf != NULL){
|
||||
while ((epdf = readdir(dpdf))){
|
||||
string file = string(epdf->d_name);
|
||||
if(file.find("A") == 0)
|
||||
{
|
||||
string path = directory;
|
||||
adapters.push_back(path.append(string(epdf->d_name)));
|
||||
}
|
||||
if(file.find("BAT") == 0)
|
||||
{
|
||||
string path = directory;
|
||||
batteries.push_back(path.append(string(epdf->d_name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(batteries.size() > 0)
|
||||
{
|
||||
for (vector<string>::iterator curbat = batteries.begin(); curbat != batteries.end(); ++curbat)
|
||||
{
|
||||
string uevent = (*curbat);
|
||||
uevent = uevent.append("/uevent");
|
||||
if(!fileExists(uevent))
|
||||
continue;
|
||||
|
||||
string batterysuffix = (*curbat).substr(3, (*curbat).length() - 3);
|
||||
|
||||
string adapterpath = string("");
|
||||
if(batterysuffix.length() > 0 && adapters.size() > 0)
|
||||
{
|
||||
for (vector<string>::iterator curadapter = adapters.begin(); curadapter != adapters.end(); ++curadapter)
|
||||
{
|
||||
string adaptersuffix = (*curadapter).substr((*curadapter).length() - batterysuffix.length(), batterysuffix.length());
|
||||
if(batterysuffix == adaptersuffix)
|
||||
{
|
||||
adapterpath = (*curadapter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createBattery((*curbat), uevent, adapterpath);
|
||||
}
|
||||
}
|
||||
closedir(dpdf);
|
||||
}
|
||||
|
||||
void StatsBattery::update(long long sampleID)
|
||||
{
|
||||
if(_items.size() == 0)
|
||||
return;
|
||||
|
||||
for (vector<battery_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
FILE * fp = NULL;
|
||||
char buf[512];
|
||||
|
||||
if (!(fp = fopen((*cur).path.c_str(), "r")))
|
||||
{
|
||||
(*cur).present = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
(*cur).active = true;
|
||||
|
||||
int state = BatteryStateUnknown;
|
||||
int source = BatterySourceAC;
|
||||
int present = 1;
|
||||
int cycles = 0;
|
||||
int percentage = -1;
|
||||
int capacityDesign = 0;
|
||||
int capacityFull = 0;
|
||||
int capacityNow = 0;
|
||||
int capacityRate = 0;
|
||||
int voltage = 0;
|
||||
float health = 0;
|
||||
|
||||
char charbuf[128];
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
sscanf(buf, "POWER_SUPPLY_PRESENT=%d", &present);
|
||||
sscanf(buf, "POWER_SUPPLY_CYCLE_COUNT=%d", &cycles);
|
||||
sscanf(buf, "POWER_SUPPLY_CAPACITY=%d", &percentage);
|
||||
sscanf(buf, "POWER_SUPPLY_ENERGY_FULL_DESIGN=%d", &capacityDesign);
|
||||
sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &capacityFull);
|
||||
sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &capacityNow);
|
||||
sscanf(buf, "POWER_SUPPLY_POWER_NOW=%d", &capacityRate);
|
||||
sscanf(buf, "POWER_SUPPLY_VOLTAGE_NOW=%d", &voltage);
|
||||
|
||||
if(sscanf(buf, "POWER_SUPPLY_STATUS=%s", charbuf) == 1)
|
||||
{
|
||||
string status = string(charbuf);
|
||||
if(status.find("Charging") == 0)
|
||||
{
|
||||
source = BatterySourceAC;
|
||||
state = BatteryStateCharging;
|
||||
}
|
||||
else if(status.find("Discharging") == 0)
|
||||
{
|
||||
source = BatterySourceBattery;
|
||||
state = BatteryStateDraining;
|
||||
}
|
||||
else if(status.find("Charged") == 0)
|
||||
{
|
||||
source = BatterySourceAC;
|
||||
state = BatteryStateCharged;
|
||||
}
|
||||
else
|
||||
{
|
||||
source = BatterySourceAC;
|
||||
state = BatteryStateCharged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(percentage == -1)
|
||||
{
|
||||
if(capacityFull > 0 && capacityNow > 0)
|
||||
percentage = (capacityNow / capacityFull) * 100;
|
||||
else
|
||||
percentage = 0;
|
||||
}
|
||||
|
||||
if(percentage > 100)
|
||||
percentage = 100;
|
||||
|
||||
float t = 0;
|
||||
if(state == BatteryStateCharging)
|
||||
{
|
||||
int remainingUntilFull = capacityFull - capacityNow;
|
||||
t = 0;
|
||||
if(remainingUntilFull > 0 && capacityRate > 0)
|
||||
t = ((float)remainingUntilFull / capacityRate);
|
||||
}
|
||||
else if(state == BatteryStateDraining)
|
||||
{
|
||||
t = 0;
|
||||
if(capacityNow > 0 && capacityRate > 0)
|
||||
t = ((float)capacityNow / capacityRate);
|
||||
}
|
||||
|
||||
int timeRemaining = t * 60;
|
||||
|
||||
if(capacityDesign > 0 && capacityFull > 0)
|
||||
health = ((float)capacityFull / (float)capacityDesign) * 100;
|
||||
|
||||
(*cur).state = state;
|
||||
(*cur).source = source;
|
||||
(*cur).present = present;
|
||||
(*cur).cycles = cycles;
|
||||
(*cur).percentage = percentage;
|
||||
(*cur).capacityDesign = capacityDesign;
|
||||
(*cur).capacityFull = capacityFull;
|
||||
(*cur).capacityNow = capacityNow;
|
||||
(*cur).capacityRate = capacityRate;
|
||||
(*cur).voltage = voltage;
|
||||
(*cur).timeRemaining = timeRemaining;
|
||||
(*cur).health = health;
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void StatsBattery::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsBattery::update(long long sampleID)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void StatsBattery::_init()
|
||||
{
|
||||
initShared();
|
||||
ready = 1;
|
||||
}
|
||||
|
||||
void StatsBattery::prepareUpdate()
|
||||
{
|
||||
StatsBase::prepareUpdate();
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<battery_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
(*cur).active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsBattery::createBattery(string key, string batterypath, string adapterpath)
|
||||
{
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<battery_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
battery_info interface = *cur;
|
||||
if (interface.key == key)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
battery_info battery;
|
||||
battery.key = key;
|
||||
battery.path = batterypath;
|
||||
battery.adapterpath = adapterpath;
|
||||
|
||||
_items.insert(_items.begin(), battery);
|
||||
}
|
||||
82
src/stats/StatsBattery.h
Normal file
82
src/stats/StatsBattery.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _STATSBATTERY_H
|
||||
#define _STATSBATTERY_H
|
||||
|
||||
#define BatteryStateUnknown -1
|
||||
#define BatteryStateCharged 0
|
||||
#define BatteryStateCharging 1
|
||||
#define BatteryStateDraining 2
|
||||
#define BatteryStateNotCharging 3
|
||||
#define BatteryStateNA 4
|
||||
|
||||
#define BatterySourceBattery 0
|
||||
#define BatterySourceAC 1
|
||||
|
||||
class battery_info
|
||||
{
|
||||
public:
|
||||
bool active;
|
||||
long long sampleID;
|
||||
std::string key;
|
||||
std::string path;
|
||||
std::string adapterpath;
|
||||
|
||||
int state;
|
||||
int source;
|
||||
int present;
|
||||
int cycles;
|
||||
int percentage;
|
||||
int capacityDesign;
|
||||
int capacityFull;
|
||||
int capacityNow;
|
||||
int capacityRate;
|
||||
int voltage;
|
||||
int timeRemaining;
|
||||
float health;
|
||||
// std::vector<net_data> history;
|
||||
};
|
||||
|
||||
class StatsBattery : public StatsBase
|
||||
{
|
||||
public:
|
||||
void update(long long sampleID);
|
||||
void init();
|
||||
void _init();
|
||||
bool fileExists(std::string path);
|
||||
std::vector<battery_info> _items;
|
||||
void createBattery(std::string key, std::string batterypath, std::string adapterpath);
|
||||
void prepareUpdate();
|
||||
};
|
||||
#endif
|
||||
625
src/stats/StatsCPU.cpp
Normal file
625
src/stats/StatsCPU.cpp
Normal file
@@ -0,0 +1,625 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsCPU.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if defined(USE_CPU_HPUX)
|
||||
|
||||
void StatsCPU::init()
|
||||
{
|
||||
_init();
|
||||
|
||||
struct pst_dynamic pstat_dynamic;
|
||||
if (pstat_getdynamic(&pstat_dynamic, sizeof(pstat_dynamic), 1, 0) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < PST_MAX_CPUSTATES; i++) {
|
||||
last_ticks[i] = (unsigned long long)pstat_dynamic.psd_cpu_time[i];
|
||||
}
|
||||
}
|
||||
|
||||
void StatsCPU::update(long long sampleID)
|
||||
{
|
||||
struct pst_dynamic pstat_dynamic;
|
||||
if (pstat_getdynamic(&pstat_dynamic, sizeof(pstat_dynamic), 1, 0) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long long user = (unsigned long long)pstat_dynamic.psd_cpu_time[CP_USER];
|
||||
unsigned long long sys = (unsigned long long)pstat_dynamic.psd_cpu_time[CP_SSYS] + (unsigned long long)pstat_dynamic.psd_cpu_time[CP_SYS];
|
||||
unsigned long long nice = (unsigned long long)pstat_dynamic.psd_cpu_time[CP_NICE];
|
||||
unsigned long long idle = (unsigned long long)pstat_dynamic.psd_cpu_time[CP_IDLE];
|
||||
unsigned long long wait = (unsigned long long)pstat_dynamic.psd_cpu_time[CP_WAIT];
|
||||
|
||||
unsigned long long total = 0;
|
||||
int i;
|
||||
for (i = 0; i < PST_MAX_CPUSTATES; i++) {
|
||||
total += (unsigned long long)pstat_dynamic.psd_cpu_time[i] - last_ticks[i];
|
||||
}
|
||||
|
||||
processSample(sampleID, user, nice, sys, idle, wait, total);
|
||||
|
||||
for (i = 0; i < PST_MAX_CPUSTATES; i++) {
|
||||
last_ticks[i] = (unsigned long long)pstat_dynamic.psd_cpu_time[i];
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(USE_CPU_SYSCTL)
|
||||
|
||||
void StatsCPU::init()
|
||||
{
|
||||
_init();
|
||||
|
||||
int mib[2] = {CTL_HW, HW_NCPU};
|
||||
size_t size = sizeof(cpu_count);
|
||||
if(sysctl(mib, 2, &cpu_count, &size, NULL, 0) < 0)
|
||||
cpu_count = 1;
|
||||
}
|
||||
|
||||
void StatsCPU::update(long long sampleID)
|
||||
{
|
||||
#if defined(__OpenBSD__) || defined(__OPENBSD)
|
||||
unsigned long long cp_time[CPUSTATES];
|
||||
#else
|
||||
long cp_time[CPUSTATES];
|
||||
#endif
|
||||
|
||||
int x;
|
||||
for(x=0;x<CPUSTATES;x++)
|
||||
cp_time[x] = 0;
|
||||
|
||||
if(cpu_count == 1)
|
||||
{
|
||||
int mib[2] = { CTL_KERN, KERN_CPTIME };
|
||||
size_t len = sizeof(cp_time);
|
||||
|
||||
if (sysctl(mib, 2, &cp_time, &len, NULL, 0) < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int x;
|
||||
for(x=0;x<cpu_count;x++)
|
||||
{
|
||||
#if defined(__OpenBSD__) || defined(__OPENBSD)
|
||||
unsigned long long cp_time2[CPUSTATES];
|
||||
#else
|
||||
long cp_time2[CPUSTATES];
|
||||
#endif
|
||||
|
||||
int x;
|
||||
for(x=0;x<CPUSTATES;x++)
|
||||
cp_time2[x] = 0;
|
||||
|
||||
int mib[3] = {CTL_KERN, KERN_CPTIME2, x};
|
||||
size_t len = sizeof(cp_time2);
|
||||
if(sysctl(mib, 3, &cp_time2, &len, NULL, 0) < 0)
|
||||
continue;
|
||||
|
||||
for(x=0;x<CPUSTATES;x++)
|
||||
cp_time[x] += cp_time2[x];
|
||||
}
|
||||
}
|
||||
|
||||
processSample(sampleID, cp_time[CP_USER], cp_time[CP_NICE], cp_time[CP_SYS], cp_time[CP_IDLE], 0, 0);
|
||||
} /*USE_CPU_SYSCTL*/
|
||||
|
||||
#elif defined(USE_CPU_SYSCTL_BYNAME)
|
||||
|
||||
void StatsCPU::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsCPU::update(long long sampleID)
|
||||
{
|
||||
size_t len;
|
||||
#if defined(__NetBSD__) || defined(__NetBSD)
|
||||
u_int64_t cp_time[CPUSTATES];
|
||||
#elif defined(__DragonFly__)
|
||||
unsigned long long cp_time[CPUSTATES];
|
||||
#else
|
||||
long cp_time[CPUSTATES];
|
||||
#endif
|
||||
|
||||
len = sizeof(cp_time);
|
||||
|
||||
if (sysctlbyname("kern.cp_time", cp_time, &len, NULL, 0) < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
processSample(sampleID, cp_time[CP_USER], cp_time[CP_NICE], cp_time[CP_SYS], cp_time[CP_IDLE], 0, 0);
|
||||
} /*USE_CPU_SYSCTL_BYNAME*/
|
||||
|
||||
#elif defined(HAVE_LIBKSTAT) && defined(USE_CPU_KSTAT)
|
||||
|
||||
void StatsCPU::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsCPU::update(long long sampleID)
|
||||
{
|
||||
unsigned long long user = 0;
|
||||
unsigned long long kernel = 0;
|
||||
unsigned long long nice = 0;
|
||||
unsigned long long idle = 0;
|
||||
unsigned long long wait = 0;
|
||||
|
||||
kstat_t *ksp;
|
||||
static int ncpu;
|
||||
int c;
|
||||
cpu_stat_t cs;
|
||||
|
||||
ncpu = sysconf(_SC_NPROCESSORS_CONF);
|
||||
kstat_chain_update(ksh);
|
||||
for(c = 0; c < ncpu; c++)
|
||||
{
|
||||
if(p_online(c, P_STATUS) != P_ONLINE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(NULL == (ksp = kstat_lookup(ksh, (char*)"cpu_stat", c, NULL))) return;
|
||||
if(-1 == kstat_read(ksh, ksp, &cs)) return;
|
||||
|
||||
user += cs.cpu_sysinfo.cpu[CPU_USER];
|
||||
kernel += cs.cpu_sysinfo.cpu[CPU_KERNEL];
|
||||
idle += cs.cpu_sysinfo.cpu[CPU_IDLE];
|
||||
wait += cs.cpu_sysinfo.cpu[CPU_WAIT];
|
||||
}
|
||||
|
||||
processSample(sampleID, user, nice, kernel, idle, wait, 0);
|
||||
}/*USE_CPU_KSTAT*/
|
||||
# elif defined(USE_CPU_PROCFS)
|
||||
|
||||
void StatsCPU::init()
|
||||
{
|
||||
_init();
|
||||
|
||||
hasIOWait = false;
|
||||
|
||||
char buf[320];
|
||||
FILE * fp = NULL;
|
||||
|
||||
if (!(fp = fopen("/proc/stat", "r"))) return;
|
||||
|
||||
if (!fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long long iowait;
|
||||
if(sscanf(buf, "cpu %*d %*d %*d %*d %llu", &iowait) == 1)
|
||||
{
|
||||
hasIOWait = true;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void StatsCPU::update(long long sampleID)
|
||||
{
|
||||
unsigned long long user = 0;
|
||||
unsigned long long kernel = 0;
|
||||
unsigned long long nice = 0;
|
||||
unsigned long long idle = 0;
|
||||
unsigned long long wait = 0;
|
||||
|
||||
char buf[320];
|
||||
FILE * fp = NULL;
|
||||
|
||||
if (!(fp = fopen("/proc/stat", "r"))) return;
|
||||
|
||||
if (!fgets(buf, sizeof(buf), fp)){
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
if(hasIOWait)
|
||||
sscanf(buf, "cpu %llu %llu %llu %llu %llu", &user, &nice, &kernel, &idle, &wait);
|
||||
else
|
||||
sscanf(buf, "cpu %llu %llu %llu %llu", &user, &nice, &kernel, &idle);
|
||||
|
||||
processSample(sampleID, user, nice, kernel, idle, wait, 0);
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
#elif defined(HAVE_LIBPERFSTAT) && defined(USE_CPU_PERFSTAT)
|
||||
|
||||
void StatsCPU::init()
|
||||
{
|
||||
_init();
|
||||
hasLpar = false;
|
||||
aixEntitlement = 1;
|
||||
|
||||
perfstat_partition_total_t lparstats;
|
||||
if(!perfstat_partition_total(NULL, &lparstats, sizeof(perfstat_partition_total_t), 1))
|
||||
return;
|
||||
|
||||
if (lparstats.type.b.shared_enabled)
|
||||
{
|
||||
hasLpar = true;
|
||||
aixEntitlement = lparstats.entitled_proc_capacity / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsCPU::update(long long sampleID)
|
||||
{
|
||||
perfstat_cpu_total_t cpustats;
|
||||
perfstat_partition_total_t lparstats;
|
||||
|
||||
if(!perfstat_partition_total(NULL, &lparstats, sizeof(perfstat_partition_total_t), 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(!perfstat_cpu_total(NULL, &cpustats, sizeof cpustats, 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(last_ticks_lpar[0] == 0)
|
||||
{
|
||||
last_ticks_lpar[0] = lparstats.puser;
|
||||
last_ticks_lpar[1] = 0;
|
||||
last_ticks_lpar[2] = lparstats.psys;
|
||||
last_ticks_lpar[3] = lparstats.pidle;
|
||||
last_ticks_lpar[4] = lparstats.pwait;
|
||||
last_time = lparstats.timebase_last;
|
||||
}
|
||||
|
||||
if(last_ticks[0] == 0)
|
||||
{
|
||||
last_ticks[0] = cpustats.user;
|
||||
last_ticks[1] = 0;
|
||||
last_ticks[2] = cpustats.sys;
|
||||
last_ticks[3] = cpustats.idle;
|
||||
last_ticks[4] = cpustats.wait;
|
||||
}
|
||||
|
||||
unsigned long long dlt_pcpu_user = lparstats.puser - last_ticks_lpar[0];
|
||||
unsigned long long dlt_pcpu_sys = lparstats.psys - last_ticks_lpar[2];
|
||||
unsigned long long dlt_pcpu_idle = lparstats.pidle - last_ticks_lpar[3];
|
||||
unsigned long long dlt_pcpu_wait = lparstats.pwait - last_ticks_lpar[4];
|
||||
|
||||
unsigned long long dlt_lcpu_user = cpustats.user - last_ticks[0];
|
||||
unsigned long long dlt_lcpu_sys = cpustats.sys - last_ticks[2];
|
||||
unsigned long long dlt_lcpu_idle = cpustats.idle - last_ticks[3];
|
||||
unsigned long long dlt_lcpu_wait = cpustats.wait - last_ticks[4];
|
||||
|
||||
unsigned long long delta_purr, pcputime, entitled_purr, unused_purr, delta_time_base;
|
||||
|
||||
double entitlement = (double)lparstats.entitled_proc_capacity / 100.0;
|
||||
delta_time_base = lparstats.timebase_last - last_time;
|
||||
|
||||
delta_purr = pcputime = dlt_pcpu_user + dlt_pcpu_sys + dlt_pcpu_idle + dlt_pcpu_wait;
|
||||
|
||||
if (lparstats.type.b.shared_enabled)
|
||||
{
|
||||
entitled_purr = delta_time_base * entitlement;
|
||||
if (entitled_purr < delta_purr) {
|
||||
/* when above entitlement, use consumption in percentages */
|
||||
entitled_purr = delta_purr;
|
||||
}
|
||||
unused_purr = entitled_purr - delta_purr;
|
||||
|
||||
/* distribute unused purr in wait and idle proportionally to logical wait and idle */
|
||||
dlt_pcpu_wait += unused_purr * ((double)dlt_lcpu_wait / (double)(dlt_lcpu_wait + dlt_lcpu_idle));
|
||||
dlt_pcpu_idle += unused_purr * ((double)dlt_lcpu_idle / (double)(dlt_lcpu_wait + dlt_lcpu_idle));
|
||||
|
||||
pcputime = entitled_purr;
|
||||
}
|
||||
|
||||
double phys_proc_consumed = 0;
|
||||
double percent_ent = 0;
|
||||
if (lparstats.type.b.shared_enabled)
|
||||
{
|
||||
/* Physical Processor Consumed */
|
||||
phys_proc_consumed = (double)delta_purr / (double)delta_time_base;
|
||||
|
||||
/* Percentage of Entitlement Consumed */
|
||||
percent_ent = (double)((phys_proc_consumed / entitlement) * 100);
|
||||
}
|
||||
|
||||
// ticks = (double)(dlt_lcpu_user + dlt_lcpu_sys + dlt_lcpu_idle + dlt_lcpu_wait);
|
||||
|
||||
last_ticks_lpar[0] = lparstats.puser;
|
||||
last_ticks_lpar[1] = 0;
|
||||
last_ticks_lpar[2] = lparstats.psys;
|
||||
last_ticks_lpar[3] = lparstats.pidle;
|
||||
last_ticks_lpar[4] = lparstats.pwait;
|
||||
|
||||
last_ticks[0] = cpustats.user;
|
||||
last_ticks[1] = 0;
|
||||
last_ticks[2] = cpustats.sys;
|
||||
last_ticks[3] = cpustats.idle;
|
||||
last_ticks[4] = cpustats.wait;
|
||||
|
||||
last_time = lparstats.timebase_last;
|
||||
|
||||
cpu_data _cpu;
|
||||
_cpu.u = (double)dlt_pcpu_user * 100.0 / (double)pcputime;
|
||||
_cpu.n = 0;
|
||||
_cpu.s = (double)dlt_pcpu_sys * 100.0 / (double)pcputime;
|
||||
_cpu.i = (double)dlt_pcpu_wait * 100.0 / (double)pcputime;
|
||||
_cpu.io = (double)dlt_pcpu_wait * 100.0 / (double)pcputime;
|
||||
_cpu.ent = percent_ent;
|
||||
_cpu.phys = phys_proc_consumed;
|
||||
|
||||
_cpu.sampleID = sampleIndex[0].sampleID;
|
||||
_cpu.time = sampleIndex[0].time;
|
||||
|
||||
|
||||
samples[0].push_front(_cpu);
|
||||
if (samples[0].size() > HISTORY_SIZE) samples[0].pop_back();
|
||||
}/*USE_CPU_PERFSTAT*/
|
||||
|
||||
#else
|
||||
|
||||
void StatsCPU::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsCPU::update(long long sampleID)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void StatsCPU::_init()
|
||||
{
|
||||
initShared();
|
||||
|
||||
last_ticks[0] = 0; last_ticks[1] = 0; last_ticks[2] = 0; last_ticks[3] = 0; last_ticks[4] = 0;
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
databaseType = 0;
|
||||
databasePrefix = "cpu_";
|
||||
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double PRIMARY KEY NOT NULL, time double NOT NULL DEFAULT 0, empty integer NOT NULL DEFAULT 0, user double NOT NULL DEFAULT 0, system double NOT NULL DEFAULT 0, nice double NOT NULL DEFAULT 0, wait double NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
}
|
||||
loadPreviousSamples();
|
||||
fillGaps();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void StatsCPU::loadPreviousSamples()
|
||||
{
|
||||
loadPreviousSamplesAtIndex(1);
|
||||
loadPreviousSamplesAtIndex(2);
|
||||
loadPreviousSamplesAtIndex(3);
|
||||
loadPreviousSamplesAtIndex(4);
|
||||
loadPreviousSamplesAtIndex(5);
|
||||
loadPreviousSamplesAtIndex(6);
|
||||
loadPreviousSamplesAtIndex(7);
|
||||
}
|
||||
|
||||
void StatsCPU::loadPreviousSamplesAtIndex(int index)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(index);
|
||||
double sampleID = sampleIdForTable(table);
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
cpu_data sample;
|
||||
sample.u = query.doubleForColumn("user");
|
||||
sample.s = query.doubleForColumn("system");
|
||||
sample.n = query.doubleForColumn("nice");
|
||||
sample.io = query.doubleForColumn("wait");
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
samples[index].insert(samples[index].begin(), sample);
|
||||
}
|
||||
if(samples[index].size() > 0)
|
||||
{
|
||||
sampleIndex[index].sampleID = samples[index][0].sampleID;
|
||||
sampleIndex[index].time = samples[index][0].time;
|
||||
sampleIndex[index].nextTime = sampleIndex[index].time + sampleIndex[index].interval;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void StatsCPU::processSample(long long sampleID, unsigned long long user, unsigned long long nice, unsigned long long kernel, unsigned long long idle, unsigned long long wait, unsigned long long total)
|
||||
{
|
||||
if(total > 0)
|
||||
ticks = total;
|
||||
else
|
||||
{
|
||||
if(last_ticks[0] == 0)
|
||||
{
|
||||
last_ticks[0] = user;
|
||||
last_ticks[1] = nice;
|
||||
last_ticks[2] = kernel;
|
||||
last_ticks[3] = idle;
|
||||
last_ticks[4] = wait;
|
||||
}
|
||||
ticks = (double)(user + kernel + nice + idle) - (last_ticks[0] + last_ticks[1] + last_ticks[2] + last_ticks[3]);
|
||||
}
|
||||
|
||||
cpu_data _cpu;
|
||||
|
||||
if(ticks > 0)
|
||||
{
|
||||
_cpu.u = ((double)(user - last_ticks[0]) / ticks) * 100;
|
||||
_cpu.n = ((double)(nice - last_ticks[1]) / ticks) * 100;
|
||||
_cpu.s = ((double)(kernel - last_ticks[2]) / ticks) * 100;
|
||||
_cpu.i = ((double)(idle - last_ticks[3]) / ticks) * 100;
|
||||
_cpu.io = ((double)(wait - last_ticks[4]) / ticks) * 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cpu.u = 0;
|
||||
_cpu.n = 0;
|
||||
_cpu.s = 0;
|
||||
_cpu.i = 0;
|
||||
_cpu.io = 0;
|
||||
}
|
||||
|
||||
_cpu.ent = -1;
|
||||
_cpu.phys = -1;
|
||||
|
||||
_cpu.sampleID = sampleIndex[0].sampleID;
|
||||
_cpu.time = sampleIndex[0].time;
|
||||
|
||||
if(total == 0)
|
||||
{
|
||||
last_ticks[0] = user;
|
||||
last_ticks[1] = nice;
|
||||
last_ticks[2] = kernel;
|
||||
last_ticks[3] = idle;
|
||||
last_ticks[4] = wait;
|
||||
}
|
||||
|
||||
samples[0].push_front(_cpu);
|
||||
if (samples[0].size() > HISTORY_SIZE) samples[0].pop_back();
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void StatsCPU::updateHistory()
|
||||
{
|
||||
int x;
|
||||
for (x = 1; x < 8; x++)
|
||||
{
|
||||
if(sampleIndex[0].time >= sampleIndex[x].nextTime)
|
||||
{
|
||||
double now = get_current_time();
|
||||
double earlistTime = now - (HISTORY_SIZE * sampleIndex[x].interval);
|
||||
while(sampleIndex[x].nextTime < now)
|
||||
{
|
||||
sampleIndex[x].sampleID = sampleIndex[x].sampleID + 1;
|
||||
sampleIndex[x].time = sampleIndex[x].nextTime;
|
||||
sampleIndex[x].nextTime = sampleIndex[x].nextTime + sampleIndex[x].interval;
|
||||
|
||||
if(sampleIndex[x].time < earlistTime)
|
||||
continue;
|
||||
|
||||
cpu_data sample = historyItemAtIndex(x);
|
||||
samples[x].push_front(sample);
|
||||
if (samples[x].size() > HISTORY_SIZE) samples[x].pop_back();
|
||||
|
||||
if(sample.empty)
|
||||
continue;
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
string sql = "insert into " + table + " (empty, sample, time, user, system, nice, wait) values(?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_int(dbItem._statement, 1, 0);
|
||||
sqlite3_bind_double(dbItem._statement, 2, (double)sample.sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sample.time);
|
||||
sqlite3_bind_double(dbItem._statement, 4, sample.u);
|
||||
sqlite3_bind_double(dbItem._statement, 5, sample.s);
|
||||
sqlite3_bind_double(dbItem._statement, 6, sample.n);
|
||||
sqlite3_bind_double(dbItem._statement, 7, sample.io);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cpu_data StatsCPU::historyItemAtIndex(int index)
|
||||
{
|
||||
std::deque<cpu_data> from = samples[sampleIndex[index].historyIndex];
|
||||
double minimumTime = sampleIndex[index].time - sampleIndex[index].interval;
|
||||
double maximumTime = sampleIndex[index].time;
|
||||
if(sampleIndex[index].historyIndex == 0)
|
||||
maximumTime += 0.99;
|
||||
|
||||
double cpuUser = 0;
|
||||
double cpuSystem = 0;
|
||||
double cpuNice = 0;
|
||||
double cpuIO = 0;
|
||||
cpu_data sample;
|
||||
|
||||
int count = 0;
|
||||
if(from.size() > 0)
|
||||
{
|
||||
for (deque<cpu_data>::iterator cur = from.begin(); cur != from.end(); ++cur)
|
||||
{
|
||||
if ((*cur).time > maximumTime)
|
||||
continue;
|
||||
|
||||
if ((*cur).time < minimumTime)
|
||||
break;
|
||||
|
||||
cpuUser += (*cur).u;
|
||||
cpuSystem += (*cur).s;
|
||||
cpuNice += (*cur).n;
|
||||
cpuIO += (*cur).io;
|
||||
count++;
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
cpuUser /= count;
|
||||
cpuSystem /= count;
|
||||
cpuNice /= count;
|
||||
cpuIO /= count;
|
||||
}
|
||||
}
|
||||
|
||||
sample.u = cpuUser;
|
||||
sample.s = cpuSystem;
|
||||
sample.n = cpuNice;
|
||||
sample.io = cpuIO;
|
||||
|
||||
sample.sampleID = sampleIndex[index].sampleID;
|
||||
sample.time = sampleIndex[index].time;
|
||||
sample.empty = false;
|
||||
if(count == 0)
|
||||
sample.empty = true;
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
75
src/stats/StatsCPU.h
Normal file
75
src/stats/StatsCPU.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _STATSCPU_H
|
||||
#define _STATSCPU_H
|
||||
|
||||
class StatsCPU : public StatsBase
|
||||
{
|
||||
public:
|
||||
void _init();
|
||||
void init();
|
||||
void update(long long sampleID);
|
||||
void processSample(long long sampleID, unsigned long long user, unsigned long long nice, unsigned long long kernel, unsigned long long idle, unsigned long long wait, unsigned long long total);
|
||||
#ifdef USE_SQLITE
|
||||
void updateHistory();
|
||||
cpu_data historyItemAtIndex(int index);
|
||||
void loadPreviousSamples();
|
||||
void loadPreviousSamplesAtIndex(int index);
|
||||
#endif
|
||||
|
||||
std::deque<cpu_data> samples[8];
|
||||
|
||||
#ifdef PST_MAX_CPUSTATES
|
||||
unsigned long long last_ticks[PST_MAX_CPUSTATES];
|
||||
#elif defined(CPUSTATES)
|
||||
unsigned long long last_ticks[CPUSTATES];
|
||||
#else
|
||||
unsigned long long last_ticks[5];
|
||||
#endif
|
||||
unsigned long long last_ticks_lpar[5];
|
||||
unsigned long long last_time;
|
||||
double ticks;
|
||||
int cpu_count;
|
||||
bool hasLpar;
|
||||
double aixEntitlement;
|
||||
|
||||
#ifdef HAVE_LIBKSTAT
|
||||
kstat_ctl_t *ksh;
|
||||
#endif
|
||||
|
||||
#ifdef USE_CPU_PROCFS
|
||||
bool hasIOWait;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
606
src/stats/StatsDisks.cpp
Normal file
606
src/stats/StatsDisks.cpp
Normal file
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsDisks.h"
|
||||
|
||||
#ifndef _PATH_MOUNTED
|
||||
#ifdef MNTTAB
|
||||
#define _PATH_MOUNTED MNTTAB
|
||||
#else
|
||||
#define _PATH_MOUNTED "/etc/mtab"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef USE_DISK_STATFS
|
||||
int StatsDisks::get_sizes(const char *dev, struct disk_data *data)
|
||||
{
|
||||
#ifdef HAVE_STATVFS
|
||||
struct statvfs space;
|
||||
if (statvfs(dev, &space) == 0)
|
||||
#else
|
||||
struct statfs space;
|
||||
if (statfs(dev, &space) == 0)
|
||||
#endif
|
||||
|
||||
{
|
||||
unsigned long long bsize;
|
||||
#ifdef HAVE_STATVFS_FRSIZE
|
||||
bsize = space.f_frsize;
|
||||
#else
|
||||
bsize = space.f_bsize;
|
||||
#endif
|
||||
|
||||
data->t = (double)(space.f_blocks * bsize);
|
||||
data->u = (double)((space.f_blocks - space.f_bavail) * bsize);
|
||||
data->f = data->t - data->u;
|
||||
if(data->t > 0)
|
||||
data->p = ((float) data->u / data->t) * 100;
|
||||
else
|
||||
data->p = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int StatsDisks::should_ignore_mount(char *mount)
|
||||
{
|
||||
if(disableFiltering == 1)
|
||||
return 0;
|
||||
|
||||
if(!strncmp(mount, "/export", 7))
|
||||
return 1;
|
||||
// if(!strncmp(mount, "/var", 4))
|
||||
// return 1;
|
||||
if(!strncmp(mount, "/boot", 5))
|
||||
return 1;
|
||||
if(!strncmp(mount, "/tmp", 4))
|
||||
return 1;
|
||||
if(!strncmp(mount, "/system", 7))
|
||||
return 1;
|
||||
if(!strncmp(mount, "/usr/obj", 8))
|
||||
return 1;
|
||||
if(!strncmp(mount, "/usr/src", 8))
|
||||
return 1;
|
||||
if(!strncmp(mount, "/usr/local", 10))
|
||||
return 1;
|
||||
if(!strncmp(mount, "/usr/X11", 8))
|
||||
return 1;
|
||||
if(!strcmp(mount, "/usr"))
|
||||
return 1;
|
||||
if(!strcmp(mount, "/kern"))
|
||||
return 1;
|
||||
if(!strcmp(mount, "/dev/pts"))
|
||||
return 1;
|
||||
if(!strcmp(mount, "/compat/linux/proc"))
|
||||
return 1;
|
||||
if(!strncmp(mount, "/usr/jails/.", 12))
|
||||
return 1;
|
||||
if(!strncmp(mount, "/usr/home", 9))
|
||||
return 1;
|
||||
if(!strcmp(mount, "/usr/jails"))
|
||||
return 1;
|
||||
if(!strcmp(mount, "/usr/ports"))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StatsDisks::should_ignore_type(char *type)
|
||||
{
|
||||
if(!strcmp(type, "rpc_pipefs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "rootfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "configfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "hugetlbfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "nfsd"))
|
||||
return 1;
|
||||
if(!strcmp(type, "mqueue"))
|
||||
return 1;
|
||||
if(!strcmp(type, "selinuxfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "linprocfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "binfmt_misc"))
|
||||
return 1;
|
||||
if(!strcmp(type, "tmpfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "devpts"))
|
||||
return 1;
|
||||
if(!strcmp(type, "devtmpfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "pstore"))
|
||||
return 1;
|
||||
if(!strcmp(type, "prl_fs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "cgroup"))
|
||||
return 1;
|
||||
if(!strcmp(type, "iso9660"))
|
||||
return 1;
|
||||
if(!strcmp(type, "securityfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "fusectl"))
|
||||
return 1;
|
||||
if(!strcmp(type, "proc"))
|
||||
return 1;
|
||||
if(!strcmp(type, "procfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "debugfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "fuse.gvfsd-fuse"))
|
||||
return 1;
|
||||
if(!strcmp(type, "sysfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "devfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "autofs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "fd"))
|
||||
return 1;
|
||||
if(!strcmp(type, "lofs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "sharefs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "objfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "mntfs"))
|
||||
return 1;
|
||||
if(!strcmp(type, "dev"))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StatsDisks::update(long long sampleID)
|
||||
{
|
||||
#ifdef USE_STRUCT_MNTENT
|
||||
struct mntent *entry;
|
||||
FILE *table;
|
||||
if (!(table = setmntent(_PATH_MOUNTED, "r"))) return;
|
||||
while ((entry = getmntent(table)) != 0)
|
||||
{
|
||||
processDisk(entry->mnt_fsname, entry->mnt_dir, entry->mnt_type);
|
||||
}
|
||||
endmntent(table);
|
||||
|
||||
#elif defined(USE_STRUCT_MNTTAB)
|
||||
struct mnttab *entry, ebuf;
|
||||
FILE *table;
|
||||
if (!(table = fopen(_PATH_MOUNTED, "r"))) return;
|
||||
resetmnttab(table);
|
||||
|
||||
entry = &ebuf;
|
||||
while (!getmntent(table, entry))
|
||||
{
|
||||
processDisk(entry->mnt_special, entry->mnt_mountp, entry->mnt_fstype);
|
||||
}
|
||||
|
||||
fclose(table);
|
||||
#else
|
||||
#ifdef GETMNTINFO_STATVFS
|
||||
struct statvfs *entry;
|
||||
#else
|
||||
struct statfs *entry;
|
||||
#endif
|
||||
|
||||
int mnts;
|
||||
if (!(mnts = getmntinfo(&entry, MNT_NOWAIT)))
|
||||
return;
|
||||
|
||||
int x;
|
||||
for(x=0;x<mnts;x++)
|
||||
{
|
||||
processDisk(entry[x].f_mntfromname, entry[x].f_mntonname, entry[x].f_fstypename);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void StatsDisks::processDisk(char *name, char *mount, char *type)
|
||||
{
|
||||
bool exists = false;
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<disk_info>::iterator curdisk = _items.begin(); curdisk != _items.end(); ++curdisk)
|
||||
{
|
||||
if(string(name) == (*curdisk).key)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!exists && (should_ignore_type(type) || should_ignore_mount(mount)))
|
||||
return;
|
||||
|
||||
createDisk(name);
|
||||
|
||||
for (vector<disk_info>::iterator curdisk = _items.begin(); curdisk != _items.end(); ++curdisk)
|
||||
{
|
||||
if(string(name) == (*curdisk).key)
|
||||
{
|
||||
if((*curdisk).is_new == true)
|
||||
{
|
||||
(*curdisk).is_new = false;
|
||||
|
||||
if((*curdisk).uuid.size() == 0)
|
||||
{
|
||||
#ifdef USE_DISKUUID_PROCFS
|
||||
DIR *dpdf;
|
||||
struct dirent *epdf;
|
||||
dpdf = opendir("/dev/disk/by-uuid");
|
||||
if (dpdf != NULL){
|
||||
while ((epdf = readdir(dpdf))){
|
||||
string file = string(epdf->d_name);
|
||||
|
||||
char buff[PATH_MAX];
|
||||
string fullpath = "/dev/disk/by-uuid/" + file;
|
||||
ssize_t len = ::readlink(fullpath.c_str(), buff, sizeof(buff)-1);
|
||||
if (len != -1) {
|
||||
buff[len] = '\0';
|
||||
vector<string> components = explode(string(buff), "/");
|
||||
if(components.size() > 0)
|
||||
{
|
||||
string p = "/dev/" + components.back();
|
||||
if(p == (*curdisk).key)
|
||||
(*curdisk).uuid = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dpdf);
|
||||
}
|
||||
#endif
|
||||
if((*curdisk).uuid.size() == 0)
|
||||
(*curdisk).uuid = (*curdisk).key;
|
||||
}
|
||||
|
||||
(*curdisk).name = string(mount);
|
||||
|
||||
string disk_label, disk_label_custom;
|
||||
disk_label = "";
|
||||
disk_label_custom = "";
|
||||
|
||||
// Read custom disk label from config
|
||||
vector<string> conf_label;
|
||||
for (vector<string>::iterator cur_label = customNames.begin(); cur_label != customNames.end(); ++cur_label)
|
||||
{
|
||||
conf_label = explode(*cur_label);
|
||||
|
||||
if (conf_label[1].length())
|
||||
{
|
||||
if (conf_label[0] == (*curdisk).key || conf_label[0] == (*curdisk).name)
|
||||
{
|
||||
disk_label_custom = trim(conf_label[1], "\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (useMountPaths)
|
||||
disk_label = (*curdisk).name;
|
||||
else
|
||||
disk_label = (*curdisk).key;
|
||||
|
||||
// Set custom disk label if configured. Will override everything.
|
||||
if (disk_label_custom.length())
|
||||
disk_label = disk_label_custom;
|
||||
|
||||
(*curdisk).displayName = disk_label;
|
||||
|
||||
loadHistoryForDisk(&(*curdisk));
|
||||
}
|
||||
|
||||
if(ready == 0)
|
||||
{
|
||||
(*curdisk).active = true;
|
||||
break;
|
||||
}
|
||||
|
||||
disk_data data;
|
||||
if (get_sizes(mount, &data) == 0)
|
||||
{
|
||||
(*curdisk).active = true;
|
||||
|
||||
data.sampleID = sampleIndex[0].sampleID;
|
||||
data.time = sampleIndex[0].time;
|
||||
|
||||
(*curdisk).samples[0].push_front(data);
|
||||
if ((*curdisk).samples[0].size() > HISTORY_SIZE)
|
||||
(*curdisk).samples[0].pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else /*USE_DISK_STATFS*/
|
||||
|
||||
void StatsDisks::update(long long sampleID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void StatsDisks::prepareUpdate()
|
||||
{
|
||||
if(ready == 0)
|
||||
return;
|
||||
|
||||
StatsBase::prepareUpdate();
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<disk_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
(*cur).active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsDisks::createDisk(string key)
|
||||
{
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<disk_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
disk_info interface = *cur;
|
||||
if (interface.key == key)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disk_info item;
|
||||
item.is_new = true;
|
||||
item.key = key;
|
||||
session++;
|
||||
|
||||
_items.insert(_items.begin(), item);
|
||||
}
|
||||
|
||||
void StatsDisks::loadHistoryForDisk(disk_info *disk)
|
||||
{
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
double sampleID = 0;
|
||||
if(samples[x].size() > 0)
|
||||
sampleID = samples[x][0].sampleID;
|
||||
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample AND uuid = ? order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
sqlite3_bind_text(query._statement, 2, disk->uuid.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
disk_data sample;
|
||||
sample.t = query.doubleForColumn("total");
|
||||
sample.u = query.doubleForColumn("used");
|
||||
sample.f = query.doubleForColumn("free");
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
disk->samples[x].push_front(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void StatsDisks::init()
|
||||
{
|
||||
initShared();
|
||||
ready = 0;
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
databaseType = 1;
|
||||
databasePrefix = "disk_";
|
||||
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x) + "_id";
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double PRIMARY KEY NOT NULL, time double NOT NULL DEFAULT 0, empty integer NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
|
||||
table = databasePrefix + tableAtIndex(x);
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double NOT NULL, time double NOT NULL DEFAULT 0, uuid varchar(255) NOT NULL, total double NOT NULL DEFAULT 0, used double NOT NULL DEFAULT 0, free double NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
}
|
||||
loadPreviousSamples();
|
||||
fillGaps();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void StatsDisks::loadPreviousSamples()
|
||||
{
|
||||
loadPreviousSamplesAtIndex(1);
|
||||
loadPreviousSamplesAtIndex(2);
|
||||
loadPreviousSamplesAtIndex(3);
|
||||
loadPreviousSamplesAtIndex(4);
|
||||
loadPreviousSamplesAtIndex(5);
|
||||
loadPreviousSamplesAtIndex(6);
|
||||
loadPreviousSamplesAtIndex(7);
|
||||
}
|
||||
|
||||
void StatsDisks::loadPreviousSamplesAtIndex(int index)
|
||||
{
|
||||
|
||||
string table = databasePrefix + tableAtIndex(index) + "_id";
|
||||
double sampleID = sampleIdForTable(table);
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
sample_data sample;
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
samples[index].insert(samples[index].begin(), sample);
|
||||
}
|
||||
if(samples[index].size() > 0)
|
||||
{
|
||||
sampleIndex[index].sampleID = samples[index][0].sampleID;
|
||||
sampleIndex[index].time = samples[index][0].time;
|
||||
sampleIndex[index].nextTime = sampleIndex[index].time + sampleIndex[index].interval;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsDisks::updateHistory()
|
||||
{
|
||||
int x;
|
||||
for (x = 1; x < 8; x++)
|
||||
{
|
||||
if(sampleIndex[0].time >= sampleIndex[x].nextTime)
|
||||
{
|
||||
double now = get_current_time();
|
||||
double earlistTime = now - (HISTORY_SIZE * sampleIndex[x].interval);
|
||||
while(sampleIndex[x].nextTime < now)
|
||||
{
|
||||
sampleIndex[x].sampleID = sampleIndex[x].sampleID + 1;
|
||||
sampleIndex[x].time = sampleIndex[x].nextTime;
|
||||
sampleIndex[x].nextTime = sampleIndex[x].nextTime + sampleIndex[x].interval;
|
||||
|
||||
if(sampleIndex[x].time < earlistTime)
|
||||
continue;
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<disk_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
disk_data sample = historyItemAtIndex(x, (*cur));
|
||||
|
||||
(*cur).samples[x].push_front(sample);
|
||||
if ((*cur).samples[x].size() > HISTORY_SIZE) (*cur).samples[x].pop_back();
|
||||
|
||||
if(sample.empty)
|
||||
continue;
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
string sql = "insert into " + table + " (sample, time, total, used, free, uuid) values(?, ?, ?, ?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(dbItem._statement, 1, (double)sample.sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 2, sample.time);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sample.t);
|
||||
sqlite3_bind_double(dbItem._statement, 4, sample.u);
|
||||
sqlite3_bind_double(dbItem._statement, 5, sample.f);
|
||||
sqlite3_bind_text(dbItem._statement, 6, (*cur).uuid.c_str(), -1, SQLITE_STATIC);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x) + "_id";
|
||||
string sql = "insert into " + table + " (empty, sample, time) values(?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_int(dbItem._statement, 1, 0);
|
||||
sqlite3_bind_double(dbItem._statement, 2, (double)sampleIndex[x].sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sampleIndex[x].time);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disk_data StatsDisks::historyItemAtIndex(int index, disk_info item)
|
||||
{
|
||||
disk_data sample;
|
||||
double u = 0;
|
||||
double f = 0;
|
||||
double t = 0;
|
||||
|
||||
std::deque<disk_data> from = item.samples[sampleIndex[index].historyIndex];
|
||||
double minimumTime = sampleIndex[index].time - sampleIndex[index].interval;
|
||||
double maximumTime = sampleIndex[index].time;
|
||||
if(sampleIndex[index].historyIndex == 0)
|
||||
maximumTime += 0.99;
|
||||
|
||||
int count = 0;
|
||||
if(from.size() > 0)
|
||||
{
|
||||
for (deque<disk_data>::iterator cursample = from.begin(); cursample != from.end(); ++cursample)
|
||||
{
|
||||
if ((*cursample).time > maximumTime)
|
||||
continue;
|
||||
|
||||
if ((*cursample).time < minimumTime)
|
||||
break;
|
||||
|
||||
u += (*cursample).u;
|
||||
f += (*cursample).f;
|
||||
t = (*cursample).t;
|
||||
count++;
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
u /= count;
|
||||
f /= count;
|
||||
}
|
||||
}
|
||||
|
||||
sample.u = u;
|
||||
sample.f = f;
|
||||
sample.t = t;
|
||||
|
||||
sample.sampleID = sampleIndex[index].sampleID;
|
||||
sample.time = sampleIndex[index].time;
|
||||
sample.empty = false;
|
||||
if(count == 0)
|
||||
sample.empty = true;
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
78
src/stats/StatsDisks.h
Normal file
78
src/stats/StatsDisks.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _STATSDISKS_H
|
||||
#define _STATSDISKS_H
|
||||
|
||||
class disk_info
|
||||
{
|
||||
public:
|
||||
bool active;
|
||||
bool is_new;
|
||||
unsigned int id;
|
||||
std::string uuid;
|
||||
std::string label;
|
||||
std::string name;
|
||||
std::string key;
|
||||
std::string displayName;
|
||||
double last_update;
|
||||
std::deque<disk_data> samples[8];
|
||||
};
|
||||
class StatsDisks : public StatsBase
|
||||
{
|
||||
public:
|
||||
int useMountPaths;
|
||||
int disableFiltering;
|
||||
void update(long long sampleID);
|
||||
int sensorType(int type);
|
||||
void prepareUpdate();
|
||||
void init();
|
||||
std::vector<disk_info> _items;
|
||||
void createDisk(std::string key);
|
||||
void processDisk(char *name, char *mount, char *type);
|
||||
int get_sizes(const char *dev, struct disk_data *data);
|
||||
int should_ignore_type(char *type);
|
||||
int should_ignore_mount(char *mount);
|
||||
std::vector<std::string> customNames;
|
||||
|
||||
void loadHistoryForDisk(disk_info *disk);
|
||||
#ifdef USE_SQLITE
|
||||
void updateHistory();
|
||||
disk_data historyItemAtIndex(int index, disk_info item);
|
||||
void loadPreviousSamples();
|
||||
void loadPreviousSamplesAtIndex(int index);
|
||||
#endif
|
||||
|
||||
std::deque<sample_data> samples[8];
|
||||
};
|
||||
#endif
|
||||
268
src/stats/StatsLoad.cpp
Normal file
268
src/stats/StatsLoad.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsLoad.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef USE_LOAD_HPUX
|
||||
|
||||
void StatsLoad::update(long long sampleID)
|
||||
{
|
||||
struct pst_dynamic pstat_dynamic;
|
||||
if (pstat_getdynamic(&pstat_dynamic, sizeof(pstat_dynamic), 1, 0) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct load_data load;
|
||||
load.one = pstat_dynamic.psd_avg_1_min;
|
||||
load.two = pstat_dynamic.psd_avg_5_min;
|
||||
load.three = pstat_dynamic.psd_avg_15_min;
|
||||
|
||||
addSample(load, sampleID);
|
||||
} /*USE_LOAD_HPUX*/
|
||||
|
||||
#elif defined(USE_LOAD_PERFSTAT)
|
||||
|
||||
void StatsLoad::update(long long sampleID)
|
||||
{
|
||||
struct load_data load;
|
||||
perfstat_cpu_total_t cpustats;
|
||||
perfstat_cpu_total(NULL, &cpustats, sizeof cpustats, 1);
|
||||
load.one = cpustats.loadavg[0]/(float)(1<<SBITS);
|
||||
load.two = cpustats.loadavg[1]/(float)(1<<SBITS);
|
||||
load.three = cpustats.loadavg[2]/(float)(1<<SBITS);
|
||||
|
||||
addSample(load, sampleID);
|
||||
} /*USE_LOAD_PERFSTAT*/
|
||||
|
||||
#elif defined(USE_LOAD_GETLOADAVG)
|
||||
|
||||
void StatsLoad::update(long long sampleID)
|
||||
{
|
||||
struct load_data load;
|
||||
double loadavg[3];
|
||||
|
||||
if(-1 == getloadavg(loadavg, 3)) return;
|
||||
load.one = (float) loadavg[0];
|
||||
load.two = (float) loadavg[1];
|
||||
load.three = (float) loadavg[2];
|
||||
|
||||
addSample(load, sampleID);
|
||||
} /*USE_LOAD_GETLOADAVG*/
|
||||
|
||||
#elif defined(USE_LOAD_PROCFS)
|
||||
|
||||
void StatsLoad::update(long long sampleID)
|
||||
{
|
||||
struct load_data load;
|
||||
static FILE * fp = NULL;
|
||||
|
||||
if (!(fp = fopen("/proc/loadavg", "r"))) return;
|
||||
|
||||
fscanf(fp, "%f %f %f", &load.one, &load.two, &load.three);
|
||||
fclose(fp);
|
||||
|
||||
addSample(load, sampleID);
|
||||
} /* USE_LOAD_PROCFS */
|
||||
#else
|
||||
|
||||
void StatsLoad::update(long long sampleID){}
|
||||
|
||||
#endif
|
||||
|
||||
void StatsLoad::init()
|
||||
{
|
||||
initShared();
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
databaseType = 0;
|
||||
databasePrefix = "load_";
|
||||
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double PRIMARY KEY NOT NULL, time double NOT NULL DEFAULT 0, empty integer NOT NULL DEFAULT 0, one double NOT NULL DEFAULT 0, five double NOT NULL DEFAULT 0, fifteen double NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
}
|
||||
loadPreviousSamples();
|
||||
fillGaps();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void StatsLoad::addSample(load_data data, long long sampleID)
|
||||
{
|
||||
data.sampleID = sampleIndex[0].sampleID;
|
||||
data.time = sampleIndex[0].time;
|
||||
|
||||
samples[0].push_front(data);
|
||||
if (samples[0].size() > HISTORY_SIZE) samples[0].pop_back();
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void StatsLoad::loadPreviousSamples()
|
||||
{
|
||||
loadPreviousSamplesAtIndex(1);
|
||||
loadPreviousSamplesAtIndex(2);
|
||||
loadPreviousSamplesAtIndex(3);
|
||||
loadPreviousSamplesAtIndex(4);
|
||||
loadPreviousSamplesAtIndex(5);
|
||||
loadPreviousSamplesAtIndex(6);
|
||||
loadPreviousSamplesAtIndex(7);
|
||||
}
|
||||
|
||||
void StatsLoad::loadPreviousSamplesAtIndex(int index)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(index);
|
||||
double sampleID = sampleIdForTable(table);
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
load_data sample;
|
||||
sample.one = query.doubleForColumn("one");
|
||||
sample.two = query.doubleForColumn("five");
|
||||
sample.three = query.doubleForColumn("fifteen");
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
samples[index].insert(samples[index].begin(), sample);
|
||||
}
|
||||
if(samples[index].size() > 0)
|
||||
{
|
||||
sampleIndex[index].sampleID = samples[index][0].sampleID;
|
||||
sampleIndex[index].time = samples[index][0].time;
|
||||
sampleIndex[index].nextTime = sampleIndex[index].time + sampleIndex[index].interval;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsLoad::updateHistory()
|
||||
{
|
||||
int x;
|
||||
for (x = 1; x < 8; x++)
|
||||
{
|
||||
if(sampleIndex[0].time >= sampleIndex[x].nextTime)
|
||||
{
|
||||
double now = get_current_time();
|
||||
double earlistTime = now - (HISTORY_SIZE * sampleIndex[x].interval);
|
||||
while(sampleIndex[x].nextTime < now)
|
||||
{
|
||||
sampleIndex[x].sampleID = sampleIndex[x].sampleID + 1;
|
||||
sampleIndex[x].time = sampleIndex[x].nextTime;
|
||||
sampleIndex[x].nextTime = sampleIndex[x].nextTime + sampleIndex[x].interval;
|
||||
|
||||
if(sampleIndex[x].time < earlistTime)
|
||||
continue;
|
||||
|
||||
load_data sample = historyItemAtIndex(x);
|
||||
|
||||
samples[x].push_front(sample);
|
||||
if (samples[x].size() > HISTORY_SIZE) samples[x].pop_back();
|
||||
|
||||
if(sample.empty)
|
||||
continue;
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
string sql = "insert into " + table + " (empty, sample, time, one, five, fifteen) values(?, ?, ?, ?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_int(dbItem._statement, 1, 0);
|
||||
sqlite3_bind_double(dbItem._statement, 2, (double)sample.sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sample.time);
|
||||
sqlite3_bind_double(dbItem._statement, 4, sample.one);
|
||||
sqlite3_bind_double(dbItem._statement, 5, sample.two);
|
||||
sqlite3_bind_double(dbItem._statement, 6, sample.three);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load_data StatsLoad::historyItemAtIndex(int index)
|
||||
{
|
||||
std::deque<load_data> from = samples[sampleIndex[index].historyIndex];
|
||||
double minimumTime = sampleIndex[index].time - sampleIndex[index].interval;
|
||||
double maximumTime = sampleIndex[index].time;
|
||||
if(sampleIndex[index].historyIndex == 0)
|
||||
maximumTime += 0.99;
|
||||
|
||||
double load1 = 0;
|
||||
double load5 = 0;
|
||||
double load15 = 0;
|
||||
load_data sample;
|
||||
|
||||
int count = 0;
|
||||
if(from.size() > 0)
|
||||
{
|
||||
for (deque<load_data>::iterator cur = from.begin(); cur != from.end(); ++cur)
|
||||
{
|
||||
if ((*cur).time > maximumTime)
|
||||
continue;
|
||||
|
||||
if ((*cur).time < minimumTime)
|
||||
break;
|
||||
|
||||
load1 += (*cur).one;
|
||||
load5 += (*cur).two;
|
||||
load15 += (*cur).three;
|
||||
count++;
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
load1 /= count;
|
||||
load15 /= count;
|
||||
load15 /= count;
|
||||
}
|
||||
}
|
||||
|
||||
sample.one = load1;
|
||||
sample.two = load15;
|
||||
sample.three = load15;
|
||||
|
||||
sample.sampleID = sampleIndex[index].sampleID;
|
||||
sample.time = sampleIndex[index].time;
|
||||
sample.empty = false;
|
||||
if(count == 0)
|
||||
sample.empty = true;
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
56
src/stats/StatsLoad.h
Normal file
56
src/stats/StatsLoad.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _STATSLOAD_H
|
||||
#define _STATSLOAD_H
|
||||
|
||||
class StatsLoad : public StatsBase
|
||||
{
|
||||
public:
|
||||
void update(long long sampleID);
|
||||
void addSample(load_data data, long long sampleID);
|
||||
std::deque<load_data> samples[8];
|
||||
|
||||
void init();
|
||||
#ifdef USE_SQLITE
|
||||
void updateHistory();
|
||||
load_data historyItemAtIndex(int index);
|
||||
void loadPreviousSamples();
|
||||
void loadPreviousSamplesAtIndex(int index);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBKSTAT
|
||||
kstat_ctl_t *ksh;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
957
src/stats/StatsMemory.cpp
Normal file
957
src/stats/StatsMemory.cpp
Normal file
@@ -0,0 +1,957 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsMemory.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if defined(USE_MEM_HPUX)
|
||||
|
||||
void StatsMemory::init()
|
||||
{
|
||||
databaseKeys.push_back("total");
|
||||
databaseKeys.push_back("free");
|
||||
databaseKeys.push_back("used");
|
||||
databaseKeys.push_back("swaptotal");
|
||||
databaseKeys.push_back("swapused");
|
||||
|
||||
databaseMap.push_back(memory_value_total);
|
||||
databaseMap.push_back(memory_value_free);
|
||||
databaseMap.push_back(memory_value_used);
|
||||
databaseMap.push_back(memory_value_swaptotal);
|
||||
databaseMap.push_back(memory_value_swapused);
|
||||
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsMemory::update(long long sampleID)
|
||||
{
|
||||
mem_data _mem;
|
||||
prepareSample(&_mem);
|
||||
|
||||
struct pst_static pstat_static;
|
||||
struct pst_dynamic pstat_dynamic;
|
||||
struct pst_vminfo vminfo;
|
||||
|
||||
if( pstat_getstatic(&pstat_static, sizeof(pstat_static), 1, 0) == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pstat_getdynamic(&pstat_dynamic, sizeof(pstat_dynamic), 1, 0) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(pstat_getvminfo( &vminfo, sizeof(vminfo), 1, 0 ) == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mem.values[memory_value_total] = (double)(pstat_static.physical_memory * pstat_static.page_size);
|
||||
_mem.values[memory_value_free] = (double)(pstat_dynamic.psd_free * pstat_static.page_size);
|
||||
_mem.values[memory_value_used] = (double)(_mem.values[memory_value_total] - _mem.values[memory_value_free]);
|
||||
|
||||
|
||||
struct pst_swapinfo swap;
|
||||
int i = 0;
|
||||
|
||||
while (pstat_getswap(&pss, sizeof(pss), (size_t) 1, i) != -1) {
|
||||
i++;
|
||||
|
||||
if ((swap.pss_flags & SW_ENABLED) != SW_ENABLED)
|
||||
continue;
|
||||
|
||||
if ((swap.pss_flags & SW_BLOCK) == SW_BLOCK) {
|
||||
_mem.values[memory_value_swaptotal] += (double)(swap.pss_nblksavail * 1024);
|
||||
_mem.values[memory_value_swapused] += (double)(swap.pss_nfpgs * 1024);
|
||||
}
|
||||
|
||||
if ((swap.pss_flags & SW_FS) == SW_FS) {
|
||||
_mem.values[memory_value_swaptotal] += (double)(swap.pss_limit * 1024);
|
||||
_mem.values[memory_value_swapused] += (double)(swap.pss_allocated * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
//vminfo.psv_spgin;
|
||||
//vminfo.psv_spgout;
|
||||
|
||||
|
||||
addSample(_mem, sampleID);
|
||||
}
|
||||
|
||||
#elif defined(USE_MEM_SYSCTLBYNAME) && defined(HAVE_SYSCTLBYNAME)
|
||||
|
||||
void StatsMemory::init()
|
||||
{
|
||||
databaseKeys.push_back("total");
|
||||
databaseKeys.push_back("free");
|
||||
databaseKeys.push_back("active");
|
||||
databaseKeys.push_back("inactive");
|
||||
databaseKeys.push_back("wired");
|
||||
databaseKeys.push_back("cached");
|
||||
databaseKeys.push_back("used");
|
||||
databaseKeys.push_back("buffer");
|
||||
databaseKeys.push_back("swaptotal");
|
||||
databaseKeys.push_back("swapused");
|
||||
|
||||
databaseMap.push_back(memory_value_total);
|
||||
databaseMap.push_back(memory_value_free);
|
||||
databaseMap.push_back(memory_value_active);
|
||||
databaseMap.push_back(memory_value_inactive);
|
||||
databaseMap.push_back(memory_value_wired);
|
||||
databaseMap.push_back(memory_value_cached);
|
||||
databaseMap.push_back(memory_value_used);
|
||||
databaseMap.push_back(memory_value_buffer);
|
||||
databaseMap.push_back(memory_value_swaptotal);
|
||||
databaseMap.push_back(memory_value_swapused);
|
||||
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsMemory::update(long long sampleID)
|
||||
{
|
||||
mem_data _mem;
|
||||
prepareSample(&_mem);
|
||||
|
||||
u_int _total, _inactive, _free, _active, _cached, _wired, _swaptotal, _swapused;
|
||||
long _buf;
|
||||
|
||||
int pagesize = getpagesize();
|
||||
|
||||
size_t len = sizeof(_total);
|
||||
|
||||
if (sysctlbyname("vm.stats.vm.v_page_count", &_total, &len, NULL, 0) < 0)
|
||||
_total = 0;
|
||||
|
||||
if (sysctlbyname("vm.stats.vm.v_free_count", &_free, &len, NULL, 0) < 0)
|
||||
_free = 0;
|
||||
|
||||
if (sysctlbyname("vm.stats.vm.v_inactive_count", &_inactive, &len, NULL, 0) < 0)
|
||||
_inactive = 0;
|
||||
|
||||
if (sysctlbyname("vm.stats.vm.v_active_count", &_active, &len, NULL, 0) < 0)
|
||||
_active = 0;
|
||||
|
||||
if (sysctlbyname("vm.stats.vm.v_cache_count", &_cached, &len, NULL, 0) < 0)
|
||||
_cached = 0;
|
||||
|
||||
if (sysctlbyname("vm.stats.vm.v_wire_count", &_wired, &len, NULL, 0) < 0)
|
||||
_wired = 0;
|
||||
|
||||
if (sysctlbyname("vm.swap_size", &_swaptotal, &len, NULL, 0) < 0)
|
||||
_swaptotal = 0;
|
||||
|
||||
if (sysctlbyname("vm.swap_anon_use", &_swapused, &len, NULL, 0) < 0)
|
||||
_swapused = 0;
|
||||
|
||||
len = sizeof(_buf);
|
||||
if (sysctlbyname("vfs.bufspace", &_buf, &len, NULL, 0) < 0)
|
||||
_buf = 0;
|
||||
|
||||
_mem.values[memory_value_total] = (double)(_total * pagesize);
|
||||
_mem.values[memory_value_free] = (double)(_free * pagesize);
|
||||
_mem.values[memory_value_active] = (double)(_active * pagesize);
|
||||
_mem.values[memory_value_inactive] = (double)(_inactive * pagesize);
|
||||
_mem.values[memory_value_wired] = (double)(_wired * pagesize);
|
||||
_mem.values[memory_value_cached] = (double)(_cached * pagesize);
|
||||
_mem.values[memory_value_used] = (double)(_mem.values[memory_value_total] - (_mem.values[memory_value_free] + _mem.values[memory_value_inactive]));
|
||||
_mem.values[memory_value_buffer] = (double)(_buf);
|
||||
_mem.values[memory_value_swapused] = (double)(_swapused * pagesize);
|
||||
_mem.values[memory_value_swaptotal] = (double)(_swaptotal * pagesize);
|
||||
|
||||
addSample(_mem, sampleID);
|
||||
}
|
||||
|
||||
#elif defined(USE_MEM_SYSCTL)
|
||||
|
||||
void StatsMemory::init()
|
||||
{
|
||||
|
||||
databaseKeys.push_back("total");
|
||||
databaseKeys.push_back("free");
|
||||
databaseKeys.push_back("used");
|
||||
databaseKeys.push_back("active");
|
||||
databaseKeys.push_back("inactive");
|
||||
databaseKeys.push_back("wired");
|
||||
databaseKeys.push_back("swaptotal");
|
||||
databaseKeys.push_back("swapused");
|
||||
|
||||
#ifdef VFS_BCACHESTAT
|
||||
databaseKeys.push_back("buffer");
|
||||
#endif
|
||||
|
||||
#ifdef VM_UVMEXP2
|
||||
databaseKeys.push_back("file");
|
||||
databaseKeys.push_back("exec");
|
||||
#endif
|
||||
|
||||
databaseMap.push_back(memory_value_total);
|
||||
databaseMap.push_back(memory_value_free);
|
||||
databaseMap.push_back(memory_value_used);
|
||||
databaseMap.push_back(memory_value_active);
|
||||
databaseMap.push_back(memory_value_inactive);
|
||||
databaseMap.push_back(memory_value_wired);
|
||||
databaseMap.push_back(memory_value_swaptotal);
|
||||
databaseMap.push_back(memory_value_swapused);
|
||||
|
||||
#ifdef VFS_BCACHESTAT
|
||||
databaseMap.push_back(memory_value_buffer);
|
||||
#endif
|
||||
|
||||
#ifdef VM_UVMEXP2
|
||||
databaseMap.push_back(memory_value_file);
|
||||
databaseMap.push_back(memory_value_ex);
|
||||
#endif
|
||||
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsMemory::updateSwap(mem_data* _mem)
|
||||
{
|
||||
struct swapent *swdev;
|
||||
int nswap, rnswap, i;
|
||||
|
||||
nswap = swapctl(SWAP_NSWAP, 0, 0);
|
||||
if (nswap == 0)
|
||||
return;
|
||||
|
||||
swdev = (struct swapent *)calloc(nswap, sizeof(*swdev));
|
||||
if (swdev == NULL)
|
||||
return;
|
||||
|
||||
rnswap = swapctl(SWAP_STATS, swdev, nswap);
|
||||
if (rnswap == -1) {
|
||||
free(swdev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if rnswap != nswap, then what? */
|
||||
/* Total things up */
|
||||
double total = 0;
|
||||
double used = 0;
|
||||
for (i = 0; i < nswap; i++) {
|
||||
if (swdev[i].se_flags & SWF_ENABLE) {
|
||||
used += (double)(swdev[i].se_inuse / (1024 / DEV_BSIZE));
|
||||
total += (double)(swdev[i].se_nblks / (1024 / DEV_BSIZE));
|
||||
}
|
||||
}
|
||||
|
||||
_mem->values[memory_value_swapused] = used;
|
||||
_mem->values[memory_value_swaptotal] = total;
|
||||
|
||||
free(swdev);
|
||||
}
|
||||
|
||||
void StatsMemory::update(long long sampleID)
|
||||
{
|
||||
mem_data _mem;
|
||||
prepareSample(&_mem);
|
||||
|
||||
#ifdef VM_UVMEXP2
|
||||
static int mib_vm[2] = { CTL_VM, VM_UVMEXP2 };
|
||||
struct uvmexp_sysctl uvmexp;
|
||||
#else
|
||||
static int mib_vm[2] = { CTL_VM, VM_UVMEXP };
|
||||
struct uvmexp uvmexp;
|
||||
#endif
|
||||
|
||||
size_t size;
|
||||
int pagesize = getpagesize();
|
||||
|
||||
size = sizeof(uvmexp);
|
||||
if (sysctl(mib_vm, 2, &uvmexp, &size, NULL, 0) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef VFS_BCACHESTAT
|
||||
static int mib_buf[3] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT };
|
||||
struct bcachestats bcstats;
|
||||
|
||||
size = sizeof(bcstats);
|
||||
if (sysctl(mib_buf, 3, &bcstats, &size, NULL, 0) >= 0) {
|
||||
_mem.values[memory_value_buffer] = (double)(bcstats.numbufpages * pagesize);
|
||||
}
|
||||
#endif
|
||||
|
||||
_mem.values[memory_value_total] = (double)(uvmexp.npages * pagesize);
|
||||
_mem.values[memory_value_free] = (double)(uvmexp.free * pagesize);
|
||||
_mem.values[memory_value_used] = (double)(_mem.values[memory_value_total] - _mem.values[memory_value_free]);
|
||||
_mem.values[memory_value_active] = (double)(uvmexp.active * pagesize);
|
||||
_mem.values[memory_value_inactive] = (double)(uvmexp.inactive * pagesize);
|
||||
_mem.values[memory_value_wired] = (double)(uvmexp.wired * pagesize);
|
||||
|
||||
#ifdef VM_UVMEXP2
|
||||
_mem.values[memory_value_file] = (double)(uvmexp.filepages * pagesize);
|
||||
_mem.values[memory_value_ex] = (double)(uvmexp.execpages * pagesize);
|
||||
#endif
|
||||
|
||||
updateSwap(&_mem);
|
||||
|
||||
addSample(_mem, sampleID);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_LIBKVM) && defined(USE_MEM_KVM)
|
||||
|
||||
void StatsMemory::init()
|
||||
{
|
||||
databaseKeys.push_back("total");
|
||||
databaseKeys.push_back("free");
|
||||
databaseKeys.push_back("active");
|
||||
databaseKeys.push_back("inactive");
|
||||
databaseKeys.push_back("wired");
|
||||
databaseKeys.push_back("cached");
|
||||
databaseKeys.push_back("used");
|
||||
databaseKeys.push_back("buffer");
|
||||
databaseKeys.push_back("swaptotal");
|
||||
databaseKeys.push_back("swapused");
|
||||
databaseKeys.push_back("swapin");
|
||||
databaseKeys.push_back("swapout");
|
||||
|
||||
databaseMap.push_back(memory_value_total);
|
||||
databaseMap.push_back(memory_value_free);
|
||||
databaseMap.push_back(memory_value_active);
|
||||
databaseMap.push_back(memory_value_inactive);
|
||||
databaseMap.push_back(memory_value_wired);
|
||||
databaseMap.push_back(memory_value_cached);
|
||||
databaseMap.push_back(memory_value_used);
|
||||
databaseMap.push_back(memory_value_buffer);
|
||||
databaseMap.push_back(memory_value_swaptotal);
|
||||
databaseMap.push_back(memory_value_swapused);
|
||||
databaseMap.push_back(memory_value_swapin);
|
||||
databaseMap.push_back(memory_value_swapout);
|
||||
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsMemory::update(long long sampleID)
|
||||
{
|
||||
mem_data _mem;
|
||||
prepareSample(&_mem);
|
||||
|
||||
kvm_t *kd;
|
||||
size_t len;
|
||||
double kbpp;
|
||||
struct vmmeter sum;
|
||||
struct kvm_swap swap[1];
|
||||
|
||||
struct nlist nl[] = {
|
||||
{ "_cnt" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL)
|
||||
{
|
||||
fprintf(stderr, "kvm_open(): %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/* get virtual memory data */
|
||||
if (kvm_nlist(kd, nl) == -1)
|
||||
{
|
||||
fprintf(stderr, "kvm_nlist(): %s\n", strerror(errno));
|
||||
kvm_close(kd);
|
||||
return;
|
||||
}
|
||||
|
||||
len = sizeof(sum);
|
||||
|
||||
if (kvm_read(kd, nl[0].n_value, &sum, len) == -1)
|
||||
{
|
||||
fprintf(stderr, "kvm_read(): %s\n", strerror(errno));
|
||||
kvm_close(kd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* kilo bytes per page */
|
||||
kbpp = sum.v_page_size;// / 1024;
|
||||
|
||||
_mem.values[memory_value_total] = (double)(sum.v_page_count * kbpp);
|
||||
_mem.values[memory_value_free] = (double)(sum.v_free_count * kbpp);
|
||||
_mem.values[memory_value_active] = (double)(sum.v_active_count * kbpp);
|
||||
_mem.values[memory_value_inactive] = (double)(sum.v_inactive_count * kbpp);
|
||||
_mem.values[memory_value_cached] = (double)(sum.v_cache_count * kbpp);
|
||||
_mem.values[memory_value_wired] = (double)(sum.v_wire_count * kbpp);
|
||||
_mem.values[memory_value_used] = (double)(_mem.values[memory_value_active] + _mem.values[memory_value_wired]);
|
||||
|
||||
_mem.values[memory_value_swapin] = (double)(sum.v_swappgsin);
|
||||
_mem.values[memory_value_swapout] = (double)(sum.v_swappgsout);
|
||||
|
||||
#ifdef HAVE_SYSCTLBYNAME
|
||||
size_t bufsizelen;
|
||||
long bufsize;
|
||||
bufsizelen = sizeof(bufsize);
|
||||
|
||||
if (sysctlbyname("kern.nbuf", &bufsize, &bufsizelen, NULL, 0) >= 0)
|
||||
{
|
||||
_mem.values[memory_value_buffer] = (double)((bufsize * 16) * 1024);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (kvm_getswapinfo(kd, swap, 1, 0) == -1)
|
||||
{
|
||||
fprintf(stderr, "kvm_getswapinfo(): %s\n", strerror(errno));
|
||||
kvm_close(kd);
|
||||
return;
|
||||
}
|
||||
|
||||
_mem.values[memory_value_swaptotal] = (double)(swap[0].ksw_total * kbpp);
|
||||
_mem.values[memory_value_swapused] = (double)(swap[0].ksw_used * kbpp);
|
||||
|
||||
kvm_close(kd);
|
||||
|
||||
addSample(_mem, sampleID);
|
||||
|
||||
} /*USE_MEM_KVM*/
|
||||
|
||||
# elif defined(HAVE_LIBPERFSTAT) && defined(USE_MEM_PERFSTAT)
|
||||
|
||||
void StatsMemory::init()
|
||||
{
|
||||
databaseKeys.push_back("total");
|
||||
databaseKeys.push_back("free");
|
||||
databaseKeys.push_back("used");
|
||||
databaseKeys.push_back("swaptotal");
|
||||
databaseKeys.push_back("swapused");
|
||||
databaseKeys.push_back("swapin");
|
||||
databaseKeys.push_back("swapout");
|
||||
databaseKeys.push_back("virtualtotal");
|
||||
databaseKeys.push_back("virtualactive");
|
||||
|
||||
databaseMap.push_back(memory_value_total);
|
||||
databaseMap.push_back(memory_value_free);
|
||||
databaseMap.push_back(memory_value_used);
|
||||
databaseMap.push_back(memory_value_swaptotal);
|
||||
databaseMap.push_back(memory_value_swapused);
|
||||
databaseMap.push_back(memory_value_swapin);
|
||||
databaseMap.push_back(memory_value_swapout);
|
||||
databaseMap.push_back(memory_value_virtualtotal);
|
||||
databaseMap.push_back(memory_value_virtualactive);
|
||||
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsMemory::update(long long sampleID)
|
||||
{
|
||||
perfstat_memory_total_t meminfo;
|
||||
int ps;
|
||||
mem_data _mem;
|
||||
prepareSample(&_mem);
|
||||
|
||||
ps=getpagesize();
|
||||
perfstat_memory_total(NULL, &meminfo, sizeof meminfo, 1);
|
||||
_mem.values[memory_value_total] = (double)(ps * meminfo.real_total);
|
||||
_mem.values[memory_value_free] = (double)(ps * meminfo.real_free);
|
||||
_mem.values[memory_value_swapin] = (double)(ps * meminfo.pgspins);
|
||||
_mem.values[memory_value_swapout] = (double)(ps * meminfo.pgspouts);
|
||||
_mem.values[memory_value_swaptotal] = (double)(ps * meminfo.pgsp_total);
|
||||
_mem.values[memory_value_swapused] = (double)(ps * (meminfo.pgsp_total- meminfo.pgsp_free));
|
||||
_mem.values[memory_value_used] = (double)(_mem.values[memory_value_total] - _mem.values[memory_value_free]);
|
||||
_mem.values[memory_value_virtualtotal] = (double)(ps * meminfo.virt_total);
|
||||
_mem.values[memory_value_virtualactive] = (double)(ps * meminfo.virt_active);
|
||||
|
||||
addSample(_mem, sampleID);
|
||||
|
||||
} /*USE_MEM_PERFSTAT*/
|
||||
#elif defined(USE_MEM_PROCFS)
|
||||
|
||||
void StatsMemory::init()
|
||||
{
|
||||
databaseKeys.push_back("total");
|
||||
databaseKeys.push_back("free");
|
||||
databaseKeys.push_back("active");
|
||||
databaseKeys.push_back("inactive");
|
||||
databaseKeys.push_back("cached");
|
||||
databaseKeys.push_back("used");
|
||||
databaseKeys.push_back("buffer");
|
||||
databaseKeys.push_back("swaptotal");
|
||||
databaseKeys.push_back("swapused");
|
||||
databaseKeys.push_back("swapin");
|
||||
databaseKeys.push_back("swapout");
|
||||
|
||||
databaseMap.push_back(memory_value_total);
|
||||
databaseMap.push_back(memory_value_free);
|
||||
databaseMap.push_back(memory_value_active);
|
||||
databaseMap.push_back(memory_value_inactive);
|
||||
databaseMap.push_back(memory_value_cached);
|
||||
databaseMap.push_back(memory_value_used);
|
||||
databaseMap.push_back(memory_value_buffer);
|
||||
databaseMap.push_back(memory_value_swaptotal);
|
||||
databaseMap.push_back(memory_value_swapused);
|
||||
databaseMap.push_back(memory_value_swapin);
|
||||
databaseMap.push_back(memory_value_swapout);
|
||||
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsMemory::update(long long sampleID)
|
||||
{
|
||||
char buf[320];
|
||||
FILE * fp = NULL;
|
||||
mem_data _mem;
|
||||
prepareSample(&_mem);
|
||||
|
||||
unsigned long long t = 0;
|
||||
unsigned long long f = 0;
|
||||
unsigned long long a = 0;
|
||||
unsigned long long i = 0;
|
||||
unsigned long long swt = 0;
|
||||
unsigned long long swf = 0;
|
||||
unsigned long long swi = 0;
|
||||
unsigned long long swo = 0;
|
||||
unsigned long long c = 0;
|
||||
unsigned long long b = 0;
|
||||
|
||||
if (!(fp = fopen("/proc/meminfo", "r"))) return;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
sscanf(buf, "MemTotal: %llu kB", &t);
|
||||
sscanf(buf, "MemFree: %llu kB", &f);
|
||||
sscanf(buf, "Active: %llu kB", &a);
|
||||
sscanf(buf, "Inactive: %llu kB", &i);
|
||||
sscanf(buf, "SwapTotal: %llu kB", &swt);
|
||||
sscanf(buf, "SwapFree: %llu kB", &swf);
|
||||
sscanf(buf, "Cached: %llu kB", &c);
|
||||
sscanf(buf, "Buffers: %llu kB", &b);
|
||||
}
|
||||
|
||||
swf = swf * 1024;
|
||||
|
||||
_mem.values[memory_value_total] = (double)(t * 1024);
|
||||
_mem.values[memory_value_free] = (double)(f * 1024);
|
||||
_mem.values[memory_value_active] = (double)(a * 1024);
|
||||
_mem.values[memory_value_inactive] = (double)(i * 1024);
|
||||
_mem.values[memory_value_swaptotal] = (double)(swt * 1024);
|
||||
_mem.values[memory_value_cached] = (double)(c * 1024);
|
||||
_mem.values[memory_value_buffer] = (double)(b * 1024);
|
||||
_mem.values[memory_value_swapused] = (double)(_mem.values[memory_value_swaptotal] - swf);
|
||||
|
||||
if (0 == _mem.values[memory_value_active] && 0 == _mem.values[memory_value_inactive] && 0 == _mem.values[memory_value_cached])
|
||||
{
|
||||
_mem.values[memory_value_active] = (double)(_mem.values[memory_value_total] - _mem.values[memory_value_free]);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (!(fp = fopen("/proc/vmstat", "r"))) return;
|
||||
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
sscanf(buf, "pswpin %llu", &swi);
|
||||
sscanf(buf, "pswpout %llu", &swo);
|
||||
}
|
||||
|
||||
_mem.values[memory_value_swapin] = (double)swi;
|
||||
_mem.values[memory_value_swapout] = (double)swo;
|
||||
|
||||
fclose(fp);
|
||||
|
||||
_mem.values[memory_value_used] = (double)(_mem.values[memory_value_total] - (_mem.values[memory_value_free] + _mem.values[memory_value_buffer] + _mem.values[memory_value_cached]));
|
||||
|
||||
addSample(_mem, sampleID);
|
||||
|
||||
} /* USE_MEM_PROCFS */
|
||||
#elif defined(USE_MEM_KSTAT)
|
||||
|
||||
void StatsMemory::init()
|
||||
{
|
||||
databaseKeys.push_back("total");
|
||||
databaseKeys.push_back("free");
|
||||
databaseKeys.push_back("active");
|
||||
databaseKeys.push_back("inactive");
|
||||
databaseKeys.push_back("cached");
|
||||
databaseKeys.push_back("used");
|
||||
databaseKeys.push_back("swaptotal");
|
||||
databaseKeys.push_back("swapused");
|
||||
databaseKeys.push_back("swapin");
|
||||
databaseKeys.push_back("swapout");
|
||||
|
||||
databaseMap.push_back(memory_value_total);
|
||||
databaseMap.push_back(memory_value_free);
|
||||
databaseMap.push_back(memory_value_active);
|
||||
databaseMap.push_back(memory_value_inactive);
|
||||
databaseMap.push_back(memory_value_cached);
|
||||
databaseMap.push_back(memory_value_used);
|
||||
databaseMap.push_back(memory_value_swaptotal);
|
||||
databaseMap.push_back(memory_value_swapused);
|
||||
databaseMap.push_back(memory_value_swapin);
|
||||
databaseMap.push_back(memory_value_swapout);
|
||||
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsMemory::update(long long sampleID)
|
||||
{
|
||||
kstat_t *ksp;
|
||||
kstat_named_t *kn;
|
||||
unsigned long long lv, ps;
|
||||
int cc, ncpu;
|
||||
cpu_stat_t cs;
|
||||
mem_data _mem;
|
||||
prepareSample(&_mem);
|
||||
|
||||
if(NULL == (ksp = kstat_lookup(ksh, (char*)"unix", -1, (char*)"system_pages"))) return;
|
||||
if(-1 == kstat_read(ksh, ksp, NULL)) return;
|
||||
|
||||
ps = getpagesize();
|
||||
|
||||
if(NULL != (kn = (kstat_named_t *) kstat_data_lookup(ksp, (char*)"pagestotal")))
|
||||
{
|
||||
lv = ksgetull(kn);
|
||||
_mem.values[memory_value_total] = (double)(lv * ps);// / 1024;
|
||||
}
|
||||
|
||||
if(NULL != (kn = (kstat_named_t *) kstat_data_lookup(ksp, (char*)"pageslocked")))
|
||||
{
|
||||
lv = ksgetull(kn);
|
||||
_mem.values[memory_value_cached] = (double)(lv * ps);// / 1024;
|
||||
}
|
||||
|
||||
if(NULL != (kn = (kstat_named_t *) kstat_data_lookup(ksp, (char*)"pagesfree")))
|
||||
{
|
||||
lv = ksgetull(kn);
|
||||
_mem.values[memory_value_free] = (double)(lv * ps);// / 1024;
|
||||
}
|
||||
|
||||
if(NULL != (kn = (kstat_named_t *) kstat_data_lookup(ksp, (char*)"pp_kernel")))
|
||||
{
|
||||
lv = ksgetull(kn);
|
||||
_mem.values[memory_value_wired] = (double)(lv * ps);// / 1024;
|
||||
}
|
||||
|
||||
_mem.values[memory_value_active] = (double)(_mem.values[memory_value_total] - _mem.values[memory_value_free]);
|
||||
_mem.values[memory_value_used] = (double)(_mem.values[memory_value_active]);
|
||||
|
||||
ncpu = sysconf(_SC_NPROCESSORS_CONF);
|
||||
|
||||
for(cc = 0; cc < ncpu; cc++)
|
||||
{
|
||||
if(p_online(cc, P_STATUS) != P_ONLINE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(NULL != (ksp = kstat_lookup(ksh, (char*)"cpu_stat", cc, NULL)))
|
||||
{
|
||||
if(-1 != kstat_read(ksh, ksp, &cs))
|
||||
{
|
||||
_mem.values[memory_value_swapin] += (double)(cs.cpu_vminfo.pgin);
|
||||
_mem.values[memory_value_swapout] += (double)(cs.cpu_vminfo.pgout);
|
||||
}
|
||||
}
|
||||
}
|
||||
get_swp_data(&_mem);
|
||||
|
||||
addSample(_mem, sampleID);
|
||||
}
|
||||
|
||||
void StatsMemory::get_swp_data(struct mem_data * _mem)
|
||||
{
|
||||
swaptbl_t *s;
|
||||
char strtab[255];
|
||||
int swap_num;
|
||||
int status;
|
||||
int i;
|
||||
int bpp = getpagesize() >> DEV_BSHIFT;
|
||||
|
||||
unsigned long long avail = 0;
|
||||
unsigned long long total = 0;
|
||||
|
||||
swap_num = swapctl (SC_GETNSWP, NULL);
|
||||
if (swap_num < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (swap_num == 0)
|
||||
return;
|
||||
|
||||
s = (swaptbl_t *) malloc (swap_num * sizeof (swapent_t) + sizeof (struct swaptable));
|
||||
if (s == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < (swap_num + 1); i++) {
|
||||
s->swt_ent[i].ste_path = strtab;
|
||||
}
|
||||
s->swt_n = swap_num + 1;
|
||||
status = swapctl (SC_LIST, s);
|
||||
if (status != swap_num)
|
||||
{
|
||||
free (s);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < swap_num; i++)
|
||||
{
|
||||
if ((s->swt_ent[i].ste_flags & ST_INDEL) != 0)
|
||||
continue;
|
||||
|
||||
avail += ((unsigned long long) s->swt_ent[i].ste_free) * bpp * DEV_BSIZE;
|
||||
total += ((unsigned long long) s->swt_ent[i].ste_pages) * bpp * DEV_BSIZE;
|
||||
}
|
||||
|
||||
if (total < avail)
|
||||
return;
|
||||
|
||||
_mem->values[memory_value_swaptotal] = (double)(total);
|
||||
_mem->values[memory_value_swapused] = (double)(total - avail);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void StatsMemory::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsMemory::update(long long sampleID) {}
|
||||
|
||||
#endif
|
||||
|
||||
void StatsMemory::prepareSample(mem_data* data)
|
||||
{
|
||||
int x;
|
||||
for(x=0;x<memory_values_count;x++)
|
||||
data->values[x] = -1;
|
||||
}
|
||||
|
||||
void StatsMemory::addSample(mem_data data, long long sampleID)
|
||||
{
|
||||
data.sampleID = sampleIndex[0].sampleID;
|
||||
data.time = sampleIndex[0].time;
|
||||
|
||||
samples[0].push_front(data);
|
||||
if (samples[0].size() > HISTORY_SIZE) samples[0].pop_back();
|
||||
}
|
||||
|
||||
void StatsMemory::_init()
|
||||
{
|
||||
initShared();
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
databaseType = 0;
|
||||
databasePrefix = "memory_";
|
||||
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double PRIMARY KEY NOT NULL, time double NOT NULL DEFAULT 0, empty integer NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
|
||||
size_t y;
|
||||
for(y=0;y<databaseKeys.size();y++)
|
||||
{
|
||||
if(!_database.columnExists(databaseKeys[y], table))
|
||||
{
|
||||
stringstream sql;
|
||||
sql << "ALTER TABLE " << table << " ADD COLUMN " << databaseKeys[y] << " DOUBLE NOT NULL DEFAULT 0";
|
||||
|
||||
DatabaseItem query = _database.databaseItem(sql.str());
|
||||
query.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
loadPreviousSamples();
|
||||
fillGaps();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void StatsMemory::loadPreviousSamples()
|
||||
{
|
||||
loadPreviousSamplesAtIndex(1);
|
||||
loadPreviousSamplesAtIndex(2);
|
||||
loadPreviousSamplesAtIndex(3);
|
||||
loadPreviousSamplesAtIndex(4);
|
||||
loadPreviousSamplesAtIndex(5);
|
||||
loadPreviousSamplesAtIndex(6);
|
||||
loadPreviousSamplesAtIndex(7);
|
||||
}
|
||||
|
||||
void StatsMemory::loadPreviousSamplesAtIndex(int index)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(index);
|
||||
double sampleID = sampleIdForTable(table);
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
mem_data sample;
|
||||
prepareSample(&sample);
|
||||
|
||||
size_t y;
|
||||
for(y=0;y<databaseKeys.size();y++)
|
||||
{
|
||||
sample.values[databaseMap[y]] = query.doubleForColumn(databaseKeys[y]);
|
||||
}
|
||||
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
samples[index].push_front(sample);
|
||||
}
|
||||
if(samples[index].size() > 0)
|
||||
{
|
||||
sampleIndex[index].sampleID = samples[index][0].sampleID;
|
||||
sampleIndex[index].time = samples[index][0].time;
|
||||
sampleIndex[index].nextTime = sampleIndex[index].time + sampleIndex[index].interval;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsMemory::updateHistory()
|
||||
{
|
||||
int x;
|
||||
for (x = 1; x < 8; x++)
|
||||
{
|
||||
if(sampleIndex[0].time >= sampleIndex[x].nextTime)
|
||||
{
|
||||
double now = get_current_time();
|
||||
double earlistTime = now - (HISTORY_SIZE * sampleIndex[x].interval);
|
||||
while(sampleIndex[x].nextTime < now)
|
||||
{
|
||||
sampleIndex[x].sampleID = sampleIndex[x].sampleID + 1;
|
||||
sampleIndex[x].time = sampleIndex[x].nextTime;
|
||||
sampleIndex[x].nextTime = sampleIndex[x].nextTime + sampleIndex[x].interval;
|
||||
|
||||
if(sampleIndex[x].time < earlistTime)
|
||||
continue;
|
||||
|
||||
mem_data sample = historyItemAtIndex(x);
|
||||
|
||||
samples[x].push_front(sample);
|
||||
if (samples[x].size() > HISTORY_SIZE) samples[x].pop_back();
|
||||
|
||||
if(sample.empty)
|
||||
continue;
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
stringstream sql;
|
||||
sql << "insert into " + table + " (empty, sample, time";
|
||||
|
||||
size_t y;
|
||||
for(y=0;y<databaseKeys.size();y++)
|
||||
{
|
||||
sql << ", " << databaseKeys[y];
|
||||
}
|
||||
|
||||
sql << ") values(?, ?, ?";
|
||||
|
||||
for(y=0;y<databaseKeys.size();y++)
|
||||
{
|
||||
sql << ", ?";
|
||||
}
|
||||
|
||||
sql << ")";
|
||||
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql.str());
|
||||
sqlite3_bind_int(dbItem._statement, 1, 0);
|
||||
sqlite3_bind_double(dbItem._statement, 2, (double)sample.sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sample.time);
|
||||
|
||||
for(y=0;y<databaseMap.size();y++)
|
||||
{
|
||||
sqlite3_bind_double(dbItem._statement, (y + 4), sample.values[databaseMap[y]]);
|
||||
}
|
||||
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mem_data StatsMemory::historyItemAtIndex(int index)
|
||||
{
|
||||
std::deque<mem_data> from = samples[sampleIndex[index].historyIndex];
|
||||
double minimumTime = sampleIndex[index].time - sampleIndex[index].interval;
|
||||
double maximumTime = sampleIndex[index].time;
|
||||
if(sampleIndex[index].historyIndex == 0)
|
||||
maximumTime += 0.99;
|
||||
|
||||
mem_data sample;
|
||||
|
||||
double values[memory_values_count];
|
||||
|
||||
int x;
|
||||
for(x=0;x<memory_values_count;x++)
|
||||
values[x] = 0;
|
||||
|
||||
int count = 0;
|
||||
if(from.size() > 0)
|
||||
{
|
||||
for (deque<mem_data>::iterator cur = from.begin(); cur != from.end(); ++cur)
|
||||
{
|
||||
if ((*cur).time > maximumTime)
|
||||
continue;
|
||||
|
||||
if ((*cur).time < minimumTime)
|
||||
break;
|
||||
|
||||
for(x=0;x<memory_values_count;x++)
|
||||
values[x] += (*cur).values[x];
|
||||
count++;
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
for(x=0;x<memory_values_count;x++)
|
||||
values[x] /= count;
|
||||
}
|
||||
}
|
||||
|
||||
for(x=0;x<memory_values_count;x++){
|
||||
if(samples[0].size() > 0)
|
||||
{
|
||||
if(samples[0][0].values[x] == -1)
|
||||
values[x] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(x=0;x<memory_values_count;x++)
|
||||
sample.values[x] = values[x];
|
||||
|
||||
sample.sampleID = sampleIndex[index].sampleID;
|
||||
sample.time = sampleIndex[index].time;
|
||||
sample.empty = false;
|
||||
if(count == 0)
|
||||
sample.empty = true;
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
66
src/stats/StatsMemory.h
Normal file
66
src/stats/StatsMemory.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _STATSMEMORY_H
|
||||
#define _STATSMEMORY_H
|
||||
|
||||
class StatsMemory : public StatsBase
|
||||
{
|
||||
public:
|
||||
void update(long long sampleID);
|
||||
void addSample(mem_data data, long long sampleID);
|
||||
void prepareSample(mem_data* data);
|
||||
std::deque<mem_data> samples[8];
|
||||
|
||||
std::deque<std::string> databaseKeys;
|
||||
std::deque<int> databaseMap;
|
||||
|
||||
void _init();
|
||||
void init();
|
||||
#ifdef USE_SQLITE
|
||||
void updateHistory();
|
||||
mem_data historyItemAtIndex(int index);
|
||||
void loadPreviousSamples();
|
||||
void loadPreviousSamplesAtIndex(int index);
|
||||
#endif
|
||||
|
||||
#if defined(USE_MEM_SYSCTL)
|
||||
void updateSwap(mem_data* data);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBKSTAT
|
||||
kstat_ctl_t *ksh;
|
||||
void get_swp_data(struct mem_data * _mem);
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
816
src/stats/StatsNetwork.cpp
Normal file
816
src/stats/StatsNetwork.cpp
Normal file
@@ -0,0 +1,816 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsNetwork.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if defined(USE_NET_GETIFADDRS) && defined(HAVE_GETIFADDRS)
|
||||
|
||||
void StatsNetwork::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsNetwork::update(long long sampleID)
|
||||
{
|
||||
struct ifaddrs *ifap, *ifa;
|
||||
|
||||
if (getifaddrs(&ifap) < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vector<string> keys;
|
||||
|
||||
for (ifa = ifap; ifa; ifa = ifa->ifa_next)
|
||||
{
|
||||
if (ifa->ifa_flags & IFF_UP)
|
||||
{
|
||||
struct ifaddrs *iftmp;
|
||||
|
||||
if (ifa->ifa_addr == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ifa->ifa_addr->sa_family != AF_LINK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (iftmp = ifa->ifa_next; iftmp != NULL; iftmp = iftmp->ifa_next)
|
||||
{
|
||||
if(strcmp(ifa->ifa_name, iftmp->ifa_name) == 0)
|
||||
{
|
||||
if(keys.size() > 0)
|
||||
{
|
||||
if (std::find(keys.begin(), keys.end(), string(ifa->ifa_name)) != keys.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
struct if_data *ifd = (struct if_data *) ifa->ifa_data;
|
||||
processInterface(string(ifa->ifa_name), sampleID, ifd->ifi_obytes, ifd->ifi_ibytes);
|
||||
keys.push_back(string(ifa->ifa_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifap);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_LIBPERFSTAT) && defined(USE_NET_PERFSTAT)
|
||||
|
||||
void StatsNetwork::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsNetwork::update(long long sampleID)
|
||||
{
|
||||
perfstat_netinterface_total_t ninfo;
|
||||
perfstat_netinterface_total(NULL, &ninfo, sizeof ninfo, 1);
|
||||
processInterface(string("net"), sampleID, ninfo.obytes, ninfo.ibytes);
|
||||
} /*NET_USE_PERFSTAT*/
|
||||
|
||||
#elif defined(USE_NET_PROCFS)
|
||||
|
||||
void StatsNetwork::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsNetwork::update(long long sampleID)
|
||||
{
|
||||
char dev[255];
|
||||
FILE * fp = NULL;
|
||||
|
||||
if (!(fp = fopen("/proc/net/dev", "r")))
|
||||
{
|
||||
cout << "fopen: /proc/net/dev" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_GETIFADDRS
|
||||
struct ifaddrs *ifap, *ifa;
|
||||
vector<string> active_infs;
|
||||
|
||||
if (getifaddrs(&ifap) >= 0) {
|
||||
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_flags & IFF_UP && ifa->ifa_name)
|
||||
{
|
||||
active_infs.push_back(string(ifa->ifa_name));
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifap);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(fscanf(fp, "%*[^\n] %*[^\n] "))
|
||||
{
|
||||
}
|
||||
|
||||
while (!feof(fp))
|
||||
{
|
||||
unsigned long long upload;
|
||||
unsigned long long download;
|
||||
|
||||
if(fscanf(fp, "%7[^:]:%llu %*u %*u %*u %*u %*u %*u %*u %llu %*[^\n] ", dev, &download, &upload) > 0)
|
||||
{
|
||||
#ifdef HAVE_GETIFADDRS
|
||||
if(active_infs.size() > 0)
|
||||
{
|
||||
if (std::find(active_infs.begin(), active_infs.end(), string(dev)) == active_infs.end())
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
processInterface(string(dev), sampleID, upload, download);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(fscanf(fp, "%*[^\n]")){}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
#elif defined(USE_NET_SYSCTL)
|
||||
|
||||
void StatsNetwork::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
int StatsNetwork::get_ifcount()
|
||||
{
|
||||
size_t len;
|
||||
int name[5], count;
|
||||
|
||||
name[0] = CTL_NET;
|
||||
name[1] = PF_LINK;
|
||||
name[2] = NETLINK_GENERIC;
|
||||
name[3] = IFMIB_SYSTEM;
|
||||
name[4] = IFMIB_IFCOUNT;
|
||||
|
||||
len = sizeof(int);
|
||||
|
||||
sysctl(name, 5, &count, &len, NULL, 0);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void StatsNetwork::update(long long sampleID)
|
||||
{
|
||||
int i;
|
||||
int name[6];
|
||||
size_t len;
|
||||
struct ifmibdata ifmd;
|
||||
int ifcount = get_ifcount();
|
||||
|
||||
len = sizeof(ifmd);
|
||||
|
||||
name[0] = CTL_NET;
|
||||
name[1] = PF_LINK;
|
||||
name[2] = NETLINK_GENERIC;
|
||||
name[3] = IFMIB_IFDATA;
|
||||
name[5] = IFDATA_GENERAL;
|
||||
|
||||
for (i = 1; i <= ifcount; i++)
|
||||
{
|
||||
name[4] = i;
|
||||
|
||||
sysctl(name, 6, &ifmd, &len, (void *) 0, 0);
|
||||
|
||||
unsigned int flags = ifmd.ifmd_flags;
|
||||
|
||||
if ((flags & IFF_UP) && (flags & IFF_RUNNING))
|
||||
{
|
||||
unsigned long long upload = ifmd.ifmd_data.ifi_obytes;
|
||||
unsigned long long download = ifmd.ifmd_data.ifi_ibytes;
|
||||
|
||||
processInterface(string(ifmd.ifmd_name), sampleID, upload, download);
|
||||
}
|
||||
}
|
||||
} /*NET_USE_SYSCTL*/
|
||||
|
||||
#elif defined(HAVE_LIBKSTAT) && defined(USE_NET_KSTAT)
|
||||
|
||||
void StatsNetwork::init()
|
||||
{
|
||||
_init();
|
||||
|
||||
vector<string> infs = interfaces();
|
||||
if(infs.size() > 0)
|
||||
{
|
||||
for (vector<string>::iterator cur = infs.begin(); cur != infs.end(); ++cur)
|
||||
{
|
||||
createInterface((*cur));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<string> StatsNetwork::interfaces()
|
||||
{
|
||||
|
||||
vector<string> interfaces;
|
||||
|
||||
int n;
|
||||
int s;
|
||||
char *buf;
|
||||
struct lifnum lifn;
|
||||
struct lifconf lifc;
|
||||
struct lifreq *lifrp;
|
||||
int numifs;
|
||||
size_t bufsize;
|
||||
|
||||
s = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if (s < 0) {
|
||||
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (s < 0)
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
lifn.lifn_family = AF_UNSPEC;
|
||||
lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
|
||||
if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0)
|
||||
return interfaces;
|
||||
numifs = lifn.lifn_count;
|
||||
|
||||
bufsize = numifs * sizeof (struct lifreq);
|
||||
buf = (char*)malloc(bufsize);
|
||||
if (buf == NULL) {
|
||||
(void) close(s);
|
||||
return interfaces;
|
||||
}
|
||||
lifc.lifc_family = AF_UNSPEC;
|
||||
lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
|
||||
lifc.lifc_len = bufsize;
|
||||
lifc.lifc_buf = buf;
|
||||
if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
|
||||
(void) close(s);
|
||||
free(buf);
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
lifrp = lifc.lifc_req;
|
||||
(void) close(s);
|
||||
|
||||
for (n = numifs; n > 0; n--, lifrp++) {
|
||||
bool found = false;
|
||||
|
||||
string infname = string(lifrp->lifr_name);
|
||||
|
||||
if (infname.find(":") != std::string::npos)
|
||||
{
|
||||
infname = infname.substr(0, infname.find(":"));
|
||||
}
|
||||
|
||||
if(interfaces.size() > 0)
|
||||
{
|
||||
for (vector<string>::iterator cur = interfaces.begin(); cur != interfaces.end(); ++cur)
|
||||
{
|
||||
if((*cur) == infname)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (found == false) {
|
||||
interfaces.push_back(infname);
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
void StatsNetwork::update(long long sampleID)
|
||||
{
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<network_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
kstat_t *ksp;
|
||||
kstat_named_t *kn;
|
||||
char name[32];
|
||||
char module[32];
|
||||
char *p;
|
||||
|
||||
char *_dev = (char*)((*cur).device.c_str());
|
||||
|
||||
unsigned long long upload;
|
||||
unsigned long long download;
|
||||
|
||||
strcpy(module, (char*)"link");
|
||||
if((p = strchr(_dev, ':')) == NULL)
|
||||
{
|
||||
strncpy(name, _dev, sizeof(name) - 1);
|
||||
name[sizeof(name) - 1] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(name, p + 1, sizeof(name) - 1);
|
||||
name[sizeof(name) - 1]=0;
|
||||
strncpy(module, _dev, sizeof(module) - 1);
|
||||
module[p - _dev] = 0;
|
||||
}
|
||||
if(NULL == (ksp = kstat_lookup(ksh, module, -1, name)))
|
||||
{
|
||||
(*cur).active = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(-1 == kstat_read(ksh, ksp, NULL))
|
||||
{
|
||||
(*cur).active = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
kn = (kstat_named_t *) ksp->ks_data;
|
||||
if(NULL == (kn = (kstat_named_t *) kstat_data_lookup(ksp, (char*)"obytes64")))
|
||||
{
|
||||
(*cur).active = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
upload = ksgetull(kn);
|
||||
|
||||
if(NULL == (kn = (kstat_named_t *) kstat_data_lookup(ksp, (char*)"rbytes64")))
|
||||
{
|
||||
(*cur).active = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
download = ksgetull(kn);
|
||||
|
||||
processInterface((*cur).device, sampleID, upload, download);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# else
|
||||
|
||||
void StatsNetwork::init()
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
void StatsNetwork::update(long long sampleID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define INT_TO_ADDR(_addr) \
|
||||
(_addr & 0xFF), \
|
||||
(_addr >> 8 & 0xFF), \
|
||||
(_addr >> 16 & 0xFF), \
|
||||
(_addr >> 24 & 0xFF)
|
||||
|
||||
// IPS_IOCTL - We dont use getifaddrs on solaris even if its available. Doesn't seem to work correctly
|
||||
#if defined(HAVE_GETIFADDRS) && !defined(IPS_IOCTL)
|
||||
void StatsNetwork::updateAddresses()
|
||||
{
|
||||
if(_items.size() == 0)
|
||||
return;
|
||||
|
||||
struct ifaddrs *ifap, *ifa;
|
||||
|
||||
if (getifaddrs(&ifap) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (vector<network_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
(*cur).addresses.clear();
|
||||
}
|
||||
|
||||
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_flags & IFF_UP)
|
||||
{
|
||||
struct ifaddrs *iftmp;
|
||||
|
||||
if (ifa->ifa_addr == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ifa->ifa_addr->sa_family != AF_LINK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (iftmp = ifa->ifa_next; iftmp != NULL; iftmp = iftmp->ifa_next)
|
||||
{
|
||||
if(strcmp(ifa->ifa_name, iftmp->ifa_name) == 0)
|
||||
{
|
||||
for (vector<network_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
network_info interface = *cur;
|
||||
if (interface.device == string(ifa->ifa_name))
|
||||
{
|
||||
if (iftmp->ifa_addr->sa_family == AF_INET) {
|
||||
(*cur).addresses.push_back(string(inet_ntoa(((struct sockaddr_in *)iftmp->ifa_addr)->sin_addr)));
|
||||
}
|
||||
|
||||
#ifdef AF_INET6
|
||||
if (iftmp->ifa_addr->sa_family == AF_INET6) {
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
const char *conversion = inet_ntop(AF_INET6, &((struct sockaddr_in6 *)iftmp->ifa_addr)->sin6_addr, ip, sizeof(ip));
|
||||
(*cur).addresses.push_back(string(conversion));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifap);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void StatsNetwork::updateAddresses()
|
||||
{
|
||||
if(_items.size() == 0)
|
||||
return;
|
||||
|
||||
struct ifconf ifc;
|
||||
struct ifreq ifr[10];
|
||||
int i, sd, ifc_num, addr;
|
||||
|
||||
for (vector<network_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
(*cur).addresses.clear();
|
||||
}
|
||||
|
||||
sd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (sd > 0)
|
||||
{
|
||||
ifc.ifc_len = sizeof(ifr);
|
||||
ifc.ifc_ifcu.ifcu_buf = (caddr_t)ifr;
|
||||
|
||||
if (ioctl(sd, SIOCGIFCONF, &ifc) == 0)
|
||||
{
|
||||
ifc_num = ifc.ifc_len / sizeof(struct ifreq);
|
||||
|
||||
for (i = 0; i < ifc_num; ++i)
|
||||
{
|
||||
if (ifr[i].ifr_addr.sa_family != AF_INET)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string ip = string("");
|
||||
|
||||
if (ioctl(sd, SIOCGIFADDR, &ifr[i]) == 0)
|
||||
{
|
||||
addr = ((struct sockaddr_in *)(&ifr[i].ifr_addr))->sin_addr.s_addr;
|
||||
|
||||
char str[32];
|
||||
sprintf(str, "%d.%d.%d.%d", INT_TO_ADDR(addr));
|
||||
ip = string(str);
|
||||
}
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<network_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
network_info interface = *cur;
|
||||
if (interface.device == string(ifr[i].ifr_name))
|
||||
{
|
||||
(*cur).addresses.push_back(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(sd);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void StatsNetwork::createInterface(string key)
|
||||
{
|
||||
if(key == "lo" || key == "lo0")
|
||||
return;
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<network_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
network_info interface = *cur;
|
||||
if (interface.device == key)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
network_info item;
|
||||
item.last_down = 0;
|
||||
item.last_up = 0;
|
||||
item.device = key;
|
||||
session++;
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
double sampleID = 0;
|
||||
if(samples[x].size() > 0)
|
||||
sampleID = samples[x][0].sampleID;
|
||||
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample AND uuid = ? order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
sqlite3_bind_text(query._statement, 2, key.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
net_data sample;
|
||||
sample.d = query.doubleForColumn("download");
|
||||
sample.u = query.doubleForColumn("upload");
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
item.samples[x].push_front(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
_items.insert(_items.begin(), item);
|
||||
|
||||
updateAddresses();
|
||||
}
|
||||
|
||||
void StatsNetwork::processInterface(string key, long long sampleID, unsigned long long upload, unsigned long long download)
|
||||
{
|
||||
if(key == "lo" || key == "lo0" || key == "nic" || key.find("virbr") == 0 || key.find("ath0") == 0)
|
||||
return;
|
||||
|
||||
createInterface(key);
|
||||
|
||||
for (vector<network_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if (key == (*cur).device)
|
||||
{
|
||||
(*cur).active = true;
|
||||
|
||||
if(ready == 0)
|
||||
return;
|
||||
|
||||
net_data data;
|
||||
|
||||
if((*cur).last_up == 0)
|
||||
(*cur).last_up = upload;
|
||||
if((*cur).last_down == 0)
|
||||
(*cur).last_down = download;
|
||||
|
||||
double u = upload - (*cur).last_up;
|
||||
double d = download - (*cur).last_down;
|
||||
|
||||
(*cur).last_up = upload;
|
||||
(*cur).last_down = download;
|
||||
|
||||
data.u = u;
|
||||
data.d = d;
|
||||
data.sampleID = sampleIndex[0].sampleID;
|
||||
data.time = sampleIndex[0].time;
|
||||
|
||||
(*cur).samples[0].push_front(data);
|
||||
if ((*cur).samples[0].size() > HISTORY_SIZE)
|
||||
(*cur).samples[0].pop_back();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsNetwork::prepareUpdate()
|
||||
{
|
||||
if(ready == 0)
|
||||
return;
|
||||
|
||||
StatsBase::prepareUpdate();
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<network_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
(*cur).active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsNetwork::_init()
|
||||
{
|
||||
initShared();
|
||||
ready = 0;
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
databaseType = 1;
|
||||
databasePrefix = "network_";
|
||||
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x) + "_id";
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double PRIMARY KEY NOT NULL, time double NOT NULL DEFAULT 0, empty integer NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
|
||||
table = databasePrefix + tableAtIndex(x);
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double NOT NULL, time double NOT NULL DEFAULT 0, uuid varchar(255) NOT NULL, download double NOT NULL DEFAULT 0, upload double NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
}
|
||||
loadPreviousSamples();
|
||||
fillGaps();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void StatsNetwork::loadPreviousSamples()
|
||||
{
|
||||
loadPreviousSamplesAtIndex(1);
|
||||
loadPreviousSamplesAtIndex(2);
|
||||
loadPreviousSamplesAtIndex(3);
|
||||
loadPreviousSamplesAtIndex(4);
|
||||
loadPreviousSamplesAtIndex(5);
|
||||
loadPreviousSamplesAtIndex(6);
|
||||
loadPreviousSamplesAtIndex(7);
|
||||
}
|
||||
|
||||
void StatsNetwork::loadPreviousSamplesAtIndex(int index)
|
||||
{
|
||||
|
||||
string table = databasePrefix + tableAtIndex(index) + "_id";
|
||||
double sampleID = sampleIdForTable(table);
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
sample_data sample;
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
samples[index].insert(samples[index].begin(), sample);
|
||||
}
|
||||
if(samples[index].size() > 0)
|
||||
{
|
||||
sampleIndex[index].sampleID = samples[index][0].sampleID;
|
||||
sampleIndex[index].time = samples[index][0].time;
|
||||
sampleIndex[index].nextTime = sampleIndex[index].time + sampleIndex[index].interval;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsNetwork::updateHistory()
|
||||
{
|
||||
int x;
|
||||
for (x = 1; x < 8; x++)
|
||||
{
|
||||
if(sampleIndex[0].time >= sampleIndex[x].nextTime)
|
||||
{
|
||||
double now = get_current_time();
|
||||
double earlistTime = now - (HISTORY_SIZE * sampleIndex[x].interval);
|
||||
while(sampleIndex[x].nextTime < now)
|
||||
{
|
||||
sampleIndex[x].sampleID = sampleIndex[x].sampleID + 1;
|
||||
sampleIndex[x].time = sampleIndex[x].nextTime;
|
||||
sampleIndex[x].nextTime = sampleIndex[x].nextTime + sampleIndex[x].interval;
|
||||
|
||||
if(sampleIndex[x].time < earlistTime)
|
||||
continue;
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<network_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
net_data sample = historyItemAtIndex(x, (*cur));
|
||||
|
||||
(*cur).samples[x].push_front(sample);
|
||||
if ((*cur).samples[x].size() > HISTORY_SIZE) (*cur).samples[x].pop_back();
|
||||
|
||||
if(sample.empty)
|
||||
continue;
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
string sql = "insert into " + table + " (sample, time, upload, download, uuid) values(?, ?, ?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(dbItem._statement, 1, (double)sample.sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 2, sample.time);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sample.u);
|
||||
sqlite3_bind_double(dbItem._statement, 4, sample.d);
|
||||
sqlite3_bind_text(dbItem._statement, 5, (*cur).device.c_str(), -1, SQLITE_STATIC);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x) + "_id";
|
||||
string sql = "insert into " + table + " (empty, sample, time) values(?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_int(dbItem._statement, 1, 0);
|
||||
sqlite3_bind_double(dbItem._statement, 2, (double)sampleIndex[x].sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sampleIndex[x].time);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
net_data StatsNetwork::historyItemAtIndex(int index, network_info item)
|
||||
{
|
||||
net_data sample;
|
||||
double u = 0;
|
||||
double d = 0;
|
||||
|
||||
std::deque<net_data> from = item.samples[sampleIndex[index].historyIndex];
|
||||
double minimumTime = sampleIndex[index].time - sampleIndex[index].interval;
|
||||
double maximumTime = sampleIndex[index].time;
|
||||
if(sampleIndex[index].historyIndex == 0)
|
||||
maximumTime += 0.99;
|
||||
|
||||
int count = 0;
|
||||
if(from.size() > 0)
|
||||
{
|
||||
for (deque<net_data>::iterator cursample = from.begin(); cursample != from.end(); ++cursample)
|
||||
{
|
||||
if ((*cursample).time > maximumTime)
|
||||
continue;
|
||||
|
||||
if ((*cursample).time < minimumTime)
|
||||
break;
|
||||
|
||||
u += (*cursample).u;
|
||||
d += (*cursample).d;
|
||||
count++;
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
u /= count;
|
||||
d /= count;
|
||||
}
|
||||
}
|
||||
|
||||
sample.u = u;
|
||||
sample.d = d;
|
||||
|
||||
sample.sampleID = sampleIndex[index].sampleID;
|
||||
sample.time = sampleIndex[index].time;
|
||||
sample.empty = false;
|
||||
if(count == 0)
|
||||
sample.empty = true;
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
81
src/stats/StatsNetwork.h
Normal file
81
src/stats/StatsNetwork.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _STATSNETWORK_H
|
||||
#define _STATSNETWORK_H
|
||||
|
||||
class network_info
|
||||
{
|
||||
public:
|
||||
bool active;
|
||||
unsigned int id;
|
||||
unsigned long long last_up;
|
||||
unsigned long long last_down;
|
||||
|
||||
std::vector<std::string> addresses;
|
||||
std::string device;
|
||||
double last_update;
|
||||
|
||||
std::deque<net_data> samples[8];
|
||||
};
|
||||
|
||||
class StatsNetwork : public StatsBase
|
||||
{
|
||||
public:
|
||||
void update(long long sampleID);
|
||||
void init();
|
||||
std::vector<network_info> _items;
|
||||
void createInterface(std::string key);
|
||||
void processInterface(std::string key, long long sampleID, unsigned long long upload, unsigned long long download);
|
||||
void updateAddresses();
|
||||
void prepareUpdate();
|
||||
#ifdef HAVE_LIBKSTAT
|
||||
kstat_ctl_t *ksh;
|
||||
std::vector<std::string> interfaces();
|
||||
#endif
|
||||
|
||||
void _init();
|
||||
#ifdef USE_SQLITE
|
||||
void updateHistory();
|
||||
net_data historyItemAtIndex(int index, network_info item);
|
||||
void loadPreviousSamples();
|
||||
void loadPreviousSamplesAtIndex(int index);
|
||||
#endif
|
||||
|
||||
std::deque<sample_data> samples[8];
|
||||
|
||||
#ifdef USE_NET_SYSCTL
|
||||
int get_ifcount();
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
575
src/stats/StatsProcesses.cpp
Normal file
575
src/stats/StatsProcesses.cpp
Normal file
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsProcesses.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#ifdef USE_PROCESSES_AIX
|
||||
|
||||
void StatsProcesses::init()
|
||||
{
|
||||
//aixEntitlement = 0.12;
|
||||
}
|
||||
|
||||
void StatsProcesses::update(long long sampleID, double totalTicks)
|
||||
{
|
||||
int treesize;
|
||||
pid_t firstproc = 0;
|
||||
if ((treesize = getprocs64(NULL, 0, NULL, 0, &firstproc, PID_MAX)) < 0) {
|
||||
return;
|
||||
}
|
||||
struct procentry64 *procs = (struct procentry64 *)malloc(treesize * sizeof (struct procentry64));;
|
||||
|
||||
firstproc = 0;
|
||||
if ((treesize = getprocs64(procs, sizeof(struct procentry64), NULL, 0, &firstproc, treesize)) < 0) {
|
||||
free(procs);
|
||||
return;
|
||||
}
|
||||
|
||||
threadCount = 0;
|
||||
|
||||
double currentTime = get_current_time();
|
||||
|
||||
for (int i = 0; i < treesize; i++)
|
||||
{
|
||||
pid_t pid = procs[i].pi_pid;
|
||||
if(pid == 0)
|
||||
continue;
|
||||
|
||||
processProcess(pid, sampleID);
|
||||
|
||||
for (vector<process_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).pid == pid)
|
||||
{
|
||||
struct psinfo psinfo;
|
||||
char buffer [BUFSIZ];
|
||||
|
||||
sprintf (buffer, "/proc/%d/psinfo", (int) pid);
|
||||
|
||||
int fd = open(buffer, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ssize_t len = pread(fd, &psinfo, sizeof (struct psinfo), 0);
|
||||
if (len != sizeof (struct psinfo))
|
||||
{
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
(*cur).exists = true;
|
||||
if((*cur).is_new == true)
|
||||
{
|
||||
sprintf((*cur).name, psinfo.pr_fname);
|
||||
(*cur).is_new = false;
|
||||
}
|
||||
|
||||
double t = (double)(procs[i].pi_ru.ru_utime.tv_sec + procs[i].pi_ru.ru_stime.tv_sec);
|
||||
t += (double)(procs[i].pi_ru.ru_utime.tv_usec + procs[i].pi_ru.ru_stime.tv_usec) / NS_PER_SEC;
|
||||
|
||||
if((*cur).cpuTime == 0)
|
||||
{
|
||||
(*cur).cpuTime = t;
|
||||
(*cur).lastClockTime = currentTime;
|
||||
}
|
||||
|
||||
double timediff = currentTime - (*cur).lastClockTime;
|
||||
|
||||
(*cur).cpu = ((t - (*cur).cpuTime) / timediff * 10000) / (aixEntitlement * 100);
|
||||
(*cur).threads = procs[i].pi_thcount;
|
||||
(*cur).memory = (uint64_t)(procs[i].pi_drss + procs[i].pi_trss) * (uint64_t)getpagesize();
|
||||
threadCount += procs[i].pi_thcount;
|
||||
|
||||
(*cur).cpuTime = t;
|
||||
(*cur).lastClockTime = currentTime;
|
||||
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(procs);
|
||||
}
|
||||
|
||||
#elif defined(USE_PROCESSES_PSINFO)
|
||||
|
||||
void StatsProcesses::init()
|
||||
{
|
||||
}
|
||||
|
||||
void StatsProcesses::update(long long sampleID, double totalTicks)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
if (!(dir = opendir("/proc")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while ((entry = readdir(dir)))
|
||||
{
|
||||
int pid = 0;
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
closedir(dir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sscanf(entry->d_name, "%d", &pid) > 0)
|
||||
{
|
||||
processProcess(pid, sampleID);
|
||||
|
||||
for (vector<process_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).pid == pid)
|
||||
{
|
||||
struct psinfo psinfo;
|
||||
char buffer [BUFSIZ];
|
||||
|
||||
sprintf (buffer, "/proc/%d/psinfo", (int) pid);
|
||||
|
||||
int fd = open(buffer, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ssize_t len = pread(fd, &psinfo, sizeof (struct psinfo), 0);
|
||||
if (len != sizeof (struct psinfo))
|
||||
{
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
(*cur).exists = true;
|
||||
if((*cur).is_new == true)
|
||||
{
|
||||
sprintf((*cur).name, psinfo.pr_fname);
|
||||
(*cur).is_new = false;
|
||||
}
|
||||
|
||||
(*cur).cpu = (double)(psinfo.pr_pctcpu * 100.0f) / 0x8000;
|
||||
(*cur).memory = psinfo.pr_rssize * 1024;
|
||||
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
#elif defined(USE_PROCESSES_KVM)
|
||||
|
||||
void StatsProcesses::init()
|
||||
{
|
||||
|
||||
}
|
||||
void StatsProcesses::update(long long sampleID, double totalTicks)
|
||||
{
|
||||
#if defined(PROCESSES_KVM_NETBSD)
|
||||
struct kinfo_proc2 *p;
|
||||
#else
|
||||
struct kinfo_proc *p;
|
||||
#endif
|
||||
int n_processes;
|
||||
int i;
|
||||
kvm_t *kd;
|
||||
|
||||
if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(PROCESSES_KVM_NETBSD)
|
||||
p = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(kinfo_proc2), &n_processes);
|
||||
#elif defined(PROCESSES_KVM_OPENBSD)
|
||||
p = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(kinfo_proc), &n_processes);
|
||||
#elif defined(PROCESSES_KVM_DRAGONFLY)
|
||||
p = kvm_getprocs(kd, KERN_PROC_ALL, sizeof(kinfo_proc), &n_processes);
|
||||
#else
|
||||
p = kvm_getprocs(kd, KERN_PROC_ALL, 0, &n_processes);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < n_processes; i++) {
|
||||
#if defined(PROCESSES_KVM_DRAGONFLY)
|
||||
if (!((p[i].kp_flags & P_SYSTEM)) && p[i].kp_comm != NULL) {
|
||||
#elif defined(PROCESSES_KVM_OPENBSD) || defined(PROCESSES_KVM_NETBSD)
|
||||
if (!((p[i].p_flag & P_SYSTEM)) && p[i].p_comm != NULL) {
|
||||
#else
|
||||
if (!((p[i].ki_flag & P_SYSTEM)) && p[i].ki_comm != NULL) {
|
||||
#endif
|
||||
|
||||
#if defined(PROCESSES_KVM_DRAGONFLY)
|
||||
int pid = p[i].kp_pid;
|
||||
struct kinfo_lwp lwp = p[i].kp_lwp;
|
||||
#elif defined(PROCESSES_KVM_OPENBSD) || defined(PROCESSES_KVM_NETBSD)
|
||||
int pid = p[i].p_pid;
|
||||
#else
|
||||
int pid = p[i].ki_pid;
|
||||
#endif
|
||||
processProcess(pid, sampleID);
|
||||
|
||||
for (vector<process_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).pid == pid)
|
||||
{
|
||||
(*cur).exists = true;
|
||||
if((*cur).is_new == true)
|
||||
{
|
||||
#if defined(PROCESSES_KVM_DRAGONFLY)
|
||||
sprintf((*cur).name, "%s", p[i].kp_comm);
|
||||
#elif defined(PROCESSES_KVM_OPENBSD) || defined(PROCESSES_KVM_NETBSD)
|
||||
sprintf((*cur).name, "%s", p[i].p_comm);
|
||||
#else
|
||||
sprintf((*cur).name, "%s", p[i].ki_comm);
|
||||
#endif
|
||||
(*cur).is_new = false;
|
||||
}
|
||||
|
||||
#if defined(PROCESSES_KVM_DRAGONFLY)
|
||||
(*cur).memory = (p[i].kp_vm_rssize * getpagesize());
|
||||
(*cur).cpu = (double)(100.0 * lwp.kl_pctcpu / FSCALE);
|
||||
#elif defined(PROCESSES_KVM_OPENBSD) || defined(PROCESSES_KVM_NETBSD)
|
||||
(*cur).memory = (p[i].p_vm_rssize * getpagesize());
|
||||
(*cur).cpu = (double)(100.0 * p[i].p_pctcpu / FSCALE);
|
||||
#else
|
||||
(*cur).memory = (p[i].ki_rssize * getpagesize());
|
||||
(*cur).cpu = (double)(100.0 * p[i].ki_pctcpu / FSCALE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kvm_close(kd);
|
||||
}
|
||||
|
||||
#elif defined(USE_PROCESSES_PROCFS)
|
||||
|
||||
void StatsProcesses::init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
vector<string> StatsProcesses::componentsFromString(string input, char seperator)
|
||||
{
|
||||
vector<string> components;
|
||||
stringstream ss(input);
|
||||
string tok;
|
||||
|
||||
while(getline(ss, tok, seperator))
|
||||
{
|
||||
components.push_back(tok);
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
string StatsProcesses::nameFromCmd(int pid, string name)
|
||||
{
|
||||
stringstream tmp;
|
||||
tmp << "/proc/" << pid << "/cmdline";
|
||||
|
||||
int fd = open(tmp.str().c_str(), O_RDONLY);
|
||||
if (fd > 0) {
|
||||
char buffer[1024];
|
||||
ssize_t len;
|
||||
if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
|
||||
buffer[len] = '\0';
|
||||
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (buffer[i] == '\0')
|
||||
buffer[i] = ' ';
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if(len == 0)
|
||||
return name;
|
||||
|
||||
string cmd = string(buffer);
|
||||
|
||||
vector<string> components = componentsFromString(cmd, ' ');
|
||||
if(components.size() > 0)
|
||||
{
|
||||
for (vector<string>::iterator cur = components.begin(); cur != components.end(); ++cur)
|
||||
{
|
||||
if((*cur).find(name) != std::string::npos)
|
||||
{
|
||||
vector<string> componentsn = componentsFromString((*cur), '/');
|
||||
if(componentsn.size() > 0)
|
||||
return componentsn.back();
|
||||
return (*cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
string StatsProcesses::nameFromStatus(int pid)
|
||||
{
|
||||
|
||||
stringstream tmp;
|
||||
tmp << "/proc/" << pid << "/status";
|
||||
|
||||
FILE * fp = NULL;
|
||||
|
||||
if ((fp = fopen(tmp.str().c_str(), "r")))
|
||||
{
|
||||
char buf[32];
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL)
|
||||
{
|
||||
if(sscanf(buf, "Name: %s", buf) > 0)
|
||||
{
|
||||
fclose(fp);
|
||||
return string(buf);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void StatsProcesses::update(long long sampleID, double totalTicks)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
if (!(dir = opendir("/proc")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while ((entry = readdir(dir)))
|
||||
{
|
||||
int pid = 0;
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (sscanf(entry->d_name, "%d", &pid) > 0)
|
||||
{
|
||||
processProcess(pid, sampleID);
|
||||
|
||||
for (vector<process_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).pid == pid)
|
||||
{
|
||||
(*cur).exists = true;
|
||||
|
||||
if((*cur).is_new == true)
|
||||
{
|
||||
string name = nameFromStatus(pid);
|
||||
if(name.length() == 15)
|
||||
{
|
||||
name = nameFromCmd(pid, name);
|
||||
}
|
||||
sprintf((*cur).name, "%s", name.c_str());
|
||||
(*cur).is_new = false;
|
||||
}
|
||||
|
||||
{
|
||||
stringstream tmp;
|
||||
tmp << "/proc/" << pid << "/stat";
|
||||
|
||||
FILE * fp = NULL;
|
||||
|
||||
if ((fp = fopen(tmp.str().c_str(), "r")))
|
||||
{
|
||||
unsigned long userTime = 0;
|
||||
unsigned long systemTime = 0;
|
||||
unsigned long rss = 0;
|
||||
long threads = 0;
|
||||
char name[255];
|
||||
|
||||
if(fscanf(fp, "%*d %s %*c %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu %*s %*s %*s %*s %ld %*s %*s %*s %lu", name, &userTime, &systemTime, &threads, &rss) > 0)
|
||||
{
|
||||
if((*cur).cpuTime == 0)
|
||||
{
|
||||
(*cur).cpuTime = (double)(userTime + systemTime);
|
||||
(*cur).lastClockTime = get_current_time();
|
||||
}
|
||||
|
||||
double cpuTime = (double)(userTime + (double)systemTime) - (*cur).cpuTime;
|
||||
double clockTimeDifference = get_current_time() - (*cur).lastClockTime;
|
||||
|
||||
if(clockTimeDifference > 0)
|
||||
{
|
||||
double procs = (double)sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if(procs < 1)
|
||||
procs = 1;
|
||||
(*cur).cpu = (((cpuTime / (double)sysconf(_SC_CLK_TCK)) / clockTimeDifference) * 100) / procs;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*cur).cpu = 0;
|
||||
}
|
||||
|
||||
threadCount += threads;
|
||||
(*cur).threads = threads;
|
||||
(*cur).memory = rss * getpagesize();
|
||||
(*cur).cpuTime = (double)(userTime + systemTime);
|
||||
(*cur).lastClockTime = get_current_time();
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
// /proc/pid/io requires root access which we usually dont run with
|
||||
/*
|
||||
{
|
||||
stringstream tmp;
|
||||
tmp << "/proc/" << pid << "/io";
|
||||
|
||||
FILE * fp = NULL;
|
||||
|
||||
if ((fp = fopen(tmp.str().c_str(), "r")))
|
||||
{
|
||||
char buf[1024];
|
||||
unsigned long long totalRead = 0;
|
||||
unsigned long long totalWrite = 0;
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
{
|
||||
sscanf(buf, "read_bytes: %llu", &totalRead);
|
||||
sscanf(buf, "write_bytes: %llu", &totalWrite);
|
||||
}
|
||||
|
||||
if((*cur).io_read_total == 0)
|
||||
{
|
||||
(*cur).io_read_total = totalRead;
|
||||
}
|
||||
|
||||
if((*cur).io_write_total == 0)
|
||||
{
|
||||
(*cur).io_write_total = totalWrite;
|
||||
}
|
||||
|
||||
(*cur).io_read = totalRead - (*cur).io_read_total;
|
||||
(*cur).io_write = totalWrite - (*cur).io_write_total;
|
||||
|
||||
(*cur).io_read_total = totalRead;
|
||||
(*cur).io_write_total = totalWrite;
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void StatsProcesses::init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void StatsProcesses::update(long long sampleID, double totalTicks)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void StatsProcesses::createProcess(int pid)
|
||||
{
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<process_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
process_info process = *cur;
|
||||
if(process.pid == pid){
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process_info process;
|
||||
process.cpu = 0;
|
||||
process.memory = 0;
|
||||
process.is_new = true;
|
||||
process.pid = pid;
|
||||
process.cpuTime = 0;
|
||||
process.io_read = 0;
|
||||
process.io_write = 0;
|
||||
process.exists = true;
|
||||
_items.insert(_items.begin(), process);
|
||||
}
|
||||
|
||||
void StatsProcesses::processProcess(int pid, long long sampleID)
|
||||
{
|
||||
processCount++;
|
||||
createProcess(pid);
|
||||
}
|
||||
|
||||
void StatsProcesses::prepareUpdate()
|
||||
{
|
||||
threadCount = 0;
|
||||
processCount = 0;
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<process_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
(*cur).exists = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsProcesses::finishUpdate()
|
||||
{
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<process_info>::iterator i = _items.begin(); i != _items.end(); ) {
|
||||
if (i->exists == false) {
|
||||
i = _items.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
src/stats/StatsProcesses.h
Normal file
81
src/stats/StatsProcesses.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _AIXVERSION_610
|
||||
int getprocs64 (struct procentry64 *procsinfo, int sizproc, struct fdsinfo64 *fdsinfo, int sizfd, pid_t *index, int count);
|
||||
#endif
|
||||
|
||||
#ifndef _STATSPROCESSES_H
|
||||
#define _STATSPROCESSES_H
|
||||
|
||||
class process_info
|
||||
{
|
||||
public:
|
||||
bool exists;
|
||||
bool is_new;
|
||||
double cpu;
|
||||
unsigned long long io_read;
|
||||
unsigned long long io_write;
|
||||
unsigned long long io_read_total;
|
||||
unsigned long long io_write_total;
|
||||
unsigned long memory;
|
||||
double cpuTime;
|
||||
double lastClockTime;
|
||||
long threads;
|
||||
int pid;
|
||||
long long sampleID;
|
||||
char name[128];
|
||||
};
|
||||
class StatsProcesses
|
||||
{
|
||||
public:
|
||||
void update(long long sampleID, double ticks);
|
||||
void prepareUpdate();
|
||||
void init();
|
||||
std::vector<process_info> _items;
|
||||
void createProcess(int pid);
|
||||
void processProcess(int pid, long long sampleID);
|
||||
|
||||
long threadCount;
|
||||
long processCount;
|
||||
double aixEntitlement;
|
||||
|
||||
#ifdef USE_PROCESSES_PROCFS
|
||||
std::string nameFromCmd(int pid, std::string name);
|
||||
std::string nameFromStatus(int pid);
|
||||
std::vector<std::string> componentsFromString(std::string input, char seperator);
|
||||
#endif
|
||||
|
||||
void finishUpdate();
|
||||
};
|
||||
#endif
|
||||
748
src/stats/StatsSensors.cpp
Normal file
748
src/stats/StatsSensors.cpp
Normal file
@@ -0,0 +1,748 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsSensors.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef HAVE_LIBSENSORS
|
||||
#if SENSORS_API_VERSION >= 0x0400 /* libsensor 4 */
|
||||
|
||||
void StatsSensors::update_libsensors(long long sampleID)
|
||||
{
|
||||
int a, b, c, num;
|
||||
const sensors_chip_name * chip;
|
||||
const sensors_feature * features;
|
||||
const sensors_subfeature * subfeatures;
|
||||
|
||||
a = num = 0;
|
||||
|
||||
while ((chip = sensors_get_detected_chips(NULL, &a)))
|
||||
{
|
||||
b = 0;
|
||||
while ((features = sensors_get_features(chip, &b)))
|
||||
{
|
||||
c = 0;
|
||||
while ((subfeatures = sensors_get_all_subfeatures(chip, features, &c)))
|
||||
{
|
||||
if (subfeatures->type == SENSORS_SUBFEATURE_FAN_INPUT ||
|
||||
subfeatures->type == SENSORS_SUBFEATURE_CURR_INPUT ||
|
||||
subfeatures->type == SENSORS_SUBFEATURE_POWER_INPUT ||
|
||||
subfeatures->type == SENSORS_SUBFEATURE_IN_INPUT ||
|
||||
subfeatures->type == SENSORS_SUBFEATURE_VID ||
|
||||
subfeatures->type == SENSORS_SUBFEATURE_TEMP_INPUT)
|
||||
{
|
||||
|
||||
//if (chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
|
||||
// printf ("%s-isa-%04x", chip->prefix, chip->addr);
|
||||
//else
|
||||
// printf ("%s\n", chip->prefix);
|
||||
//fflush(stdout);
|
||||
|
||||
|
||||
char *label = sensors_get_label(chip, features);
|
||||
stringstream key;
|
||||
key << label << "_" << sensorType(subfeatures->type);
|
||||
|
||||
if(createSensor(key.str()) == 1)
|
||||
{
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).key == key.str())
|
||||
{
|
||||
(*cur).method = 1;
|
||||
(*cur).chip = chip->addr;
|
||||
(*cur).sensor = features->number;
|
||||
(*cur).label = string(label);
|
||||
(*cur).kind = sensorType(subfeatures->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double value;
|
||||
sensors_get_value(chip, subfeatures->number, &value);
|
||||
|
||||
processSensor(key.str(), sampleID, value);
|
||||
|
||||
free(label);
|
||||
|
||||
num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int StatsSensors::sensorType(int type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case SENSORS_SUBFEATURE_FAN_INPUT:
|
||||
return 2;
|
||||
break;
|
||||
case SENSORS_SUBFEATURE_CURR_INPUT:
|
||||
return 4;
|
||||
break;
|
||||
case SENSORS_SUBFEATURE_POWER_INPUT:
|
||||
return 5;
|
||||
break;
|
||||
case SENSORS_SUBFEATURE_IN_INPUT:
|
||||
case SENSORS_SUBFEATURE_VID:
|
||||
return 3;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#elif SENSORS_API_VERSION < 0x0400 /* libsensor 3 and earlier */
|
||||
|
||||
void StatsSensors::update_libsensors(long long sampleID)
|
||||
{
|
||||
int a, b, c, num;
|
||||
const sensors_chip_name * chip;
|
||||
const sensors_feature_data * features;
|
||||
|
||||
a = num = 0;
|
||||
|
||||
while ((chip = sensors_get_detected_chips(&a)))
|
||||
{
|
||||
b = c = 0;
|
||||
|
||||
while ((features = sensors_get_all_features(*chip, &b, &c)))
|
||||
{
|
||||
if ((!memcmp(features->name, "fan", 3) && features->name[4]=='\0') || (!memcmp(features->name, "temp", 3) && features->name[5]=='\0')){
|
||||
stringstream key;
|
||||
key << num;
|
||||
|
||||
if(createSensor(key.str()) == 1)
|
||||
{
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).key == key.str())
|
||||
{
|
||||
(*cur).method = 1;
|
||||
(*cur).chip = chip->addr;
|
||||
(*cur).sensor = features->number;
|
||||
if(!memcmp(features->name, "fan", 3) && features->name[4]=='\0')
|
||||
(*cur).kind = 2;
|
||||
else
|
||||
(*cur).kind = 0;
|
||||
|
||||
char *label;
|
||||
sensors_get_label(*chip, (*cur).sensor, &label);
|
||||
(*cur).label = string(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double value;
|
||||
sensors_get_feature(*chip, features->number, &value);
|
||||
|
||||
processSensor(key.str(), sampleID, value);
|
||||
|
||||
num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBSENSORS
|
||||
void StatsSensors::init_libsensors()
|
||||
{
|
||||
#if SENSORS_API_VERSION >= 0x0400 /* libsensor 4 */
|
||||
sensors_init(NULL);
|
||||
libsensors_ready = true;
|
||||
#else
|
||||
FILE *fp;
|
||||
|
||||
if ((fp = fopen("/etc/sensors.conf", "r")) == NULL)
|
||||
return;
|
||||
|
||||
if (sensors_init(fp) != 0) {
|
||||
libsensors_ready = false;
|
||||
fclose(fp);
|
||||
}
|
||||
libsensors_ready = true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void StatsSensors::init_dev_cpu()
|
||||
{
|
||||
#if defined(HAVE_SYSCTLBYNAME)
|
||||
int x;
|
||||
for(x=0;x<32;x++)
|
||||
{
|
||||
stringstream key;
|
||||
key << "dev.cpu." << x << ".temperature";
|
||||
|
||||
size_t len;
|
||||
long buf;
|
||||
len = sizeof(buf);
|
||||
|
||||
if (sysctlbyname(key.str().c_str(), &buf, &len, NULL, 0) >= 0)
|
||||
{
|
||||
createSensor(key.str());
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).key == key.str())
|
||||
{
|
||||
stringstream label;
|
||||
label << "CPU " << x;
|
||||
(*cur).label = label.str();
|
||||
(*cur).method = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void StatsSensors::init_acpi_thermal()
|
||||
{
|
||||
#if defined(HAVE_SYSCTLBYNAME)
|
||||
int x;
|
||||
for(x=0;x<32;x++)
|
||||
{
|
||||
stringstream key;
|
||||
key << "hw.acpi.thermal.tz" << x << ".temperature";
|
||||
|
||||
size_t len;
|
||||
long buf;
|
||||
len = sizeof(buf);
|
||||
|
||||
if (sysctlbyname(key.str().c_str(), &buf, &len, NULL, 0) >= 0)
|
||||
{
|
||||
createSensor(key.str());
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).key == key.str())
|
||||
{
|
||||
stringstream label;
|
||||
label << "Thermal Zone " << x;
|
||||
(*cur).label = label.str();
|
||||
(*cur).method = 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void StatsSensors::init_acpi_freq()
|
||||
{
|
||||
#if defined(HAVE_SYSCTLBYNAME)
|
||||
int x;
|
||||
for(x=0;x<32;x++)
|
||||
{
|
||||
stringstream key;
|
||||
key << "dev.cpu." << x << ".freq";
|
||||
|
||||
size_t len;
|
||||
long buf;
|
||||
len = sizeof(buf);
|
||||
|
||||
if (sysctlbyname(key.str().c_str(), &buf, &len, NULL, 0) >= 0)
|
||||
{
|
||||
createSensor(key.str());
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).key == key.str())
|
||||
{
|
||||
stringstream label;
|
||||
label << "CPU " << x << " Frequency";
|
||||
(*cur).label = label.str();
|
||||
(*cur).method = 5;
|
||||
(*cur).kind = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void StatsSensors::init_qnap()
|
||||
{
|
||||
int temp;
|
||||
|
||||
char *systempfile=(char*)"/proc/tsinfo/systemp";
|
||||
FILE *systempfp;
|
||||
if ((systempfp=fopen(systempfile, "r"))==NULL) {
|
||||
return;
|
||||
}
|
||||
if (fscanf(systempfp, "%d", &temp)!=1) {
|
||||
fclose(systempfp);
|
||||
return;
|
||||
}
|
||||
fclose(systempfp);
|
||||
|
||||
createSensor("qnap");
|
||||
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
(*cur).label = "Temperature";
|
||||
(*cur).method = 3;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsSensors::update_qnap(long long sampleID)
|
||||
{
|
||||
char *systempfile=(char*)"/proc/tsinfo/systemp";
|
||||
FILE *systempfp;
|
||||
int systemp;
|
||||
if ((systempfp=fopen(systempfile, "r"))==NULL)
|
||||
return;
|
||||
else {
|
||||
fseek(systempfp, 0l, 0);
|
||||
if (fscanf(systempfp, "%d", &systemp) == 1){
|
||||
processSensor("qnap", sampleID, systemp);
|
||||
}
|
||||
fclose(systempfp);
|
||||
}
|
||||
}
|
||||
|
||||
void StatsSensors::update_dev_cpu(long long sampleID)
|
||||
{
|
||||
#if defined(HAVE_SYSCTLBYNAME)
|
||||
if(_items.size() == 0)
|
||||
return;
|
||||
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).method != 2)
|
||||
continue;
|
||||
|
||||
size_t len;
|
||||
int buf;
|
||||
len = sizeof(buf);
|
||||
|
||||
if (sysctlbyname((*cur).key.c_str(), &buf, &len, NULL, 0) >= 0)
|
||||
{
|
||||
double value = (buf - 2732) / 10.0f;
|
||||
processSensor((*cur).key, sampleID, value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void StatsSensors::update_acpi_thermal(long long sampleID)
|
||||
{
|
||||
#if defined(HAVE_SYSCTLBYNAME)
|
||||
if(_items.size() == 0)
|
||||
return;
|
||||
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).method != 4)
|
||||
continue;
|
||||
|
||||
size_t len;
|
||||
int buf;
|
||||
len = sizeof(buf);
|
||||
|
||||
if (sysctlbyname((*cur).key.c_str(), &buf, &len, NULL, 0) >= 0)
|
||||
{
|
||||
double value = (buf - 2732) / 10.0f;
|
||||
processSensor((*cur).key, sampleID, value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void StatsSensors::update_acpi_freq(long long sampleID)
|
||||
{
|
||||
#if defined(HAVE_SYSCTLBYNAME)
|
||||
if(_items.size() == 0)
|
||||
return;
|
||||
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).method != 5)
|
||||
continue;
|
||||
|
||||
size_t len;
|
||||
int buf;
|
||||
len = sizeof(buf);
|
||||
|
||||
if (sysctlbyname((*cur).key.c_str(), &buf, &len, NULL, 0) >= 0)
|
||||
{
|
||||
processSensor((*cur).key, sampleID, (double)buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
Methods
|
||||
|
||||
1 = libsensors
|
||||
2 = dev.cpu.(x).temperature
|
||||
3 = qnap
|
||||
4 = hw.acpi.thermal.tz(x).temperature"
|
||||
5 = dev.cpu.(x).freq
|
||||
|
||||
*/
|
||||
|
||||
void StatsSensors::init()
|
||||
{
|
||||
_init();
|
||||
|
||||
#ifdef HAVE_LIBSENSORS
|
||||
init_libsensors();
|
||||
#endif
|
||||
|
||||
init_dev_cpu();
|
||||
init_qnap();
|
||||
init_acpi_thermal();
|
||||
init_acpi_freq();
|
||||
}
|
||||
|
||||
void StatsSensors::update(long long sampleID)
|
||||
{
|
||||
#ifdef HAVE_LIBSENSORS
|
||||
if(libsensors_ready == true)
|
||||
update_libsensors(sampleID);
|
||||
#endif
|
||||
|
||||
update_qnap(sampleID);
|
||||
update_dev_cpu(sampleID);
|
||||
update_acpi_thermal(sampleID);
|
||||
update_acpi_freq(sampleID);
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).recordedValueChanged)
|
||||
{
|
||||
string sql = "UPDATE sensor_limits SET low = ?, high = ? where uuid = ?";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(dbItem._statement, 1, (*cur).lowestValue);
|
||||
sqlite3_bind_double(dbItem._statement, 2, (*cur).highestValue);
|
||||
sqlite3_bind_text(dbItem._statement, 3, (*cur).key.c_str(), -1, SQLITE_STATIC);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int StatsSensors::createSensor(string key)
|
||||
{
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
sensor_info sensor = *cur;
|
||||
if(sensor.key == key){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sensor_info item;
|
||||
item.key = key;
|
||||
item.lowestValue = -1;
|
||||
item.highestValue = 0;
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
string sql = "select * from sensor_limits where uuid = ?";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_text(query._statement, 1, key.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
bool hasRow = false;
|
||||
while(query.next())
|
||||
{
|
||||
hasRow = true;
|
||||
item.lowestValue = query.doubleForColumn("low");
|
||||
item.highestValue = query.doubleForColumn("high");
|
||||
}
|
||||
|
||||
if(!hasRow){
|
||||
string sql = "insert into sensor_limits (uuid) values(?)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_text(dbItem._statement, 1, key.c_str(), -1, SQLITE_STATIC);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
double sampleID = 0;
|
||||
if(samples[x].size() > 0)
|
||||
sampleID = samples[x][0].sampleID;
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample AND uuid = ? order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
sqlite3_bind_text(query._statement, 2, key.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
sensor_data sample;
|
||||
sample.value = query.doubleForColumn("value");
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
item.samples[x].push_front(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_items.insert(_items.begin(), item);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void StatsSensors::processSensor(string key, long long sampleID, double value)
|
||||
{
|
||||
if(ready == 0)
|
||||
return;
|
||||
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
if((*cur).key == key){
|
||||
sensor_data data;
|
||||
data.value = value;
|
||||
data.sampleID = sampleIndex[0].sampleID;
|
||||
data.time = sampleIndex[0].time;
|
||||
|
||||
(*cur).samples[0].push_front(data);
|
||||
if ((*cur).samples[0].size() > HISTORY_SIZE)
|
||||
(*cur).samples[0].pop_back();
|
||||
|
||||
bool changed = false;
|
||||
(*cur).recordedValueChanged = false;
|
||||
|
||||
if(value > (*cur).highestValue){
|
||||
(*cur).highestValue = value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if(value < (*cur).lowestValue || (*cur).lowestValue == -1){
|
||||
(*cur).lowestValue = value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
(*cur).recordedValueChanged = changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsSensors::_init()
|
||||
{
|
||||
initShared();
|
||||
ready = 0;
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
if(historyEnabled == true)
|
||||
{
|
||||
databaseType = 1;
|
||||
databasePrefix = "sensors_";
|
||||
|
||||
int x;
|
||||
for(x=1;x<8;x++)
|
||||
{
|
||||
string table = databasePrefix + tableAtIndex(x) + "_id";
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double PRIMARY KEY NOT NULL, time double NOT NULL DEFAULT 0, empty integer NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
|
||||
table = databasePrefix + tableAtIndex(x);
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table " + table + " (sample double NOT NULL, time double NOT NULL DEFAULT 0, uuid varchar(255) NOT NULL, value double NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
string table = "sensor_limits";
|
||||
if(!_database.tableExists(table))
|
||||
{
|
||||
string sql = "create table sensor_limits (uuid varchar(255) NOT NULL, low double NOT NULL DEFAULT 0, high double NOT NULL DEFAULT 0)";
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
dbItem.executeUpdate();
|
||||
}
|
||||
loadPreviousSamples();
|
||||
fillGaps();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void StatsSensors::loadPreviousSamples()
|
||||
{
|
||||
loadPreviousSamplesAtIndex(1);
|
||||
loadPreviousSamplesAtIndex(2);
|
||||
loadPreviousSamplesAtIndex(3);
|
||||
loadPreviousSamplesAtIndex(4);
|
||||
loadPreviousSamplesAtIndex(5);
|
||||
loadPreviousSamplesAtIndex(6);
|
||||
loadPreviousSamplesAtIndex(7);
|
||||
}
|
||||
|
||||
void StatsSensors::loadPreviousSamplesAtIndex(int index)
|
||||
{
|
||||
|
||||
string table = databasePrefix + tableAtIndex(index) + "_id";
|
||||
double sampleID = sampleIdForTable(table);
|
||||
|
||||
string sql = "select * from " + table + " where sample >= @sample order by sample asc limit 602";
|
||||
DatabaseItem query = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(query._statement, 1, sampleID - 602);
|
||||
|
||||
while(query.next())
|
||||
{
|
||||
sample_data sample;
|
||||
sample.sampleID = (long long)query.doubleForColumn("sample");
|
||||
sample.time = query.doubleForColumn("time");
|
||||
samples[index].insert(samples[index].begin(), sample);
|
||||
}
|
||||
if(samples[index].size() > 0)
|
||||
{
|
||||
sampleIndex[index].sampleID = samples[index][0].sampleID;
|
||||
sampleIndex[index].time = samples[index][0].time;
|
||||
sampleIndex[index].nextTime = sampleIndex[index].time + sampleIndex[index].interval;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsSensors::updateHistory()
|
||||
{
|
||||
int x;
|
||||
for (x = 1; x < 8; x++)
|
||||
{
|
||||
if(sampleIndex[0].time >= sampleIndex[x].nextTime)
|
||||
{
|
||||
double now = get_current_time();
|
||||
double earlistTime = now - (HISTORY_SIZE * sampleIndex[x].interval);
|
||||
while(sampleIndex[x].nextTime < now)
|
||||
{
|
||||
sampleIndex[x].sampleID = sampleIndex[x].sampleID + 1;
|
||||
sampleIndex[x].time = sampleIndex[x].nextTime;
|
||||
sampleIndex[x].nextTime = sampleIndex[x].nextTime + sampleIndex[x].interval;
|
||||
|
||||
if(sampleIndex[x].time < earlistTime)
|
||||
continue;
|
||||
|
||||
if(_items.size() > 0)
|
||||
{
|
||||
for (vector<sensor_info>::iterator cur = _items.begin(); cur != _items.end(); ++cur)
|
||||
{
|
||||
sensor_data sample = historyItemAtIndex(x, (*cur));
|
||||
|
||||
(*cur).samples[x].push_front(sample);
|
||||
if ((*cur).samples[x].size() > HISTORY_SIZE) (*cur).samples[x].pop_back();
|
||||
|
||||
if(sample.empty)
|
||||
continue;
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x);
|
||||
string sql = "insert into " + table + " (sample, time, value, uuid) values(?, ?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_double(dbItem._statement, 1, (double)sample.sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 2, sample.time);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sample.value);
|
||||
sqlite3_bind_text(dbItem._statement, 4, (*cur).key.c_str(), -1, SQLITE_STATIC);
|
||||
databaseQueue.push_back(dbItem);
|
||||
//dbItem.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
string table = databasePrefix + tableAtIndex(x) + "_id";
|
||||
string sql = "insert into " + table + " (empty, sample, time) values(?, ?, ?)";
|
||||
|
||||
DatabaseItem dbItem = _database.databaseItem(sql);
|
||||
sqlite3_bind_int(dbItem._statement, 1, 0);
|
||||
sqlite3_bind_double(dbItem._statement, 2, (double)sampleIndex[x].sampleID);
|
||||
sqlite3_bind_double(dbItem._statement, 3, sampleIndex[x].time);
|
||||
databaseQueue.push_back(dbItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sensor_data StatsSensors::historyItemAtIndex(int index, sensor_info item)
|
||||
{
|
||||
sensor_data sample;
|
||||
double value = 0;
|
||||
|
||||
std::deque<sensor_data> from = item.samples[sampleIndex[index].historyIndex];
|
||||
double minimumTime = sampleIndex[index].time - sampleIndex[index].interval;
|
||||
double maximumTime = sampleIndex[index].time;
|
||||
if(sampleIndex[index].historyIndex == 0)
|
||||
maximumTime += 0.99;
|
||||
|
||||
int count = 0;
|
||||
if(from.size() > 0)
|
||||
{
|
||||
for (deque<sensor_data>::iterator cursample = from.begin(); cursample != from.end(); ++cursample)
|
||||
{
|
||||
if ((*cursample).time > maximumTime){
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((*cursample).time < minimumTime)
|
||||
break;
|
||||
|
||||
value += (*cursample).value;
|
||||
count++;
|
||||
}
|
||||
if (count > 0 && value > 0)
|
||||
{
|
||||
value /= count;
|
||||
}
|
||||
}
|
||||
|
||||
sample.value = value;
|
||||
sample.sampleID = sampleIndex[index].sampleID;
|
||||
sample.time = sampleIndex[index].time;
|
||||
sample.empty = false;
|
||||
if(count == 0)
|
||||
sample.empty = true;
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
99
src/stats/StatsSensors.h
Normal file
99
src/stats/StatsSensors.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _STATSSENSORS_H
|
||||
#define _STATSSENSORS_H
|
||||
|
||||
class sensor_info
|
||||
{
|
||||
public:
|
||||
int id;
|
||||
std::string key;
|
||||
double last_update;
|
||||
double lowestValue;
|
||||
double highestValue;
|
||||
bool recordedValueChanged;
|
||||
|
||||
int kind;
|
||||
std::string label;
|
||||
unsigned int chip;
|
||||
unsigned int sensor;
|
||||
int method;
|
||||
|
||||
std::deque<sensor_data> samples[8];
|
||||
};
|
||||
|
||||
class StatsSensors : public StatsBase
|
||||
{
|
||||
public:
|
||||
void init();
|
||||
void _init();
|
||||
void init_dev_cpu();
|
||||
void init_qnap();
|
||||
void init_acpi_thermal();
|
||||
void init_acpi_freq();
|
||||
#ifdef HAVE_LIBSENSORS
|
||||
void init_libsensors();
|
||||
#endif
|
||||
|
||||
void update(long long sampleID);
|
||||
void update_qnap(long long sampleID);
|
||||
void update_dev_cpu(long long sampleID);
|
||||
void update_acpi_thermal(long long sampleID);
|
||||
void update_acpi_freq(long long sampleID);
|
||||
#ifdef HAVE_LIBSENSORS
|
||||
void update_libsensors(long long sampleID);
|
||||
#endif
|
||||
|
||||
std::vector<sensor_info> _items;
|
||||
int createSensor(std::string key);
|
||||
void processSensor(std::string key, long long sampleID, double value);
|
||||
|
||||
|
||||
#ifdef HAVE_LIBSENSORS
|
||||
bool libsensors_ready;
|
||||
#if SENSORS_API_VERSION >= 0x0400 /* libsensor 4 */
|
||||
int sensorType(int type);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void updateHistory();
|
||||
sensor_data historyItemAtIndex(int index, sensor_info item);
|
||||
void loadPreviousSamples();
|
||||
void loadPreviousSamplesAtIndex(int index);
|
||||
#endif
|
||||
|
||||
std::deque<sample_data> samples[8];
|
||||
};
|
||||
#endif
|
||||
141
src/stats/StatsUptime.cpp
Normal file
141
src/stats/StatsUptime.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatsUptime.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if defined(USE_UPTIME_HPUX)
|
||||
|
||||
long StatsUptime::getUptime()
|
||||
{
|
||||
struct pst_static pstat_static;
|
||||
if (pstat_getstatic(&pstat_static, sizeof(pstat_static), 1, 0) == -1) {
|
||||
return;
|
||||
}
|
||||
return time(NULL) - pstat_static.boot_time;
|
||||
}
|
||||
|
||||
#elif defined(HAVE_LIBKSTAT) && defined(USE_UPTIME_KSTAT)
|
||||
|
||||
long StatsUptime::getUptime()
|
||||
{
|
||||
kstat_t *ksp;
|
||||
kstat_named_t *kn;
|
||||
static time_t boottime;
|
||||
|
||||
if(0 == boottime)
|
||||
{
|
||||
kstat_chain_update(ksh);
|
||||
if(NULL == (ksp = kstat_lookup(ksh, (char*)"unix", -1, (char*)"system_misc"))) return -1;
|
||||
if(-1 == kstat_read(ksh, ksp, NULL)) return -1;
|
||||
if(NULL == (kn = (kstat_named_t *) kstat_data_lookup(ksp, (char*)"boot_time"))) return -1;
|
||||
boottime = (time_t) ksgetull(kn);
|
||||
}
|
||||
return time(NULL) - boottime;
|
||||
}
|
||||
|
||||
/*USE_UPTIME_KSTAT*/
|
||||
# elif defined(USE_UPTIME_PROCFS)
|
||||
|
||||
long StatsUptime::getUptime()
|
||||
{
|
||||
long uptime;
|
||||
FILE * fp = NULL;
|
||||
|
||||
if (!(fp = fopen("/proc/uptime", "r"))) return -1;
|
||||
|
||||
if(1 != fscanf(fp, "%ld", &uptime))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return uptime;
|
||||
}
|
||||
|
||||
#elif defined(USE_UPTIME_PERFSTAT)
|
||||
|
||||
long StatsUptime::getUptime()
|
||||
{
|
||||
perfstat_cpu_total_t cpustats;
|
||||
if (perfstat_cpu_total(NULL, &cpustats, sizeof cpustats, 1)!=1)
|
||||
return 0;
|
||||
|
||||
int hertz = sysconf(_SC_CLK_TCK);
|
||||
if (hertz <= 0)
|
||||
hertz = HZ;
|
||||
|
||||
return cpustats.lbolt / hertz;
|
||||
}/*USE_UPTIME_PERFSTAT*/
|
||||
|
||||
#elif defined(USE_UPTIME_GETTIME)
|
||||
|
||||
long StatsUptime::getUptime()
|
||||
{
|
||||
struct timespec tp;
|
||||
|
||||
tp.tv_sec = 0;
|
||||
tp.tv_nsec = 0;
|
||||
|
||||
clock_gettime(CLOCK_UPTIME, &tp);
|
||||
|
||||
return (long)(tp.tv_sec);
|
||||
}/*USE_UPTIME_GETTIME*/
|
||||
|
||||
#elif defined(USE_UPTIME_SYSCTL)
|
||||
|
||||
long StatsUptime::getUptime()
|
||||
{
|
||||
struct timeval tm;
|
||||
time_t now;
|
||||
int mib[2];
|
||||
size_t size;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_BOOTTIME;
|
||||
size = sizeof(tm);
|
||||
now = time(NULL);
|
||||
if(-1 != sysctl(mib, 2, &tm, &size, NULL, 0) && 0 != tm.tv_sec)
|
||||
{
|
||||
return (long)(now - tm.tv_sec);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
||||
long StatsUptime::getUptime()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
46
src/stats/StatsUptime.h
Normal file
46
src/stats/StatsUptime.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2016 Bjango Pty Ltd. All rights reserved.
|
||||
* Copyright 2010 William Tisäter. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the copyright holder may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL WILLIAM TISÄTER BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StatBase.h"
|
||||
|
||||
#ifndef _STATSUPTIME_H
|
||||
#define _STATSUPTIME_H
|
||||
|
||||
class StatsUptime
|
||||
{
|
||||
public:
|
||||
long getUptime();
|
||||
|
||||
#ifdef HAVE_LIBKSTAT
|
||||
kstat_ctl_t *ksh;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
Reference in New Issue
Block a user