Skip to content

freebsd: add per-process block I/O columns and an I/O screen - #2142

Open
Renan2010p wants to merge 1 commit into
htop-dev:mainfrom
Renan2010p:freebsd-io
Open

Renan2010p wants to merge 1 commit into
htop-dev:mainfrom
Renan2010p:freebsd-io

Conversation

@Renan2010p

Copy link
Copy Markdown

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_oublock of the rusage in kinfo_proc, summed over the threads by rufetch()), which is what top -m io shows 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.

  • No extra system call: the counters come with kinfo_proc, and are readable for all processes without privileges.
  • Rates use the delta between scans on the monotonic clock, as on Darwin (19201a8).
  • Writes through the buffer cache are counted when they reach the disk, partly to the process and partly to bufdaemon/syncer, as in 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-by in the commit, and reviewed by me.

🤖 Generated with Claude Code

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>
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Walkthrough

FreeBSD process scans now record read and write operation counts and calculate their rates. The process table exposes read, write, and combined I/O rates and counts as sortable fields. A default I/O screen displays these fields and sorts by the combined rate.

Priority: Normal

Change: Feature

Merge Risk: Low · up to 32ec5

The FreeBSD I/O screen is mergeable with owner awareness, but long-running processes on 32-bit systems could show misleading I/O counts and rates after counter overflow.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Counts mark each passing scan
Rates follow time and change
Read and write fields take their place
One combined rate joins the view
The I/O screen sorts the flow

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Commits

Reviewing files that changed from the base of the PR and between 394b4a3 and 32ec5a8.

Files selected for processing: 5
  • freebsd/FreeBSDProcess.c
  • freebsd/FreeBSDProcess.h
  • freebsd/FreeBSDProcessTable.c
  • freebsd/Platform.c
  • freebsd/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.

Comment on lines +175 to +176
fp->io_read_ops = (uint64_t)kproc->ki_rusage.ru_inblock;
fp->io_write_ops = (uint64_t)kproc->ki_rusage.ru_oublock;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Quick win

🔎 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>citeturn1search4 [wordlim: 200] Published: 4 months ago; Crawled: last month; bio_doread(struct vnode *vp, daddr_t blkno, int size, int async) ... curproc-&gt;p_ru.ru_inblock++; /* XXX */
return recovered;

if (bufpages - pdelta &lt; 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&#39;t return it.

* Therefore, it&#39;s valid if its I/O has completed or been delayed.

*/

if (!ISSET(bp-&gt;b_flags, (B_DONE | B_DELWRI))) {

SET(bp-&gt;b_flags, B_READ | async);

bcstats.pendingreads++;

bcstats.numreads++;

VOP_STRATEGY(bp-&gt;b_vp, bp);

/* Pay for the read. */

curproc-&gt;p_ru.ru_inblock++; /* XXX */

} else if (async) {

brelse(bp);

}

mp = vp-&gt;v_type == VBLK ? vp-&gt;v_specmountpoint : vp-&gt;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-&gt;mnt_stat.f_syncreads++;

else

mp-&gt;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>citeturn3search0 [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 &quot; */
    #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&#39;s Manual</title>
<location>https://www.kernel.org/pub/linux/docs/man-pages/book/man-pages-6.13.pdf</location>
<excerpt>citeturn0search12 [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 `&lt;sys/resource.h&gt;`. ... 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:

&lt;visual_element id=&quot;e1&quot;&gt;
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
&lt;/visual_element&gt;

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>citeturn0search0 [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 &lt; __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 &quot; */

#endif /* __DARWIN_C_LEVEL &gt;= __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 &quot; */

user64_long_t ru_isrss; /* integral unshared stack &quot; */

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 &quot; */

};

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 &quot; */

user32_long_t ru_isrss; /* integral unshared stack &quot; */

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 &quot; */

};

</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&amp;manpath=FreeBSD+15.0-RELEASE+and+Ports&amp;query=getrusage&amp;sektion=2</location>
<excerpt>citeturn0search1 [wordlim: 200] Crawled: 4 weeks ago;          #include &lt;sys/resource.h&gt; ... 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 &quot;swapped&quot; 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 increment

Result:

<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>citeturn2view0 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({&quot;ref_id&quot;:&quot;turn1search5&quot;,&quot;lineno&quot;:null}); Total lines: 7340
L4421:     		sx_assert(&amp;proctree_lock, SA_LOCKED);
L4422:     #endif
L4423: 
L4424:     	cnt = 0;
L4425:     	LIST_FOREACH(q, &amp;pgrp-&gt;pg_members, p_pglist) {
L4426:     		if ((q-&gt;p_treeflag &amp; P_TREE_GRPEXITED) != 0 ||
L4427:     		    q-&gt;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(&amp;proctree_lock, SX_XLOCKED);
L4444:     	PROC_LOCK_ASSERT(p, MA_NOTOWNED);
L4445:     	PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED);
L4446:     	PGRP_LOCK_ASSERT(p-&gt;p_pgrp, MA_NOTOWNED);
L4447:     	SESS_LOCK_ASSERT(p-&gt;p_session, MA_NOTOWNED);
L4448: 
L4449:     	savepgrp = p-&gt;p_pgrp;
L4450:     	pp = jobc_parent(p, NULL);
L4451: 
L4452:     	PGRP_LOCK(pgrp);
L4453:     	PGRP_LOCK(savepgrp);
L4454:     	if (isjobproc(pp, savepgrp) &amp;&amp; pgrp_calc_jobc(savepgrp) == 1)
L4455:     		orphanpg(savepgrp);
L4456:     	PROC_LOCK(p);
L4457:     	LIST_REMOVE(p, p_pglist);
L4458:     	p-&gt;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&#39; children to see whether they qualify
L4560:     	 * their process groups after reparenting to reaper.
L4561:     	 */
L4562:     	LIST_FOREACH(q, &amp;p-&gt;p_children, p_sibling) {
L4563:     		pgrp = q-&gt;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-&gt;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 &amp; KERN_PROC_NOTHREADS) != 0) {
L5202:     #ifdef COMPAT_FREEBSD32
L5203:     		if ((flags &amp; 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 &amp; KERN_PROC_MASK32) != 0)
L5211:     			size += sizeof(struct kinfo_proc32) * p-&gt;p_numthreads;
L5212:     		else
L5213:     #endif
L5214:     			size += sizeof(struct kinfo_proc) * p-&gt;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, &amp;ki);
L5235:     	if ((flags &amp; KERN_PROC_NOTHREADS) != 0) {
L5236:     #ifdef COMPAT_FREEBSD32
L5237:     		if ((flags &amp; 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>citeturn2view1 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({&quot;ref_id&quot;:&quot;turn1search8&quot;,&quot;lineno&quot;:null}); Total lines: 3592
L2718:     	error = getrlimitusage_one(curproc, uap-&gt;which, uap-&gt;flags, &amp;res);
L2719:     	if (error == 0)
L2720:     		error = copyout(&amp;res, uap-&gt;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, &amp;p-&gt;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-&gt;td_proc == p) {
L2757:     		u = cpu_ticks();
L2758:     		runtime = u - PCPU_GET(switchtime);
L2759:     		td-&gt;td_runtime += runtime;
L2760:     		td-&gt;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-&gt;td_incruntime == 0)
L2766:     			continue;
L2767:     		ruxagg(p, td);
L2768:     	}
L2769:     	calcru1(p, &amp;p-&gt;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-&gt;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-&gt;who, &amp;ru);
L2972:     	if (error == 0)
L2973:     		error = copyout(&amp;ru, uap-&gt;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-&gt;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-&gt;ru_maxrss &lt; ru2-&gt;ru_maxrss)
L3019:     		ru-&gt;ru_maxrss = ru2-&gt;ru_maxrss;
L3020:     	ip = &amp;ru-&gt;ru_first;
L3021:     	ip2 = &amp;ru2-&gt;ru_first;
L3022:     	for (i = &amp;ru-&gt;ru_last - &amp;ru-&gt;ru_first; i &gt;= 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-&gt;rux_runtime += rux2-&gt;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>citeturn2view2 [wordlim: 200] Crawled: last week; Content type: text/html; Source: open({&quot;ref_id&quot;:&quot;turn0search0&quot;,&quot;lineno&quot;: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 &quot;Unknown architecture&quot;
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&#39;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>citeturn2view3 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({&quot;ref_id&quot;:&quot;turn1search0&quot;,&quot;lineno&quot;:null}); Total lines: 410
L285:     	long	ru_isrss;		/* integral unshared stack &quot; */
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 &quot; */
</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>citeturn0search1 [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 &quot; */
    #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 source

Result:

<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>citeturn0search1 [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 &quot; */
    	long	ru_isrss;		/* integral unshared stack &quot; */
    	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 &quot; */
    #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 */
    	&quot;core&quot;,
    	&quot;rss&quot;,
    	&quot;memlock&quot;,
    	&quot;nproc&quot;,
    	&quot;nofile&quot;,
    	&quot;sbsize&quot;,
    	&quot;vmem&quot;,
    	&quot;npts&quot;,
    	&quot;swap&quot;,
    	&quot;kqueues&quot;,
    	&quot;umtx&quot;,
    	&quot;pipebuf&quot;,
    	&quot;vms&quot;,
    };
    #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>citeturn0search0 [wordlim: 200] Crawled: 6 days ago;     #include &lt;compat/freebsd32/freebsd32_util.h&gt; ...     	lru-&gt;ru_utime.tv_usec = ru-&gt;ru_utime.tv_usec; ...     	lru-&gt;ru_oublock = ru-&gt;ru_oublock;
    #include &lt;sys/fcntl.h&gt;
    #include &lt;sys/file.h&gt;
    #include &lt;sys/imgact.h&gt;
    #include &lt;sys/kernel.h&gt;
    #include &lt;sys/limits.h&gt;
    #include &lt;sys/lock.h&gt;
    #include &lt;sys/malloc.h&gt;
    #include &lt;sys/mman.h&gt;
    #include &lt;sys/mutex.h&gt;
    #include &lt;sys/priv.h&gt;
    #include &lt;sys/proc.h&gt;
    #include &lt;sys/resource.h&gt;
    #include &lt;sys/resourcevar.h&gt;
    #include &lt;sys/syscallsubr.h&gt;
    #include &lt;sys/sysproto.h&gt;
    #include &lt;sys/systm.h&gt;
    #include &lt;sys/unistd.h&gt;
    #include &lt;sys/wait.h&gt;
    
    #include &lt;machine/frame.h&gt;
    #include &lt;machine/md_var.h&gt;
    #include &lt;machine/pcb.h&gt;
    #include &lt;machine/psl.h&gt;
    #include &lt;machine/segments.h&gt;
    #include &lt;machine/specialreg.h&gt;
    #include &lt;x86/ifunc.h&gt;
    
    #include &lt;vm/pmap.h&gt;
    #include &lt;vm/vm.h&gt;
    #include &lt;vm/vm_map.h&gt;
    
    #include &lt;security/audit/audit.h&gt;
    
    #include &lt;compat/freebsd32/freebsd32_util.h&gt;
    #include &lt;amd64/linux32/linux.h&gt;
    #include &lt;amd64/linux32/linux32_proto.h&gt;
    #include &lt;compat/linux/linux_emul.h&gt;
    #include &lt;compat/linux/linux_ipc.h&gt;
    #include &lt;compat/linux/linux_misc.h&gt;
    #include &lt;compat/linux/linux_mmap.h&gt;
    #include &lt;compat/linux/linux_signal.h&gt;
    #include &lt;compat/linux/linux_util.h&gt;
    
    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-&gt;ru_utime.tv_sec = ru-&gt;ru_utime.tv_sec;
    	lru-&gt;ru_utime.tv_usec = ru-&gt;ru_utime.tv_usec;
    	lru-&gt;ru_stime.tv_sec = ru-&gt;ru_stime.tv_sec;
    	lru-&gt;ru_stime.tv_usec = ru-&gt;ru_stime.tv_usec;
    	lru-&gt;ru_maxrss = ru-&gt;ru_maxrss;
    	lru-&gt;ru_ixrss = ru-&gt;ru_ixrss;
    	lru-&gt;ru_idrss = ru-&gt;ru_idrss;
    	lru-&gt;ru_isrss = ru-&gt;ru_isrss;
    	lru-&gt;ru_minflt = ru-&gt;ru_minflt;
    	lru-&gt;ru_majflt = ru-&gt;ru_majflt;
    	lru-&gt;ru_nswap = ru-&gt;ru_nswap;
    	lru-&gt;ru_inblock = ru-&gt;ru_inblock;
    	lru-&gt;ru_oublock = ru-&gt;ru_oublock;
    	lru-&gt;ru_msgsnd = ru-&gt;ru_msgsnd;
    	lru-&gt;ru_msgrcv = ru-&gt;ru_msgrcv;
    	lru-&gt;ru_nsignals = ru-&gt;ru_nsignals;
    	lru-&gt;ru_nvcsw = ru-&gt;ru_nvcsw;
    	lru-&gt;ru_nivcsw = ru-&gt;ru_nivcsw;
    }
    
    int
    linux_copyout_rusage(struct rusage *ru, void *uaddr)
    {
    	struct l_rusage lru;
    
    	bsd_to_linux_rusage(ru, &amp;lru);
    
    	return (copyout(&amp;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-&gt;path, &amp;path);
    
    	error = freebsd32_exec_copyin_args(&amp;eargs, path, UIO_SYSSPACE,
    	    args-&gt;argp, args-&gt;envp);
    	free(path, M_TEMP);
    	if (error == 0)
    		error = linux_common_execve(td, &amp;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>citeturn0search2 [wordlim: 200] Crawled: last month;      * Copyright (c) 2002 Doug Rabson ...     	CP(*s, *s32, ru_oublock); ...     		freebsd32_rusage_out(&amp;ru, &amp;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-&gt;rusage != NULL)
    		rup = &amp;ru;
    	else
    		rup = NULL;
    	error = kern_wait(td, uap-&gt;pid, &amp;status, uap-&gt;options, rup);
    	if (uap-&gt;status != NULL &amp;&amp; error == 0 &amp;&amp; td-&gt;td_retval[0] != 0)
    		error = copyout(&amp;status, uap-&gt;status, sizeof(status));
    	if (uap-&gt;rusage != NULL &amp;&amp; error == 0 &amp;&amp; td-&gt;td_retval[0] != 0) {
    		freebsd32_rusage_out(&amp;ru, &amp;ru32);
    		error = copyout(&amp;ru32, uap-&gt;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>citeturn0search3 [wordlim: 200] Crawled: last week;      * Copyright (c) 2002 Doug Rabson ...     	CP(*s, *s32, ru_oublock); ...     		freebsd32_rusage_out(&amp;ru, &amp;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-&gt;rusage != NULL)
    		rup = &amp;ru;
    	else
    		rup = NULL;
    	error = kern_wait(td, uap-&gt;pid, &amp;status, uap-&gt;options, rup);
    	if (error)
    		return (error);
    	if (uap-&gt;status != NULL)
    		error = copyout(&amp;status, uap-&gt;status, sizeof(status));
    	if (uap-&gt;rusage != NULL &amp;&amp; error == 0) {
    		freebsd32_rusage_out(&amp;ru, &amp;ru32);
    		error = copyout(&amp;ru32, uap-&gt;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-&gt;wrusage != NULL)
    		wrup = &amp;wru;
    	else
    		wrup = NULL;
    	if (uap-&gt;info != NULL) {
    		sip = &amp;si;
    		bzero(sip, sizeof(*sip));
    	} else
    		sip = NULL;
    	error = kern_wait6(td, uap-&gt;idtype, PAIR32TO64(id_t, uap-&gt;id),
    	    &amp;status, uap-&gt;options, wrup, sip);
    	if (error != 0)
    		return (error);
    	if (uap-&gt;status != NULL)
    		error = copyout(&amp;status, uap-&gt;status, sizeof(status));
    	if (uap-&gt;wrusage != NULL &amp;&amp; error == 0) {
    		freebsd32_rusage_out(&amp;wru.wru_self, &amp;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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant