This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository evisum.
View the commit online.
commit 8ce76404ca9c2663c7479cdfb9b3e69106b9b37a
Author: Alastair Poole <[email protected]>
AuthorDate: Tue Apr 28 22:33:25 2026 +0100
Fix process network I/O accounting
Restore process RX/TX data from Enigmatic snapshots, make daemon-side process network counters monotonic,
and prevent duplicated socket FDs from double-counting transfer totals.
Also fix RX/s and TX/s rate calculation to use snapshot timestamps, prune stale UI process usage cache entries, and handle cache hash insertion failures.
---
NEWS | 1 +
src/bin/engine/evisum_engine.c | 50 +++++++++++++++++++++++++++++--
src/bin/enigmatic/monitor/processes.c | 33 +++++++++++++++++++++
src/bin/enigmatic/system/process.c | 31 ++++++++++++++++++++
src/bin/enigmatic/system/process.h | 2 ++
src/bin/ui/evisum_ui_process_list.c | 55 ++++++++++++++++++++++++++++++++---
src/bin/ui/evisum_ui_process_view.c | 21 +++++++++++--
7 files changed, 185 insertions(+), 8 deletions(-)
diff --git a/NEWS b/NEWS
index 8b57d96..c79fb91 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ Evisum 2.0.1
============
* Ensure loading snapshot puises each time it's shown
+ * Fix reliabilty of process network usage.
============
Evisum 2.0.0
diff --git a/src/bin/engine/evisum_engine.c b/src/bin/engine/evisum_engine.c
index eaa8096..284a597 100644
--- a/src/bin/engine/evisum_engine.c
+++ b/src/bin/engine/evisum_engine.c
@@ -1219,15 +1219,59 @@ system_network_ifaces_get(int *n)
Proc_Net **
system_network_process_usage_get(int *n)
{
+ const Snapshot *snap;
+ Eina_List *l;
+ Proc_Info_Log *proc;
+ Proc_Net **procs;
+ int count = 0;
+ int i = 0;
+
if (n) *n = 0;
- return NULL;
+ if (!_engine_snapshot_acquire(&snap)) return NULL;
+
+ EINA_LIST_FOREACH(snap->processes, l, proc) {
+ if ((!proc->net_in) && (!proc->net_out)) continue;
+ count++;
+ }
+
+ if (!count) {
+ _engine_snapshot_release();
+ return NULL;
+ }
+
+ procs = calloc(count, sizeof(*procs));
+ if (!procs) {
+ _engine_snapshot_release();
+ return NULL;
+ }
+
+ EINA_LIST_FOREACH(snap->processes, l, proc) {
+ if ((!proc->net_in) && (!proc->net_out)) continue;
+
+ procs[i] = calloc(1, sizeof(**procs));
+ if (!procs[i]) {
+ system_network_process_usage_free(procs, i);
+ _engine_snapshot_release();
+ return NULL;
+ }
+
+ procs[i]->pid = proc->pid;
+ procs[i]->in = proc->net_in;
+ procs[i]->out = proc->net_out;
+ i++;
+ }
+
+ if (n) *n = i;
+ _engine_snapshot_release();
+ return procs;
}
void
system_network_process_usage_free(Proc_Net **procs, int n)
{
- (void) n;
if (!procs) return;
+ for (int i = 0; i < n; i++)
+ free(procs[i]);
free(procs);
}
@@ -1273,6 +1317,8 @@ _proc_from_log(const Proc_Info_Log *src)
p->mem_shared = src->mem_shared;
p->net_in = src->net_in;
p->net_out = src->net_out;
+ p->net_in_raw = src->net_in;
+ p->net_out_raw = src->net_out;
p->disk_read = src->disk_read;
p->disk_write = src->disk_write;
p->numfiles = src->numfiles;
diff --git a/src/bin/enigmatic/monitor/processes.c b/src/bin/enigmatic/monitor/processes.c
index f815469..c95faaf 100644
--- a/src/bin/enigmatic/monitor/processes.c
+++ b/src/bin/enigmatic/monitor/processes.c
@@ -182,6 +182,34 @@ _process_log_string(Enigmatic *enigmatic, pid_t pid, Object_Type object_type, co
*changed = 1;
}
+static void
+_process_network_totals_update(Proc_Info *proc, const Proc_Info *prev)
+{
+ uint64_t raw_in, raw_out;
+
+ if (!proc) return;
+
+ raw_in = proc->net_in;
+ raw_out = proc->net_out;
+ proc->net_in_raw = raw_in;
+ proc->net_out_raw = raw_out;
+
+ if (!prev || (proc->start != prev->start))
+ {
+ proc->net_in = 0;
+ proc->net_out = 0;
+ return;
+ }
+
+ proc->net_in = prev->net_in;
+ proc->net_out = prev->net_out;
+
+ if (raw_in >= prev->net_in_raw)
+ proc->net_in += raw_in - prev->net_in_raw;
+ if (raw_out >= prev->net_out_raw)
+ proc->net_out += raw_out - prev->net_out_raw;
+}
+
static void
processes_refresh(Enigmatic *enigmatic, Eina_Hash **cache_hash)
{
@@ -226,6 +254,7 @@ enigmatic_monitor_processes(Enigmatic *enigmatic, Eina_Hash **cache_hash)
*cache_hash = eina_hash_int32_new(cb_process_free);
EINA_LIST_FOREACH(processes, l, proc)
{
+ _process_network_totals_update(proc, NULL);
DEBUG("add pid => %i => %s", proc->pid, proc->command);
proc->is_new = 1;
int32_t pid = proc->pid;
@@ -282,6 +311,7 @@ enigmatic_monitor_processes(Enigmatic *enigmatic, Eina_Hash **cache_hash)
{
Proc_Info_Log proc_log;
+ _process_network_totals_update(proc, NULL);
proc_info_log_fill(proc, &proc_log);
enigmatic_log_process_write(enigmatic, &proc_log);
@@ -291,6 +321,9 @@ enigmatic_monitor_processes(Enigmatic *enigmatic, Eina_Hash **cache_hash)
continue;
}
+ if (proc != p1)
+ _process_network_totals_update(proc, p1);
+
proc_info_log_fill(p1, &old_log);
proc_info_log_fill(proc, &new_log);
diff --git a/src/bin/enigmatic/system/process.c b/src/bin/enigmatic/system/process.c
index f22327a..c83ec32 100644
--- a/src/bin/enigmatic/system/process.c
+++ b/src/bin/enigmatic/system/process.c
@@ -364,6 +364,12 @@ typedef struct {
uint64_t out;
} Linux_Proc_Net_Stat;
+typedef struct {
+ unsigned long *inodes;
+ int count;
+ int capacity;
+} Linux_Proc_Inode_Set;
+
static int
_linux_proc_socket_stat_cmp(const void *a, const void *b)
{
@@ -521,6 +527,28 @@ _linux_proc_socket_stat_find(const Linux_Proc_Socket_Stat *stats, int count, uns
return bsearch(&key, stats, count, sizeof(*stats), _linux_proc_socket_stat_cmp);
}
+static int
+_linux_proc_inode_seen_add(Linux_Proc_Inode_Set *set, unsigned long inode)
+{
+ unsigned long *tmp;
+ int next_capacity;
+
+ for (int i = 0; i < set->count; i++) {
+ if (set->inodes[i] == inode) return 0;
+ }
+
+ if (set->count >= set->capacity) {
+ next_capacity = set->capacity ? set->capacity * 2 : 16;
+ tmp = realloc(set->inodes, next_capacity * sizeof(*set->inodes));
+ if (!tmp) return -1;
+ set->inodes = tmp;
+ set->capacity = next_capacity;
+ }
+
+ set->inodes[set->count++] = inode;
+ return 1;
+}
+
static Eina_Bool
_linux_proc_net_usage_add(Linux_Proc_Net_Stat ***procs, int *n, pid_t pid, uint64_t in, uint64_t out)
{
@@ -575,6 +603,7 @@ _linux_process_network_usage_get(int *n)
DIR *fd;
struct dirent *fd_entry;
uint64_t in = 0, out = 0;
+ Linux_Proc_Inode_Set seen = { 0 };
if (!isdigit((unsigned char) entry->d_name[0])) continue;
@@ -611,6 +640,7 @@ _linux_process_network_usage_get(int *n)
inode = strtoul(target + 8, &end, 10);
if (!end || (*end != ']')) continue;
+ if (_linux_proc_inode_seen_add(&seen, inode) <= 0) continue;
sock = _linux_proc_socket_stat_find(sockets, socket_count, inode);
if (!sock) continue;
@@ -620,6 +650,7 @@ _linux_process_network_usage_get(int *n)
}
closedir(fd);
+ free(seen.inodes);
if ((!in) && (!out)) continue;
if (!_linux_proc_net_usage_add(&procs, n, pid, in, out)) break;
diff --git a/src/bin/enigmatic/system/process.h b/src/bin/enigmatic/system/process.h
index ad9194a..fe5f032 100644
--- a/src/bin/enigmatic/system/process.h
+++ b/src/bin/enigmatic/system/process.h
@@ -29,6 +29,8 @@ typedef struct _Proc_Info
uint64_t mem_shared;
uint64_t net_in;
uint64_t net_out;
+ uint64_t net_in_raw;
+ uint64_t net_out_raw;
uint64_t disk_read;
uint64_t disk_write;
diff --git a/src/bin/ui/evisum_ui_process_list.c b/src/bin/ui/evisum_ui_process_list.c
index 9fbfd33..14dd38f 100644
--- a/src/bin/ui/evisum_ui_process_list.c
+++ b/src/bin/ui/evisum_ui_process_list.c
@@ -97,7 +97,9 @@ typedef struct {
#define PROC_COL_MIN_WIDTH 48
typedef struct {
+ int64_t pid;
int64_t start;
+ uint32_t sample_time;
uint64_t net_in;
uint64_t net_out;
uint64_t disk_read;
@@ -1296,6 +1298,15 @@ _evisum_ui_process_list_usage_cache_free_cb(void *data) {
free(cache);
}
+static uint32_t
+_evisum_ui_process_list_sample_time_get(void) {
+ uint32_t sample_time;
+
+ sample_time = evisum_engine_history_time_get();
+ if (!sample_time) sample_time = evisum_engine_live_time_get();
+ return sample_time;
+}
+
static Eina_Bool
_evisum_ui_process_list_process_ignore(Evisum_Ui_Process_List_View *view, Proc_Info *proc) {
Evisum_Ui *ui = view->ui;
@@ -1320,16 +1331,26 @@ _evisum_ui_process_list_process_ignore(Evisum_Ui_Process_List_View *view, Proc_I
static Eina_List *
_evisum_ui_process_list_search_trim_cache(Eina_List *list, Evisum_Ui_Process_List_View *view) {
Eina_List *l, *l_next;
+ Eina_List *purge = NULL;
Proc_Info *proc;
+ Proc_Usage_Cache *cache;
+ Eina_Hash *active_pids;
+ uint32_t sample_time;
+ void *d = NULL;
+
+ sample_time = _evisum_ui_process_list_sample_time_get();
+ active_pids = eina_hash_int64_new(NULL);
EINA_LIST_FOREACH_SAFE(list, l, l_next, proc) {
+ int64_t id = proc->pid;
+
+ if (active_pids)
+ eina_hash_add(active_pids, &id, proc);
+
if (_evisum_ui_process_list_process_ignore(view, proc)) {
proc_info_free(proc);
list = eina_list_remove_list(list, l);
} else {
- Proc_Usage_Cache *cache;
- int64_t id = proc->pid;
-
if ((cache = eina_hash_find(view->proc_usage_cache, &id))) {
uint64_t net_in_abs = proc->net_in;
uint64_t net_out_abs = proc->net_out;
@@ -1337,8 +1358,12 @@ _evisum_ui_process_list_search_trim_cache(Eina_List *list, Evisum_Ui_Process_Lis
uint64_t disk_write_abs = proc->disk_write;
int elapsed = 1;
+ if (sample_time && cache->sample_time && (sample_time > cache->sample_time))
+ elapsed = sample_time - cache->sample_time;
+
if (cache->start != proc->start) {
cache->start = proc->start;
+ cache->sample_time = sample_time;
cache->net_in = net_in_abs;
cache->net_out = net_out_abs;
cache->disk_read = proc->disk_read;
@@ -1369,12 +1394,15 @@ _evisum_ui_process_list_search_trim_cache(Eina_List *list, Evisum_Ui_Process_Lis
cache->net_out = net_out_abs;
cache->disk_read = disk_read_abs;
cache->disk_write = disk_write_abs;
+ cache->sample_time = sample_time;
} else {
cache = calloc(1, sizeof(Proc_Usage_Cache));
if (cache) {
uint64_t net_in_abs = proc->net_in;
uint64_t net_out_abs = proc->net_out;
+ cache->pid = id;
cache->start = proc->start;
+ cache->sample_time = sample_time;
cache->net_in = net_in_abs;
cache->net_out = net_out_abs;
cache->disk_read = proc->disk_read;
@@ -1383,12 +1411,30 @@ _evisum_ui_process_list_search_trim_cache(Eina_List *list, Evisum_Ui_Process_Lis
proc->net_out = 0;
proc->disk_read = 0;
proc->disk_write = 0;
- eina_hash_add(view->proc_usage_cache, &id, cache);
+ if (!eina_hash_add(view->proc_usage_cache, &id, cache))
+ free(cache);
}
}
}
}
+ if (active_pids) {
+ Eina_Iterator *it = eina_hash_iterator_data_new(view->proc_usage_cache);
+ while (eina_iterator_next(it, &d)) {
+ cache = d;
+ if (!eina_hash_find(active_pids, &cache->pid))
+ purge = eina_list_prepend(purge, cache);
+ }
+ eina_iterator_free(it);
+
+ EINA_LIST_FREE(purge, cache) {
+ int64_t id = cache->pid;
+ eina_hash_del(view->proc_usage_cache, &id, NULL);
+ }
+
+ eina_hash_free(active_pids);
+ }
+
return list;
}
@@ -2212,6 +2258,7 @@ _evisum_ui_process_list_config_changed_cb(void *data, int type EINA_UNUSED, void
cache->net_out = 0;
cache->disk_read = 0;
cache->disk_write = 0;
+ cache->sample_time = 0;
}
eina_iterator_free(it);
diff --git a/src/bin/ui/evisum_ui_process_view.c b/src/bin/ui/evisum_ui_process_view.c
index 9278c24..cfc856c 100644
--- a/src/bin/ui/evisum_ui_process_view.c
+++ b/src/bin/ui/evisum_ui_process_view.c
@@ -106,6 +106,7 @@ typedef struct {
typedef struct {
int64_t start;
+ uint32_t sample_time;
int64_t cpu_time;
#if defined(__linux__)
uint64_t net_in;
@@ -193,6 +194,14 @@ _evisum_ui_process_view_usage_cache_free_cb(void *data) {
free(cache);
}
+static uint32_t
+_evisum_ui_process_view_sample_time_get(void) {
+ uint32_t sample_time;
+
+ sample_time = evisum_engine_live_time_get();
+ return sample_time;
+}
+
static Evas_Object *
_evisum_ui_process_view_content_get(void *data, Evas_Object *obj, const char *source) {
Evisum_Ui_Process_View *view;
@@ -843,6 +852,7 @@ _evisum_ui_process_view_proc_info_feedback_cb(void *data, Ecore_Thread *thread,
Proc_Usage_Cache *cache;
int64_t id;
int elapsed;
+ uint32_t sample_time;
#if defined(__linux__)
uint64_t net_in_abs = 0;
uint64_t net_out_abs = 0;
@@ -853,6 +863,7 @@ _evisum_ui_process_view_proc_info_feedback_cb(void *data, Ecore_Thread *thread,
view = data;
proc = msg;
elapsed = 1;
+ sample_time = _evisum_ui_process_view_sample_time_get();
if (!proc || (view->start && (proc->start != view->start))) {
if (proc) proc_info_free(proc);
@@ -888,6 +899,7 @@ _evisum_ui_process_view_proc_info_feedback_cb(void *data, Ecore_Thread *thread,
cache = calloc(1, sizeof(Proc_Usage_Cache));
if (cache) {
cache->start = proc->start;
+ cache->sample_time = sample_time;
cache->cpu_time = proc->cpu_time;
#if defined(__linux__)
cache->net_in = net_in_abs;
@@ -900,8 +912,8 @@ _evisum_ui_process_view_proc_info_feedback_cb(void *data, Ecore_Thread *thread,
proc->disk_write = 0;
#endif
proc->cpu_usage = 0.0;
- if (view->proc_usage_cache) eina_hash_add(view->proc_usage_cache, &id, cache);
- else free(cache);
+ if (!view->proc_usage_cache || !eina_hash_add(view->proc_usage_cache, &id, cache))
+ free(cache);
} else {
proc->cpu_usage = 0.0;
#if defined(__linux__)
@@ -913,6 +925,7 @@ _evisum_ui_process_view_proc_info_feedback_cb(void *data, Ecore_Thread *thread,
}
} else if (cache->start != proc->start) {
cache->start = proc->start;
+ cache->sample_time = sample_time;
cache->cpu_time = proc->cpu_time;
#if defined(__linux__)
cache->net_in = net_in_abs;
@@ -926,10 +939,14 @@ _evisum_ui_process_view_proc_info_feedback_cb(void *data, Ecore_Thread *thread,
#endif
proc->cpu_usage = 0.0;
} else {
+ if (sample_time && cache->sample_time && (sample_time > cache->sample_time))
+ elapsed = sample_time - cache->sample_time;
+
if (cache->cpu_time && (proc->cpu_time >= cache->cpu_time))
proc->cpu_usage = (double) (proc->cpu_time - cache->cpu_time) / elapsed;
else proc->cpu_usage = 0.0;
cache->cpu_time = proc->cpu_time;
+ cache->sample_time = sample_time;
#if defined(__linux__)
if (cache->net_in && (net_in_abs >= cache->net_in)) proc->net_in = (net_in_abs - cache->net_in) / elapsed;
else proc->net_in = 0;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.