Run pure function tests without loading any kernel modules:
make unitThis builds and runs:
src/proclens_module_tests.c– verifiescompute_usage_permyriad(),compute_bss_range(),compute_heap_range(),is_address_in_range(),get_thread_state_char(),build_cpu_affinity_string(), and memory-pressure helperssrc/proclens_tests.c– verifiesbuild_proc_path(), PID argument bounds, process-info print path, cmdline formatting, and section filtering for memory/network/I/O live views
Artifacts are created under build/.
For maximum safety, test the kernel module in an isolated QEMU virtual machine.
# One-time setup
sudo ./e2e/qemu-setup.sh
# Start VM
sudo ./e2e/qemu-run.sh
# In another terminal, run automated tests
sudo ./e2e/qemu-test.sh- Downloads Ubuntu 24.04 VM image
- Configures VM to install kernel and headers matching host
uname -r - Installs build prerequisites and repairs interrupted package state during setup
- Provides SSH access on port 2222
- Completely isolates module testing from your host
- Automated build, install, test, and uninstall cycle
After starting the VM with sudo ./e2e/qemu-run.sh:
# SSH into the VM
ssh -p 2222 ubuntu@localhost
# Password: ubuntu
# Inside VM - build and test
cd ProcLens
make clean && make all
sudo make install
./build/proclens
# Multi-threaded module test
make run-multithread
# Check kernel logs
sudo dmesg | tail -20
# Unload module
sudo make uninstall- SSH:
ssh -p 2222 ubuntu@localhost - Password:
ubuntu(change after first login) - Exit QEMU console: Ctrl+A then X
- Copy files TO VM:
scp -P 2222 file.txt ubuntu@localhost:~/ - Copy files FROM VM:
scp -P 2222 ubuntu@localhost:~/file.txt ./
# Remove QEMU environment and start fresh
rm -rf e2e/qemu-env/
sudo ./e2e/qemu-setup.shValidated testing path in this repository:
- QEMU image: Ubuntu 24.04 LTS
- Guest kernel: installed to match host
uname -rduringqemu-setup.sh
Requirements:
- Kernel 5.6+ required (proc_ops API)
- Kernel 6.8+ recommended (VMA iterator API with maple tree)
For reproducible results, use the QEMU workflow (qemu-setup.sh, qemu-run.sh, qemu-test.sh).
# Check kernel logs
dmesg | tail -n 20
# Verify kernel headers are installed
ls /lib/modules/$(uname -r)/build# Install missing dependencies
sudo apt-get install -y build-essential linux-headers-$(uname -r)
# Clean and rebuild
make clean
make allIf you are developing from WSL or macOS using Docker/dev containers, the container
kernel may not provide a usable /lib/modules/$(uname -r)/build tree. When you
see errors like No such file or directory for that path, run the QEMU e2e flow
instead of building modules directly in the container:
sudo ./e2e/qemu-setup.sh
sudo ./e2e/qemu-run.sh
sudo ./e2e/qemu-test.sh# Ensure module is loaded
lsmod | grep proclens_module
# Check proc entries exist (should show det, pid, threads)
ls -la /proc/proclens_module/# Test with single-threaded process
./build/proclens $$
# Test with multi-threaded process
# Start the bundled multi-threaded test program
make build-multithread
./build/test_multithread &
./build/proclens $!
# Manually check thread info
echo "<PID>" | sudo tee /proc/proclens_module/pid
sudo cat /proc/proclens_module/threadsThe thread output should include:
- TID (Thread ID)
- Thread name
- Per-thread CPU usage
- Thread state (R/S/D/T/t/Z/X)
- Priority and nice values
- CPU affinity mask
- Total thread count
Multi-threaded processes (browsers, IDEs, servers) will show multiple threads.
Verify no-argument live mode behavior:
./build/proclensExpected behavior:
- Program prints the startup logo once, then validates module/proc readiness
- If
proclens_moduleis not loaded, program exits immediately with status-1 - If required proc files are missing (
pid,det,threads), program exits immediately with status-1 - Screen refreshes every 1 second
- Header shows controls (
1memory,2network,3threads,4I/O,5overview,0switch PID) - Each refresh shows
Snapshot startandSnapshot endtimestamps inYY/MM/DD HH:MM:SS - Overview page shows 32-sample block-style histogram trend lanes for CPU, RSS, RX/s, TX/s, and WR/s once enough samples are collected
- Trend lanes are separated by one blank line (for example between RX/s and TX/s)
Up/kmoves to older snapshots,Down/jmoves toward newer snapshots- While browsing old snapshots, header indicates history-browsing mode
- Press
fto return to live-follow mode - Default section is overview (
5) - Press
4to view only I/O statistics ([io]section) - Press
5to return to the condensed overview page - Pressing
0prompts for a new PID and continues live refresh for that PID
Note: Live mode requires a real TTY for raw terminal input (tcgetattr/tcsetattr).
The single-keypress navigation cannot be exercised by unit tests; use manual testing
or the QEMU VM to verify it end-to-end.
Quick deterministic check with a fake proc directory:
mkdir -p /tmp/fake_proc
: > /tmp/fake_proc/pid
printf 'Memory Pressure Statistics:\n[network]\nsockets_total: 2\n' > /tmp/fake_proc/det
printf 'tid header\nthread line\n' > /tmp/fake_proc/threads
PROCLENS_PROC_DIR=/tmp/fake_proc ./build/proclensTo verify socket functionality, test with processes that have open sockets:
# Test with systemd (PID 1) - usually has several sockets
./build/proclens 1
# Test with SSH daemon
pgrep sshd | head -1 | xargs ./build/proclens
# Test with current shell (will show SSH connection socket if remote)
./build/proclens $$The socket output should include:
- File descriptor numbers
- Socket family (AF_INET, AF_INET6, AF_UNIX, AF_NETLINK)
- Socket type (STREAM, DGRAM, RAW)
- Connection state (ESTABLISHED, LISTEN, CLOSE_WAIT, etc.)
- Protocol label (TCP, UDP, OTHER)
- Per-socket traffic line for TCP/UDP sockets (
Traffic: RX ... TX ...) - Local and remote addresses/ports for inet sockets
- IPv6 addresses in full format
Processes with no open sockets will display "No open sockets".
The process output should include a short network section when sockets exist:
./build/proclens $$Look for:
[network]sockets_total:net_devices:top_talkers:
Note: RX/TX bytes and packets are aggregated from TCP sockets only. UNIX sockets are counted but do not contribute to byte/packet totals.
Top talkers behavior:
- Up to three sockets are listed in descending
RX + TXbyte order. - TCP entries use lifetime byte counters.
- UDP entries use queue-based byte snapshots.
- If no socket has byte activity, the section displays
none.
In the open sockets section:
- TCP sockets include lifetime packet/byte counters.
- UDP sockets include queue-based packet/byte counters (current queued data).
The process output should include an I/O section:
./build/proclens $$Look for:
[io]rchar:,wchar:,syscr:,syscw:read_bytes:,write_bytes:,cancelled_write_bytes:avg_read_bytes_per_syscall:,avg_write_bytes_per_syscall:io_intensity:
Byte-count fields (rchar, wchar, read_bytes, write_bytes, cancelled_write_bytes, the two averages, and io_intensity) display values with human-readable units (e.g. 12 KB, 3 MB, 2 GB). syscr and syscw are raw syscall counts with no unit suffix.
If the running kernel was built without task I/O accounting (CONFIG_TASK_XACCT),
the section will show status: unavailable instead of per-field counters.
Complete unit test coverage for all pure helper functions:
compute_usage_permyriad()- CPU usage calculationcompute_bss_range()- BSS boundary validationcompute_heap_range()- Heap boundary validationis_address_in_range()- Address containment check
get_thread_state_char()- Thread state conversionbuild_cpu_affinity_string()- CPU affinity formatting
calculate_rss_pages()- RSS calculationpages_to_kb()- Page to KB conversioncalculate_total_faults()- Total page faultsis_valid_oom_score_adj()- OOM score validationcalculate_memory_usage_percent()- Memory percentageformat_page_fault_stats()- Page fault formattingis_high_memory_pressure()- Memory pressure detection
socket_family_to_string()- Socket family conversion (AF_INET, AF_INET6, AF_UNIX, AF_NETLINK)socket_type_to_string()- Socket type conversion (STREAM, DGRAM, RAW)socket_state_to_string()- TCP state conversion (ESTABLISHED, LISTEN, etc.)add_netdev_count()- Net device usage aggregation helper
format_size_with_unit()- Human-readable byte formatting (B, KB, MB, GB)calculate_avg_bytes_per_syscall()- Average byte payload per syscallcalculate_io_intensity()- Aggregate storage traffic (read + write bytes)
All helpers are tested with:
- Normal cases
- Edge cases (zero, maximum values, boundaries)
- Error cases (invalid input, NULL pointers)
- All valid enum/state values