forked from enlightenment/efl
312 lines
11 KiB
C
312 lines
11 KiB
C
# ifndef _GNU_SOURCE
|
|
# define _GNU_SOURCE 1
|
|
# endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include "eina_debug.h"
|
|
#include "eina_types.h"
|
|
#include "eina_list.h"
|
|
#include "eina_mempool.h"
|
|
#include "eina_util.h"
|
|
#include "eina_evlog.h"
|
|
#include "eina_debug_private.h"
|
|
|
|
volatile int _eina_debug_sysmon_reset = 0;
|
|
volatile int _eina_debug_sysmon_active = 0;
|
|
volatile int _eina_debug_evlog_active = 0;
|
|
|
|
static Eina_Lock _sysmon_lock;
|
|
|
|
static Eina_Bool _sysmon_thread_runs = EINA_FALSE;
|
|
static pthread_t _sysmon_thread;
|
|
|
|
// this is a DEDICATED thread tojust collect system info and to have the
|
|
// least impact it can on a cpu core or system. all this does right now
|
|
// is sleep and wait for a command to begin polling for the cpu state.
|
|
// right now that means iterating through cpu's and getting their cpu
|
|
// frequency to match up with event logs.
|
|
static void *
|
|
_sysmon(void *_data EINA_UNUSED)
|
|
{
|
|
static int cpufreqs[64] = { 0 };
|
|
int i, fd, freq;
|
|
char buf[256], path[256];
|
|
ssize_t red;
|
|
#if defined(__clockid_t_defined)
|
|
static struct timespec t_last = { 0, 0 };
|
|
static Eina_Debug_Thread *prev_threads = NULL;
|
|
static int prev_threads_num = 0;
|
|
int j, cpu;
|
|
Eina_Bool prev_threads_redo;
|
|
clockid_t cid;
|
|
struct timespec t, t_now;
|
|
unsigned long long tim_span, tim1, tim2;
|
|
#endif
|
|
|
|
// set a name for this thread for system debugging
|
|
#ifdef EINA_HAVE_PTHREAD_SETNAME
|
|
# ifndef __linux__
|
|
pthread_set_name_np
|
|
# else
|
|
pthread_setname_np
|
|
# endif
|
|
(pthread_self(), "Edbg-sys");
|
|
#endif
|
|
for (;;)
|
|
{
|
|
// wait on a mutex that will be locked for as long as this
|
|
// threead is not meant to go running
|
|
eina_lock_take(&_sysmon_lock);
|
|
// if we need to reset as we just started polling system stats...
|
|
if (_eina_debug_sysmon_reset)
|
|
{
|
|
_eina_debug_sysmon_reset = 0;
|
|
// clear out all the clocks for threads
|
|
#if defined(__clockid_t_defined)
|
|
// reset the last clock timestamp when we checked to "now"
|
|
clock_gettime(CLOCK_MONOTONIC, &t);
|
|
t_last = t;
|
|
// walk over all threads
|
|
eina_spinlock_take(&_eina_debug_thread_lock);
|
|
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
|
{
|
|
// get the correct clock id to use for the target thread
|
|
pthread_getcpuclockid
|
|
(_eina_debug_thread_active[i].thread, &cid);
|
|
// get the clock cpu time accumulation for that threas
|
|
clock_gettime(cid, &t);
|
|
_eina_debug_thread_active[i].clok = t;
|
|
}
|
|
eina_spinlock_release(&_eina_debug_thread_lock);
|
|
#endif
|
|
// clear all the cpu freq (up to 64 cores) to 0
|
|
for (i = 0; i < 64; i++) cpufreqs[i] = 0;
|
|
}
|
|
eina_lock_release(&_sysmon_lock);
|
|
|
|
#if defined(__clockid_t_defined)
|
|
// get the current time
|
|
clock_gettime(CLOCK_MONOTONIC, &t_now);
|
|
tim1 = (t_last.tv_sec * 1000000000LL) + t_last.tv_nsec;
|
|
// the time span between now and last time we checked
|
|
tim_span = ((t_now.tv_sec * 1000000000LL) + t_now.tv_nsec) - tim1;
|
|
// if the time span is non-zero we might get sensible results
|
|
if (tim_span > 0)
|
|
{
|
|
prev_threads_redo = EINA_FALSE;
|
|
eina_spinlock_take(&_eina_debug_thread_lock);
|
|
// figure out if the list of thread id's has changed since
|
|
// our last poll. this is imporant as we need to set the
|
|
// thread cpu usage to 0 for threads that have disappeared
|
|
if (prev_threads_num != _eina_debug_thread_active_num)
|
|
prev_threads_redo = EINA_TRUE;
|
|
else
|
|
{
|
|
// XXX: isolate this out of hot path
|
|
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
|
{
|
|
if (_eina_debug_thread_active[i].thread !=
|
|
prev_threads[i].thread)
|
|
{
|
|
prev_threads_redo = EINA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
|
{
|
|
pthread_t thread = _eina_debug_thread_active[i].thread;
|
|
// get the clock for the thread and its cpu time usage
|
|
pthread_getcpuclockid(thread, &cid);
|
|
clock_gettime(cid, &t);
|
|
// calculate a long timestamp (64bits)
|
|
tim1 = (_eina_debug_thread_active[i].clok.tv_sec * 1000000000LL) +
|
|
_eina_debug_thread_active[i].clok.tv_nsec;
|
|
// and get the difference in clock time in NS
|
|
tim2 = ((t.tv_sec * 1000000000LL) + t.tv_nsec) - tim1;
|
|
// and that as percentage of the timespan
|
|
cpu = (int)((100 * (int)tim2) / (int)tim_span);
|
|
// round to the nearest 10 percent - rough anyway
|
|
cpu = ((cpu + 5) / 10) * 10;
|
|
if (cpu > 100) cpu = 100;
|
|
// if the usage changed since last time we checked...
|
|
if (cpu != _eina_debug_thread_active[i].val)
|
|
{
|
|
// log this change
|
|
snprintf(buf, sizeof(buf), "*CPUUSED %llu",
|
|
(unsigned long long)thread);
|
|
snprintf(path, sizeof(path), "%i", _eina_debug_thread_active[i].val);
|
|
eina_evlog(buf, NULL, 0.0, path);
|
|
snprintf(path, sizeof(path), "%i", cpu);
|
|
eina_evlog(buf, NULL, 0.0, path);
|
|
// store the clock time + cpu we got for next poll
|
|
_eina_debug_thread_active[i].val = cpu;
|
|
}
|
|
_eina_debug_thread_active[i].clok = t;
|
|
}
|
|
// so threads changed between this poll and last so we need
|
|
// to redo our mapping/storage of them
|
|
if (prev_threads_redo)
|
|
{
|
|
prev_threads_redo = EINA_FALSE;
|
|
// find any threads from our last run that do not
|
|
// exist now in our new list of threads
|
|
for (j = 0; j < prev_threads_num; j++)
|
|
{
|
|
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
|
{
|
|
if (prev_threads[j].thread ==
|
|
_eina_debug_thread_active[i].thread) break;
|
|
}
|
|
// thread was there before and not now
|
|
if (i == _eina_debug_thread_active_num)
|
|
{
|
|
// log it finishing - ie 0
|
|
snprintf(buf, sizeof(buf), "*CPUUSED %llu",
|
|
(unsigned long long)
|
|
prev_threads[i].thread);
|
|
eina_evlog(buf, NULL, 0.0, "0");
|
|
}
|
|
}
|
|
// if the thread count changed then allocate a new shadow
|
|
// buffer of thread id's etc.
|
|
if (prev_threads_num != _eina_debug_thread_active_num)
|
|
{
|
|
if (prev_threads) free(prev_threads);
|
|
prev_threads_num = _eina_debug_thread_active_num;
|
|
prev_threads = malloc(prev_threads_num *
|
|
sizeof(Eina_Debug_Thread));
|
|
}
|
|
// store the thread handles/id's
|
|
for (i = 0; i < prev_threads_num; i++)
|
|
prev_threads[i].thread =
|
|
_eina_debug_thread_active[i].thread;
|
|
}
|
|
eina_spinlock_release(&_eina_debug_thread_lock);
|
|
t_last = t_now;
|
|
}
|
|
#endif
|
|
// poll up to 64 cpu cores for cpufreq info to log alongside
|
|
// the evlog call data
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
// look at sysfs nodes per cpu
|
|
snprintf
|
|
(buf, sizeof(buf),
|
|
"/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i);
|
|
fd = open(buf, O_RDONLY);
|
|
freq = 0;
|
|
// if the node is there, read it
|
|
if (fd >= 0)
|
|
{
|
|
// really low overhead read from cpufreq node (just an int)
|
|
red = read(fd, buf, sizeof(buf) - 1);
|
|
if (red > 0)
|
|
{
|
|
// read something - it should be an int with whitespace
|
|
buf[red] = 0;
|
|
freq = atoi(buf);
|
|
// move to mhz
|
|
freq = (freq + 500) / 1000;
|
|
// round mhz to the nearest 100mhz - to have less noise
|
|
freq = ((freq + 50) / 100) * 100;
|
|
}
|
|
// close the fd so we can freshly poll next time around
|
|
close(fd);
|
|
}
|
|
// node not there - ran out of cpu's to poll?
|
|
else break;
|
|
// if the current frequency changed vs previous poll, then log
|
|
if (freq != cpufreqs[i])
|
|
{
|
|
snprintf(buf, sizeof(buf), "*CPUFREQ %i", i);
|
|
snprintf(path, sizeof(path), "%i", freq);
|
|
eina_evlog(buf, NULL, 0.0, path);
|
|
cpufreqs[i] = freq;
|
|
}
|
|
}
|
|
usleep(1000); // 1ms sleep
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_cpufreq_on_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
|
|
{
|
|
if (!_eina_debug_evlog_active)
|
|
{
|
|
_eina_debug_evlog_active = 1;
|
|
eina_evlog_start();
|
|
}
|
|
if (!_eina_debug_sysmon_active)
|
|
{
|
|
_eina_debug_sysmon_reset = 1;
|
|
_eina_debug_sysmon_active = 1;
|
|
eina_lock_release(&_sysmon_lock);
|
|
}
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_cpufreq_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
|
|
{
|
|
if (_eina_debug_sysmon_active)
|
|
{
|
|
eina_lock_take(&_sysmon_lock);
|
|
_eina_debug_sysmon_active = 0;
|
|
}
|
|
if (_eina_debug_evlog_active)
|
|
{
|
|
eina_evlog_stop();
|
|
_eina_debug_evlog_active = 0;
|
|
}
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EINA_DEBUG_OPCODES_ARRAY_DEFINE(_OPS,
|
|
{"CPU/Freq/on", NULL, &_cpufreq_on_cb},
|
|
{"CPU/Freq/off", NULL, &_cpufreq_off_cb},
|
|
{NULL, NULL, NULL}
|
|
);
|
|
|
|
Eina_Bool
|
|
_eina_debug_cpu_init(void)
|
|
{
|
|
// if it's already running - we're good.
|
|
if (!_sysmon_thread_runs)
|
|
{
|
|
int err;
|
|
eina_lock_new(&_sysmon_lock);
|
|
eina_lock_take(&_sysmon_lock);
|
|
err = pthread_create(&_sysmon_thread, NULL, _sysmon, NULL);
|
|
if (err != 0)
|
|
{
|
|
e_debug("EINA DEBUG ERROR: Can't create debug sysmon thread!");
|
|
abort();
|
|
}
|
|
_sysmon_thread_runs = EINA_TRUE;
|
|
}
|
|
eina_debug_opcodes_register(NULL, _OPS(), NULL, NULL);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
Eina_Bool
|
|
_eina_debug_cpu_shutdown(void)
|
|
{
|
|
return EINA_TRUE;
|
|
}
|
|
|