How witr Tracks Process Start Times and Restart Counts: A Deep Dive into the Source Code
witr tracks process start times by reading kernel-level timestamps from /proc/[pid]/stat on Linux and native APIs on Windows/macOS, while restart counts are fetched from service managers like systemd and launchd, storing both values in a model.Process struct for downstream analysis.
The open-source process monitoring tool witr (available at pranshuparmar/witr) provides detailed visibility into running processes by capturing metadata directly from the operating system. Understanding how witr track process start times and restart counts reveals the platform-specific implementations that power its monitoring capabilities, from parsing Linux kernel statistics to querying service manager databases.
Tracking Process Start Times Across Platforms
witr populates the StartedAt field in model.Process by using platform-specific methods to determine when a process was created.
Linux: Converting Kernel Ticks to Wall-Clock Time
On Linux, witr reads /proc/[pid]/stat to extract the process creation timestamp. The implementation in internal/proc/process_linux.go parses the 22nd field (starttime), which contains the number of clock ticks since system boot.
// internal/proc/process_linux.go
startTicks, _ := strconv.ParseInt(fields[19], 10, 64) // fields[19] is the 22‑nd field
The internal/proc/boot_linux.go file provides helper functions to convert these ticks into a usable time.Time. First, it retrieves the system boot time from /proc/stat and the tick frequency via ticksPerSecond(). Then, startTimeFromTicks performs the conversion:
// internal/proc/boot_linux.go
func startTimeFromTicks(boot time.Time, startTicks int64, hz int) time.Time {
// guard against divide‑by‑zero and overflow, then:
// start = boot + (startTicks / hz) seconds
// plus any remainder converted to nanoseconds.
}
Windows: Reading FILETIME from Process Handles
Windows does not expose /proc. Instead, witr calls the native API GetProcessTimes via internal/proc/peb_windows.go, which returns a FILETIME containing the creation time.
// internal/proc/peb_windows.go
func getProcessStartTime(handle syscall.Handle) time.Time {
var creation, exit, kernel, user windows.Filetime
windows.GetProcessTimes(handle, &creation, &exit, &kernel, &user)
return time.Unix(0, creation.Nanoseconds())
}
macOS: Using proc_pidinfo System Calls
For macOS (darwin), witr uses the same PEB abstraction pattern found in peb_windows.go, wrapping the proc_pidinfo system call within getProcessStartTime to obtain the process creation timestamp.
Tracking Restart Counts via Service Managers
The RestartCount field represents how many times the service or unit owning the process has been restarted. witr normalizes this value from platform-specific service managers.
Systemd on Linux: Querying the NRestarts Property
For systemd-managed services, internal/source/systemd_linux.go queries the DBus property NRestarts of the unit that owns the process.
// internal/source/systemd_linux.go (excerpt)
func (s *systemdSource) RestartCount(pid int) (int, error) {
// Resolve the unit name from the cgroup, then ask systemd via DBus:
// property := "NRestarts"
// value := getUint32Property(unit, property)
// Return the parsed integer.
}
Launchd on macOS: Detecting Keepalive Behavior
On macOS, internal/source/launchd_darwin.go checks the keepalive flag. If keepalive is configured as "Yes (restarts if killed)", witr treats the restart count as ≥ 1 and reports a generic "restarted" warning.
Aggregating Restart Data in the Analysis Pipeline
During the ancestry walk in internal/pipeline/analyze.go, witr records the highest restart count encountered in the process tree:
// internal/pipeline/analyze.go (excerpt)
restartCount := 0
for _, proc := range ancestry {
if count, ok := proc.RestartCount(); ok && count > restartCount {
restartCount = count
}
}
result := model.Process{
// …
RestartCount: restartCount,
Warnings: source.Warnings(ancestry, restartCount, src.Type),
}
The internal/source/detect.go file contains the Warnings helper that flags services exceeding a threshold (default 5 restarts).
Practical Code Examples
Reading Process Start Time on Linux
pid := 1234
proc, err := proc.ReadProcess(pid) // reads /proc files
if err != nil { panic(err) }
fmt.Printf("PID %d started at %s\n", pid, proc.StartedAt.Format(time.RFC3339))
Querying Restart Count from Systemd
src, _ := source.Detect(pid) // selects the appropriate source impl
count, _ := src.RestartCount(pid)
if count > 0 {
fmt.Printf("Process %d belongs to a service that restarted %d times\n", pid, count)
}
Full Process Record with Warnings
proc, _ := proc.ReadProcess(pid)
source := source.Detect(pid)
restartCount, _ := source.RestartCount(pid)
warnings := source.Warnings([]model.Process{proc}, restartCount, source.Type())
fmt.Printf("%+v\nWarnings: %v\n", proc, warnings)
Summary
- Start times are derived from low-level OS data: kernel tick counts on Linux via
internal/proc/process_linux.goandboot_linux.go, and native process-creation timestamps on Windows/macOS viapeb_windows.go. - Restart counts come from the underlying service manager (systemd's
NRestartsproperty or launchd'skeepaliveflag) and are normalized into a single integer. - The analysis pipeline in
internal/pipeline/analyze.goaggregates the maximum restart count across process ancestry and generates warnings when thresholds are exceeded. - Both values are stored in the
model.Processstruct for consumption by output formatters and monitoring warnings.
Frequently Asked Questions
How does witr calculate process start times on Linux systems?
witr reads the 22nd field (starttime) from /proc/[pid]/stat, which provides clock ticks since boot. It then converts these ticks to wall-clock time using the system boot time from /proc/stat and the tick frequency, as implemented in internal/proc/boot_linux.go.
Where does witr obtain restart count information for systemd services?
witr queries the systemd DBus interface for the NRestarts property of the unit owning the process, as implemented in internal/source/systemd_linux.go. This value indicates how many times systemd has automatically restarted the service.
What file contains the logic for aggregating restart counts across process ancestry?
The aggregation logic resides in internal/pipeline/analyze.go, which walks the process tree and records the highest restart count encountered. It also invokes the warning generator from internal/source/detect.go when counts exceed configured thresholds.
How does witr handle restart detection on macOS without systemd?
On macOS, witr uses internal/source/launchd_darwin.go to check the keepalive flag in the launchd configuration. If the service is configured to restart automatically, witr reports it as having been restarted, though it does not expose a numeric count as it does with systemd's NRestarts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →