Repository navigation
freebsd: add per-process block I/O columns and an I/O screen - #2142
Renan2010p wants to merge 1 commit into
Conversation
Add IO_READ_OPS, IO_WRITE_OPS, IO_READ_OPS_RATE, IO_WRITE_OPS_RATE and IO_OPS_RATE process columns on FreeBSD, and an "I/O" default screen with them, as Linux and Darwin have. FreeBSD does not count the bytes of a process's disk I/O, only its block input and output operations: ru_inblock and ru_oublock of the rusage in kinfo_proc, which rufetch() sums over the process's threads. They are what top(1) -m io shows as READ and WRITE. So these are new columns counting operations, rather than the byte-based IO_READ_RATE and IO_WRITE_RATE of Linux and Darwin, which would mislabel them. The counters come with kinfo_proc, so no extra system call is made, and they are readable for every process without privileges. Rates are derived from the delta between consecutive scans on the monotonic clock, as on Darwin. Writes that go through the buffer cache are counted when they reach the disk, partly to the process and partly to bufdaemon and the syncer, as in top(1) -m io. Tested on FreeBSD 15.1 amd64 (Renux) with dd(1) writing and find(1) and cat(1) reading: the I/O screen follows top -m io. Signed-off-by: Renan2010p <renanluscad@proton.me> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Walkthrough
Priority: Change: Feature Merge Risk:
|
There was a problem hiding this comment.
Actionable comments posted: 1

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: ASSERTIVE
- Plan: Advanced
- Run ID:
b3dfb4e1-9cac-4f3e-9fef-ae838cbc631f
Files selected for processing: 5-
freebsd/FreeBSDProcess.cfreebsd/FreeBSDProcess.hfreebsd/FreeBSDProcessTable.cfreebsd/Platform.cfreebsd/ProcessField.h
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.
-
| fp->io_read_ops = (uint64_t)kproc->ki_rusage.ru_inblock; | ||
| fp->io_write_ops = (uint64_t)kproc->ki_rusage.ru_oublock; |
There was a problem hiding this comment.
🔎 Supported by static analysis
-
Web query:
On FreeBSD 14/i386, can kinfo_proc.ki_rusage.ru_inblock or ru_oublock become negative after its signed long counter exceeds LONG_MAX, or does the kernel clamp or reset the counter?Result:
<source_evidence> <source kind="search"> <title>src/sys/kern/vfs_bio.c at master · openbsd/src · GitHub</title> <location>https://github.com/openbsd/src/blob/master/sys/kern/vfs_bio.c</location> <excerpt>citeturn1search4 [wordlim: 200] Published: 4 months ago; Crawled: last month; bio_doread(struct vnode *vp, daddr_t blkno, int size, int async) ... curproc->p_ru.ru_inblock++; /* XXX */ return recovered; if (bufpages - pdelta < buflowpages) pdelta = bufpages - buflowpages; oldbufpages = bufpages; bufadjust(bufpages - pdelta); recovered = oldbufpages - bufpages; return recovered; } struct buf * bio_doread(struct vnode *vp, daddr_t blkno, int size, int async) { struct buf *bp; struct mount *mp; bp = getblk(vp, blkno, size, 0, INFSLP); /* * If buffer does not have valid data, start a read. * Note that if buffer is B_INVAL, getblk() won't return it. * Therefore, it's valid if its I/O has completed or been delayed. */ if (!ISSET(bp->b_flags, (B_DONE | B_DELWRI))) { SET(bp->b_flags, B_READ | async); bcstats.pendingreads++; bcstats.numreads++; VOP_STRATEGY(bp->b_vp, bp); /* Pay for the read. */ curproc->p_ru.ru_inblock++; /* XXX */ } else if (async) { brelse(bp); } mp = vp->v_type == VBLK ? vp->v_specmountpoint : vp->v_mount; /* * Collect statistics on synchronous and asynchronous reads. * Reads from block devices are charged to their associated * filesystem (if any). */ if (mp != NULL) { if (async == 0) mp->mnt_stat.f_syncreads++; else mp->mnt_stat.f_asyncreads++; } return (bp); } /* * Read a disk block. * This algorithm described in Bach (p.54). */ int bread(struct vnode *vp, daddr_t blkno, int size, struct buf **bpp) { struct buf *bp; /* Get buffer for block. */ bp = *bpp = bio_doread(vp, blkno, size, 0); /* Wait for the read to complete, and return result. */ return (biowait(bp)); } /* * Read-ahead multiple disk blocks. The first is sync, the rest async. * Trivial modification to the breada algorithm presented in Bach (p.55). */ int breadn(struct vnode *vp, daddr_t blkno, int size, daddr_t rablks[], int rasizes[], int nrablks, struct buf **bpp) { struct buf *bp; int i; </excerpt> </source> <source kind="search"> <title>resource.h « sys « sys - src - FreeBSD source tree</title> <location>https://cgit.freebsd.org/src/tree/sys/sys/resource.h?id=f2a89e7e1d87c5a3aaa243aff3f6086e7312ecf5</location> <excerpt>citeturn3search0 [wordlim: 200] Crawled: 6 days ago; long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_minflt; /* page reclaims */ long ru_majflt; /* page faults */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* messages sent */ long ru_msgrcv; /* messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary " */ #define ru_last ru_nivcsw }; #if __BSD_VISIBLE struct __wrusage { struct rusage wru_self; struct rusage wru_children; }; #endif /* * Resource limits */ #define RLIMIT_CPU 0 /* maximum cpu time in seconds */ #define RLIMIT_FSIZE 1 /* maximum file size */ #define RLIMIT_DATA 2 /* data size */ #define RLIMIT_STACK 3 /* stack size */ #define RLIMIT_CORE 4 /* core file size */ #define RLIMIT_RSS 5 /* resident set size */ #define RLIMIT_MEMLOCK 6 /* locked-in-memory address space */ </excerpt> </source> <source kind="search"> <title>GNU/Linux Programmer's Manual</title> <location>https://www.kernel.org/pub/linux/docs/man-pages/book/man-pages-6.13.pdf</location> <excerpt>citeturn0search12 [wordlim: 200] Published: 1.6 years ago; The `_GNU_SOURCE` feature test macro must be defined (before including *any* header file) in order to obtain the definition of this constant from `<sys/resource.h>`. ... Code block showing `struct rusage` with fields and comments: ... - `long ru_inblock;` — block input operations ... Not all fields are completed; unmaintained fields are set to zero by the kernel. The resource usages are returned in the structure pointed to by `usage`, which has the following form: <visual_element id="e1"> Code block showing `struct rusage` with fields and comments: - `struct timeval ru_utime;` — user CPU time used - `struct timeval ru_stime;` — system CPU time used - `long ru_maxrss;` — maximum resident set size - `long ru_ixrss;` — integral shared memory size - `long ru_idrss;` — integral unshared data size - `long ru_isrss;` — integral unshared stack size - `long ru_minflt;` — page reclaims (soft page faults) - `long ru_majflt;` — page faults (hard page faults) - `long ru_nswap;` — swaps - `long ru_inblock;` — block input operations - `long ru_oublock;` — block output operations - `long ru_msgsnd;` — IPC messages sent - `long ru_msgrcv;` — IPC messages received - `long ru_nsignals;` — signals received - `long ru_nvcsw;` — voluntary context switches - `long ru_nivcsw;` — involuntary context switches </visual_element> Not all fields are completed; unmaintained fields are set to zero by the kernel. (The unmaintained fields are provided for compatibility with other systems, and because they may one day be supported on Linux.) The fields are interpreted as follows: Linux man-pages 6.13 — 2024-07-23 — 301</excerpt> </source> <source kind="search"> <title>darwin-xnu/bsd/sys/resource.h at main · apple/darwin-xnu · GitHub</title> <location>https://github.com/apple/darwin-xnu/blob/main/bsd/sys/resource.h</location> <excerpt>citeturn0search0 [wordlim: 200] Crawled: 2 weeks ago; * RESOURCE USAGE ... long ru_opaque[14]; /* implementation defined */ ... long ru_inblock; /* block input operations (atomic) */ ... #ifdef KERNEL ... struct rusage ru; * getrusage(). * * Note: All values other than ru_utime and ru_stime are implementaiton * defined and subject to change in a future release. Their use * is discouraged for standards compliant programs. */ struct rusage { struct timeval ru_utime; /* user time used (PL) */ struct timeval ru_stime; /* system time used (PL) */ #if __DARWIN_C_LEVEL < __DARWIN_C_FULL long ru_opaque[14]; /* implementation defined */ #else /* * Informational aliases for source compatibility with programs * that need more information than that provided by standards, * and which do not mind being OS-dependent. */ long ru_maxrss; /* max resident set size (PL) */ #define ru_first ru_ixrss /* internal: ruadd() range start */ long ru_ixrss; /* integral shared memory size (NU) */ long ru_idrss; /* integral unshared data (NU) */ long ru_isrss; /* integral unshared stack (NU) */ long ru_minflt; /* page reclaims (NU) */ long ru_majflt; /* page faults (NU) */ long ru_nswap; /* swaps (NU) */ long ru_inblock; /* block input operations (atomic) */ long ru_oublock; /* block output operations (atomic) */ long ru_msgsnd; /* messages sent (atomic) */ long ru_msgrcv; /* messages received (atomic) */ long ru_nsignals; /* signals received (atomic) */ long ru_nvcsw; /* voluntary context switches (atomic) */ long ru_nivcsw; /* involuntary " */ #endif /* __DARWIN_C_LEVEL >= __DARWIN_C_FULL */ #ifdef KERNEL struct rusage_superset { struct rusage ru; rusage_info_current ri; }; struct rusage_info_child { uint64_t ri_child_user_time; uint64_t ri_child_system_time; uint64_t ri_child_pkg_idle_wkups; uint64_t ri_child_interrupt_wkups; uint64_t ri_child_pageins; uint64_t ri_child_elapsed_abstime; }; struct user64_rusage { struct user64_timeval ru_utime; /* user time used */ struct user64_timeval ru_stime; /* system time used */ user64_long_t ru_maxrss; /* max resident set size */ user64_long_t ru_ixrss; /* integral shared memory size */ user64_long_t ru_idrss; /* integral unshared data " */ user64_long_t ru_isrss; /* integral unshared stack " */ user64_long_t ru_minflt; /* page reclaims */ user64_long_t ru_majflt; /* page faults */ user64_long_t ru_nswap; /* swaps */ user64_long_t ru_inblock; /* block input operations */ user64_long_t ru_oublock; /* block output operations */ user64_long_t ru_msgsnd; /* messages sent */ user64_long_t ru_msgrcv; /* messages received */ user64_long_t ru_nsignals; /* signals received */ user64_long_t ru_nvcsw; /* voluntary context switches */ user64_long_t ru_nivcsw; /* involuntary " */ }; struct user32_rusage { struct user32_timeval ru_utime; /* user time used */ struct user32_timeval ru_stime; /* system time used */ user32_long_t ru_maxrss; /* max resident set size */ user32_long_t ru_ixrss; /* integral shared memory size */ user32_long_t ru_idrss; /* integral unshared data " */ user32_long_t ru_isrss; /* integral unshared stack " */ user32_long_t ru_minflt; /* page reclaims */ user32_long_t ru_majflt; /* page faults */ user32_long_t ru_nswap; /* swaps */ user32_long_t ru_inblock; /* block input operations */ user32_long_t ru_oublock; /* block output operations */ user32_long_t ru_msgsnd; /* messages sent */ user32_long_t ru_msgrcv; /* messages received */ user32_long_t ru_nsignals; /* signals received */ user32_long_t ru_nvcsw; /* voluntary context switches */ user32_long_t ru_nivcsw; /* involuntary " */ }; </excerpt> </source> <source kind="search"> <title>getrusage(2) - FreeBSD 15.0-RELEASE and Ports Manual Pages</title> <location>https://man.freebsd.org/cgi/man.cgi?apropos=0&manpath=FreeBSD+15.0-RELEASE+and+Ports&query=getrusage&sektion=2</location> <excerpt>citeturn0search1 [wordlim: 200] Crawled: 4 weeks ago; #include <sys/resource.h> ... The buffer to which rusage points will be filled in with ... ru_inblock the number of times the file system had to perform input. ... first appeared in FreeBSD 8.1. long ru_maxrss; /* max resident set size */ long ru_ixrss; /* integral shared text memory size */ long ru_idrss; /* integral unshared data size */ long ru_isrss; /* integral unshared stack size */ long ru_minflt; /* page reclaims */ long ru_majflt; /* page faults */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* messages sent */ long ru_msgrcv; /* messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary context switches */ }; The fields are interpreted as follows: ru_utime the total amount of time spent executing in user mode. ru_stime the total amount of time spent in the system executing on be- the list of pages awaiting reallocation. ru_majflt the number of page faults serviced that required I/O activity. ru_nswap the number of times a process was "swapped" out of main mem- ory. ru_inblock the number of times the file system had to perform input. ru_oublock the number of times the file system had to perform output. ru_msgsnd the number of IPC messages sent. ru_msgrcv the number of IPC messages received. ru_nsignals the number of signals delivered. ru_nvcsw the number of times a context switch resulted due to a process voluntarily giving up the processor before its time slice was completed (usually to await availability of a resource). ru_nivcsw the number of times a context switch resulted due to a higher priority process becoming runnable or because the current process exceeded its time slice. NOTES The numbers ru_inblock and ru_oublock account only for real I/O; data sup- plied by the caching mechanism is charged only to the first process to read or write the data. RETURN VALUES The getrusage() function returns the value 0 if successful; otherwise the value -1 is returned and the global variable errno is set to indicate the error. ERRORS The getrusage() system call will fail if: [EINVAL] The who argument is not a valid value. [EFAULT] The address specified by the rusage argument is not in a valid part of the process address space. SEE ALSO gettimeofday(2), wait(2), clocks(7) HISTORY The getrusage() system call appeared in 4.2BSD. The RUSAGE_THREAD facility first appeared in FreeBSD 8.1. BUGS There is no way to obtain information about a child process that has not yet terminated. FreeBSD 15.0 May 1, 2010 GETRUSAGE(2)</excerpt> </source> <omitted_sources count="52">Additional source records were omitted from this bounded result.</omitted_sources> </source_evidence> Citations: - 1: https://cgit.freebsd.org/src/tree/sys/sys/resource.h?id=f2a89e7e1d87c5a3aaa243aff3f6086e7312ecf5&utm_source=openai - 2: https://github.com/openbsd/src/blob/master/sys/kern/vfs_bio.c?utm_source=openai
Script executed:
set -o pipefail printf '%s\n' '--- changed file ---' nl -ba freebsd/FreeBSDProcessTable.c | sed -n '130,205p' printf '%s\n' '--- relevant diff ---' git diff --no-ext-diff --unified=30 394b4a3fc0df593a00be452a1862057b88b4eaf7 32ec5a8fef3e2a5589d79ed12a2a30fb9c271fca -- freebsd/FreeBSDProcessTable.c printf '%s\n' '--- local references ---' rg -n -F --glob '*.{c,h}' -e 'ru_inblock' -e 'ru_oublock' -e 'saturatingSub' freebsd linux | head -80 || test "$?" -eq 1 printf '%s\n' '--- repository revisions ---' git cat-file -t 394b4a3fc0df593a00be452a1862057b88b4eaf7 git cat-file -t 32ec5a8fef3e2a5589d79ed12a2a30fb9c271fca
Repository: htop-dev/htop
Length of output: 13053
Web query:
FreeBSD kernel source ru_inblock ru_oublock overflow long kinfo_proc rusage incrementResult:
<source_evidence> <source kind="open_page"> <title>kern_proc.c « kern « sys - src - FreeBSD source tree</title> <location>https://cgit.freebsd.org/src/tree/sys/kern/kern_proc.c?id=8fbbc185a3ff66d95c30d9b9062d6d8a349f0454</location> <excerpt>citeturn2view0 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({"ref_id":"turn1search5","lineno":null}); Total lines: 7340 L4421: sx_assert(&proctree_lock, SA_LOCKED); L4422: #endif L4423: L4424: cnt = 0; L4425: LIST_FOREACH(q, &pgrp->pg_members, p_pglist) { L4426: if ((q->p_treeflag & P_TREE_GRPEXITED) != 0 || L4427: q->p_pptr == NULL) L4428: continue; L4429: if (isjobproc(jobc_parent(q, NULL), pgrp)) L4430: cnt++; L4431: } L4432: return (cnt); L4433: } L4434: L4435: /* L4436: * Move p to a process group L4437: */ L4438: static void L4439: doenterpgrp(struct proc *p, struct pgrp *pgrp) L4440: { L4441: struct pgrp *savepgrp; L4442: struct proc *pp; L4443: sx_assert(&proctree_lock, SX_XLOCKED); L4444: PROC_LOCK_ASSERT(p, MA_NOTOWNED); L4445: PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); L4446: PGRP_LOCK_ASSERT(p->p_pgrp, MA_NOTOWNED); L4447: SESS_LOCK_ASSERT(p->p_session, MA_NOTOWNED); L4448: L4449: savepgrp = p->p_pgrp; L4450: pp = jobc_parent(p, NULL); L4451: L4452: PGRP_LOCK(pgrp); L4453: PGRP_LOCK(savepgrp); L4454: if (isjobproc(pp, savepgrp) && pgrp_calc_jobc(savepgrp) == 1) L4455: orphanpg(savepgrp); L4456: PROC_LOCK(p); L4457: LIST_REMOVE(p, p_pglist); L4458: p->p_pgrp = pgrp; L4551: if (isjobproc(jobc_parent(p, NULL), pgrp)) { L4552: PGRP_LOCK(pgrp); L4553: if (pgrp_calc_jobc(pgrp) == 0) L4554: orphanpg(pgrp); L4555: PGRP_UNLOCK(pgrp); L4556: } L4557: L4558: /* L4559: * Check this process' children to see whether they qualify L4560: * their process groups after reparenting to reaper. L4561: */ L4562: LIST_FOREACH(q, &p->p_children, p_sibling) { L4563: pgrp = q->p_pgrp; L4564: PGRP_LOCK(pgrp); L4565: if (pgrp_calc_jobc(pgrp) == 0) { L4566: /* L4567: * We want to handle exactly the children that L5101: freebsd32_kinfo_proc_out(const struct kinfo_proc *ki, struct kinfo_proc32 *ki32) L5102: { L5103: int i; L5104: L5105: bzero(ki32, sizeof(struct kinfo_proc32)); L5106: ki32->ki_structsize = sizeof(struct kinfo_proc32); L5107: CP(*ki, *ki32, ki_layout); L5108: PTRTRIM_CP(*ki, *ki32, ki_args); L5109: PTRTRIM_CP(*ki, *ki32, ki_paddr); L5110: PTRTRIM_CP(*ki, *ki32, ki_addr); L5111: PTRTRIM_CP(*ki, *ki32, ki_tracep); L5112: PTRTRIM_CP(*ki, *ki32, ki_textvp); L5113: PTRTRIM_CP(*ki, *ki32, ki_fd); L5200: L5201: if ((flags & KERN_PROC_NOTHREADS) != 0) { L5202: #ifdef COMPAT_FREEBSD32 L5203: if ((flags & KERN_PROC_MASK32) != 0) { L5204: size += sizeof(struct kinfo_proc32); L5205: } else L5206: #endif L5207: size += sizeof(struct kinfo_proc); L5208: } else { L5209: #ifdef COMPAT_FREEBSD32 L5210: if ((flags & KERN_PROC_MASK32) != 0) L5211: size += sizeof(struct kinfo_proc32) * p->p_numthreads; L5212: else L5213: #endif L5214: size += sizeof(struct kinfo_proc) * p->p_numthreads; L5215: } L5216: PROC_UNLOCK(p); L5217: return (size); L5218: } L5219: L5220: int L5221: kern_proc_out(struct proc *p, struct sbuf *sb, int flags) L5222: { L5223: struct thread *td; L5224: struct kinfo_proc ki; L5225: #ifdef COMPAT_FREEBSD32 L5226: struct kinfo_proc32 ki32; L5227: #endif L5228: int error; L5229: L5230: PROC_LOCK_ASSERT(p, MA_OWNED); L5231: MPASS(FIRST_THREAD_IN_PROC(p) != NULL); L5232: L5233: error = 0; L5234: fill_kinfo_proc(p, &ki); L5235: if ((flags & KERN_PROC_NOTHREADS) != 0) { L5236: #ifdef COMPAT_FREEBSD32 L5237: if ((flags & KERN_PROC_MASK32) != 0) { L5238: freebsd32_kinfo_proc_out(…[truncated]</excerpt> </source> <source kind="open_page"> <title>kern_resource.c « kern « sys - src - FreeBSD source tree</title> <location>https://cgit.freebsd.org/src/tree/sys/kern/kern_resource.c?id=4c9e89c85d7c6cf50b09865de4dacedd015c5f39</location> <excerpt>citeturn2view1 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({"ref_id":"turn1search8","lineno":null}); Total lines: 3592 L2718: error = getrlimitusage_one(curproc, uap->which, uap->flags, &res); L2719: if (error == 0) L2720: error = copyout(&res, uap->res, sizeof(res)); L2721: return (error); L2722: } L2723: L2724: /* L2725: * Transform the running time and tick information for children of proc p L2726: * into user and system time usage. L2727: */ L2728: void L2729: calccru(struct proc *p, struct timeval *up, struct timeval *sp) L2730: { L2731: L2732: PROC_LOCK_ASSERT(p, MA_OWNED); L2733: calcru1(p, &p->p_crux, up, sp); L2734: } L2735: L2736: /* L2737: * Transform the running time and tick information in proc p into user L2738: * and system time usage. If appropriate, include the current time slice L2739: * on this CPU. L2740: */ L2741: void L2742: calcru(struct proc *p, struct timeval *up, struct timeval *sp) L2743: { L2744: struct thread *td; L2745: uint64_t runtime, u; L2746: L2747: PROC_LOCK_ASSERT(p, MA_OWNED); L2748: PROC_STATLOCK_ASSERT(p, MA_OWNED); L2749: /* L2750: * If we are getting stats for the current process, then add in the L2751: * stats that this thread has accumulated in its current time slice. L2752: * We reset the thread and CPU state as if we had performed a context L2753: * switch right here. L2754: */ L2755: td = curthread; L2756: if (td->td_proc == p) { L2757: u = cpu_ticks(); L2758: runtime = u - PCPU_GET(switchtime); L2759: td->td_runtime += runtime; L2760: td->td_incruntime += runtime; L2761: PCPU_SET(switchtime, u); L2762: } L2763: /* Make sure the per-thread stats are current. */ L2764: FOREACH_THREAD_IN_PROC(p, td) { L2765: if (td->td_incruntime == 0) L2766: continue; L2767: ruxagg(p, td); L2768: } L2769: calcru1(p, &p->p_rux, up, sp); L2770: } L2771: L2772: /* Collect resource usage for a single thread. */ L2773: void L2774: rufetchtd(struct thread *td, struct rusage *ru) L2775: { L2776: struct proc *p; L2777: uint64_t runtime, u; L2778: L2779: p = td->td_proc; L2780: PROC_STATLOCK_ASSERT(p, MA_OWNED); L2781: THREAD_LOCK_ASSERT(td, MA_OWNED); L2782: /* L2783: * If we are getting stats for the current thread, then add in the L2962: struct rusage *rusage; L2963: }; L2964: #endif L2965: int L2966: sys_getrusage(struct thread *td, struct getrusage_args *uap) L2967: { L2968: struct rusage ru; L2969: int error; L2970: L2971: error = kern_getrusage(td, uap->who, &ru); L2972: if (error == 0) L2973: error = copyout(&ru, uap->rusage, sizeof(struct rusage)); L2974: return (error); L2975: } L2976: L2977: int L2978: kern_getrusage(struct thread *td, int who, struct rusage *rup) L2979: { L2980: struct proc *p; L2981: int error; L2982: L2983: error = 0; L2984: p = td->td_proc; L3009: return (error); L3010: } L3011: L3012: void L3013: rucollect(struct rusage *ru, struct rusage *ru2) L3014: { L3015: long *ip, *ip2; L3016: int i; L3017: L3018: if (ru->ru_maxrss < ru2->ru_maxrss) L3019: ru->ru_maxrss = ru2->ru_maxrss; L3020: ip = &ru->ru_first; L3021: ip2 = &ru2->ru_first; L3022: for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--) L3023: *ip++ += *ip2++; L3024: } L3025: L3026: void L3027: ruadd(struct rusage *ru, struct rusage_ext *rux, struct rusage *ru2, L3028: struct rusage_ext *rux2) L3029: { L3030: rux->rux_runtime += rux2->rux_runtime; L303…[truncated]</excerpt> </source> <source kind="open_page"> <title>freebsd-src/sys/sys/user.h at main · freebsd/freebsd-src · GitHub</title> <location>https://github.com/freebsd/freebsd-src/blob/main/sys/sys/user.h</location> <excerpt>citeturn2view2 [wordlim: 200] Crawled: last week; Content type: text/html; Source: open({"ref_id":"turn0search0","lineno":null}); Total lines: 3097 L1870: * Once you have added the new field, you will need to add code to initialize L1871: L1872: * it in two places: function fill_kinfo_proc in sys/kern/kern_proc.c and L1873: L1874: * function kvm_proclist in lib/libkvm/kvm_proc.c . L1875: L1876: */ L1877: #define KI_NSPARE_INT 2 L1878: L1879: #define KI_NSPARE_LONG 12 L1880: L1881: #define KI_NSPARE_PTR 4 L1882: L1883: #ifndef _KERNEL L1884: L1885: #ifndef KINFO_PROC_SIZE L1886: L1887: #error "Unknown architecture" L1888: L1889: #endif L1890: L1891: #endif /* !_KERNEL */ L1892: L1893: #define WMESGLEN 8 /* size of returned wchan message */ L1894: L1895: #define LOCKNAMELEN 8 /* size of returned lock name */ L1896: L1897: #define TDNAMLEN 16 /* size of returned thread name */ L1898: L2130: #define ki_childstime ki_rusage_ch.ru_stime L2131: L2132: #define ki_childutime ki_rusage_ch.ru_utime L2133: L2134: /* L2135: L2136: * Legacy PS_ flag. This moved to p_flag but is maintained for L2137: L2138: * compatibility. L2139: L2140: */ L2141: L2142: #define PS_INMEM 0x00001 /* Loaded into memory, always true. */ L2143: L2144: /* ki_sessflag values */ L2145: L2146: #define KI_CTTY 0x00000001 /* controlling tty vnode active */ L2147: L2148: #define KI_SLEADER 0x00000002 /* session leader */ L2149: #define KI_LOCKBLOCK 0x00000004 /* proc blocked on lock ki_lockname */ L2150: L2151: /* L2152: L2153: * This used to be the per-process structure containing data that L2154: L2155: * isn't needed in core when the process is swapped out, but now it L2156: L2157: * remains only for the benefit of a.out core dumps. L2158: L2159: */ L2160: L2161: struct user { L2162: L2163: struct pstats u_stats; /* *p_stats */ L2164: L2165: struct kinfo_proc u_kproc; /* eproc */ L2166: L2167: }; L2168: L2169: /* L2170: L2171: * The KERN_PROC_FILE sysctl allows a process to dump the file descriptor L2172: L2173: * array of another process. L2174: L2175: */ L2176: L2177: #define KF_ATTR_VALID 0x0001 </excerpt> </source> <source kind="open_page"> <title>resource.h « sys « sys - src - FreeBSD source tree</title> <location>https://cgit.freebsd.org/src/tree/sys/sys/resource.h?id=f2a89e7e1d87c5a3aaa243aff3f6086e7312ecf5</location> <excerpt>citeturn2view3 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({"ref_id":"turn1search0","lineno":null}); Total lines: 410 L285: long ru_isrss; /* integral unshared stack " */ L286: long ru_minflt; /* page reclaims */ L287: long ru_majflt; /* page faults */ L288: long ru_nswap; /* swaps */ L289: long ru_inblock; /* block input operations */ L290: long ru_oublock; /* block output operations */ L291: long ru_msgsnd; /* messages sent */ L292: long ru_msgrcv; /* messages received */ L293: long ru_nsignals; /* signals received */ L294: long ru_nvcsw; /* voluntary context switches */ L295: long ru_nivcsw; /* involuntary " */ </excerpt> </source> <source kind="search"> <title>resource.h « sys « sys - src - FreeBSD source tree</title> <location>https://cgit.freebsd.org/src/tree/sys/sys/resource.h?id=f2a89e7e1d87c5a3aaa243aff3f6086e7312ecf5</location> <excerpt>citeturn0search1 [wordlim: 200] Crawled: 6 days ago; * Redistribution and use in source and binary forms, with or without ... struct rusage { ... long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_minflt; /* page reclaims */ long ru_majflt; /* page faults */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* messages sent */ long ru_msgrcv; /* messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary " */ #define ru_last ru_nivcsw }; #if __BSD_VISIBLE struct __wrusage { struct rusage wru_self; struct rusage wru_children; }; #endif /* * Resource limits */ #define RLIMIT_CPU 0 /* maximum cpu time in seconds */ #define RLIMIT_FSIZE 1 /* maximum file size */ #define RLIMIT_DATA 2 /* data size */ #define RLIMIT_STACK 3 /* stack size */ #define RLIMIT_CORE 4 /* core file size */ #define RLIMIT_RSS 5 /* resident set size */ #define RLIMIT_MEMLOCK 6 /* locked-in-memory address space */ </excerpt> </source> <omitted_sources count="32">Additional source records were omitted from this bounded result.</omitted_sources> </source_evidence> Citations: - 1: https://cgit.freebsd.org/src/tree/sys/sys/resource.h?id=f2a89e7e1d87c5a3aaa243aff3f6086e7312ecf5 - 2: https://cgit.freebsd.org/src/tree/sys/kern/kern_resource.c?id=4c9e89c85d7c6cf50b09865de4dacedd015c5f39 - 3: https://cgit.freebsd.org/src/tree/sys/compat/freebsd32/freebsd32_misc.c?id=1a71d24ecd0dbaf61fd2a44166e9be07c328b198&utm_source=openai
Script executed:
nl -ba freebsd/FreeBSDProcessTable.c | sed -n '130,205p' git diff --no-ext-diff --unified=30 394b4a3fc0df593a00be452a1862057b88b4eaf7 32ec5a8fef3e2a5589d79ed12a2a30fb9c271fca -- freebsd/FreeBSDProcessTable.c rg -n -F --glob '*.{c,h}' -e 'ru_inblock' -e 'ru_oublock' -e 'saturatingSub' freebsd linux || test "$?" -eq 1 git cat-file -t 394b4a3fc0df593a00be452a1862057b88b4eaf7 git cat-file -t 32ec5a8fef3e2a5589d79ed12a2a30fb9c271fca
Repository: htop-dev/htop
Length of output: 12956
Web query:
site:cgit.freebsd.org/src ru_inblock++ OR ru_oublock++ FreeBSD kernel sourceResult:
<source_evidence> <source kind="search"> <title>resource.h « sys « sys - src - FreeBSD source tree</title> <location>https://cgit.freebsd.org/src/tree/sys/sys/resource.h?id=f2a89e7e1d87c5a3aaa243aff3f6086e7312ecf5</location> <excerpt>citeturn0search1 [wordlim: 200] Crawled: 6 days ago; long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ #ifndef _ID_T_DECLARED typedef __id_t id_t; #define _ID_T_DECLARED #endif #ifndef _RLIM_T_DECLARED typedef __rlim_t rlim_t; #define _RLIM_T_DECLARED #endif /* * Process priority specifications to get/setpriority. */ #define PRIO_MIN -20 #define PRIO_MAX 20 #define PRIO_PROCESS 0 #define PRIO_PGRP 1 #define PRIO_USER 2 /* * Resource utilization information. * * All fields are only modified by curthread and * no locks are required to read. */ #define RUSAGE_SELF 0 #define RUSAGE_CHILDREN -1 #define RUSAGE_THREAD 1 struct rusage { struct timeval ru_utime; /* user time used */ struct timeval ru_stime; /* system time used */ long ru_maxrss; /* max resident set size */ #define ru_first ru_ixrss long ru_ixrss; /* integral shared memory size */ long ru_idrss; /* integral unshared data " */ long ru_isrss; /* integral unshared stack " */ long ru_minflt; /* page reclaims */ long ru_majflt; /* page faults */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* messages sent */ long ru_msgrcv; /* messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary " */ #define ru_last ru_nivcsw }; #if __BSD_VISIBLE struct __wrusage { struct rusage wru_self; struct rusage wru_children; }; #endif /* * Resource limits */ #define RLIMIT_CPU 0 /* maximum cpu time in seconds */ #define RLIMIT_FSIZE 1 /* maximum file size */ #define RLIMIT_DATA 2 /* data size */ #define RLIMIT_STACK 3 /* stack size */ #define RLIMIT_CORE 4 /* core file size */ #define RLIMIT_RSS 5 /* resident set size */ #define RLIMIT_MEMLOCK 6 /* locked-in-memory address space */ "core", "rss", "memlock", "nproc", "nofile", "sbsize", "vmem", "npts", "swap", "kqueues", "umtx", "pipebuf", "vms", }; #endif struct rlimit { rlim_t rlim_cur; /* current (soft) limit */ rlim_t rlim_max; /* maximum value for rlim_cur */ }; #if __BSD_VISIBLE struct orlimit { __int32_t rlim_cur; /* current (soft) limit */ __int32_t rlim_max; /* maximum value for rlim_cur */ }; struct loadavg { __fixpt_t ldavg[3]; long fscale; }; #define CP_USER 0 #define CP_NICE 1 #define CP_SYS 2 #define CP_INTR 3 #define CP_IDLE 4 #define CPUSTATES 5 /* getrlimitusage flags */ #define GETRLIMITUSAGE_EUID 0x0001 #endif /* __BSD_VISIBLE */ #ifdef _KERNEL extern struct loadavg averunnable; void read_cpu_time(long *cp_time); /* Writes array of CPUSTATES */ #else __BEGIN_DECLS /* XXX 2nd arg to [gs]etpriority() should be an id_t */ int getpriority(int, int); int getrlimit(int, struct rlimit *); int getrusage(int, struct rusage *); int setpriority(int, int, int); int setrlimit(int, const struct rlimit *); #if __BSD_VISIBLE int getrlimitusage(unsigned which, int flags, rlim_t *res); #endif __END_DECLS #endif /* _KERNEL */ #endif /* !_SYS_RESOURCE_H_ */</excerpt> </source> <source kind="search"> <title>linux32_machdep.c « linux32 « amd64 « sys - src - FreeBSD source tree</title> <location>https://cgit.freebsd.org/src/tree/sys/amd64/linux32/linux32_machdep.c?id=a7883464fcc45b78e6aa01222682ae40f787a378</location> <excerpt>citeturn0search0 [wordlim: 200] Crawled: 6 days ago; #include <compat/freebsd32/freebsd32_util.h> ... lru->ru_utime.tv_usec = ru->ru_utime.tv_usec; ... lru->ru_oublock = ru->ru_oublock; #include <sys/fcntl.h> #include <sys/file.h> #include <sys/imgact.h> #include <sys/kernel.h> #include <sys/limits.h> #include <sys/lock.h> #include <sys/malloc.h> #include <sys/mman.h> #include <sys/mutex.h> #include <sys/priv.h> #include <sys/proc.h> #include <sys/resource.h> #include <sys/resourcevar.h> #include <sys/syscallsubr.h> #include <sys/sysproto.h> #include <sys/systm.h> #include <sys/unistd.h> #include <sys/wait.h> #include <machine/frame.h> #include <machine/md_var.h> #include <machine/pcb.h> #include <machine/psl.h> #include <machine/segments.h> #include <machine/specialreg.h> #include <x86/ifunc.h> #include <vm/pmap.h> #include <vm/vm.h> #include <vm/vm_map.h> #include <security/audit/audit.h> #include <compat/freebsd32/freebsd32_util.h> #include <amd64/linux32/linux.h> #include <amd64/linux32/linux32_proto.h> #include <compat/linux/linux_emul.h> #include <compat/linux/linux_ipc.h> #include <compat/linux/linux_misc.h> #include <compat/linux/linux_mmap.h> #include <compat/linux/linux_signal.h> #include <compat/linux/linux_util.h> static void bsd_to_linux_rusage(struct rusage *ru, struct l_rusage *lru); struct l_old_select_argv { l_int nfds; l_uintptr_t readfds; l_uintptr_t writefds; l_uintptr_t exceptfds; l_uintptr_t timeout; } __packed; static void bsd_to_linux_rusage(struct rusage *ru, struct l_rusage *lru) { lru->ru_utime.tv_sec = ru->ru_utime.tv_sec; lru->ru_utime.tv_usec = ru->ru_utime.tv_usec; lru->ru_stime.tv_sec = ru->ru_stime.tv_sec; lru->ru_stime.tv_usec = ru->ru_stime.tv_usec; lru->ru_maxrss = ru->ru_maxrss; lru->ru_ixrss = ru->ru_ixrss; lru->ru_idrss = ru->ru_idrss; lru->ru_isrss = ru->ru_isrss; lru->ru_minflt = ru->ru_minflt; lru->ru_majflt = ru->ru_majflt; lru->ru_nswap = ru->ru_nswap; lru->ru_inblock = ru->ru_inblock; lru->ru_oublock = ru->ru_oublock; lru->ru_msgsnd = ru->ru_msgsnd; lru->ru_msgrcv = ru->ru_msgrcv; lru->ru_nsignals = ru->ru_nsignals; lru->ru_nvcsw = ru->ru_nvcsw; lru->ru_nivcsw = ru->ru_nivcsw; } int linux_copyout_rusage(struct rusage *ru, void *uaddr) { struct l_rusage lru; bsd_to_linux_rusage(ru, &lru); return (copyout(&lru, uaddr, sizeof(struct l_rusage))); } int linux_execve(struct thread *td, struct linux_execve_args *args) { struct image_args eargs; char *path; int error; LCONVPATHEXIST(td, args->path, &path); error = freebsd32_exec_copyin_args(&eargs, path, UIO_SYSSPACE, args->argp, args->envp); free(path, M_TEMP); if (error == 0) error = linux_common_execve(td, &eargs); AUDIT_SYSCALL_EXIT(error == EJUSTRETURN ? 0 : error, td); return (error); } CTASSERT(sizeof(struct l_iovec32) == 8); int linux32_copyinuio(struct l_iovec32 *iovp, l_ulong iovcnt, struct uio **uiop) { struct l_iovec32 iov32; </excerpt> </source> <source kind="search"> <title>freebsd32_misc.c « freebsd32 « compat « sys - src - FreeBSD source tree</title> <location>https://cgit.freebsd.org/src/tree/sys/compat/freebsd32/freebsd32_misc.c?id=1a71d24ecd0dbaf61fd2a44166e9be07c328b198</location> <excerpt>citeturn0search2 [wordlim: 200] Crawled: last month; * Copyright (c) 2002 Doug Rabson ... CP(*s, *s32, ru_oublock); ... freebsd32_rusage_out(&ru, &ru32); ... siginfo_to_siginfo32(const siginfo_t *src, struct __siginfo32 *dst) ... * interprets it as flags or pointer. CP(*s, *s32, ru_inblock); CP(*s, *s32, ru_oublock); CP(*s, *s32, ru_msgsnd); CP(*s, *s32, ru_msgrcv); CP(*s, *s32, ru_nsignals); CP(*s, *s32, ru_nvcsw); CP(*s, *s32, ru_nivcsw); } int freebsd32_wait4(struct thread *td, struct freebsd32_wait4_args *uap) { int error, status; struct rusage32 ru32; struct rusage ru, *rup; if (uap->rusage != NULL) rup = &ru; else rup = NULL; error = kern_wait(td, uap->pid, &status, uap->options, rup); if (uap->status != NULL && error == 0 && td->td_retval[0] != 0) error = copyout(&status, uap->status, sizeof(status)); if (uap->rusage != NULL && error == 0 && td->td_retval[0] != 0) { freebsd32_rusage_out(&ru, &ru32); error = copyout(&ru32, uap->rusage, sizeof(ru32)); } return (error); } int freebsd32_wait6(struct thread *td, struct freebsd32_wait6_args *uap) { struct __wrusage32 wru32; struct __wrusage wru, *wrup; </excerpt> </source> <source kind="search"> <title>freebsd32_misc.c « freebsd32 « compat « sys - src - FreeBSD source tree</title> <location>https://cgit.freebsd.org/src/tree/sys/compat/freebsd32/freebsd32_misc.c?id=3f8455b0905a05552b1155e09165787e1d098060</location> <excerpt>citeturn0search3 [wordlim: 200] Crawled: last week; * Copyright (c) 2002 Doug Rabson ... CP(*s, *s32, ru_oublock); ... freebsd32_rusage_out(&ru, &ru32); ... siginfo_to_siginfo32(const siginfo_t *src, struct siginfo32 *dst) void freebsd32_rusage_out(const struct rusage *s, struct rusage32 *s32) { TV_CP(*s, *s32, ru_utime); TV_CP(*s, *s32, ru_stime); CP(*s, *s32, ru_maxrss); CP(*s, *s32, ru_ixrss); CP(*s, *s32, ru_idrss); CP(*s, *s32, ru_isrss); CP(*s, *s32, ru_minflt); CP(*s, *s32, ru_majflt); CP(*s, *s32, ru_nswap); CP(*s, *s32, ru_inblock); CP(*s, *s32, ru_oublock); CP(*s, *s32, ru_msgsnd); CP(*s, *s32, ru_msgrcv); CP(*s, *s32, ru_nsignals); CP(*s, *s32, ru_nvcsw); CP(*s, *s32, ru_nivcsw); } int freebsd32_wait4(struct thread *td, struct freebsd32_wait4_args *uap) { int error, status; struct rusage32 ru32; struct rusage ru, *rup; if (uap->rusage != NULL) rup = &ru; else rup = NULL; error = kern_wait(td, uap->pid, &status, uap->options, rup); if (error) return (error); if (uap->status != NULL) error = copyout(&status, uap->status, sizeof(status)); if (uap->rusage != NULL && error == 0) { freebsd32_rusage_out(&ru, &ru32); error = copyout(&ru32, uap->rusage, sizeof(ru32)); } return (error); } int freebsd32_wait6(struct thread *td, struct freebsd32_wait6_args *uap) { struct wrusage32 wru32; struct __wrusage wru, *wrup; struct siginfo32 si32; struct __siginfo si, *sip; int error, status; if (uap->wrusage != NULL) wrup = &wru; else wrup = NULL; if (uap->info != NULL) { sip = &si; bzero(sip, sizeof(*sip)); } else sip = NULL; error = kern_wait6(td, uap->idtype, PAIR32TO64(id_t, uap->id), &status, uap->options, wrup, sip); if (error != 0) return (error); if (uap->status != NULL) error = copyout(&status, uap->status, sizeof(status)); if (uap->wrusage != NULL && error == 0) { freebsd32_rusage_out(&wru.wru_self, &wru32.wru_self); </excerpt> </source> </source_evidence> Citations: - 1: https://cgit.freebsd.org/src/tree/sys/sys/resource.h?id=f2a89e7e1d87c5a3aaa243aff3f6086e7312ecf5&utm_source=openai
Reject negative counters before conversion.
FreeBSD declares ru_inblock and ru_oublock as signed long values. Its kernel resource aggregation adds these fields without clamping. On 32-bit systems, cumulative overflow can produce a negative value. The casts at lines 175-176 then convert that value to a near-UINT64_MAX count, and the next rate calculation can report a large spike. Check each signed counter before conversion and mark invalid counts and rates as unavailable.
Source: Learnings
Adds an "I/O" default screen on FreeBSD, as Linux and Darwin have, with five new process columns: IO_OPS_RATE, IO_READ_OPS_RATE, IO_WRITE_OPS_RATE, IO_READ_OPS and IO_WRITE_OPS.
FreeBSD does not count the bytes of a process's disk I/O, only its block input and output operations (
ru_inblock/ru_oublockof the rusage inkinfo_proc, summed over the threads byrufetch()), which is whattop -m ioshows as READ and WRITE. So these are new operation-count columns rather than the byte-based IO_READ_RATE/IO_WRITE_RATE, which would mislabel them.kinfo_proc, and are readable for all processes without privileges.top -m io.Tested on FreeBSD 15.1 amd64 with dd(1) writing and find(1)/cat(1) reading: the numbers follow
top -m io.AI use: written with Claude (Claude Code), acknowledged with
Co-authored-byin the commit, and reviewed by me.🤖 Generated with Claude Code