mirror of
https://github.com/ossrs/srs.git
synced 2025-02-13 11:51:57 +00:00
move some utilities from kernel to app. 0.9.99
This commit is contained in:
parent
4b8d3fe05f
commit
2f89c8b2e8
8 changed files with 701 additions and 696 deletions
|
@ -128,7 +128,7 @@ int SrsForwarder::on_publish(SrsRequest* req, std::string forward_server)
|
||||||
source_ep.c_str(), dest_ep.c_str(), ret);
|
source_ep.c_str(), dest_ep.c_str(), ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
srs_trace("start forward %s to %s, stream: %s/%s",
|
srs_trace("start forward %s to %s, tcUrl=%s, stream=%s",
|
||||||
source_ep.c_str(), dest_ep.c_str(), tc_url.c_str(),
|
source_ep.c_str(), dest_ep.c_str(), tc_url.c_str(),
|
||||||
stream_name.c_str());
|
stream_name.c_str());
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ using namespace std;
|
||||||
#include <srs_app_json.hpp>
|
#include <srs_app_json.hpp>
|
||||||
#include <srs_app_config.hpp>
|
#include <srs_app_config.hpp>
|
||||||
#include <srs_kernel_utility.hpp>
|
#include <srs_kernel_utility.hpp>
|
||||||
|
#include <srs_app_utility.hpp>
|
||||||
|
|
||||||
SrsApiRoot::SrsApiRoot()
|
SrsApiRoot::SrsApiRoot()
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,6 +45,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#include <srs_app_ingest.hpp>
|
#include <srs_app_ingest.hpp>
|
||||||
#endif
|
#endif
|
||||||
#include <srs_app_source.hpp>
|
#include <srs_app_source.hpp>
|
||||||
|
#include <srs_app_utility.hpp>
|
||||||
|
|
||||||
#define SERVER_LISTEN_BACKLOG 512
|
#define SERVER_LISTEN_BACKLOG 512
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#include <srs_app_utility.hpp>
|
#include <srs_app_utility.hpp>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <srs_kernel_log.hpp>
|
#include <srs_kernel_log.hpp>
|
||||||
#include <srs_app_config.hpp>
|
#include <srs_app_config.hpp>
|
||||||
|
#include <srs_kernel_utility.hpp>
|
||||||
|
|
||||||
int srs_get_log_level(std::string level)
|
int srs_get_log_level(std::string level)
|
||||||
{
|
{
|
||||||
|
@ -42,3 +45,393 @@ int srs_get_log_level(std::string level)
|
||||||
return SrsLogLevel::Trace;
|
return SrsLogLevel::Trace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SrsRusage _srs_system_rusage;
|
||||||
|
|
||||||
|
SrsRusage::SrsRusage()
|
||||||
|
{
|
||||||
|
ok = false;
|
||||||
|
sample_time = 0;
|
||||||
|
memset(&r, 0, sizeof(rusage));
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRusage* srs_get_system_rusage()
|
||||||
|
{
|
||||||
|
return &_srs_system_rusage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void srs_update_system_rusage()
|
||||||
|
{
|
||||||
|
if (getrusage(RUSAGE_SELF, &_srs_system_rusage.r) < 0) {
|
||||||
|
srs_warn("getrusage failed, ignore");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_update_system_time_ms();
|
||||||
|
_srs_system_rusage.sample_time = srs_get_system_time_ms();
|
||||||
|
|
||||||
|
_srs_system_rusage.ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SrsProcSelfStat _srs_system_cpu_self_stat;
|
||||||
|
static SrsProcSystemStat _srs_system_cpu_system_stat;
|
||||||
|
|
||||||
|
SrsProcSelfStat::SrsProcSelfStat()
|
||||||
|
{
|
||||||
|
ok = false;
|
||||||
|
sample_time = 0;
|
||||||
|
percent = 0;
|
||||||
|
|
||||||
|
pid = 0;
|
||||||
|
memset(comm, 0, sizeof(comm));
|
||||||
|
state = 0;
|
||||||
|
ppid = 0;
|
||||||
|
pgrp = 0;
|
||||||
|
session = 0;
|
||||||
|
tty_nr = 0;
|
||||||
|
tpgid = 0;
|
||||||
|
flags = 0;
|
||||||
|
minflt = 0;
|
||||||
|
cminflt = 0;
|
||||||
|
majflt = 0;
|
||||||
|
cmajflt = 0;
|
||||||
|
utime = 0;
|
||||||
|
stime = 0;
|
||||||
|
cutime = 0;
|
||||||
|
cstime = 0;
|
||||||
|
priority = 0;
|
||||||
|
nice = 0;
|
||||||
|
num_threads = 0;
|
||||||
|
itrealvalue = 0;
|
||||||
|
starttime = 0;
|
||||||
|
vsize = 0;
|
||||||
|
rss = 0;
|
||||||
|
rsslim = 0;
|
||||||
|
startcode = 0;
|
||||||
|
endcode = 0;
|
||||||
|
startstack = 0;
|
||||||
|
kstkesp = 0;
|
||||||
|
kstkeip = 0;
|
||||||
|
signal = 0;
|
||||||
|
blocked = 0;
|
||||||
|
sigignore = 0;
|
||||||
|
sigcatch = 0;
|
||||||
|
wchan = 0;
|
||||||
|
nswap = 0;
|
||||||
|
cnswap = 0;
|
||||||
|
exit_signal = 0;
|
||||||
|
processor = 0;
|
||||||
|
rt_priority = 0;
|
||||||
|
policy = 0;
|
||||||
|
delayacct_blkio_ticks = 0;
|
||||||
|
guest_time = 0;
|
||||||
|
cguest_time = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsProcSystemStat::SrsProcSystemStat()
|
||||||
|
{
|
||||||
|
ok = false;
|
||||||
|
sample_time = 0;
|
||||||
|
percent = 0;
|
||||||
|
memset(label, 0, sizeof(label));
|
||||||
|
user = 0;
|
||||||
|
nice = 0;
|
||||||
|
sys = 0;
|
||||||
|
idle = 0;
|
||||||
|
iowait = 0;
|
||||||
|
irq = 0;
|
||||||
|
softirq = 0;
|
||||||
|
steal = 0;
|
||||||
|
guest = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsProcSelfStat* srs_get_self_proc_stat()
|
||||||
|
{
|
||||||
|
return &_srs_system_cpu_self_stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsProcSystemStat* srs_get_system_proc_stat()
|
||||||
|
{
|
||||||
|
return &_srs_system_cpu_system_stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_proc_system_stat(SrsProcSystemStat& r)
|
||||||
|
{
|
||||||
|
FILE* f = fopen("/proc/stat", "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
srs_warn("open system cpu stat failed, ignore");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int ret = fscanf(f, "%4s %lu %lu %lu %lu %lu "
|
||||||
|
"%lu %lu %lu %lu\n",
|
||||||
|
r.label, &r.user, &r.nice, &r.sys, &r.idle, &r.iowait,
|
||||||
|
&r.irq, &r.softirq, &r.steal, &r.guest);
|
||||||
|
r.ok = false;
|
||||||
|
|
||||||
|
if (ret == EOF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp("cpu", r.label) == 0) {
|
||||||
|
r.ok = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return r.ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_proc_self_stat(SrsProcSelfStat& r)
|
||||||
|
{
|
||||||
|
FILE* f = fopen("/proc/self/stat", "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
srs_warn("open self cpu stat failed, ignore");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = fscanf(f, "%d %32s %c %d %d %d %d "
|
||||||
|
"%d %u %lu %lu %lu %lu "
|
||||||
|
"%lu %lu %ld %ld %ld %ld "
|
||||||
|
"%ld %ld %llu %lu %ld "
|
||||||
|
"%lu %lu %lu %lu %lu "
|
||||||
|
"%lu %lu %lu %lu %lu "
|
||||||
|
"%lu %lu %lu %d %d "
|
||||||
|
"%u %u %llu "
|
||||||
|
"%lu %ld",
|
||||||
|
&r.pid, r.comm, &r.state, &r.ppid, &r.pgrp, &r.session, &r.tty_nr,
|
||||||
|
&r.tpgid, &r.flags, &r.minflt, &r.cminflt, &r.majflt, &r.cmajflt,
|
||||||
|
&r.utime, &r.stime, &r.cutime, &r.cstime, &r.priority, &r.nice,
|
||||||
|
&r.num_threads, &r.itrealvalue, &r.starttime, &r.vsize, &r.rss,
|
||||||
|
&r.rsslim, &r.startcode, &r.endcode, &r.startstack, &r.kstkesp,
|
||||||
|
&r.kstkeip, &r.signal, &r.blocked, &r.sigignore, &r.sigcatch,
|
||||||
|
&r.wchan, &r.nswap, &r.cnswap, &r.exit_signal, &r.processor,
|
||||||
|
&r.rt_priority, &r.policy, &r.delayacct_blkio_ticks,
|
||||||
|
&r.guest_time, &r.cguest_time);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
if (ret >= 0) {
|
||||||
|
r.ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void srs_update_proc_stat()
|
||||||
|
{
|
||||||
|
srs_update_system_time_ms();
|
||||||
|
|
||||||
|
// system cpu stat
|
||||||
|
if (true) {
|
||||||
|
SrsProcSystemStat r;
|
||||||
|
if (!get_proc_system_stat(r)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
r.sample_time = srs_get_system_time_ms();
|
||||||
|
|
||||||
|
// calc usage in percent
|
||||||
|
SrsProcSystemStat& o = _srs_system_cpu_system_stat;
|
||||||
|
|
||||||
|
// @see: http://blog.csdn.net/nineday/article/details/1928847
|
||||||
|
int64_t total = (r.user + r.nice + r.sys + r.idle + r.iowait + r.irq + r.softirq + r.steal + r.guest)
|
||||||
|
- (o.user + o.nice + o.sys + o.idle + o.iowait + o.irq + o.softirq + o.steal + o.guest);
|
||||||
|
int64_t idle = r.idle - o.idle;
|
||||||
|
if (total > 0) {
|
||||||
|
r.percent = (float)(1 - idle / (double)total);
|
||||||
|
}
|
||||||
|
|
||||||
|
// upate cache.
|
||||||
|
_srs_system_cpu_system_stat = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// self cpu stat
|
||||||
|
if (true) {
|
||||||
|
SrsProcSelfStat r;
|
||||||
|
if (!get_proc_self_stat(r)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_update_system_time_ms();
|
||||||
|
r.sample_time = srs_get_system_time_ms();
|
||||||
|
|
||||||
|
// calc usage in percent
|
||||||
|
SrsProcSelfStat& o = _srs_system_cpu_self_stat;
|
||||||
|
|
||||||
|
// @see: http://stackoverflow.com/questions/16011677/calculating-cpu-usage-using-proc-files
|
||||||
|
int64_t total = r.sample_time - o.sample_time;
|
||||||
|
int64_t usage = (r.utime + r.stime) - (o.utime + o.stime);
|
||||||
|
if (total > 0) {
|
||||||
|
r.percent = (float)(usage * 1000 / (double)total / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// upate cache.
|
||||||
|
_srs_system_cpu_self_stat = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsMemInfo::SrsMemInfo()
|
||||||
|
{
|
||||||
|
ok = false;
|
||||||
|
sample_time = 0;
|
||||||
|
|
||||||
|
percent_ram = 0;
|
||||||
|
percent_swap = 0;
|
||||||
|
|
||||||
|
MemActive = 0;
|
||||||
|
RealInUse = 0;
|
||||||
|
NotInUse = 0;
|
||||||
|
MemTotal = 0;
|
||||||
|
MemFree = 0;
|
||||||
|
Buffers = 0;
|
||||||
|
Cached = 0;
|
||||||
|
SwapTotal = 0;
|
||||||
|
SwapFree = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SrsMemInfo _srs_system_meminfo;
|
||||||
|
|
||||||
|
SrsMemInfo* srs_get_meminfo()
|
||||||
|
{
|
||||||
|
return &_srs_system_meminfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void srs_update_meminfo()
|
||||||
|
{
|
||||||
|
FILE* f = fopen("/proc/meminfo", "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
srs_warn("open meminfo failed, ignore");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsMemInfo& r = _srs_system_meminfo;
|
||||||
|
r.ok = false;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
static char label[64];
|
||||||
|
static unsigned long value;
|
||||||
|
static char postfix[64];
|
||||||
|
int ret = fscanf(f, "%64s %lu %64s\n", label, &value, postfix);
|
||||||
|
|
||||||
|
if (ret == EOF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp("MemTotal:", label) == 0) {
|
||||||
|
r.MemTotal = value;
|
||||||
|
} else if (strcmp("MemFree:", label) == 0) {
|
||||||
|
r.MemFree = value;
|
||||||
|
} else if (strcmp("Buffers:", label) == 0) {
|
||||||
|
r.Buffers = value;
|
||||||
|
} else if (strcmp("Cached:", label) == 0) {
|
||||||
|
r.Cached = value;
|
||||||
|
} else if (strcmp("SwapTotal:", label) == 0) {
|
||||||
|
r.SwapTotal = value;
|
||||||
|
} else if (strcmp("SwapFree:", label) == 0) {
|
||||||
|
r.SwapFree = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
r.sample_time = srs_get_system_time_ms();
|
||||||
|
r.MemActive = r.MemTotal - r.MemFree;
|
||||||
|
r.RealInUse = r.MemActive - r.Buffers - r.Cached;
|
||||||
|
r.NotInUse = r.MemTotal - r.RealInUse;
|
||||||
|
|
||||||
|
r.ok = true;
|
||||||
|
if (r.MemTotal > 0) {
|
||||||
|
r.percent_ram = (float)(r.RealInUse / (double)r.MemTotal);
|
||||||
|
}
|
||||||
|
if (r.SwapTotal > 0) {
|
||||||
|
r.percent_swap = (float)((r.SwapTotal - r.SwapFree) / (double)r.SwapTotal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsCpuInfo::SrsCpuInfo()
|
||||||
|
{
|
||||||
|
ok = false;
|
||||||
|
|
||||||
|
nb_processors = 0;
|
||||||
|
nb_processors_online = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsCpuInfo* srs_get_cpuinfo()
|
||||||
|
{
|
||||||
|
static SrsCpuInfo* cpu = NULL;
|
||||||
|
if (cpu != NULL) {
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize cpu info.
|
||||||
|
cpu = new SrsCpuInfo();
|
||||||
|
cpu->ok = true;
|
||||||
|
cpu->nb_processors = sysconf(_SC_NPROCESSORS_CONF);
|
||||||
|
cpu->nb_processors_online = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsPlatformInfo::SrsPlatformInfo()
|
||||||
|
{
|
||||||
|
ok = false;
|
||||||
|
|
||||||
|
srs_startup_time = srs_get_system_time_ms();
|
||||||
|
|
||||||
|
os_uptime = 0;
|
||||||
|
os_ilde_time = 0;
|
||||||
|
|
||||||
|
load_one_minutes = 0;
|
||||||
|
load_five_minutes = 0;
|
||||||
|
load_fifteen_minutes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SrsPlatformInfo _srs_system_platform_info;
|
||||||
|
|
||||||
|
SrsPlatformInfo* srs_get_platform_info()
|
||||||
|
{
|
||||||
|
return &_srs_system_platform_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void srs_update_platform_info()
|
||||||
|
{
|
||||||
|
SrsPlatformInfo& r = _srs_system_platform_info;
|
||||||
|
r.ok = true;
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
FILE* f = fopen("/proc/uptime", "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
srs_warn("open uptime failed, ignore");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = fscanf(f, "%lf %lf\n", &r.os_uptime, &r.os_ilde_time);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
r.ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
FILE* f = fopen("/proc/loadavg", "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
srs_warn("open loadavg failed, ignore");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = fscanf(f, "%lf %lf %lf\n",
|
||||||
|
&r.load_one_minutes, &r.load_five_minutes, &r.load_fifteen_minutes);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
r.ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,10 +30,314 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert level in string to log level in int.
|
* convert level in string to log level in int.
|
||||||
* @return the log level defined in SrsLogLevel.
|
* @return the log level defined in SrsLogLevel.
|
||||||
*/
|
*/
|
||||||
extern int srs_get_log_level(std::string level);
|
extern int srs_get_log_level(std::string level);
|
||||||
|
|
||||||
|
// @see: man getrusage
|
||||||
|
struct SrsRusage
|
||||||
|
{
|
||||||
|
// whether the data is ok.
|
||||||
|
bool ok;
|
||||||
|
// the time in ms when sample.
|
||||||
|
int64_t sample_time;
|
||||||
|
|
||||||
|
rusage r;
|
||||||
|
|
||||||
|
SrsRusage();
|
||||||
|
};
|
||||||
|
|
||||||
|
// get system rusage, use cache to avoid performance problem.
|
||||||
|
extern SrsRusage* srs_get_system_rusage();
|
||||||
|
// the deamon st-thread will update it.
|
||||||
|
extern void srs_update_system_rusage();
|
||||||
|
|
||||||
|
// @see: man 5 proc, /proc/[pid]/stat
|
||||||
|
struct SrsProcSelfStat
|
||||||
|
{
|
||||||
|
// whether the data is ok.
|
||||||
|
bool ok;
|
||||||
|
// the time in ms when sample.
|
||||||
|
int64_t sample_time;
|
||||||
|
// the percent of usage. 0.153 is 15.3%.
|
||||||
|
float percent;
|
||||||
|
|
||||||
|
// pid %d The process ID.
|
||||||
|
int pid;
|
||||||
|
// comm %s The filename of the executable, in parentheses. This is visible whether or not the executable is
|
||||||
|
// swapped out.
|
||||||
|
char comm[32];
|
||||||
|
// state %c One character from the string "RSDZTW" where R is running, S is sleeping in an interruptible wait, D
|
||||||
|
// is waiting in uninterruptible disk sleep, Z is zombie, T is traced or stopped (on a signal), and W is
|
||||||
|
// paging.
|
||||||
|
char state;
|
||||||
|
// ppid %d The PID of the parent.
|
||||||
|
int ppid;
|
||||||
|
// pgrp %d The process group ID of the process.
|
||||||
|
int pgrp;
|
||||||
|
// session %d The session ID of the process.
|
||||||
|
int session;
|
||||||
|
// tty_nr %d The controlling terminal of the process. (The minor device number is contained in the combination of
|
||||||
|
// bits 31 to 20 and 7 to 0; the major device number is in bits 15 t0 8.)
|
||||||
|
int tty_nr;
|
||||||
|
// tpgid %d The ID of the foreground process group of the controlling terminal of the process.
|
||||||
|
int tpgid;
|
||||||
|
// flags %u (%lu before Linux 2.6.22)
|
||||||
|
// The kernel flags word of the process. For bit meanings, see the PF_* defines in <linux/sched.h>.
|
||||||
|
// Details depend on the kernel version.
|
||||||
|
unsigned int flags;
|
||||||
|
// minflt %lu The number of minor faults the process has made which have not required loading a memory page from
|
||||||
|
// disk.
|
||||||
|
unsigned long minflt;
|
||||||
|
// cminflt %lu The number of minor faults that the process’s waited-for children have made.
|
||||||
|
unsigned long cminflt;
|
||||||
|
// majflt %lu The number of major faults the process has made which have required loading a memory page from disk.
|
||||||
|
unsigned long majflt;
|
||||||
|
// cmajflt %lu The number of major faults that the process’s waited-for children have made.
|
||||||
|
unsigned long cmajflt;
|
||||||
|
// utime %lu Amount of time that this process has been scheduled in user mode, measured in clock ticks (divide by
|
||||||
|
// sysconf(_SC_CLK_TCK). This includes guest time, guest_time (time spent running a virtual CPU, see
|
||||||
|
// below), so that applications that are not aware of the guest time field do not lose that time from
|
||||||
|
// their calculations.
|
||||||
|
unsigned long utime;
|
||||||
|
// stime %lu Amount of time that this process has been scheduled in kernel mode, measured in clock ticks (divide by
|
||||||
|
// sysconf(_SC_CLK_TCK).
|
||||||
|
unsigned long stime;
|
||||||
|
// cutime %ld Amount of time that this process’s waited-for children have been scheduled in user mode, measured in
|
||||||
|
// clock ticks (divide by sysconf(_SC_CLK_TCK). (See also times(2).) This includes guest time,
|
||||||
|
// cguest_time (time spent running a virtual CPU, see below).
|
||||||
|
long cutime;
|
||||||
|
// cstime %ld Amount of time that this process’s waited-for children have been scheduled in kernel mode, measured in
|
||||||
|
// clock ticks (divide by sysconf(_SC_CLK_TCK).
|
||||||
|
long cstime;
|
||||||
|
// priority %ld
|
||||||
|
// (Explanation for Linux 2.6) For processes running a real-time scheduling policy (policy below; see
|
||||||
|
// sched_setscheduler(2)), this is the negated scheduling priority, minus one; that is, a number in the
|
||||||
|
// range -2 to -100, corresponding to real-time priorities 1 to 99. For processes running under a non-
|
||||||
|
// real-time scheduling policy, this is the raw nice value (setpriority(2)) as represented in the kernel.
|
||||||
|
// The kernel stores nice values as numbers in the range 0 (high) to 39 (low), corresponding to the user-
|
||||||
|
// visible nice range of -20 to 19.
|
||||||
|
//
|
||||||
|
// Before Linux 2.6, this was a scaled value based on the scheduler weighting given to this process.
|
||||||
|
long priority;
|
||||||
|
// nice %ld The nice value (see setpriority(2)), a value in the range 19 (low priority) to -20 (high priority).
|
||||||
|
long nice;
|
||||||
|
// num_threads %ld
|
||||||
|
// Number of threads in this process (since Linux 2.6). Before kernel 2.6, this field was hard coded to
|
||||||
|
// 0 as a placeholder for an earlier removed field.
|
||||||
|
long num_threads;
|
||||||
|
// itrealvalue %ld
|
||||||
|
// The time in jiffies before the next SIGALRM is sent to the process due to an interval timer. Since
|
||||||
|
// kernel 2.6.17, this field is no longer maintained, and is hard coded as 0.
|
||||||
|
long itrealvalue;
|
||||||
|
// starttime %llu (was %lu before Linux 2.6)
|
||||||
|
// The time in jiffies the process started after system boot.
|
||||||
|
long long starttime;
|
||||||
|
// vsize %lu Virtual memory size in bytes.
|
||||||
|
unsigned long vsize;
|
||||||
|
// rss %ld Resident Set Size: number of pages the process has in real memory. This is just the pages which count
|
||||||
|
// towards text, data, or stack space. This does not include pages which have not been demand-loaded in,
|
||||||
|
// or which are swapped out.
|
||||||
|
long rss;
|
||||||
|
// rsslim %lu Current soft limit in bytes on the rss of the process; see the description of RLIMIT_RSS in getprior-
|
||||||
|
// ity(2).
|
||||||
|
unsigned long rsslim;
|
||||||
|
// startcode %lu
|
||||||
|
// The address above which program text can run.
|
||||||
|
unsigned long startcode;
|
||||||
|
// endcode %lu The address below which program text can run.
|
||||||
|
unsigned long endcode;
|
||||||
|
// startstack %lu
|
||||||
|
// The address of the start (i.e., bottom) of the stack.
|
||||||
|
unsigned long startstack;
|
||||||
|
// kstkesp %lu The current value of ESP (stack pointer), as found in the kernel stack page for the process.
|
||||||
|
unsigned long kstkesp;
|
||||||
|
// kstkeip %lu The current EIP (instruction pointer).
|
||||||
|
unsigned long kstkeip;
|
||||||
|
// signal %lu The bitmap of pending signals, displayed as a decimal number. Obsolete, because it does not provide
|
||||||
|
// information on real-time signals; use /proc/[pid]/status instead.
|
||||||
|
unsigned long signal;
|
||||||
|
// blocked %lu The bitmap of blocked signals, displayed as a decimal number. Obsolete, because it does not provide
|
||||||
|
// information on real-time signals; use /proc/[pid]/status instead.
|
||||||
|
unsigned long blocked;
|
||||||
|
// sigignore %lu
|
||||||
|
// The bitmap of ignored signals, displayed as a decimal number. Obsolete, because it does not provide
|
||||||
|
// information on real-time signals; use /proc/[pid]/status instead.
|
||||||
|
unsigned long sigignore;
|
||||||
|
// sigcatch %lu
|
||||||
|
// The bitmap of caught signals, displayed as a decimal number. Obsolete, because it does not provide
|
||||||
|
// information on real-time signals; use /proc/[pid]/status instead.
|
||||||
|
unsigned long sigcatch;
|
||||||
|
// wchan %lu This is the "channel" in which the process is waiting. It is the address of a system call, and can be
|
||||||
|
// looked up in a namelist if you need a textual name. (If you have an up-to-date /etc/psdatabase, then
|
||||||
|
// try ps -l to see the WCHAN field in action.)
|
||||||
|
unsigned long wchan;
|
||||||
|
// nswap %lu Number of pages swapped (not maintained).
|
||||||
|
unsigned long nswap;
|
||||||
|
// cnswap %lu Cumulative nswap for child processes (not maintained).
|
||||||
|
unsigned long cnswap;
|
||||||
|
// exit_signal %d (since Linux 2.1.22)
|
||||||
|
// Signal to be sent to parent when we die.
|
||||||
|
int exit_signal;
|
||||||
|
// processor %d (since Linux 2.2.8)
|
||||||
|
// CPU number last executed on.
|
||||||
|
int processor;
|
||||||
|
// rt_priority %u (since Linux 2.5.19; was %lu before Linux 2.6.22)
|
||||||
|
// Real-time scheduling priority, a number in the range 1 to 99 for processes scheduled under a real-time
|
||||||
|
// policy, or 0, for non-real-time processes (see sched_setscheduler(2)).
|
||||||
|
unsigned int rt_priority;
|
||||||
|
// policy %u (since Linux 2.5.19; was %lu before Linux 2.6.22)
|
||||||
|
// Scheduling policy (see sched_setscheduler(2)). Decode using the SCHED_* constants in linux/sched.h.
|
||||||
|
unsigned int policy;
|
||||||
|
// delayacct_blkio_ticks %llu (since Linux 2.6.18)
|
||||||
|
// Aggregated block I/O delays, measured in clock ticks (centiseconds).
|
||||||
|
unsigned long long delayacct_blkio_ticks;
|
||||||
|
// guest_time %lu (since Linux 2.6.24)
|
||||||
|
// Guest time of the process (time spent running a virtual CPU for a guest operating system), measured in
|
||||||
|
// clock ticks (divide by sysconf(_SC_CLK_TCK).
|
||||||
|
unsigned long guest_time;
|
||||||
|
// cguest_time %ld (since Linux 2.6.24)
|
||||||
|
// Guest time of the process’s children, measured in clock ticks (divide by sysconf(_SC_CLK_TCK).
|
||||||
|
long cguest_time;
|
||||||
|
|
||||||
|
SrsProcSelfStat();
|
||||||
|
};
|
||||||
|
|
||||||
|
// @see: man 5 proc, /proc/stat
|
||||||
|
struct SrsProcSystemStat
|
||||||
|
{
|
||||||
|
// whether the data is ok.
|
||||||
|
bool ok;
|
||||||
|
// the time in ms when sample.
|
||||||
|
int64_t sample_time;
|
||||||
|
// the percent of usage. 0.153 is 15.3%.
|
||||||
|
float percent;
|
||||||
|
|
||||||
|
// always be cpu
|
||||||
|
char label[32];
|
||||||
|
|
||||||
|
//The amount of time, measured in units of USER_HZ (1/100ths of a second on most architectures, use
|
||||||
|
// sysconf(_SC_CLK_TCK) to obtain the right value)
|
||||||
|
//
|
||||||
|
// the system spent in user mode,
|
||||||
|
unsigned long user;
|
||||||
|
// user mode with low priority (nice),
|
||||||
|
unsigned long nice;
|
||||||
|
// system mode,
|
||||||
|
unsigned long sys;
|
||||||
|
// and the idle task, respectively.
|
||||||
|
unsigned long idle;
|
||||||
|
|
||||||
|
// In Linux 2.6 this line includes three additional columns:
|
||||||
|
//
|
||||||
|
// iowait - time waiting for I/O to complete (since 2.5.41);
|
||||||
|
unsigned long iowait;
|
||||||
|
// irq - time servicing interrupts (since 2.6.0-test4);
|
||||||
|
unsigned long irq;
|
||||||
|
// softirq - time servicing softirqs (since 2.6.0-test4).
|
||||||
|
unsigned long softirq;
|
||||||
|
|
||||||
|
// Since Linux 2.6.11, there is an eighth column,
|
||||||
|
// steal - stolen time, which is the time spent in other oper-
|
||||||
|
// ating systems when running in a virtualized environment
|
||||||
|
unsigned long steal;
|
||||||
|
|
||||||
|
// Since Linux 2.6.24, there is a ninth column,
|
||||||
|
// guest, which is the time spent running a virtual CPU for guest
|
||||||
|
// operating systems under the control of the Linux kernel.
|
||||||
|
unsigned long guest;
|
||||||
|
|
||||||
|
SrsProcSystemStat();
|
||||||
|
};
|
||||||
|
|
||||||
|
// get system cpu stat, use cache to avoid performance problem.
|
||||||
|
extern SrsProcSelfStat* srs_get_self_proc_stat();
|
||||||
|
// get system cpu stat, use cache to avoid performance problem.
|
||||||
|
extern SrsProcSystemStat* srs_get_system_proc_stat();
|
||||||
|
// the deamon st-thread will update it.
|
||||||
|
extern void srs_update_proc_stat();
|
||||||
|
|
||||||
|
// @see: cat /proc/meminfo
|
||||||
|
struct SrsMemInfo
|
||||||
|
{
|
||||||
|
// whether the data is ok.
|
||||||
|
bool ok;
|
||||||
|
// the time in ms when sample.
|
||||||
|
int64_t sample_time;
|
||||||
|
// the percent of usage. 0.153 is 15.3%.
|
||||||
|
float percent_ram;
|
||||||
|
float percent_swap;
|
||||||
|
|
||||||
|
// MemActive = MemTotal - MemFree
|
||||||
|
int64_t MemActive;
|
||||||
|
// RealInUse = MemActive - Buffers - Cached
|
||||||
|
int64_t RealInUse;
|
||||||
|
// NotInUse = MemTotal - RealInUse
|
||||||
|
// = MemTotal - MemActive + Buffers + Cached
|
||||||
|
// = MemTotal - MemTotal + MemFree + Buffers + Cached
|
||||||
|
// = MemFree + Buffers + Cached
|
||||||
|
int64_t NotInUse;
|
||||||
|
|
||||||
|
int64_t MemTotal;
|
||||||
|
int64_t MemFree;
|
||||||
|
int64_t Buffers;
|
||||||
|
int64_t Cached;
|
||||||
|
int64_t SwapTotal;
|
||||||
|
int64_t SwapFree;
|
||||||
|
|
||||||
|
SrsMemInfo();
|
||||||
|
};
|
||||||
|
|
||||||
|
// get system meminfo, use cache to avoid performance problem.
|
||||||
|
extern SrsMemInfo* srs_get_meminfo();
|
||||||
|
// the deamon st-thread will update it.
|
||||||
|
extern void srs_update_meminfo();
|
||||||
|
|
||||||
|
// @see: cat /proc/cpuinfo
|
||||||
|
struct SrsCpuInfo
|
||||||
|
{
|
||||||
|
// whether the data is ok.
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
// The number of processors configured.
|
||||||
|
int nb_processors;
|
||||||
|
// The number of processors currently online (available).
|
||||||
|
int nb_processors_online;
|
||||||
|
|
||||||
|
SrsCpuInfo();
|
||||||
|
};
|
||||||
|
|
||||||
|
// get system cpu info, use cache to avoid performance problem.
|
||||||
|
extern SrsCpuInfo* srs_get_cpuinfo();
|
||||||
|
|
||||||
|
// platform(os, srs) summary
|
||||||
|
struct SrsPlatformInfo
|
||||||
|
{
|
||||||
|
// whether the data is ok.
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
// srs startup time, in ms.
|
||||||
|
int64_t srs_startup_time;
|
||||||
|
|
||||||
|
// @see: cat /proc/uptime
|
||||||
|
double os_uptime;
|
||||||
|
double os_ilde_time;
|
||||||
|
|
||||||
|
// @see: cat /proc/loadavg
|
||||||
|
double load_one_minutes;
|
||||||
|
double load_five_minutes;
|
||||||
|
double load_fifteen_minutes;
|
||||||
|
|
||||||
|
SrsPlatformInfo();
|
||||||
|
};
|
||||||
|
|
||||||
|
// get platform info, use cache to avoid performance problem.
|
||||||
|
extern SrsPlatformInfo* srs_get_platform_info();
|
||||||
|
// the deamon st-thread will update it.
|
||||||
|
extern void srs_update_platform_info();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// current release version
|
// current release version
|
||||||
#define VERSION_MAJOR "0"
|
#define VERSION_MAJOR "0"
|
||||||
#define VERSION_MINOR "9"
|
#define VERSION_MINOR "9"
|
||||||
#define VERSION_REVISION "98"
|
#define VERSION_REVISION "99"
|
||||||
#define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION
|
#define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION
|
||||||
// server info.
|
// server info.
|
||||||
#define RTMP_SIG_SRS_KEY "srs"
|
#define RTMP_SIG_SRS_KEY "srs"
|
||||||
|
|
|
@ -58,393 +58,3 @@ void srs_update_system_time_ms()
|
||||||
|
|
||||||
_srs_system_time_us_cache = now_us;
|
_srs_system_time_us_cache = now_us;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SrsRusage _srs_system_rusage;
|
|
||||||
|
|
||||||
SrsRusage::SrsRusage()
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
sample_time = 0;
|
|
||||||
memset(&r, 0, sizeof(rusage));
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsRusage* srs_get_system_rusage()
|
|
||||||
{
|
|
||||||
return &_srs_system_rusage;
|
|
||||||
}
|
|
||||||
|
|
||||||
void srs_update_system_rusage()
|
|
||||||
{
|
|
||||||
if (getrusage(RUSAGE_SELF, &_srs_system_rusage.r) < 0) {
|
|
||||||
srs_warn("getrusage failed, ignore");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_update_system_time_ms();
|
|
||||||
_srs_system_rusage.sample_time = srs_get_system_time_ms();
|
|
||||||
|
|
||||||
_srs_system_rusage.ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SrsProcSelfStat _srs_system_cpu_self_stat;
|
|
||||||
static SrsProcSystemStat _srs_system_cpu_system_stat;
|
|
||||||
|
|
||||||
SrsProcSelfStat::SrsProcSelfStat()
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
sample_time = 0;
|
|
||||||
percent = 0;
|
|
||||||
|
|
||||||
pid = 0;
|
|
||||||
memset(comm, 0, sizeof(comm));
|
|
||||||
state = 0;
|
|
||||||
ppid = 0;
|
|
||||||
pgrp = 0;
|
|
||||||
session = 0;
|
|
||||||
tty_nr = 0;
|
|
||||||
tpgid = 0;
|
|
||||||
flags = 0;
|
|
||||||
minflt = 0;
|
|
||||||
cminflt = 0;
|
|
||||||
majflt = 0;
|
|
||||||
cmajflt = 0;
|
|
||||||
utime = 0;
|
|
||||||
stime = 0;
|
|
||||||
cutime = 0;
|
|
||||||
cstime = 0;
|
|
||||||
priority = 0;
|
|
||||||
nice = 0;
|
|
||||||
num_threads = 0;
|
|
||||||
itrealvalue = 0;
|
|
||||||
starttime = 0;
|
|
||||||
vsize = 0;
|
|
||||||
rss = 0;
|
|
||||||
rsslim = 0;
|
|
||||||
startcode = 0;
|
|
||||||
endcode = 0;
|
|
||||||
startstack = 0;
|
|
||||||
kstkesp = 0;
|
|
||||||
kstkeip = 0;
|
|
||||||
signal = 0;
|
|
||||||
blocked = 0;
|
|
||||||
sigignore = 0;
|
|
||||||
sigcatch = 0;
|
|
||||||
wchan = 0;
|
|
||||||
nswap = 0;
|
|
||||||
cnswap = 0;
|
|
||||||
exit_signal = 0;
|
|
||||||
processor = 0;
|
|
||||||
rt_priority = 0;
|
|
||||||
policy = 0;
|
|
||||||
delayacct_blkio_ticks = 0;
|
|
||||||
guest_time = 0;
|
|
||||||
cguest_time = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsProcSystemStat::SrsProcSystemStat()
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
sample_time = 0;
|
|
||||||
percent = 0;
|
|
||||||
memset(label, 0, sizeof(label));
|
|
||||||
user = 0;
|
|
||||||
nice = 0;
|
|
||||||
sys = 0;
|
|
||||||
idle = 0;
|
|
||||||
iowait = 0;
|
|
||||||
irq = 0;
|
|
||||||
softirq = 0;
|
|
||||||
steal = 0;
|
|
||||||
guest = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsProcSelfStat* srs_get_self_proc_stat()
|
|
||||||
{
|
|
||||||
return &_srs_system_cpu_self_stat;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsProcSystemStat* srs_get_system_proc_stat()
|
|
||||||
{
|
|
||||||
return &_srs_system_cpu_system_stat;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_proc_system_stat(SrsProcSystemStat& r)
|
|
||||||
{
|
|
||||||
FILE* f = fopen("/proc/stat", "r");
|
|
||||||
if (f == NULL) {
|
|
||||||
srs_warn("open system cpu stat failed, ignore");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
int ret = fscanf(f, "%4s %lu %lu %lu %lu %lu "
|
|
||||||
"%lu %lu %lu %lu\n",
|
|
||||||
r.label, &r.user, &r.nice, &r.sys, &r.idle, &r.iowait,
|
|
||||||
&r.irq, &r.softirq, &r.steal, &r.guest);
|
|
||||||
r.ok = false;
|
|
||||||
|
|
||||||
if (ret == EOF) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp("cpu", r.label) == 0) {
|
|
||||||
r.ok = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
return r.ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_proc_self_stat(SrsProcSelfStat& r)
|
|
||||||
{
|
|
||||||
FILE* f = fopen("/proc/self/stat", "r");
|
|
||||||
if (f == NULL) {
|
|
||||||
srs_warn("open self cpu stat failed, ignore");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = fscanf(f, "%d %32s %c %d %d %d %d "
|
|
||||||
"%d %u %lu %lu %lu %lu "
|
|
||||||
"%lu %lu %ld %ld %ld %ld "
|
|
||||||
"%ld %ld %llu %lu %ld "
|
|
||||||
"%lu %lu %lu %lu %lu "
|
|
||||||
"%lu %lu %lu %lu %lu "
|
|
||||||
"%lu %lu %lu %d %d "
|
|
||||||
"%u %u %llu "
|
|
||||||
"%lu %ld",
|
|
||||||
&r.pid, r.comm, &r.state, &r.ppid, &r.pgrp, &r.session, &r.tty_nr,
|
|
||||||
&r.tpgid, &r.flags, &r.minflt, &r.cminflt, &r.majflt, &r.cmajflt,
|
|
||||||
&r.utime, &r.stime, &r.cutime, &r.cstime, &r.priority, &r.nice,
|
|
||||||
&r.num_threads, &r.itrealvalue, &r.starttime, &r.vsize, &r.rss,
|
|
||||||
&r.rsslim, &r.startcode, &r.endcode, &r.startstack, &r.kstkesp,
|
|
||||||
&r.kstkeip, &r.signal, &r.blocked, &r.sigignore, &r.sigcatch,
|
|
||||||
&r.wchan, &r.nswap, &r.cnswap, &r.exit_signal, &r.processor,
|
|
||||||
&r.rt_priority, &r.policy, &r.delayacct_blkio_ticks,
|
|
||||||
&r.guest_time, &r.cguest_time);
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
if (ret >= 0) {
|
|
||||||
r.ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
void srs_update_proc_stat()
|
|
||||||
{
|
|
||||||
srs_update_system_time_ms();
|
|
||||||
|
|
||||||
// system cpu stat
|
|
||||||
if (true) {
|
|
||||||
SrsProcSystemStat r;
|
|
||||||
if (!get_proc_system_stat(r)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
r.sample_time = srs_get_system_time_ms();
|
|
||||||
|
|
||||||
// calc usage in percent
|
|
||||||
SrsProcSystemStat& o = _srs_system_cpu_system_stat;
|
|
||||||
|
|
||||||
// @see: http://blog.csdn.net/nineday/article/details/1928847
|
|
||||||
int64_t total = (r.user + r.nice + r.sys + r.idle + r.iowait + r.irq + r.softirq + r.steal + r.guest)
|
|
||||||
- (o.user + o.nice + o.sys + o.idle + o.iowait + o.irq + o.softirq + o.steal + o.guest);
|
|
||||||
int64_t idle = r.idle - o.idle;
|
|
||||||
if (total > 0) {
|
|
||||||
r.percent = (float)(1 - idle / (double)total);
|
|
||||||
}
|
|
||||||
|
|
||||||
// upate cache.
|
|
||||||
_srs_system_cpu_system_stat = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// self cpu stat
|
|
||||||
if (true) {
|
|
||||||
SrsProcSelfStat r;
|
|
||||||
if (!get_proc_self_stat(r)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_update_system_time_ms();
|
|
||||||
r.sample_time = srs_get_system_time_ms();
|
|
||||||
|
|
||||||
// calc usage in percent
|
|
||||||
SrsProcSelfStat& o = _srs_system_cpu_self_stat;
|
|
||||||
|
|
||||||
// @see: http://stackoverflow.com/questions/16011677/calculating-cpu-usage-using-proc-files
|
|
||||||
int64_t total = r.sample_time - o.sample_time;
|
|
||||||
int64_t usage = (r.utime + r.stime) - (o.utime + o.stime);
|
|
||||||
if (total > 0) {
|
|
||||||
r.percent = (float)(usage * 1000 / (double)total / 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// upate cache.
|
|
||||||
_srs_system_cpu_self_stat = r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsMemInfo::SrsMemInfo()
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
sample_time = 0;
|
|
||||||
|
|
||||||
percent_ram = 0;
|
|
||||||
percent_swap = 0;
|
|
||||||
|
|
||||||
MemActive = 0;
|
|
||||||
RealInUse = 0;
|
|
||||||
NotInUse = 0;
|
|
||||||
MemTotal = 0;
|
|
||||||
MemFree = 0;
|
|
||||||
Buffers = 0;
|
|
||||||
Cached = 0;
|
|
||||||
SwapTotal = 0;
|
|
||||||
SwapFree = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SrsMemInfo _srs_system_meminfo;
|
|
||||||
|
|
||||||
SrsMemInfo* srs_get_meminfo()
|
|
||||||
{
|
|
||||||
return &_srs_system_meminfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
void srs_update_meminfo()
|
|
||||||
{
|
|
||||||
FILE* f = fopen("/proc/meminfo", "r");
|
|
||||||
if (f == NULL) {
|
|
||||||
srs_warn("open meminfo failed, ignore");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsMemInfo& r = _srs_system_meminfo;
|
|
||||||
r.ok = false;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
static char label[64];
|
|
||||||
static unsigned long value;
|
|
||||||
static char postfix[64];
|
|
||||||
int ret = fscanf(f, "%64s %lu %64s\n", label, &value, postfix);
|
|
||||||
|
|
||||||
if (ret == EOF) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp("MemTotal:", label) == 0) {
|
|
||||||
r.MemTotal = value;
|
|
||||||
} else if (strcmp("MemFree:", label) == 0) {
|
|
||||||
r.MemFree = value;
|
|
||||||
} else if (strcmp("Buffers:", label) == 0) {
|
|
||||||
r.Buffers = value;
|
|
||||||
} else if (strcmp("Cached:", label) == 0) {
|
|
||||||
r.Cached = value;
|
|
||||||
} else if (strcmp("SwapTotal:", label) == 0) {
|
|
||||||
r.SwapTotal = value;
|
|
||||||
} else if (strcmp("SwapFree:", label) == 0) {
|
|
||||||
r.SwapFree = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
r.sample_time = srs_get_system_time_ms();
|
|
||||||
r.MemActive = r.MemTotal - r.MemFree;
|
|
||||||
r.RealInUse = r.MemActive - r.Buffers - r.Cached;
|
|
||||||
r.NotInUse = r.MemTotal - r.RealInUse;
|
|
||||||
|
|
||||||
r.ok = true;
|
|
||||||
if (r.MemTotal > 0) {
|
|
||||||
r.percent_ram = (float)(r.RealInUse / (double)r.MemTotal);
|
|
||||||
}
|
|
||||||
if (r.SwapTotal > 0) {
|
|
||||||
r.percent_swap = (float)((r.SwapTotal - r.SwapFree) / (double)r.SwapTotal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsCpuInfo::SrsCpuInfo()
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
|
|
||||||
nb_processors = 0;
|
|
||||||
nb_processors_online = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsCpuInfo* srs_get_cpuinfo()
|
|
||||||
{
|
|
||||||
static SrsCpuInfo* cpu = NULL;
|
|
||||||
if (cpu != NULL) {
|
|
||||||
return cpu;
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize cpu info.
|
|
||||||
cpu = new SrsCpuInfo();
|
|
||||||
cpu->ok = true;
|
|
||||||
cpu->nb_processors = sysconf(_SC_NPROCESSORS_CONF);
|
|
||||||
cpu->nb_processors_online = sysconf(_SC_NPROCESSORS_ONLN);
|
|
||||||
|
|
||||||
return cpu;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsPlatformInfo::SrsPlatformInfo()
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
|
|
||||||
srs_startup_time = srs_get_system_time_ms();
|
|
||||||
|
|
||||||
os_uptime = 0;
|
|
||||||
os_ilde_time = 0;
|
|
||||||
|
|
||||||
load_one_minutes = 0;
|
|
||||||
load_five_minutes = 0;
|
|
||||||
load_fifteen_minutes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SrsPlatformInfo _srs_system_platform_info;
|
|
||||||
|
|
||||||
SrsPlatformInfo* srs_get_platform_info()
|
|
||||||
{
|
|
||||||
return &_srs_system_platform_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
void srs_update_platform_info()
|
|
||||||
{
|
|
||||||
SrsPlatformInfo& r = _srs_system_platform_info;
|
|
||||||
r.ok = true;
|
|
||||||
|
|
||||||
if (true) {
|
|
||||||
FILE* f = fopen("/proc/uptime", "r");
|
|
||||||
if (f == NULL) {
|
|
||||||
srs_warn("open uptime failed, ignore");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = fscanf(f, "%lf %lf\n", &r.os_uptime, &r.os_ilde_time);
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
r.ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (true) {
|
|
||||||
FILE* f = fopen("/proc/loadavg", "r");
|
|
||||||
if (f == NULL) {
|
|
||||||
srs_warn("open loadavg failed, ignore");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = fscanf(f, "%lf %lf %lf\n",
|
|
||||||
&r.load_one_minutes, &r.load_five_minutes, &r.load_fifteen_minutes);
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
r.ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,313 +30,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
#include <sys/resource.h>
|
|
||||||
|
|
||||||
// get current system time in ms, use cache to avoid performance problem
|
// get current system time in ms, use cache to avoid performance problem
|
||||||
extern int64_t srs_get_system_time_ms();
|
extern int64_t srs_get_system_time_ms();
|
||||||
// the deamon st-thread will update it.
|
// the deamon st-thread will update it.
|
||||||
extern void srs_update_system_time_ms();
|
extern void srs_update_system_time_ms();
|
||||||
|
|
||||||
// @see: man getrusage
|
|
||||||
struct SrsRusage
|
|
||||||
{
|
|
||||||
// whether the data is ok.
|
|
||||||
bool ok;
|
|
||||||
// the time in ms when sample.
|
|
||||||
int64_t sample_time;
|
|
||||||
|
|
||||||
rusage r;
|
|
||||||
|
|
||||||
SrsRusage();
|
|
||||||
};
|
|
||||||
|
|
||||||
// get system rusage, use cache to avoid performance problem.
|
|
||||||
extern SrsRusage* srs_get_system_rusage();
|
|
||||||
// the deamon st-thread will update it.
|
|
||||||
extern void srs_update_system_rusage();
|
|
||||||
|
|
||||||
// @see: man 5 proc, /proc/[pid]/stat
|
|
||||||
struct SrsProcSelfStat
|
|
||||||
{
|
|
||||||
// whether the data is ok.
|
|
||||||
bool ok;
|
|
||||||
// the time in ms when sample.
|
|
||||||
int64_t sample_time;
|
|
||||||
// the percent of usage. 0.153 is 15.3%.
|
|
||||||
float percent;
|
|
||||||
|
|
||||||
// pid %d The process ID.
|
|
||||||
int pid;
|
|
||||||
// comm %s The filename of the executable, in parentheses. This is visible whether or not the executable is
|
|
||||||
// swapped out.
|
|
||||||
char comm[32];
|
|
||||||
// state %c One character from the string "RSDZTW" where R is running, S is sleeping in an interruptible wait, D
|
|
||||||
// is waiting in uninterruptible disk sleep, Z is zombie, T is traced or stopped (on a signal), and W is
|
|
||||||
// paging.
|
|
||||||
char state;
|
|
||||||
// ppid %d The PID of the parent.
|
|
||||||
int ppid;
|
|
||||||
// pgrp %d The process group ID of the process.
|
|
||||||
int pgrp;
|
|
||||||
// session %d The session ID of the process.
|
|
||||||
int session;
|
|
||||||
// tty_nr %d The controlling terminal of the process. (The minor device number is contained in the combination of
|
|
||||||
// bits 31 to 20 and 7 to 0; the major device number is in bits 15 t0 8.)
|
|
||||||
int tty_nr;
|
|
||||||
// tpgid %d The ID of the foreground process group of the controlling terminal of the process.
|
|
||||||
int tpgid;
|
|
||||||
// flags %u (%lu before Linux 2.6.22)
|
|
||||||
// The kernel flags word of the process. For bit meanings, see the PF_* defines in <linux/sched.h>.
|
|
||||||
// Details depend on the kernel version.
|
|
||||||
unsigned int flags;
|
|
||||||
// minflt %lu The number of minor faults the process has made which have not required loading a memory page from
|
|
||||||
// disk.
|
|
||||||
unsigned long minflt;
|
|
||||||
// cminflt %lu The number of minor faults that the process’s waited-for children have made.
|
|
||||||
unsigned long cminflt;
|
|
||||||
// majflt %lu The number of major faults the process has made which have required loading a memory page from disk.
|
|
||||||
unsigned long majflt;
|
|
||||||
// cmajflt %lu The number of major faults that the process’s waited-for children have made.
|
|
||||||
unsigned long cmajflt;
|
|
||||||
// utime %lu Amount of time that this process has been scheduled in user mode, measured in clock ticks (divide by
|
|
||||||
// sysconf(_SC_CLK_TCK). This includes guest time, guest_time (time spent running a virtual CPU, see
|
|
||||||
// below), so that applications that are not aware of the guest time field do not lose that time from
|
|
||||||
// their calculations.
|
|
||||||
unsigned long utime;
|
|
||||||
// stime %lu Amount of time that this process has been scheduled in kernel mode, measured in clock ticks (divide by
|
|
||||||
// sysconf(_SC_CLK_TCK).
|
|
||||||
unsigned long stime;
|
|
||||||
// cutime %ld Amount of time that this process’s waited-for children have been scheduled in user mode, measured in
|
|
||||||
// clock ticks (divide by sysconf(_SC_CLK_TCK). (See also times(2).) This includes guest time,
|
|
||||||
// cguest_time (time spent running a virtual CPU, see below).
|
|
||||||
long cutime;
|
|
||||||
// cstime %ld Amount of time that this process’s waited-for children have been scheduled in kernel mode, measured in
|
|
||||||
// clock ticks (divide by sysconf(_SC_CLK_TCK).
|
|
||||||
long cstime;
|
|
||||||
// priority %ld
|
|
||||||
// (Explanation for Linux 2.6) For processes running a real-time scheduling policy (policy below; see
|
|
||||||
// sched_setscheduler(2)), this is the negated scheduling priority, minus one; that is, a number in the
|
|
||||||
// range -2 to -100, corresponding to real-time priorities 1 to 99. For processes running under a non-
|
|
||||||
// real-time scheduling policy, this is the raw nice value (setpriority(2)) as represented in the kernel.
|
|
||||||
// The kernel stores nice values as numbers in the range 0 (high) to 39 (low), corresponding to the user-
|
|
||||||
// visible nice range of -20 to 19.
|
|
||||||
//
|
|
||||||
// Before Linux 2.6, this was a scaled value based on the scheduler weighting given to this process.
|
|
||||||
long priority;
|
|
||||||
// nice %ld The nice value (see setpriority(2)), a value in the range 19 (low priority) to -20 (high priority).
|
|
||||||
long nice;
|
|
||||||
// num_threads %ld
|
|
||||||
// Number of threads in this process (since Linux 2.6). Before kernel 2.6, this field was hard coded to
|
|
||||||
// 0 as a placeholder for an earlier removed field.
|
|
||||||
long num_threads;
|
|
||||||
// itrealvalue %ld
|
|
||||||
// The time in jiffies before the next SIGALRM is sent to the process due to an interval timer. Since
|
|
||||||
// kernel 2.6.17, this field is no longer maintained, and is hard coded as 0.
|
|
||||||
long itrealvalue;
|
|
||||||
// starttime %llu (was %lu before Linux 2.6)
|
|
||||||
// The time in jiffies the process started after system boot.
|
|
||||||
long long starttime;
|
|
||||||
// vsize %lu Virtual memory size in bytes.
|
|
||||||
unsigned long vsize;
|
|
||||||
// rss %ld Resident Set Size: number of pages the process has in real memory. This is just the pages which count
|
|
||||||
// towards text, data, or stack space. This does not include pages which have not been demand-loaded in,
|
|
||||||
// or which are swapped out.
|
|
||||||
long rss;
|
|
||||||
// rsslim %lu Current soft limit in bytes on the rss of the process; see the description of RLIMIT_RSS in getprior-
|
|
||||||
// ity(2).
|
|
||||||
unsigned long rsslim;
|
|
||||||
// startcode %lu
|
|
||||||
// The address above which program text can run.
|
|
||||||
unsigned long startcode;
|
|
||||||
// endcode %lu The address below which program text can run.
|
|
||||||
unsigned long endcode;
|
|
||||||
// startstack %lu
|
|
||||||
// The address of the start (i.e., bottom) of the stack.
|
|
||||||
unsigned long startstack;
|
|
||||||
// kstkesp %lu The current value of ESP (stack pointer), as found in the kernel stack page for the process.
|
|
||||||
unsigned long kstkesp;
|
|
||||||
// kstkeip %lu The current EIP (instruction pointer).
|
|
||||||
unsigned long kstkeip;
|
|
||||||
// signal %lu The bitmap of pending signals, displayed as a decimal number. Obsolete, because it does not provide
|
|
||||||
// information on real-time signals; use /proc/[pid]/status instead.
|
|
||||||
unsigned long signal;
|
|
||||||
// blocked %lu The bitmap of blocked signals, displayed as a decimal number. Obsolete, because it does not provide
|
|
||||||
// information on real-time signals; use /proc/[pid]/status instead.
|
|
||||||
unsigned long blocked;
|
|
||||||
// sigignore %lu
|
|
||||||
// The bitmap of ignored signals, displayed as a decimal number. Obsolete, because it does not provide
|
|
||||||
// information on real-time signals; use /proc/[pid]/status instead.
|
|
||||||
unsigned long sigignore;
|
|
||||||
// sigcatch %lu
|
|
||||||
// The bitmap of caught signals, displayed as a decimal number. Obsolete, because it does not provide
|
|
||||||
// information on real-time signals; use /proc/[pid]/status instead.
|
|
||||||
unsigned long sigcatch;
|
|
||||||
// wchan %lu This is the "channel" in which the process is waiting. It is the address of a system call, and can be
|
|
||||||
// looked up in a namelist if you need a textual name. (If you have an up-to-date /etc/psdatabase, then
|
|
||||||
// try ps -l to see the WCHAN field in action.)
|
|
||||||
unsigned long wchan;
|
|
||||||
// nswap %lu Number of pages swapped (not maintained).
|
|
||||||
unsigned long nswap;
|
|
||||||
// cnswap %lu Cumulative nswap for child processes (not maintained).
|
|
||||||
unsigned long cnswap;
|
|
||||||
// exit_signal %d (since Linux 2.1.22)
|
|
||||||
// Signal to be sent to parent when we die.
|
|
||||||
int exit_signal;
|
|
||||||
// processor %d (since Linux 2.2.8)
|
|
||||||
// CPU number last executed on.
|
|
||||||
int processor;
|
|
||||||
// rt_priority %u (since Linux 2.5.19; was %lu before Linux 2.6.22)
|
|
||||||
// Real-time scheduling priority, a number in the range 1 to 99 for processes scheduled under a real-time
|
|
||||||
// policy, or 0, for non-real-time processes (see sched_setscheduler(2)).
|
|
||||||
unsigned int rt_priority;
|
|
||||||
// policy %u (since Linux 2.5.19; was %lu before Linux 2.6.22)
|
|
||||||
// Scheduling policy (see sched_setscheduler(2)). Decode using the SCHED_* constants in linux/sched.h.
|
|
||||||
unsigned int policy;
|
|
||||||
// delayacct_blkio_ticks %llu (since Linux 2.6.18)
|
|
||||||
// Aggregated block I/O delays, measured in clock ticks (centiseconds).
|
|
||||||
unsigned long long delayacct_blkio_ticks;
|
|
||||||
// guest_time %lu (since Linux 2.6.24)
|
|
||||||
// Guest time of the process (time spent running a virtual CPU for a guest operating system), measured in
|
|
||||||
// clock ticks (divide by sysconf(_SC_CLK_TCK).
|
|
||||||
unsigned long guest_time;
|
|
||||||
// cguest_time %ld (since Linux 2.6.24)
|
|
||||||
// Guest time of the process’s children, measured in clock ticks (divide by sysconf(_SC_CLK_TCK).
|
|
||||||
long cguest_time;
|
|
||||||
|
|
||||||
SrsProcSelfStat();
|
|
||||||
};
|
|
||||||
|
|
||||||
// @see: man 5 proc, /proc/stat
|
|
||||||
struct SrsProcSystemStat
|
|
||||||
{
|
|
||||||
// whether the data is ok.
|
|
||||||
bool ok;
|
|
||||||
// the time in ms when sample.
|
|
||||||
int64_t sample_time;
|
|
||||||
// the percent of usage. 0.153 is 15.3%.
|
|
||||||
float percent;
|
|
||||||
|
|
||||||
// always be cpu
|
|
||||||
char label[32];
|
|
||||||
|
|
||||||
//The amount of time, measured in units of USER_HZ (1/100ths of a second on most architectures, use
|
|
||||||
// sysconf(_SC_CLK_TCK) to obtain the right value)
|
|
||||||
//
|
|
||||||
// the system spent in user mode,
|
|
||||||
unsigned long user;
|
|
||||||
// user mode with low priority (nice),
|
|
||||||
unsigned long nice;
|
|
||||||
// system mode,
|
|
||||||
unsigned long sys;
|
|
||||||
// and the idle task, respectively.
|
|
||||||
unsigned long idle;
|
|
||||||
|
|
||||||
// In Linux 2.6 this line includes three additional columns:
|
|
||||||
//
|
|
||||||
// iowait - time waiting for I/O to complete (since 2.5.41);
|
|
||||||
unsigned long iowait;
|
|
||||||
// irq - time servicing interrupts (since 2.6.0-test4);
|
|
||||||
unsigned long irq;
|
|
||||||
// softirq - time servicing softirqs (since 2.6.0-test4).
|
|
||||||
unsigned long softirq;
|
|
||||||
|
|
||||||
// Since Linux 2.6.11, there is an eighth column,
|
|
||||||
// steal - stolen time, which is the time spent in other oper-
|
|
||||||
// ating systems when running in a virtualized environment
|
|
||||||
unsigned long steal;
|
|
||||||
|
|
||||||
// Since Linux 2.6.24, there is a ninth column,
|
|
||||||
// guest, which is the time spent running a virtual CPU for guest
|
|
||||||
// operating systems under the control of the Linux kernel.
|
|
||||||
unsigned long guest;
|
|
||||||
|
|
||||||
SrsProcSystemStat();
|
|
||||||
};
|
|
||||||
|
|
||||||
// get system cpu stat, use cache to avoid performance problem.
|
|
||||||
extern SrsProcSelfStat* srs_get_self_proc_stat();
|
|
||||||
// get system cpu stat, use cache to avoid performance problem.
|
|
||||||
extern SrsProcSystemStat* srs_get_system_proc_stat();
|
|
||||||
// the deamon st-thread will update it.
|
|
||||||
extern void srs_update_proc_stat();
|
|
||||||
|
|
||||||
// @see: cat /proc/meminfo
|
|
||||||
struct SrsMemInfo
|
|
||||||
{
|
|
||||||
// whether the data is ok.
|
|
||||||
bool ok;
|
|
||||||
// the time in ms when sample.
|
|
||||||
int64_t sample_time;
|
|
||||||
// the percent of usage. 0.153 is 15.3%.
|
|
||||||
float percent_ram;
|
|
||||||
float percent_swap;
|
|
||||||
|
|
||||||
// MemActive = MemTotal - MemFree
|
|
||||||
int64_t MemActive;
|
|
||||||
// RealInUse = MemActive - Buffers - Cached
|
|
||||||
int64_t RealInUse;
|
|
||||||
// NotInUse = MemTotal - RealInUse
|
|
||||||
// = MemTotal - MemActive + Buffers + Cached
|
|
||||||
// = MemTotal - MemTotal + MemFree + Buffers + Cached
|
|
||||||
// = MemFree + Buffers + Cached
|
|
||||||
int64_t NotInUse;
|
|
||||||
|
|
||||||
int64_t MemTotal;
|
|
||||||
int64_t MemFree;
|
|
||||||
int64_t Buffers;
|
|
||||||
int64_t Cached;
|
|
||||||
int64_t SwapTotal;
|
|
||||||
int64_t SwapFree;
|
|
||||||
|
|
||||||
SrsMemInfo();
|
|
||||||
};
|
|
||||||
|
|
||||||
// get system meminfo, use cache to avoid performance problem.
|
|
||||||
extern SrsMemInfo* srs_get_meminfo();
|
|
||||||
// the deamon st-thread will update it.
|
|
||||||
extern void srs_update_meminfo();
|
|
||||||
|
|
||||||
// @see: cat /proc/cpuinfo
|
|
||||||
struct SrsCpuInfo
|
|
||||||
{
|
|
||||||
// whether the data is ok.
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
// The number of processors configured.
|
|
||||||
int nb_processors;
|
|
||||||
// The number of processors currently online (available).
|
|
||||||
int nb_processors_online;
|
|
||||||
|
|
||||||
SrsCpuInfo();
|
|
||||||
};
|
|
||||||
|
|
||||||
// get system cpu info, use cache to avoid performance problem.
|
|
||||||
extern SrsCpuInfo* srs_get_cpuinfo();
|
|
||||||
|
|
||||||
// platform(os, srs) summary
|
|
||||||
struct SrsPlatformInfo
|
|
||||||
{
|
|
||||||
// whether the data is ok.
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
// srs startup time, in ms.
|
|
||||||
int64_t srs_startup_time;
|
|
||||||
|
|
||||||
// @see: cat /proc/uptime
|
|
||||||
double os_uptime;
|
|
||||||
double os_ilde_time;
|
|
||||||
|
|
||||||
// @see: cat /proc/loadavg
|
|
||||||
double load_one_minutes;
|
|
||||||
double load_five_minutes;
|
|
||||||
double load_fifteen_minutes;
|
|
||||||
|
|
||||||
SrsPlatformInfo();
|
|
||||||
};
|
|
||||||
|
|
||||||
// get platform info, use cache to avoid performance problem.
|
|
||||||
extern SrsPlatformInfo* srs_get_platform_info();
|
|
||||||
// the deamon st-thread will update it.
|
|
||||||
extern void srs_update_platform_info();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue