How to get process details from its PID

Learn how to get process details from its pid with practical examples, diagrams, and best practices. Covers go development techniques with visual explanations.

Retrieving Process Details by PID in Go

Hero image for How to get process details from its PID

Learn how to programmatically obtain comprehensive information about a running process using its Process ID (PID) in Go, covering cross-platform considerations and practical examples.

Understanding and managing running processes is a fundamental aspect of system programming. Often, you might need to inspect a process's state, resource usage, or other metadata given only its Process ID (PID). This article will guide you through obtaining process details in Go, highlighting common approaches and libraries that simplify this task across different operating systems.

The Challenge of Cross-Platform Process Information

Retrieving process details can be surprisingly complex due to the varying ways operating systems manage and expose this information. Linux systems typically rely on the /proc filesystem, while Windows uses its own set of APIs (e.g., CreateToolhelp32Snapshot, OpenProcess). macOS, being Unix-like, shares some similarities with Linux but also has its unique system calls. Go's standard library offers basic process management, but for detailed information, external libraries are often necessary to abstract away these OS-specific differences.

flowchart TD
    A[Start with PID] --> B{Operating System?}
    B -->|Linux/Unix| C[Read /proc/PID/*]
    B -->|Windows| D[Use Windows API (e.g., `CreateToolhelp32Snapshot`)]
    C --> E[Parse Files (status, cmdline, etc.)]
    D --> F[Query Process Information]
    E --> G[Extract Details (Name, Memory, CPU, etc.)]
    F --> G
    G --> H[Return Process Details]
    H[Return Process Details] --> I[End]

Cross-platform process detail retrieval workflow

Using the shirou/gopsutil Library

For robust and cross-platform process information retrieval in Go, the shirou/gopsutil library is an excellent choice. It provides a unified API to access system and process metrics, abstracting away the underlying OS-specific implementations. This library is widely used and actively maintained, making it a reliable solution for most use cases.

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/shirou/gopsutil/v3/process"
)

func main() {
	// Example PID (replace with a real PID on your system)
	// You can find a PID using `pgrep` on Linux/macOS or Task Manager on Windows
	pid := int32(os.Getpid()) // Get current process's PID for demonstration

	proc, err := process.NewProcess(pid)
	if err != nil {
		log.Fatalf("Failed to get process for PID %d: %v", pid, err)
	}

	// Get process name
	name, err := proc.Name()
	if err != nil {
		log.Printf("Warning: Could not get process name for PID %d: %v", pid, err)
	} else {
		fmt.Printf("PID: %d\n", pid)
		fmt.Printf("Name: %s\n", name)
	}

	// Get CPU percent
	cpuPercent, err := proc.CPUPercent()
	if err != nil {
		log.Printf("Warning: Could not get CPU percent for PID %d: %v", pid, err)
	} else {
		fmt.Printf("CPU Percent: %.2f%%\n", cpuPercent)
	}

	// Get memory info
	memInfo, err := proc.MemoryInfo()
	if err != nil {
		log.Printf("Warning: Could not get memory info for PID %d: %v", pid, err)
	} else {
		fmt.Printf("Memory RSS: %d bytes\n", memInfo.RSS)
		fmt.Printf("Memory VMS: %d bytes\n", memInfo.VMS)
	}

	// Get command line arguments
	cmdline, err := proc.Cmdline()
	if err != nil {
		log.Printf("Warning: Could not get command line for PID %d: %v", pid, err)
	} else {
		fmt.Printf("Command Line: %s\n", cmdline)
	}

	// Get status
	status, err := proc.Status()
	if err != nil {
		log.Printf("Warning: Could not get status for PID %d: %v", pid, err)
	} else {
		fmt.Printf("Status: %s\n", status[0]) // Status can return multiple states
	}

	// Get creation time
	createTime, err := proc.CreateTime()
	if err != nil {
		log.Printf("Warning: Could not get creation time for PID %d: %v", pid, err)
	} else {
		fmt.Printf("Create Time: %s\n", time.Unix(0, createTime*int64(time.Millisecond)))
	}

	// Get parent PID
	ppid, err := proc.Ppid()
	if err != nil {
		log.Printf("Warning: Could not get parent PID for PID %d: %v", pid, err)
	} else {
		fmt.Printf("Parent PID: %d\n", ppid)
	}
}

Go program using gopsutil to retrieve various process details.

Handling Permissions and Errors

Accessing process information, especially for processes owned by other users or system processes, often requires elevated privileges. On Linux, this might mean running your Go program as root or with appropriate capabilities. On Windows, running as Administrator is usually necessary. Always include robust error handling, as processes can terminate unexpectedly, or you might lack the necessary permissions to query certain details.

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/shirou/gopsutil/v3/process"
)

func main() {
	// Attempt to get details for a PID that might not exist or be accessible
	pid := int32(99999) // A PID that is likely not running or inaccessible

	proc, err := process.NewProcess(pid)
	if err != nil {
		// Handle cases where the process doesn't exist or access is denied
		if os.IsNotExist(err) {
			fmt.Printf("Error: Process with PID %d does not exist.\n", pid)
		} else if os.IsPermission(err) {
			fmt.Printf("Error: Permission denied to access process with PID %d. Try running as administrator/root.\n", pid)
		} else {
			log.Fatalf("An unexpected error occurred for PID %d: %v", pid, err)
		}
		return
	}

	// If we reach here, the process object was successfully created
	name, err := proc.Name()
	if err != nil {
		fmt.Printf("Could not get name for PID %d: %v\n", pid, err)
	} else {
		fmt.Printf("Successfully accessed process %d: %s\n", pid, name)
	}
}

Example of error handling for non-existent or inaccessible processes.