Debugging a memory leak with nothing but /proc

#linux#debugging

A weekend incident, a growing RSS number, and no profiler available on the box.

The numbers below are from my own machine and my own workload. Treat them as a method, not a benchmark: the point is how the measurement was taken, not the exact figures it produced.

Where I started

By the end of this post you should be able to reproduce the whole thing yourself in under an hour, including the parts I got wrong the first time — those are marked so you can skip them if you're in a hurry.

I kept a log of every decision during this project, and reading it back a month later was humbling: the choices that aged well were the boring ones, and the clever ones all came with a footnote I hadn't written yet.

def load_config(path: str) -> dict:
    with open(path, encoding="utf-8") as f:
        return {k.strip(): v.strip() for k, v in
                (line.split("=", 1) for line in f if "=" in line)}

What went wrong

What surprised me most was how small the final change was. Most of the work was understanding the problem precisely enough that the fix became obvious, and that understanding took far longer than the fix itself.

By the end of this post you should be able to reproduce the whole thing yourself in under an hour, including the parts I got wrong the first time — those are marked so you can skip them if you're in a hurry.

A destructive or irreversible operation.

The fix

Tools matter less than the feedback loop they enable. A fast, boring loop beat every sophisticated setup I tried, and I've kept it ever since, even as the rest of the stack changed twice over.

There is a version of this article that opens with theory. I decided against it, because the theory is easy to find and the motivation is not — so let's start with the thing that actually broke.

  • Read the error message twice before changing anything
  • Reproduce it with the smallest input you can
  • Write the failing test first, even if it feels ceremonial
  • Only then reach for the fix

What I'd do differently

I kept a log of every decision during this project, and reading it back a month later was humbling: the choices that aged well were the boring ones, and the clever ones all came with a footnote I hadn't written yet.

Tools matter less than the feedback loop they enable. A fast, boring loop beat every sophisticated setup I tried, and I've kept it ever since, even as the rest of the stack changed twice over.

I want to be honest about the trade-offs here. The approach I settled on solves the problem I had, not every problem of this shape, and there is a clear point where you should reach for something more heavy-duty.