mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2026-02-12 16:10:54 +00:00
libsysprof-profile: add various CPU parsing
This still needs some work because the read operations are blocked currently.
This commit is contained in:
@ -38,6 +38,7 @@ libsysprof_profile_deps = [
|
|||||||
dependency('libdex-1', version: dex_req_version),
|
dependency('libdex-1', version: dex_req_version),
|
||||||
dependency('polkit-gobject-1', version: polkit_req_version),
|
dependency('polkit-gobject-1', version: polkit_req_version),
|
||||||
|
|
||||||
|
liblinereader_static_dep,
|
||||||
libsysprof_capture_dep,
|
libsysprof_capture_dep,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -28,12 +28,17 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <glib-unix.h>
|
||||||
#include <glib/gstdio.h>
|
#include <glib/gstdio.h>
|
||||||
|
|
||||||
|
#include "line-reader-private.h"
|
||||||
|
|
||||||
#include "sysprof-cpu-usage.h"
|
#include "sysprof-cpu-usage.h"
|
||||||
#include "sysprof-instrument-private.h"
|
#include "sysprof-instrument-private.h"
|
||||||
#include "sysprof-recording-private.h"
|
#include "sysprof-recording-private.h"
|
||||||
|
|
||||||
|
#define PROC_STAT_BUF_SIZE (4096*4)
|
||||||
|
|
||||||
struct _SysprofCpuUsage
|
struct _SysprofCpuUsage
|
||||||
{
|
{
|
||||||
SysprofInstrument parent_instance;
|
SysprofInstrument parent_instance;
|
||||||
@ -44,14 +49,30 @@ struct _SysprofCpuUsageClass
|
|||||||
SysprofInstrumentClass parent_class;
|
SysprofInstrumentClass parent_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
PROP_0,
|
|
||||||
N_PROPS
|
|
||||||
};
|
|
||||||
|
|
||||||
G_DEFINE_FINAL_TYPE (SysprofCpuUsage, sysprof_cpu_usage, SYSPROF_TYPE_INSTRUMENT)
|
G_DEFINE_FINAL_TYPE (SysprofCpuUsage, sysprof_cpu_usage, SYSPROF_TYPE_INSTRUMENT)
|
||||||
|
|
||||||
static GParamSpec *properties [N_PROPS];
|
typedef struct _CpuInfo
|
||||||
|
{
|
||||||
|
int counter_base;
|
||||||
|
double total;
|
||||||
|
glong last_user;
|
||||||
|
glong last_idle;
|
||||||
|
glong last_system;
|
||||||
|
glong last_nice;
|
||||||
|
glong last_iowait;
|
||||||
|
glong last_irq;
|
||||||
|
glong last_softirq;
|
||||||
|
glong last_steal;
|
||||||
|
glong last_guest;
|
||||||
|
glong last_guest_nice;
|
||||||
|
} CpuInfo;
|
||||||
|
|
||||||
|
typedef struct _CpuFreq
|
||||||
|
{
|
||||||
|
gint64 max;
|
||||||
|
int stat_fd;
|
||||||
|
char buf[116];
|
||||||
|
} CpuFreq;
|
||||||
|
|
||||||
typedef struct _Record
|
typedef struct _Record
|
||||||
{
|
{
|
||||||
@ -69,14 +90,48 @@ record_free (gpointer data)
|
|||||||
g_free (record);
|
g_free (record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
freq_info_clear (gpointer data)
|
||||||
|
{
|
||||||
|
CpuFreq *cpu_freq = data;
|
||||||
|
g_clear_fd (&cpu_freq->stat_fd, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
get_cpu_freq (int stat_fd,
|
||||||
|
guint cpu,
|
||||||
|
double max,
|
||||||
|
char *buf,
|
||||||
|
gssize len)
|
||||||
|
{
|
||||||
|
gint64 val;
|
||||||
|
|
||||||
|
if (stat_fd == -1)
|
||||||
|
return .0;
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
return .0;
|
||||||
|
|
||||||
|
buf[len] = 0;
|
||||||
|
g_strchug (buf);
|
||||||
|
val = g_ascii_strtoll (buf, NULL, 10);
|
||||||
|
|
||||||
|
return (double)val / max * 100.;
|
||||||
|
}
|
||||||
|
|
||||||
static DexFuture *
|
static DexFuture *
|
||||||
sysprof_cpu_usage_record_fiber (gpointer user_data)
|
sysprof_cpu_usage_record_fiber (gpointer user_data)
|
||||||
{
|
{
|
||||||
Record *record = user_data;
|
Record *record = user_data;
|
||||||
|
g_autoptr(GArray) cpu_info = NULL;
|
||||||
|
g_autoptr(GArray) freq_info = NULL;
|
||||||
|
g_autofd int stat_fd = -1;
|
||||||
|
g_autofree char *read_buffer = NULL;
|
||||||
|
SysprofCaptureCounterValue *values;
|
||||||
SysprofCaptureCounter *counters;
|
SysprofCaptureCounter *counters;
|
||||||
SysprofCaptureCounter *counter;
|
SysprofCaptureCounter *counter;
|
||||||
SysprofCaptureWriter *writer;
|
SysprofCaptureWriter *writer;
|
||||||
g_autofd int stat_fd = -1;
|
guint *ids;
|
||||||
guint n_cpu;
|
guint n_cpu;
|
||||||
|
|
||||||
g_assert (record != NULL);
|
g_assert (record != NULL);
|
||||||
@ -86,7 +141,16 @@ sysprof_cpu_usage_record_fiber (gpointer user_data)
|
|||||||
writer = _sysprof_recording_writer (record->recording);
|
writer = _sysprof_recording_writer (record->recording);
|
||||||
n_cpu = g_get_num_processors ();
|
n_cpu = g_get_num_processors ();
|
||||||
stat_fd = open ("/proc/stat", O_RDONLY|O_CLOEXEC);
|
stat_fd = open ("/proc/stat", O_RDONLY|O_CLOEXEC);
|
||||||
|
g_unix_set_fd_nonblocking (stat_fd, TRUE, NULL);
|
||||||
|
read_buffer = g_malloc (PROC_STAT_BUF_SIZE);
|
||||||
|
|
||||||
counters = g_alloca (sizeof *counters * ((n_cpu * 2) + 1));
|
counters = g_alloca (sizeof *counters * ((n_cpu * 2) + 1));
|
||||||
|
ids = g_alloca (sizeof *ids * ((n_cpu * 2) + 1));
|
||||||
|
values = g_alloca (sizeof *values * ((n_cpu * 2) + 1));
|
||||||
|
|
||||||
|
cpu_info = g_array_new (FALSE, TRUE, sizeof (CpuInfo));
|
||||||
|
freq_info = g_array_new (FALSE, TRUE, sizeof (CpuFreq));
|
||||||
|
g_array_set_clear_func (freq_info, freq_info_clear);
|
||||||
|
|
||||||
/* Create counter information for all of our counters that we will need
|
/* Create counter information for all of our counters that we will need
|
||||||
* to submit in upcoming parses.
|
* to submit in upcoming parses.
|
||||||
@ -94,6 +158,13 @@ sysprof_cpu_usage_record_fiber (gpointer user_data)
|
|||||||
for (guint i = 0; i < n_cpu; i++)
|
for (guint i = 0; i < n_cpu; i++)
|
||||||
{
|
{
|
||||||
guint counter_base = sysprof_capture_writer_request_counter (writer, 2);
|
guint counter_base = sysprof_capture_writer_request_counter (writer, 2);
|
||||||
|
g_autofree char *max_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_max_freq", i);
|
||||||
|
g_autofree char *cur_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i);
|
||||||
|
g_autofree char *max_value = NULL;
|
||||||
|
CpuFreq cf;
|
||||||
|
|
||||||
|
ids[n_cpu*2] = counter_base;
|
||||||
|
ids[n_cpu*2+1] = counter_base + 1;
|
||||||
|
|
||||||
counter = &counters[i*2];
|
counter = &counters[i*2];
|
||||||
counter->id = counter_base;
|
counter->id = counter_base;
|
||||||
@ -112,11 +183,21 @@ sysprof_cpu_usage_record_fiber (gpointer user_data)
|
|||||||
g_snprintf (counter->name, sizeof counter->name, "CPU %d", i);
|
g_snprintf (counter->name, sizeof counter->name, "CPU %d", i);
|
||||||
g_snprintf (counter->description, sizeof counter->description,
|
g_snprintf (counter->description, sizeof counter->description,
|
||||||
"Frequency of CPU %d", i);
|
"Frequency of CPU %d", i);
|
||||||
|
|
||||||
|
cf.stat_fd = open (cur_path, O_RDONLY|O_CLOEXEC);
|
||||||
|
g_unix_set_fd_nonblocking (cf.stat_fd, TRUE, NULL);
|
||||||
|
cf.buf[0] = 0;
|
||||||
|
if (g_file_get_contents (max_path, &max_value, NULL, NULL))
|
||||||
|
cf.max = g_ascii_strtoll (max_value, NULL, 10);
|
||||||
|
else
|
||||||
|
cf.max = 0;
|
||||||
|
g_array_append_val (freq_info, cf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now create our combined counter */
|
/* Now create our combined counter */
|
||||||
|
ids[n_cpu*2] = sysprof_capture_writer_request_counter (writer, 1);
|
||||||
counter = &counters[n_cpu*2];
|
counter = &counters[n_cpu*2];
|
||||||
counter->id = sysprof_capture_writer_request_counter (writer, 1);
|
counter->id = ids[n_cpu*2];
|
||||||
counter->type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
|
counter->type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
|
||||||
counter->value.vdbl = 0;
|
counter->value.vdbl = 0;
|
||||||
g_strlcpy (counter->category, "CPU Percent", sizeof counter->category);
|
g_strlcpy (counter->category, "CPU Percent", sizeof counter->category);
|
||||||
@ -131,7 +212,151 @@ sysprof_cpu_usage_record_fiber (gpointer user_data)
|
|||||||
counters,
|
counters,
|
||||||
n_cpu * 2 + 1);
|
n_cpu * 2 + 1);
|
||||||
|
|
||||||
g_print ("Registering %d counters\n", n_cpu * 2 + 1);
|
for (;;)
|
||||||
|
{
|
||||||
|
g_autoptr(GPtrArray) futures = g_ptr_array_new_with_free_func (dex_unref);
|
||||||
|
g_autoptr(DexFuture) cpu_future = NULL;
|
||||||
|
LineReader reader;
|
||||||
|
glong total_usage = 0;
|
||||||
|
gsize line_len;
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
g_print ("parsing\n");
|
||||||
|
|
||||||
|
/* First collect all our reads and then wait for them to finish before
|
||||||
|
* parsing in a pass. With io_uring, this lets us coalesce all the lseek
|
||||||
|
* and reads into a single set of iops.
|
||||||
|
*/
|
||||||
|
for (guint i = 0; i < n_cpu; i++)
|
||||||
|
{
|
||||||
|
CpuFreq *cf = &g_array_index (freq_info, CpuFreq, i);
|
||||||
|
|
||||||
|
g_ptr_array_add (futures, dex_aio_read (NULL, cf->stat_fd, cf->buf, sizeof cf->buf - 1, 0));
|
||||||
|
}
|
||||||
|
cpu_future = dex_aio_read (NULL, stat_fd, read_buffer, PROC_STAT_BUF_SIZE, 0);
|
||||||
|
g_ptr_array_add (futures, dex_ref (cpu_future));
|
||||||
|
g_ptr_array_add (futures, dex_ref (record->cancellable));
|
||||||
|
if (!dex_await (dex_future_allv ((DexFuture **)futures->pdata, futures->len), NULL))
|
||||||
|
break;
|
||||||
|
|
||||||
|
g_print ("Waiting for completions\n");
|
||||||
|
|
||||||
|
/* Now parse all the contents of the stat files which should be
|
||||||
|
* populated in the various files.
|
||||||
|
*/
|
||||||
|
line_reader_init (&reader, read_buffer, dex_await_int64 (cpu_future, NULL));
|
||||||
|
while ((line = line_reader_next (&reader, &line_len)))
|
||||||
|
{
|
||||||
|
CpuInfo *ci;
|
||||||
|
char cpu[64];
|
||||||
|
glong user;
|
||||||
|
glong sys;
|
||||||
|
glong nice;
|
||||||
|
glong idle;
|
||||||
|
int id;
|
||||||
|
int ret;
|
||||||
|
glong iowait;
|
||||||
|
glong irq;
|
||||||
|
glong softirq;
|
||||||
|
glong steal;
|
||||||
|
glong guest;
|
||||||
|
glong guest_nice;
|
||||||
|
glong user_calc;
|
||||||
|
glong system_calc;
|
||||||
|
glong nice_calc;
|
||||||
|
glong idle_calc;
|
||||||
|
glong iowait_calc;
|
||||||
|
glong irq_calc;
|
||||||
|
glong softirq_calc;
|
||||||
|
glong steal_calc;
|
||||||
|
glong guest_calc;
|
||||||
|
glong guest_nice_calc;
|
||||||
|
glong total;
|
||||||
|
|
||||||
|
line[line_len] = 0;
|
||||||
|
|
||||||
|
/* CPU lines come first, short-circuit after */
|
||||||
|
if (!g_str_has_prefix (line, "cpu"))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* First line is "cpu ..." */
|
||||||
|
if (!g_ascii_isdigit (line[3]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Parse the various counters in order */
|
||||||
|
user = nice = sys = idle = id = 0;
|
||||||
|
ret = sscanf (line, "%s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
|
||||||
|
cpu, &user, &nice, &sys, &idle,
|
||||||
|
&iowait, &irq, &softirq, &steal, &guest, &guest_nice);
|
||||||
|
if (ret != 11)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Get the CPU identifier */
|
||||||
|
ret = sscanf (cpu, "cpu%d", &id);
|
||||||
|
if (ret != 1 || id < 0 || id >= n_cpu)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ci = &g_array_index (cpu_info, CpuInfo, id);
|
||||||
|
|
||||||
|
user_calc = user - ci->last_user;
|
||||||
|
nice_calc = nice - ci->last_nice;
|
||||||
|
system_calc = sys - ci->last_system;
|
||||||
|
idle_calc = idle - ci->last_idle;
|
||||||
|
iowait_calc = iowait - ci->last_iowait;
|
||||||
|
irq_calc = irq - ci->last_irq;
|
||||||
|
softirq_calc = softirq - ci->last_softirq;
|
||||||
|
steal_calc = steal - ci->last_steal;
|
||||||
|
guest_calc = guest - ci->last_guest;
|
||||||
|
guest_nice_calc = guest_nice - ci->last_guest_nice;
|
||||||
|
|
||||||
|
total = user_calc + nice_calc + system_calc + idle_calc + iowait_calc + irq_calc + softirq_calc + steal_calc + guest_calc + guest_nice_calc;
|
||||||
|
ci->total = ((total - idle_calc) / (double)total) * 100.;
|
||||||
|
|
||||||
|
ci->last_user = user;
|
||||||
|
ci->last_nice = nice;
|
||||||
|
ci->last_idle = idle;
|
||||||
|
ci->last_system = sys;
|
||||||
|
ci->last_iowait = iowait;
|
||||||
|
ci->last_irq = irq;
|
||||||
|
ci->last_softirq = softirq;
|
||||||
|
ci->last_steal = steal;
|
||||||
|
ci->last_guest = guest;
|
||||||
|
ci->last_guest_nice = guest_nice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Publish counters to the capture file */
|
||||||
|
for (guint i = 0; i < n_cpu; i++)
|
||||||
|
{
|
||||||
|
const CpuInfo *ci = &g_array_index (cpu_info, CpuInfo, i);
|
||||||
|
CpuFreq *cf = &g_array_index (freq_info, CpuFreq, i);
|
||||||
|
DexFuture *freq_future = g_ptr_array_index (futures, i);
|
||||||
|
gssize len = dex_await_int64 (dex_ref (freq_future), NULL);
|
||||||
|
|
||||||
|
values[n_cpu*i].vdbl = ci->total;
|
||||||
|
values[n_cpu*i+1].vdbl = get_cpu_freq (cf->stat_fd, i, cf->max, cf->buf, len);
|
||||||
|
|
||||||
|
total_usage += ci->total;
|
||||||
|
}
|
||||||
|
|
||||||
|
values[n_cpu*2].vdbl = total_usage / (double)n_cpu;
|
||||||
|
sysprof_capture_writer_set_counters (writer,
|
||||||
|
SYSPROF_CAPTURE_CURRENT_TIME,
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
ids,
|
||||||
|
values,
|
||||||
|
n_cpu * 2 + 1);
|
||||||
|
|
||||||
|
g_print ("adding counters\n");
|
||||||
|
|
||||||
|
/* Wait for cancellation or ½ second */
|
||||||
|
dex_await (dex_future_any (dex_ref (record->cancellable),
|
||||||
|
dex_timeout_new_usec (G_USEC_PER_SEC / 2),
|
||||||
|
NULL),
|
||||||
|
NULL);
|
||||||
|
if (dex_future_get_status (record->cancellable) != DEX_FUTURE_STATUS_PENDING)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return dex_future_new_for_boolean (TRUE);
|
return dex_future_new_for_boolean (TRUE);
|
||||||
}
|
}
|
||||||
@ -157,54 +382,11 @@ sysprof_cpu_usage_record (SysprofInstrument *instrument,
|
|||||||
record_free);
|
record_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
sysprof_cpu_usage_finalize (GObject *object)
|
|
||||||
{
|
|
||||||
SysprofCpuUsage *self = (SysprofCpuUsage *)object;
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (sysprof_cpu_usage_parent_class)->finalize (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sysprof_cpu_usage_get_property (GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
SysprofCpuUsage *self = SYSPROF_CPU_USAGE (object);
|
|
||||||
|
|
||||||
switch (prop_id)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sysprof_cpu_usage_set_property (GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
const GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
SysprofCpuUsage *self = SYSPROF_CPU_USAGE (object);
|
|
||||||
|
|
||||||
switch (prop_id)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sysprof_cpu_usage_class_init (SysprofCpuUsageClass *klass)
|
sysprof_cpu_usage_class_init (SysprofCpuUsageClass *klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
||||||
SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass);
|
SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass);
|
||||||
|
|
||||||
object_class->finalize = sysprof_cpu_usage_finalize;
|
|
||||||
object_class->get_property = sysprof_cpu_usage_get_property;
|
|
||||||
object_class->set_property = sysprof_cpu_usage_set_property;
|
|
||||||
|
|
||||||
instrument_class->record = sysprof_cpu_usage_record;
|
instrument_class->record = sysprof_cpu_usage_record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user