Skip to content
Go back

/proc as a Debugging Tool

By SumGuy 5 min read
/proc as a Debugging Tool

The /proc filesystem is a goldmine of information about running processes, and it lives entirely in RAM. It’s not a real filesystem — the kernel generates these files on the fly. Want to know what a process is doing? Don’t install another tool. Just read /proc.

Here’s the thing: ps, lsof, strace — they all read from /proc. You can do the same thing directly. Understanding /proc gives you a way to debug that doesn’t rely on specific tools being installed or having the right permissions.

The Structure

Every running process gets a directory named after its PID:

Terminal window
$ ls /proc
1 cmdline devices filesystems mounts swaps
10 cpuinfo diskstats interrupts partitions sys
2 crypto dma iomem pressure sysvipc
3 cgroups domainname ioports sched_debug thread-self
acpi cgroup_rw exec-domains kallsyms schedstat timer_list
...

The numeric directories are processes. Let’s look inside one:

Terminal window
$ ls -la /proc/1234/
total 0
dr-xr-xr-x 9 root root 0 Mar 1 10:45 .
-r--r--r-- 1 root root 0 Mar 1 10:45 attr
-rw-r--r-- 1 root root 0 Mar 1 10:45 autogroup
-r--r--r-- 1 root root 0 Mar 1 10:45 cgroup
--w------- 1 root root 0 Mar 1 10:45 clear_refs
-r--r--r-- 1 root root 0 Mar 1 10:45 cmdline
-r--r--r-- 1 root root 0 Mar 1 10:45 comm
...

Each file tells you something about the process.

The Essential Files

1. cmdline — The Command That Started the Process

Terminal window
$ cat /proc/1234/cmdline | tr '\0' ' '; echo
/usr/bin/python3 /opt/myapp/server.py --config /etc/app.conf --debug

This is the exact command line. Useful when ps output is truncated or you need the full arguments.

2. environ — Environment Variables

Terminal window
$ cat /proc/1234/environ | tr '\0' '\n' | head -n 10
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
HOME=/root
USER=root
LANG=en_US.UTF-8
APP_DEBUG=1
DATABASE_URL=postgresql://localhost/mydb

See what environment variables the process inherited. Useful for debugging why a config setting isn’t being picked up.

3. fd/ — Open File Descriptors

Terminal window
$ ls -la /proc/1234/fd
total 0
lrwx------ 1 root root 64 Mar 1 10:45 0 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 1 10:45 1 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 1 10:45 2 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 1 10:45 3 -> socket:[12345]
lrwx------ 1 root root 64 Mar 1 10:45 4 -> /var/log/app.log
lrwx------ 1 root root 64 Mar 1 10:45 5 -> /etc/app.conf
lrwx------ 1 root root 64 Mar 1 10:45 6 -> /dev/null

Each number is a file descriptor. The symlink target shows what it points to. Socket fds show socket:[inode]. This is exactly what lsof reads under the hood.

4. status — Process State and Limits

Terminal window
$ cat /proc/1234/status
Name: python3
Umask: 0022
State: S (sleeping)
Tgid: 1234
Pid: 1234
PPid: 1000
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 256
VmPeak: 123456 kB
VmHWM: 56789 kB
VmRSS: 45678 kB
...

Process state (sleeping, running, zombie), memory usage (VmRSS is resident set size), and more. The FDSize tells you the max file descriptors.

5. maps — Memory Layout

Terminal window
$ cat /proc/1234/maps
55555555b000-55555555c000 r-xp 00000000 08:05 9999999 /usr/bin/python3
55555575b000-55555575c000 r--p 00001000 08:05 9999999 /usr/bin/python3
55555575c000-55555575d000 rw-p 00002000 08:05 9999999 /usr/bin/python3
7ffff7dd5000-7ffff7dff000 r-xp 00000000 08:05 8888888 /lib64/libc-2.31.so
...

Every memory region the process has mapped. Useful for understanding what libraries are loaded and memory layout during debugging.

6. cgroup — Control Group Info

Terminal window
$ cat /proc/1234/cgroup
12:freezer:/
11:memory:/system.slice/myapp.service
10:cpuacct:/system.slice
...

Which cgroups the process belongs to. Useful for understanding resource limits and systemd service behavior.

7. limits — System Limits

Terminal window
$ cat /proc/1234/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 31767 31767 processes
Max open files 1024 65536 files

The resource limits for this process. This is what ulimit shows.

Real-World Debugging Scenarios

What files is my app actually opening?

Terminal window
$ ls -la /proc/$(pgrep myapp)/fd | awk 'NF>9 {print $NF}' | sort | uniq

How much memory is my process using?

Terminal window
$ grep VmRSS /proc/$(pgrep myapp)/status
VmRSS: 45678 kB

Is my app running with the right UID/GID?

Terminal window
$ grep Uid /proc/$(pgrep myapp)/status
Uid: 1000 1000 1000 1000

What command was used to start this process?

Terminal window
$ cat /proc/$(pgrep myapp)/cmdline | tr '\0' ' '; echo

See all open network sockets:

Terminal window
$ cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 0A0A0A01:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 123456

Not human-readable, but it’s there. That’s what ss parses.

The Key Insight

/proc is the single source of truth for process information. Every process monitoring tool reads from /proc. When tools fail or aren’t installed, /proc is still there.

You don’t need ps to see processes — they’re in /proc. You don’t need lsof to see file descriptors — they’re in /proc/[pid]/fd. You don’t need special tools to see environment variables — they’re in /proc/[pid]/environ.

Learning /proc gives you a foundation that works on any Linux system, with or without tools. And honestly, understanding how the kernel exposes this data makes you a better systems engineer.

Next time something goes wrong, don’t reach for another tool. Start with /proc. The answers are already there.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
Docker Network Aliases: The Feature Nobody Uses
Next Post
Docker Healthcheck Patterns That Actually Work

Related Posts