Skip to main content
Learn how to work with system processes using gopsutil. This example shows you how to list processes, get detailed process information, and monitor resource usage.

What You’ll Build

A command-line tool that can:
  • List all running processes
  • Get detailed information about specific processes
  • Monitor CPU and memory usage of processes
  • Find processes by name
  • Track process status and parent-child relationships

Prerequisites

1

Install gopsutil

go get github.com/shirou/gopsutil/v4
2

Create your project

mkdir process-monitor
cd process-monitor
go mod init process-monitor

Building the Process Monitor

1

Import required packages

Create a main.go file with the necessary imports:
main.go
package main

import (
    "fmt"
    "os"
    "sort"
    "strings"
    "time"

    "github.com/shirou/gopsutil/v4/process"
)
2

Create a process information structure

Define a struct to hold process information:
main.go
type ProcessInfo struct {
    PID         int32
    Name        string
    Status      string
    CPUPercent  float64
    MemPercent  float32
    MemoryMB    uint64
    NumThreads  int32
    CreateTime  string
}

func (p ProcessInfo) String() string {
    return fmt.Sprintf(
        "PID: %-6d | Name: %-20s | CPU: %6.2f%% | Mem: %6.2f%% (%d MB) | Threads: %d | Status: %s",
        p.PID, p.Name, p.CPUPercent, p.MemPercent, p.MemoryMB,
        p.NumThreads, p.Status,
    )
}
3

Get information for a single process

Create a function to retrieve detailed process information:
main.go
func getProcessInfo(p *process.Process) (*ProcessInfo, error) {
    info := &ProcessInfo{PID: p.Pid}

    // Get process name
    name, err := p.Name()
    if err != nil {
        return nil, err
    }
    info.Name = name

    // Get process status
    status, err := p.Status()
    if err == nil && len(status) > 0 {
        info.Status = status[0]
    }

    // Get CPU percentage (requires a measurement interval)
    cpuPercent, err := p.CPUPercent()
    if err == nil {
        info.CPUPercent = cpuPercent
    }

    // Get memory percentage
    memPercent, err := p.MemoryPercent()
    if err == nil {
        info.MemPercent = memPercent
    }

    // Get memory info in MB
    memInfo, err := p.MemoryInfo()
    if err == nil {
        info.MemoryMB = memInfo.RSS / 1024 / 1024
    }

    // Get number of threads
    numThreads, err := p.NumThreads()
    if err == nil {
        info.NumThreads = numThreads
    }

    // Get creation time
    createTime, err := p.CreateTime()
    if err == nil {
        t := time.Unix(createTime/1000, 0)
        info.CreateTime = t.Format("2006-01-02 15:04:05")
    }

    return info, nil
}
Process methods may return errors if the process terminates or if you lack permissions. Always check errors and handle them gracefully.
4

List all running processes

Create a function to list all processes:
main.go
func listAllProcesses() error {
    // Get all process PIDs
    pids, err := process.Pids()
    if err != nil {
        return fmt.Errorf("error getting process list: %w", err)
    }

    fmt.Printf("\nFound %d running processes\n\n", len(pids))

    var processes []*ProcessInfo

    // Get information for each process
    for _, pid := range pids {
        p, err := process.NewProcess(pid)
        if err != nil {
            continue // Process may have terminated
        }

        info, err := getProcessInfo(p)
        if err != nil {
            continue // Skip processes we can't access
        }

        processes = append(processes, info)
    }

    // Sort by CPU usage (descending)
    sort.Slice(processes, func(i, j int) bool {
        return processes[i].CPUPercent > processes[j].CPUPercent
    })

    // Display top 20 processes by CPU usage
    fmt.Println("Top 20 Processes by CPU Usage:")
    fmt.Println(strings.Repeat("=", 120))
    for i, info := range processes {
        if i >= 20 {
            break
        }
        fmt.Println(info)
    }

    return nil
}
5

Find processes by name

Add a function to search for processes:
main.go
func findProcessByName(searchName string) error {
    pids, err := process.Pids()
    if err != nil {
        return fmt.Errorf("error getting process list: %w", err)
    }

    fmt.Printf("\nSearching for processes matching '%s'...\n\n", searchName)
    found := false

    for _, pid := range pids {
        p, err := process.NewProcess(pid)
        if err != nil {
            continue
        }

        name, err := p.Name()
        if err != nil {
            continue
        }

        // Case-insensitive search
        if strings.Contains(strings.ToLower(name), 
                           strings.ToLower(searchName)) {
            info, err := getProcessInfo(p)
            if err != nil {
                continue
            }

            fmt.Println(info)
            found = true
        }
    }

    if !found {
        fmt.Printf("No processes found matching '%s'\n", searchName)
    }

    return nil
}
6

Get detailed process information

Create a function to show comprehensive process details:
main.go
func showProcessDetails(pid int32) error {
    p, err := process.NewProcess(pid)
    if err != nil {
        return fmt.Errorf("error getting process: %w", err)
    }

    fmt.Printf("\n=== Process Details (PID: %d) ===\n\n", pid)

    // Basic information
    if name, err := p.Name(); err == nil {
        fmt.Printf("Name: %s\n", name)
    }

    if exe, err := p.Exe(); err == nil {
        fmt.Printf("Executable: %s\n", exe)
    }

    if cwd, err := p.Cwd(); err == nil {
        fmt.Printf("Working Directory: %s\n", cwd)
    }

    if cmdline, err := p.Cmdline(); err == nil {
        fmt.Printf("Command Line: %s\n", cmdline)
    }

    // Status and times
    if status, err := p.Status(); err == nil && len(status) > 0 {
        fmt.Printf("Status: %s\n", status[0])
    }

    if createTime, err := p.CreateTime(); err == nil {
        t := time.Unix(createTime/1000, 0)
        fmt.Printf("Started: %s\n", t.Format("2006-01-02 15:04:05"))
        fmt.Printf("Running for: %s\n", time.Since(t).Round(time.Second))
    }

    // Parent process
    if ppid, err := p.Ppid(); err == nil {
        fmt.Printf("Parent PID: %d\n", ppid)
        if parent, err := process.NewProcess(ppid); err == nil {
            if parentName, err := parent.Name(); err == nil {
                fmt.Printf("Parent Name: %s\n", parentName)
            }
        }
    }

    // Resource usage
    fmt.Println("\n--- Resource Usage ---")

    if cpuPercent, err := p.CPUPercent(); err == nil {
        fmt.Printf("CPU: %.2f%%\n", cpuPercent)
    }

    if memPercent, err := p.MemoryPercent(); err == nil {
        fmt.Printf("Memory: %.2f%%\n", memPercent)
    }

    if memInfo, err := p.MemoryInfo(); err == nil {
        fmt.Printf("Memory RSS: %d MB\n", memInfo.RSS/1024/1024)
        fmt.Printf("Memory VMS: %d MB\n", memInfo.VMS/1024/1024)
    }

    if numThreads, err := p.NumThreads(); err == nil {
        fmt.Printf("Threads: %d\n", numThreads)
    }

    // I/O counters (Linux only)
    if ioCounters, err := p.IOCounters(); err == nil {
        fmt.Println("\n--- I/O Statistics ---")
        fmt.Printf("Read Count: %d\n", ioCounters.ReadCount)
        fmt.Printf("Write Count: %d\n", ioCounters.WriteCount)
        fmt.Printf("Read Bytes: %d MB\n", ioCounters.ReadBytes/1024/1024)
        fmt.Printf("Write Bytes: %d MB\n", ioCounters.WriteBytes/1024/1024)
    }

    // Network connections
    if connections, err := p.Connections(); err == nil && len(connections) > 0 {
        fmt.Println("\n--- Network Connections ---")
        for i, conn := range connections {
            if i >= 10 { // Limit to first 10
                fmt.Printf("... and %d more\n", len(connections)-10)
                break
            }
            fmt.Printf("%s: %s:%d -> %s:%d [%s]\n",
                conn.Type, conn.Laddr.IP, conn.Laddr.Port,
                conn.Raddr.IP, conn.Raddr.Port, conn.Status)
        }
    }

    return nil
}
Some process information (like I/O counters and connections) may only be available on certain platforms or require elevated privileges.
7

Monitor a process continuously

Add a function to monitor a process over time:
main.go
func monitorProcess(pid int32, duration time.Duration) error {
    p, err := process.NewProcess(pid)
    if err != nil {
        return fmt.Errorf("error getting process: %w", err)
    }

    name, _ := p.Name()
    fmt.Printf("\nMonitoring process %d (%s) for %s...\n\n", pid, name, duration)
    fmt.Println("Time     | CPU %  | Memory % | Memory MB | Threads")
    fmt.Println(strings.Repeat("-", 60))

    ticker := time.NewTicker(2 * time.Second)
    defer ticker.Stop()

    timeout := time.After(duration)

    for {
        select {
        case <-timeout:
            fmt.Println("\nMonitoring complete.")
            return nil

        case t := <-ticker.C:
            // Check if process still exists
            isRunning, err := p.IsRunning()
            if err != nil || !isRunning {
                fmt.Println("\nProcess terminated.")
                return nil
            }

            info, err := getProcessInfo(p)
            if err != nil {
                continue
            }

            fmt.Printf("%s | %5.2f | %7.2f | %9d | %7d\n",
                t.Format("15:04:05"),
                info.CPUPercent,
                info.MemPercent,
                info.MemoryMB,
                info.NumThreads,
            )
        }
    }
}
8

Create the main function with CLI

Add a simple command-line interface:
main.go
func main() {
    if len(os.Args) < 2 {
        printUsage()
        return
    }

    command := os.Args[1]

    switch command {
    case "list":
        if err := listAllProcesses(); err != nil {
            fmt.Printf("Error: %v\n", err)
        }

    case "find":
        if len(os.Args) < 3 {
            fmt.Println("Usage: go run main.go find <process-name>")
            return
        }
        if err := findProcessByName(os.Args[2]); err != nil {
            fmt.Printf("Error: %v\n", err)
        }

    case "details":
        if len(os.Args) < 3 {
            fmt.Println("Usage: go run main.go details <pid>")
            return
        }
        var pid int32
        if _, err := fmt.Sscanf(os.Args[2], "%d", &pid); err != nil {
            fmt.Printf("Invalid PID: %v\n", err)
            return
        }
        if err := showProcessDetails(pid); err != nil {
            fmt.Printf("Error: %v\n", err)
        }

    case "monitor":
        if len(os.Args) < 3 {
            fmt.Println("Usage: go run main.go monitor <pid>")
            return
        }
        var pid int32
        if _, err := fmt.Sscanf(os.Args[2], "%d", &pid); err != nil {
            fmt.Printf("Invalid PID: %v\n", err)
            return
        }
        if err := monitorProcess(pid, 30*time.Second); err != nil {
            fmt.Printf("Error: %v\n", err)
        }

    default:
        printUsage()
    }
}

func printUsage() {
    fmt.Println("Process Monitor")
    fmt.Println("===============")
    fmt.Println("\nUsage:")
    fmt.Println("  go run main.go list              - List top processes by CPU")
    fmt.Println("  go run main.go find <name>       - Find processes by name")
    fmt.Println("  go run main.go details <pid>     - Show detailed process info")
    fmt.Println("  go run main.go monitor <pid>     - Monitor process for 30s")
}
9

Test the application

Run the different commands:
# List top processes
go run main.go list

# Find processes by name
go run main.go find chrome

# Show details for a specific PID
go run main.go details 1234

# Monitor a process
go run main.go monitor 1234

Example Output

List Command

Top 20 Processes by CPU Usage:
========================================================
PID: 1234   | Name: chrome             | CPU:  45.32% | Mem:   8.50% (1024 MB) | Threads: 32 | Status: running
PID: 5678   | Name: docker             | CPU:  12.45% | Mem:   2.30% (256 MB)  | Threads: 12 | Status: running
...

Details Command

=== Process Details (PID: 1234) ===

Name: chrome
Executable: /usr/bin/chrome
Working Directory: /home/user
Command Line: /usr/bin/chrome --type=renderer
Status: running
Started: 2024-03-09 10:30:00
Running for: 2h15m30s
Parent PID: 1000
Parent Name: chrome

--- Resource Usage ---
CPU: 45.32%
Memory: 8.50%
Memory RSS: 1024 MB
Memory VMS: 2048 MB
Threads: 32

Key Concepts

Process Creation

Use process.NewProcess(pid) to get a process handle. Always check if the process exists.

Resource Monitoring

Methods like CPUPercent() and MemoryPercent() provide real-time usage data.

Process Hierarchy

Use Ppid() to get parent process ID and track process relationships.

Error Handling

Processes can terminate anytime. Always handle errors gracefully.
Some process operations require elevated privileges (root/administrator). If you get permission errors, try running your program with sudo or as administrator.

Next Steps

System Dashboard

Combine process monitoring with system metrics to build a complete monitoring dashboard.

Build docs developers (and LLMs) love