The kernel module creates entries in /proc/proclens_module/:
/proc/proclens_module/pid- Read/write file to set target PID and read back current buffer value/proc/proclens_module/det- Read-only file to retrieve process information/proc/proclens_module/threads- Read-only file to retrieve thread information
proclens_module_show()- Main function to gather and format process informationproclens_module_threads_show()- Gathers thread information for all threads in a processfind_stack_vma_end()- Finds stack VMA lower boundary by iterating VMAsprocfile_write()- Handles PID input from user spaceprocfile_read()- Returns formatted/proc/proclens_module/pidbuffer content
| Field | Description | Source |
|---|---|---|
| Code Section | start_code to end_code |
Executable code region (includes rodata) |
| Data Section | start_data to end_data |
Initialized data region |
| BSS Section | end_data to start_brk |
Uninitialized data region |
| Heap Section | start_brk to brk |
brk-based dynamic memory allocation |
| Stack | start_stack to stack_end |
Stack region (grows downward) |
| ELF Base | First VMA start | Base address of ELF binary (for PIE) |
The process information output includes a memory pressure section with:
| Field | Description | Source |
|---|---|---|
| RSS (Resident) | Total physical memory used | Sum of anonymous, file-backed, and shared pages |
| Anonymous | Private memory pages | MM_ANONPAGES |
| File-backed | Mapped file pages | MM_FILEPAGES |
| Shared Mem | Shared memory pages | MM_SHMEMPAGES |
| VSZ (Virtual) | Total virtual memory | mm->total_vm |
| Swap Usage | Pages swapped out | MM_SWAPENTS |
| Page Faults | Major and minor faults | task->maj_flt, task->min_flt |
| OOM Score Adj | OOM killer adjustment | task->signal->oom_score_adj |
The process information output includes an open sockets section that lists all open socket file descriptors:
| Field | Description | Details |
|---|---|---|
| FD | File descriptor number | File descriptor index in process fd table |
| Family | Socket address family | AF_INET, AF_INET6, AF_UNIX, AF_NETLINK, etc. |
| Type | Socket type | STREAM (TCP), DGRAM (UDP), RAW |
| State | Connection state (TCP) | ESTABLISHED, LISTEN, CLOSE_WAIT, etc. |
| Proto | Socket protocol | TCP, UDP, or OTHER |
| Traffic | Per-socket RX/TX packet and byte counters | TCP: lifetime counters; UDP: current queue counters |
| Local | Local address and port | IPv4 (e.g., 127.0.0.1:8080) or IPv6 format |
| Remote | Remote address and port | Destination address for connected sockets |
The socket listing provides visibility into network connections and IPC sockets in use by the process. For processes with no open sockets, displays "No open sockets".
Notes:
- TCP traffic values come from
struct tcp_sockcounters (segs_in,segs_out,bytes_received,bytes_sent). - UDP traffic values are queue-based snapshots (
sk_receive_queue,sk_write_queue,sk_rmem_alloc,sk_wmem_queued). - Protocols other than TCP/UDP are labeled
OTHERand do not include a traffic line.
The process information output includes a brief network stats section, aggregated across the process sockets:
| Field | Description | Source |
|---|---|---|
| sockets_total | Total sockets and TCP/UDP/UNIX counts | File descriptor scan + sk_protocol/sk_family |
| rx_packets | Total TCP segments received | struct tcp_sock::segs_in |
| tx_packets | Total TCP segments sent | struct tcp_sock::segs_out |
| rx_bytes | Total TCP bytes received | struct tcp_sock::bytes_received |
| tx_bytes | Total TCP bytes sent | struct tcp_sock::bytes_sent |
| tcp_retransmits | TCP retransmitted segments | struct tcp_sock::retrans_out |
| drops | Raw/UDP drops | struct sock::sk_drops |
| net_devices | Device names with socket counts | sk_bound_dev_if or sk_rx_dst_ifindex |
| top_talkers | Up to 3 sockets ranked by total bytes (RX + TX) | Per-socket TCP lifetime bytes, UDP queued bytes |
Notes:
- Packet and byte counters are best-effort and only reflect TCP sockets. UDP and UNIX sockets are counted but do not contribute to byte/packet totals.
- Device mapping uses the socket bound interface or RX route ifindex; if neither is set, the socket is not attributed to a device.
- Top talkers ranking can include UDP sockets using queued RX/TX memory (
sk_rmem_alloc+sk_wmem_queued).
Modern ELF binaries often have end_data == start_brk, resulting in zero-length BSS. This is normal, not an error.
The read-only data segment is typically merged with the code section (start_code to end_code) in modern binaries. It's not shown separately.
The heap shown is brk-based only (traditional heap managed by brk/sbrk syscalls). Modern allocators like glibc's malloc also use:
- mmap-based allocations for large requests (>128KB typically)
- Arena heaps (multiple heap regions)
- These are NOT included in the brk-based heap range shown
- To see full heap usage, parse
/proc/pid/mapsfor anonymous[heap]entries and unnamed mmap regions
Shows both start_stack (top/base) and stack_end (current lower boundary). The stack grows downward from start_stack. The actual current stack pointer (in CPU registers) may be anywhere between these bounds.
proc_fs.h- Proc filesystem operationsseq_file.h- Sequential file interfacesched.h- Task/process structuresmm_types.h- Memory management structures- Maple tree API - Modern VMA iteration (kernel 6.8+)
Simple C program that supports two modes:
./build/proclensPrompts for a PID, then enters a 1-second refresh loop with section filtering.
Each refresh prints start/end timestamps in YY/MM/DD HH:MM:SS.
Snapshots are kept in an in-memory ring buffer (120 entries) for history browsing.
Default section is 5 (Overview).
Overview mode includes trend lanes rendered from recent snapshot history:
- CPU%
- RSS
- RX/s
- TX/s
- WR/s
Each lane uses 32 samples and block-style histogram glyphs, with one blank line between lanes for readability.
The Overview page is divided into four sub-sections printed below the trend lanes:
- MEMORY SNAPSHOT — RSS, VSZ, Swap Usage, and Page Fault counts (major/minor).
- NETWORK SNAPSHOT — socket total, cumulative rx/tx bytes, TCP retransmits, drops, and a top-talkers table (up to 2 sockets ranked by total bytes: FD, proto, family, RX_BYTES, TX_BYTES, TOTAL_BYTES).
- I/O SNAPSHOT — read_bytes, write_bytes, syscr, syscw, and io_intensity (or an
aggregated
statusline when available). - THREAD HOTSPOTS — total thread count and up to 3 top threads by CPU usage (columns: TID, name, CPU%, state, priority, nice, CPU affinity).
Controls shown in the header:
1- Memory section2- Network section3- Thread section4- I/O section5- Overview section (default)0- Prompt for a new PID (switch process)Upork- Older snapshotDownorj- Newer snapshotf- Return to live-follow mode
Keys 1, 2, 3, 4, 5 switch sections instantly (no Enter required). Pressing 0
temporarily restores cooked terminal mode so the user can type a PID, then returns to raw mode.
The process information output includes an I/O section ([io]) sourced from task I/O accounting:
| Field | Description | Source |
|---|---|---|
| rchar | Bytes returned by read-like syscalls | task->ioac.rchar |
| wchar | Bytes passed to write-like syscalls | task->ioac.wchar |
| syscr | Number of read-like syscalls | task->ioac.syscr |
| syscw | Number of write-like syscalls | task->ioac.syscw |
| read_bytes | Bytes actually read from storage | task->ioac.read_bytes |
| write_bytes | Bytes actually written to storage | task->ioac.write_bytes |
| cancelled_write_bytes | Dirty bytes not written due to truncation/deletion | task->ioac.cancelled_write_bytes |
| avg_read_bytes_per_syscall | Average read payload per syscall | rchar / max(syscr, 1) |
| avg_write_bytes_per_syscall | Average write payload per syscall | wchar / max(syscw, 1) |
| io_intensity | Aggregate storage traffic | read_bytes + write_bytes |
Notes:
- The section is available when
CONFIG_TASK_XACCTis enabled in the running kernel. - If task accounting is unavailable, the section prints an explicit
status: unavailableline. - All byte-count fields (
rchar,wchar,read_bytes,write_bytes,cancelled_write_bytes,avg_read_bytes_per_syscall,avg_write_bytes_per_syscall,io_intensity) are formatted viaformat_size_with_unitand displayed with human-readable units: B, KB, MB, or GB.syscrandsyscwremain raw counts.
./build/proclens <PID>Non-interactive mode - write PID once and print both process and thread information.
You can override the proc directory for testing:
PROCLENS_PROC_DIR=/tmp/fakeproc ./build/proclens 12345Internally, path construction is handled via build_proc_path().
Pure functions for CPU usage, BSS range, heap range, thread state, and address range checking:
compute_usage_permyriad()- CPU usage calculationcompute_bss_range()- BSS boundary validationcompute_heap_range()- Heap boundary validationis_address_in_range()- Address containment check
Works in both kernel and user space contexts.
Path building with environment override:
build_proc_path()- Constructs/proc/proclens_module/paths withPROCLENS_PROC_DIRsupport
The output is human-readable and grouped into sections:
- Basic process info (PID, name, CPU usage)
- Memory pressure statistics (RSS, VSZ, swap, faults, OOM adjustment)
- Memory layout (code/data/BSS/heap/stack/ELF base)
- Memory layout visualization
- Network stats (brief)
- Open sockets (file descriptors, address families, connection states)
TID NAME CPU(%) STATE PRIORITY NICE CPU_AFF
Example:
01234 bash 0.50 S 0 0 0,1,2,3
01235 worker 0.01 R 0 0 0,1
Total threads: 2
- R - Running or runnable (on run queue)
- S - Interruptible sleep (waiting for an event)
- D - Uninterruptible sleep (usually I/O)
- T - Stopped (by job control signal)
- t - Tracing stop (by debugger)
- Z - Zombie (terminated but not reaped)
- X - Dead (should never be seen)
- TID - Thread ID (same as PID for main thread)
- NAME - Thread name (typically same as process name)
- CPU(%) - Per-thread CPU usage since thread start
- STATE - Current thread state (see codes above)
- PRIORITY - Shown as nice value (-20 to 19, lower = higher priority)
- NICE - Nice value for the thread
- CPU_AFF - CPU affinity mask (which CPUs thread can run on)
Note: BSS_START and BSS_END may be equal (zero-length BSS) in modern ELF binaries. This is normal.