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.

Reply via email to