]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Feature] util: add per-process memory info shim
authorVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 2 May 2026 14:26:50 +0000 (15:26 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 2 May 2026 14:26:50 +0000 (15:26 +0100)
Add struct rspamd_proc_mem_info and rspamd_get_process_memory_info()
that fills it in from OS-specific sources: /proc/self/status on Linux
(VmSize/VmRSS/VmData/RssAnon/etc.), task_info(MACH_TASK_BASIC_INFO) on
macOS, and getrusage(RUSAGE_SELF) as a portable fallback. Will be used
by the memstat control command to expose worker-process footprint.

src/libutil/util.c
src/libutil/util.h

index 2f432c253eaedd2774e65a5697f64ed824e66d7f..1b514df24a3de8e93a198afe7700b9763f0356f8 100644 (file)
@@ -36,6 +36,8 @@
 #include <mach/mach_init.h>
 #include <mach/thread_act.h>
 #include <mach/mach_port.h>
+#include <mach/task.h>
+#include <mach/task_info.h>
 #endif
 /* poll */
 #ifdef HAVE_POLL_H
@@ -1416,8 +1418,7 @@ restart:
        /* Restore terminal state */
        if (memcmp(&term, &oterm, sizeof(term)) != 0) {
                while (tcsetattr(input, TCSAFLUSH, &oterm) == -1 &&
-                          errno == EINTR && !saved_signo[SIGTTOU])
-                       ;
+                          errno == EINTR && !saved_signo[SIGTTOU]);
        }
 
        /* Restore signal handlers */
@@ -2826,3 +2827,118 @@ void rspamd_normalize_path_inplace(char *path, unsigned int len, gsize *nlen)
                *nlen = (o - path);
        }
 }
+
+#ifdef __linux__
+static gboolean
+rspamd_proc_mem_info_linux(struct rspamd_proc_mem_info *info)
+{
+       FILE *f;
+       char line[256];
+       gboolean got_rss = FALSE;
+
+       f = fopen("/proc/self/status", "r");
+       if (f == NULL) {
+               return FALSE;
+       }
+
+       while (fgets(line, sizeof(line), f) != NULL) {
+               uint64_t val_kb = 0;
+
+               /*
+                * All Vm* / Rss* fields in /proc/self/status are reported in kB.
+                * Format: "<Field>:\t<value> kB\n"
+                */
+               if (sscanf(line, "VmSize: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->vm_size = val_kb * 1024;
+               }
+               else if (sscanf(line, "VmRSS: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->vm_rss = val_kb * 1024;
+                       got_rss = TRUE;
+               }
+               else if (sscanf(line, "VmData: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->vm_data = val_kb * 1024;
+               }
+               else if (sscanf(line, "VmStk: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->vm_stack = val_kb * 1024;
+               }
+               else if (sscanf(line, "VmExe: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->vm_text = val_kb * 1024;
+               }
+               else if (sscanf(line, "VmLib: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->vm_lib = val_kb * 1024;
+               }
+               else if (sscanf(line, "VmPTE: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->vm_pte = val_kb * 1024;
+               }
+               else if (sscanf(line, "RssAnon: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->rss_anon = val_kb * 1024;
+               }
+               else if (sscanf(line, "RssFile: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->rss_file = val_kb * 1024;
+               }
+               else if (sscanf(line, "RssShmem: %" SCNu64 " kB", &val_kb) == 1) {
+                       info->rss_shmem = val_kb * 1024;
+               }
+       }
+
+       fclose(f);
+       return got_rss;
+}
+#endif
+
+#ifdef __APPLE__
+static gboolean
+rspamd_proc_mem_info_macos(struct rspamd_proc_mem_info *info)
+{
+       struct mach_task_basic_info ti;
+       mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
+
+       if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
+                                 (task_info_t) &ti, &count) != KERN_SUCCESS) {
+               return FALSE;
+       }
+
+       info->vm_size = ti.virtual_size;
+       info->vm_rss = ti.resident_size;
+       return TRUE;
+}
+#endif
+
+gboolean rspamd_get_process_memory_info(struct rspamd_proc_mem_info *info)
+{
+       if (info == NULL) {
+               return FALSE;
+       }
+
+       memset(info, 0, sizeof(*info));
+
+#if defined(__linux__)
+       if (rspamd_proc_mem_info_linux(info)) {
+               return TRUE;
+       }
+#elif defined(__APPLE__)
+       if (rspamd_proc_mem_info_macos(info)) {
+               return TRUE;
+       }
+#endif
+
+       /* Fallback: use getrusage to fill at least vm_rss */
+#ifdef HAVE_RUSAGE_SELF
+       {
+               struct rusage ru;
+
+               if (getrusage(RUSAGE_SELF, &ru) == 0) {
+#ifdef __APPLE__
+                       /* macOS reports ru_maxrss in bytes */
+                       info->vm_rss = (uint64_t) ru.ru_maxrss;
+#else
+                       /* Most Unixes report ru_maxrss in kilobytes */
+                       info->vm_rss = (uint64_t) ru.ru_maxrss * 1024;
+#endif
+                       return TRUE;
+               }
+       }
+#endif
+
+       return FALSE;
+}
index 0cff29b72478653b11c94f5a5e0e32b1389bbecb..19052bc83be3ba1531be04a2e6502218a695be6b 100644 (file)
@@ -594,6 +594,30 @@ float rspamd_sum_floats(float *ar, gsize *nelts);
  */
 void rspamd_normalize_path_inplace(char *path, unsigned int len, gsize *nlen);
 
+/**
+ * Per-process memory information collected from OS-specific sources.
+ * All fields are in bytes; unsupported fields are left at zero.
+ */
+struct rspamd_proc_mem_info {
+       uint64_t vm_size;   /**< total virtual memory size                       */
+       uint64_t vm_rss;    /**< resident set size                                */
+       uint64_t vm_data;   /**< data segment size (Linux only)                   */
+       uint64_t vm_stack;  /**< stack size (Linux only)                          */
+       uint64_t vm_text;   /**< text/code size (Linux only)                      */
+       uint64_t vm_lib;    /**< shared library size (Linux only)                 */
+       uint64_t vm_pte;    /**< page table entries size (Linux only)             */
+       uint64_t rss_anon;  /**< anonymous resident pages (Linux only)            */
+       uint64_t rss_file;  /**< file-backed resident pages (Linux only)          */
+       uint64_t rss_shmem; /**< shared memory resident pages (Linux only)        */
+};
+
+/**
+ * Fill the structure with current process memory info using OS-specific APIs.
+ * Returns TRUE if at least vm_rss could be obtained. Fields that are not
+ * available on the current platform are left set to zero.
+ */
+gboolean rspamd_get_process_memory_info(struct rspamd_proc_mem_info *info);
+
 #ifdef __cplusplus
 }
 #endif