From 5dcbc0dec92259ce8ab87a16fce39d9ce5d15ec8 Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 10:13:24 +0800 Subject: [PATCH] [dfs][procfs] Fix Linux-compatible procfs fields Grow maps without a 256 VMA cap, skip RSS walks for maps, report matching status text, real RSS/statm, MemAvailable after page-pool free pages, load averages plus last PID, cumulative processes, RV32 cpuinfo, and per-character mount escaping. --- .../dfs_v2/filesystems/procfs/proc_cpuinfo.c | 4 + .../dfs_v2/filesystems/procfs/proc_loadavg.c | 69 ++++++- .../dfs_v2/filesystems/procfs/proc_meminfo.c | 2 +- .../dfs_v2/filesystems/procfs/proc_mounts.c | 42 ++-- .../dfs/dfs_v2/filesystems/procfs/proc_pid.c | 183 +++++++++++++++--- .../dfs/dfs_v2/filesystems/procfs/proc_stat.c | 2 +- components/lwp/lwp_pid.c | 24 +++ components/lwp/lwp_pid.h | 2 + 8 files changed, 279 insertions(+), 49 deletions(-) diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c b/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c index ca3a8dc48a..094fa39e12 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c @@ -76,7 +76,11 @@ static int seq_show(struct dfs_seq_file *seq, void *data) dfs_seq_printf(seq, "CPU revision\t: %lu\n", (unsigned long)revision); #elif defined(__riscv) dfs_seq_puts(seq, "model name\t: RISC-V Generic\n"); +#if defined(__riscv_xlen) && (__riscv_xlen == 32) + dfs_seq_puts(seq, "isa\t\t: rv32\n"); +#else dfs_seq_puts(seq, "isa\t\t: rv64\n"); +#endif #else dfs_seq_puts(seq, "model name\t: RT-Thread Generic CPU\n"); #endif diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c b/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c index acb6ee35b5..44e95ff5f1 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c @@ -19,10 +19,8 @@ #include #ifdef RT_USING_SMART -#include "lwp_pid.h" -#ifdef RT_USING_MUSLLIBC #include "lwp.h" -#endif +#include "lwp_pid.h" #endif #ifdef RT_USING_SMART @@ -32,6 +30,18 @@ struct loadavg_count int runnable; }; +/* Fixed-point load averages, 11 bits of fraction like Linux FSHIFT. */ +#define PROC_LOAD_FSHIFT 11 +#define PROC_LOAD_FIXED_1 (1UL << PROC_LOAD_FSHIFT) +#define PROC_LOAD_EXP_1 1884UL /* 1 / exp(5s / 60s) * 2048 */ +#define PROC_LOAD_EXP_5 2014UL /* 1 / exp(5s / 300s) * 2048 */ +#define PROC_LOAD_EXP_15 2037UL /* 1 / exp(5s / 900s) * 2048 */ + +static unsigned long load_1 = 0; +static unsigned long load_5 = 0; +static unsigned long load_15 = 0; +static rt_tick_t load_last_tick = 0; + static int loadavg_count_pid(pid_t pid, void *arg) { struct loadavg_count *count = (struct loadavg_count *)arg; @@ -42,7 +52,6 @@ static int loadavg_count_pid(pid_t pid, void *arg) return 0; } count->total++; -#ifdef RT_USING_MUSLLIBC { rt_list_t *node; @@ -50,7 +59,9 @@ static int loadavg_count_pid(pid_t pid, void *arg) while (node != &lwp->t_grp) { rt_thread_t thread = rt_list_entry(node, struct rt_thread, sibling); - if (RT_SCHED_CTX(thread).stat == RT_THREAD_RUNNING) + int stat = RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK; + + if (stat == RT_THREAD_RUNNING || stat == RT_THREAD_READY) { count->runnable++; break; @@ -58,18 +69,62 @@ static int loadavg_count_pid(pid_t pid, void *arg) node = node->next; } } -#endif return 0; } + +static void loadavg_fold(unsigned long active) +{ + load_1 = load_1 * PROC_LOAD_EXP_1 / PROC_LOAD_FIXED_1 + + active * (PROC_LOAD_FIXED_1 - PROC_LOAD_EXP_1) / PROC_LOAD_FIXED_1; + load_5 = load_5 * PROC_LOAD_EXP_5 / PROC_LOAD_FIXED_1 + + active * (PROC_LOAD_FIXED_1 - PROC_LOAD_EXP_5) / PROC_LOAD_FIXED_1; + load_15 = load_15 * PROC_LOAD_EXP_15 / PROC_LOAD_FIXED_1 + + active * (PROC_LOAD_FIXED_1 - PROC_LOAD_EXP_15) / PROC_LOAD_FIXED_1; +} + +static void loadavg_update(unsigned long active) +{ + rt_tick_t now = rt_tick_get(); + rt_tick_t period = RT_TICK_PER_SECOND * 5; + + if (load_last_tick == 0) + { + load_1 = load_5 = load_15 = active; + load_last_tick = now; + return; + } + + while ((rt_tick_t)(now - load_last_tick) >= period) + { + loadavg_fold(active); + load_last_tick += period; + } +} + +static void loadavg_print(struct dfs_seq_file *seq, unsigned long load) +{ + unsigned long integer = load >> PROC_LOAD_FSHIFT; + unsigned long frac = ((load & (PROC_LOAD_FIXED_1 - 1)) * 100) >> PROC_LOAD_FSHIFT; + + dfs_seq_printf(seq, "%lu.%02lu", integer, frac); +} #endif static int single_show(struct dfs_seq_file *seq, void *data) { + RT_UNUSED(data); #ifdef RT_USING_SMART struct loadavg_count count = { 0, 0 }; lwp_pid_for_each(loadavg_count_pid, &count); - dfs_seq_printf(seq, "0.00 0.00 0.00 %d/%d 0\n", count.runnable, count.total); + loadavg_update(((unsigned long)count.runnable) << PROC_LOAD_FSHIFT); + loadavg_print(seq, load_1); + dfs_seq_puts(seq, " "); + loadavg_print(seq, load_5); + dfs_seq_puts(seq, " "); + loadavg_print(seq, load_15); + dfs_seq_printf(seq, " %d/%d %d\n", count.runnable, count.total, + (int)lwp_pid_get_last()); #else dfs_seq_puts(seq, "0.00 0.00 0.00 0/0 0\n"); #endif diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c b/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c index b1617f8bfd..1d8048b06b 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c @@ -35,12 +35,12 @@ static int single_show(struct dfs_seq_file *seq, void *data) total_freed = total_freed + total - used; dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemMaxUsed:", (unsigned long)(max_used / 1024)); - dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemAvailable:", (unsigned long)((total - used) / 1024)); rt_page_get_info(&total, &freed); total_sum = total_sum + total * RT_MM_PAGE_SIZE; total_freed = total_freed + freed * RT_MM_PAGE_SIZE; + dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemAvailable:", (unsigned long)(total_freed / 1024)); dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemTotal:", (unsigned long)(total_sum / 1024)); dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemFree:", (unsigned long)(total_freed / 1024)); dfs_seq_printf(seq, "%-16s%8lu kB\n", "Buffers:", 0UL); diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c b/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c index 1bb3b6a6bc..5d2e75f5a1 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c @@ -29,35 +29,43 @@ static void mnt_escape(const char *source, char *target, rt_size_t target_size) return; } - while (*cursor != '\0' && offset + 4 < target_size) + while (*cursor != '\0') { + const char *escape = RT_NULL; + rt_size_t need = 1; + if (*cursor == ' ') { - target[offset++] = '\\'; - target[offset++] = '0'; - target[offset++] = '4'; - target[offset++] = '0'; + escape = "\\040"; + need = 4; } else if (*cursor == '\t') { - target[offset++] = '\\'; - target[offset++] = '0'; - target[offset++] = '1'; - target[offset++] = '1'; + escape = "\\011"; + need = 4; } else if (*cursor == '\n') { - target[offset++] = '\\'; - target[offset++] = '0'; - target[offset++] = '1'; - target[offset++] = '2'; + escape = "\\012"; + need = 4; } else if (*cursor == '\\') { - target[offset++] = '\\'; - target[offset++] = '1'; - target[offset++] = '3'; - target[offset++] = '4'; + escape = "\\134"; + need = 4; + } + + if (offset + need >= target_size) + { + break; + } + + if (escape) + { + target[offset++] = escape[0]; + target[offset++] = escape[1]; + target[offset++] = escape[2]; + target[offset++] = escape[3]; } else { diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c b/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c index 9212351ac4..a3b0422c38 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c @@ -46,17 +46,76 @@ struct pid_dentry static char stat_transform(int __stat) { - switch (__stat) + int stat = __stat & RT_THREAD_STAT_MASK; + + if (stat == RT_THREAD_RUNNING) { - case RT_THREAD_RUNNING: return 'R'; + } + if (stat == RT_THREAD_READY) + { + return 'R'; + } + if (stat == RT_THREAD_CLOSE) + { + return 'Z'; + } + if (stat & RT_THREAD_SUSPEND_MASK) + { + return 'S'; + } + return 'T'; +} + +static const char *proc_state_desc(char state) +{ + switch (state) + { + case 'R': + return "running"; + case 'S': + return "sleeping"; + case 'Z': + return "zombie"; + case 'T': + return "stopped"; default: - return 'T'; + return "unknown"; } } +static char proc_lwp_state(struct rt_lwp *lwp) +{ + rt_list_t *node; + char state = 'S'; + + if (!lwp || lwp->terminated) + { + return 'Z'; + } + + node = lwp->t_grp.next; + while (node != &lwp->t_grp) + { + rt_thread_t thread = rt_list_entry(node, struct rt_thread, sibling); + char thread_state = stat_transform(RT_SCHED_CTX(thread).stat); + + if (thread_state == 'R') + { + return 'R'; + } + if (thread_state == 'T') + { + state = 'T'; + } + node = node->next; + } + + return state; +} + #ifdef ARCH_MM_MMU -#define PROC_MAP_MAX 256 +#define PROC_MAP_GROW 32 struct proc_map_entry { @@ -75,6 +134,8 @@ struct proc_map_context struct rt_lwp *lwp; struct proc_map_entry *entries; rt_size_t count; + rt_size_t capacity; + rt_bool_t count_resident; rt_bool_t smaps; }; @@ -104,17 +165,68 @@ static void proc_map_name(rt_varea_t varea, struct proc_map_entry *entry) entry->name[sizeof(entry->name) - 1] = '\0'; } +static int proc_map_grow(struct proc_map_context *context) +{ + struct proc_map_entry *entries; + rt_size_t capacity; + + if (context->count < context->capacity) + { + return 0; + } + + capacity = context->capacity ? context->capacity * 2 : PROC_MAP_GROW; + entries = rt_realloc(context->entries, capacity * sizeof(*entries)); + if (!entries) + { + return -RT_ENOMEM; + } + + rt_memset(entries + context->capacity, 0, + (capacity - context->capacity) * sizeof(*entries)); + context->entries = entries; + context->capacity = capacity; + return 0; +} + +static rt_size_t proc_map_count_resident(rt_varea_t varea) +{ + uintptr_t address; + uintptr_t end; + rt_size_t resident_pages = 0; + + if (!varea || varea->size == 0) + { + return 0; + } + + address = (uintptr_t)varea->start; + end = address + varea->size; + while (address < end) + { + if (rt_hw_mmu_v2p(varea->aspace, (void *)address) != ARCH_MAP_FAILED) + { + resident_pages++; + } + address += ARCH_PAGE_SIZE; + } + + return resident_pages; +} + static int proc_map_collect(rt_varea_t varea, void *arg) { struct proc_map_context *context = (struct proc_map_context *)arg; struct proc_map_entry *entry; - uintptr_t address; - uintptr_t end; - if (context->count >= PROC_MAP_MAX || !varea || varea->size == 0) + if (!varea || varea->size == 0) { return 0; } + if (proc_map_grow(context) != 0) + { + return -RT_ENOMEM; + } entry = &context->entries[context->count++]; entry->start = (uintptr_t)varea->start; @@ -124,20 +236,36 @@ static int proc_map_collect(rt_varea_t varea, void *arg) proc_map_permissions(varea, entry); proc_map_name(varea, entry); - address = entry->start; - end = entry->end; - while (address < end) + if (context->count_resident) { - if (rt_hw_mmu_v2p(varea->aspace, (void *)address) != ARCH_MAP_FAILED) - { - entry->resident_pages++; - } - address += ARCH_PAGE_SIZE; + entry->resident_pages = proc_map_count_resident(varea); } return 0; } +static rt_size_t proc_lwp_resident_pages(struct rt_lwp *lwp) +{ + rt_varea_t varea; + rt_size_t resident_pages = 0; + + if (!lwp || !lwp->aspace) + { + return 0; + } + + RD_LOCK(lwp->aspace); + varea = ASPACE_VAREA_FIRST(lwp->aspace); + while (varea) + { + resident_pages += proc_map_count_resident(varea); + varea = ASPACE_VAREA_NEXT(varea); + } + RD_UNLOCK(lwp->aspace); + + return resident_pages; +} + static void proc_map_context_free(struct proc_map_context *context) { if (context) @@ -244,7 +372,9 @@ static int proc_maps_open(struct dfs_file *file) return -RT_ENOENT; } context->smaps = !rt_strcmp(dentry->name, "smaps"); - context->entries = rt_calloc(PROC_MAP_MAX, sizeof(*context->entries)); + context->count_resident = context->smaps; + context->capacity = PROC_MAP_GROW; + context->entries = rt_calloc(context->capacity, sizeof(*context->entries)); if (!context->entries) { proc_map_context_free(context); @@ -255,7 +385,12 @@ static int proc_maps_open(struct dfs_file *file) varea = ASPACE_VAREA_FIRST(context->lwp->aspace); while (varea) { - proc_map_collect(varea, context); + if (proc_map_collect(varea, context) != 0) + { + RD_UNLOCK(context->lwp->aspace); + proc_map_context_free(context); + return -RT_ENOMEM; + } varea = ASPACE_VAREA_NEXT(varea); } RD_UNLOCK(context->lwp->aspace); @@ -447,6 +582,7 @@ static int proc_pid_status_show(struct dfs_seq_file *seq, void *data) struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; struct rt_lwp *lwp = lwp_from_pid_and_lock(dentry->pid); rt_size_t vm_size = 0; + rt_size_t vm_rss = 0; rt_size_t thread_count = 0; rt_list_t *node; char state; @@ -461,6 +597,7 @@ static int proc_pid_status_show(struct dfs_seq_file *seq, void *data) if (lwp->aspace) { vm_size = rt_aspace_count_vsz(lwp->aspace); + vm_rss = proc_lwp_resident_pages(lwp) * ARCH_PAGE_SIZE; } #endif node = lwp->t_grp.next; @@ -469,15 +606,15 @@ static int proc_pid_status_show(struct dfs_seq_file *seq, void *data) thread_count++; node = node->next; } - state = lwp->terminated ? 'Z' : 'S'; + state = proc_lwp_state(lwp); dfs_seq_printf(seq, "Name:\t%s\n", lwp->cmd); - dfs_seq_printf(seq, "State:\t%c (sleeping)\n", state); + dfs_seq_printf(seq, "State:\t%c (%s)\n", state, proc_state_desc(state)); dfs_seq_printf(seq, "Pid:\t%d\n", lwp->pid); dfs_seq_printf(seq, "PPid:\t%d\n", lwp->parent ? lwp->parent->pid : 0); dfs_seq_puts(seq, "Uid:\t0\t0\t0\t0\nGid:\t0\t0\t0\t0\n"); dfs_seq_printf(seq, "Threads:\t%lu\n", (unsigned long)thread_count); dfs_seq_printf(seq, "VmSize:\t%lu kB\n", (unsigned long)(vm_size / 1024)); - dfs_seq_printf(seq, "VmRSS:\t%lu kB\n", (unsigned long)(vm_size / 1024)); + dfs_seq_printf(seq, "VmRSS:\t%lu kB\n", (unsigned long)(vm_rss / 1024)); dfs_seq_puts(seq, "VmData:\t0 kB\nVmStk:\t0 kB\nVmExe:\t0 kB\nVmLib:\t0 kB\n"); lwp_from_pid_release_lock(lwp); return 0; @@ -488,6 +625,7 @@ static int proc_pid_statm_show(struct dfs_seq_file *seq, void *data) struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; struct rt_lwp *lwp = lwp_from_pid_and_lock(dentry->pid); rt_size_t size_pages = 0; + rt_size_t resident_pages = 0; rt_size_t text_pages = 0; rt_size_t data_pages = 0; @@ -501,14 +639,13 @@ static int proc_pid_statm_show(struct dfs_seq_file *seq, void *data) if (lwp->aspace) { size_pages = (rt_aspace_count_vsz(lwp->aspace) + ARCH_PAGE_SIZE - 1) / ARCH_PAGE_SIZE; + resident_pages = proc_lwp_resident_pages(lwp); } -#endif -#ifdef ARCH_MM_MMU text_pages = (lwp->text_size + ARCH_PAGE_SIZE - 1) / ARCH_PAGE_SIZE; data_pages = (lwp->data_size + ARCH_PAGE_SIZE - 1) / ARCH_PAGE_SIZE; #endif dfs_seq_printf(seq, "%lu %lu 0 %lu 0 %lu 0\n", - (unsigned long)size_pages, (unsigned long)size_pages, + (unsigned long)size_pages, (unsigned long)resident_pages, (unsigned long)text_pages, (unsigned long)data_pages); lwp_from_pid_release_lock(lwp); return 0; diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c b/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c index c24adb291e..9b8a6a80c9 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c @@ -118,7 +118,7 @@ static int seq_show(struct dfs_seq_file *seq, void *data) { struct stat_process_count process_count = { 0, 0 }; lwp_pid_for_each(stat_process_count, &process_count); - dfs_seq_printf(seq, "processes %d\n", process_count.total); + dfs_seq_printf(seq, "processes %lu\n", lwp_pid_get_create_count()); dfs_seq_printf(seq, "procs_running %d\n", process_count.running); dfs_seq_puts(seq, "procs_blocked 0\n"); } diff --git a/components/lwp/lwp_pid.c b/components/lwp/lwp_pid.c index 12a4dde86f..1f0e09ae9c 100644 --- a/components/lwp/lwp_pid.c +++ b/components/lwp/lwp_pid.c @@ -68,6 +68,7 @@ static struct lwp_avl_struct *lwp_pid_free_head = RT_NULL; static int lwp_pid_ary_alloced = 0; static struct lwp_avl_struct *lwp_pid_root = RT_NULL; static pid_t current_pid = 0; +static unsigned long lwp_create_count = 0; static struct rt_mutex pid_mtx; static struct rt_wqueue _pid_emptyq; @@ -166,6 +167,28 @@ static int _before_cb(struct lwp_avl_struct *node, void *data) return param->cb(pid, param->data); } +unsigned long lwp_pid_get_create_count(void) +{ + unsigned long count; + + lwp_pid_lock_take(); + count = lwp_create_count; + lwp_pid_lock_release(); + + return count; +} + +pid_t lwp_pid_get_last(void) +{ + pid_t pid; + + lwp_pid_lock_take(); + pid = current_pid; + lwp_pid_lock_release(); + + return pid; +} + /** * @brief Iterate over all process IDs * @@ -694,6 +717,7 @@ rt_lwp_t lwp_create(rt_base_t flags) { new_lwp->pid = pid; lwp_pid_set_lwp_locked(pid, new_lwp); + lwp_create_count++; } lwp_pid_lock_release(); } diff --git a/components/lwp/lwp_pid.h b/components/lwp/lwp_pid.h index 5b85b32197..a295137fbc 100644 --- a/components/lwp/lwp_pid.h +++ b/components/lwp/lwp_pid.h @@ -28,6 +28,8 @@ struct lwp_avl_struct *lwp_get_pid_ary(void); int lwp_pid_init(void); int lwp_pid_wait_for_empty(int wait_flags, rt_tick_t to); int lwp_pid_for_each(int (*cb)(pid_t pid, void *data), void *data); +unsigned long lwp_pid_get_create_count(void); +pid_t lwp_pid_get_last(void); void lwp_pid_put(struct rt_lwp *lwp); void lwp_pid_rollback(struct rt_lwp *lwp); void lwp_pid_lock_take(void);