Skip to content

Fix memory and CPU readings; add tests and CI - #1

Merged
fasharif merged 5 commits into
mainfrom
fix/memory-and-cpu-readings
Sep 25, 2026
Merged

fasharif merged 5 commits into
mainfrom
fix/memory-and-cpu-readings

Conversation

@fasharif

Copy link
Copy Markdown
Owner

What was wrong

  • Memory was overstated by thousands of times. After stime, the parser skipped 7 fields and read field 23 of /proc/[pid]/stat (vsize, the virtual size in bytes) as the resident set size. It then multiplied that by a hard-coded 4 and labelled it KB. For a shell using under 1 MB of RAM, it would print 6,766,592 KB.
  • CPU% used the wrong formula. It subtracted the process's CPU time from the system uptime, instead of the process's start time. Even the corrected formula only gives a lifetime average, not current use like top.
  • Names containing ) broke parsing, because the name was read with %255[^)].

What changed

  • Memory now comes from field 24 (rss, in pages) multiplied by sysconf(_SC_PAGESIZE).
  • CPU% compares two readings of utime + stime taken -i seconds apart, as top does.
  • The name is taken from the first ( to the last ), and every file read and parse is checked.
  • New options: -i (sampling interval), -n (row limit) and -s cpu|mem (sort order).
  • Parsing and calculations moved into src/proc.c so they can be unit tested.

How it's tested

  • Unit tests (make test): a real /proc/1/stat line, which asserts that memory comes from field 24 and not field 23; names with spaces and parentheses; overlong names; malformed lines; and the CPU% and memory calculations.
  • End-to-end (make integration): runs the program on a fake /proc tree, compares its memory figure for PID 1 with ps on the live system, and checks that invalid options exit with status 2.
  • CI: builds with GCC and Clang with warnings as errors, runs both suites, repeats them under AddressSanitizer and UBSan, and runs cppcheck.

🤖 Generated with Claude Code

fasharif and others added 5 commits September 25, 2026 20:19
Memory was read from field 23 of /proc/[pid]/stat (virtual size, in bytes)
instead of field 24 (resident pages), then multiplied by a hard-coded 4 KiB
page size, overstating it by thousands of times. CPU% subtracted the
process's CPU time from system uptime instead of its start time.

Memory now uses field 24 and sysconf(_SC_PAGESIZE). CPU% compares two
readings taken an interval apart, as top does. Names are taken from the
first '(' to the last ')' so names containing parentheses parse correctly.
Adds -i, -n and -s options, and moves parsing into src/proc.c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The key unit test uses a real /proc/1/stat line and asserts that memory
comes from field 24, not field 23. The end-to-end script also compares the
reading for PID 1 with ps(1) on a live system.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI builds with GCC and Clang with warnings as errors, runs the unit and
end-to-end tests, repeats them under AddressSanitizer and UBSan, and runs
cppcheck.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fasharif
fasharif merged commit e2f91f6 into main Sep 25, 2026
4 checks passed
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