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.go and boot_linux.go, and native process-creation timestamps on Windows/macOS via peb_windows.go.
  • Restart counts come from the underlying service manager (systemd's NRestarts property or launchd's keepalive flag) and are normalized into a single integer.
  • The analysis pipeline in internal/pipeline/analyze.go aggregates the maximum restart count across process ancestry and generates warnings when thresholds are exceeded.
  • Both values are stored in the model.Process struct 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →