What is GDB Poor Man’s Profiler-Embedded Linux Training in Hyderabad

GDB Poor Man’s Profiler

A free linux kernel development course lecture: profile any running Linux application using nothing but GDB — no special tools, no kernel config changes.

Before you reach for a dedicated profiler, it helps to understand the simplest form of statistical profiling that exists on Linux: repeatedly interrupting a running program with a debugger and recording where it stopped. This lecture is part of our free linux kernel development course and our broader free embedded systems course track, and it lays the conceptual groundwork you will need before the next lecture, where we move on to the kernel’s own perf_events subsystem. Everyone who takes a free linux device drivers course eventually needs to answer “why is my driver’s user-space test app slow?” — this technique is often the fastest way to get a first answer, and it works on any target that has GDB, embedded or not.

Key Concepts in This Lecture

statistical profiling gdbserver backtrace SIGINT sampling remote debugging free linux kernel development course

Prerequisites

You should be comfortable compiling C on Linux and know basic GDB commands (break, run, continue). Familiarity with a target/host cross-debug setup helps but is not required — this lecture works the same on a native desktop Linux install as it does on an embedded board.

Why Sampling Works

A profiler’s job is to tell you where a program spends its time. There are two broad families of technique. Instrumenting profilers insert extra code at every function entry and exit and measure exact elapsed time — accurate, but they change the program’s timing and can be heavy to set up. Statistical profilers take the opposite approach: they interrupt the running program at intervals and simply record which function it happened to be in at that instant. A single sample tells you almost nothing. A few hundred samples tell you a great deal, because functions where the program spends more time are proportionally more likely to be caught by a random interrupt. This is the same statistical principle behind opinion polling — you don’t need to ask every voter to get a good estimate of the result.

The “poor man’s profiler” is simply this idea applied using a tool every embedded developer already has installed: GDB. You attach, note the stack, resume, and repeat. No extra packages, no kernel reconfiguration, no cross-compiling a profiling agent for your target — which is exactly why it is worth knowing even in 2026, when far more capable tools exist.

The Manual Sampling Loop

The procedure has four steps, repeated as many times as you have patience for:

Manual Statistical Sampling Loop

1. Attach GDB to the running process (native) or connect to gdbserver (remote). The process halts immediately. 2. Run “bt” (backtrace) to record the current call stack. 3. Run “continue” to let the process resume executing. 4. Wait a moment, send Ctrl+C to halt it again, go to step 2. Repeat 20-50+ times. Functions that appear again and again across your recorded backtraces are your hotspots.

Attaching Locally vs. Remotely

On a desktop or a target where GDB itself runs natively, you attach directly to a process ID:

$ gdb -p $(pidof ep_hotspot_demo)
(gdb) bt
(gdb) continue

On a resource-constrained embedded target, running full GDB on the board is often impractical. Instead you run the small gdbserver stub on the target and drive it from a cross-GDB on your build host:

# On the target board:
target$ gdbserver --attach :2345 $(pidof ep_hotspot_demo)

# On the development host:
host$ arm-linux-gnueabihf-gdb ep_hotspot_demo
(gdb) target remote target-ip:2345
(gdb) bt
(gdb) continue
Note: Each Ctrl+C freezes the process for the time it takes you to read the backtrace and type continue — typically a second or more. For a program with a tight real-time deadline, this pause itself can distort behavior. Keep that intrusiveness in mind before you conclude a function is “slow”: you may just be looking at it because it happens to be running whenever you glance at the clock.

Hands-On Demo: Catching a Hidden Hotspot

Here is a small original demo program with one obviously slow function buried among several fast ones, so you can watch the sampling loop find it.

/* ep_hotspot_demo.c */
#include <stdio.h>
#include <unistd.h>

static void ep_fast_task(void)
{
    volatile int i;
    for (i = 0; i < 1000; i++)
        ;
}

static void ep_slow_task(void)
{
    volatile long i;
    for (i = 0; i < 200000000L; i++)
        ;
}

static void ep_work_cycle(void)
{
    ep_fast_task();
    ep_slow_task();   /* the hidden hotspot */
    ep_fast_task();
}

int main(void)
{
    while (1)
        ep_work_cycle();
    return 0;
}

Build it with debug symbols so backtraces show real function names and line numbers:

$ gcc -g -O0 -o ep_hotspot_demo ep_hotspot_demo.c
$ ./ep_hotspot_demo &

Now attach and sample it a few times:

$ gdb -p $(pidof ep_hotspot_demo)
(gdb) bt
#0  ep_slow_task () at ep_hotspot_demo.c:15
#1  ep_work_cycle () at ep_hotspot_demo.c:21
#2  main () at ep_hotspot_demo.c:27
(gdb) continue
Continuing.
^C
Program received signal SIGINT, Interrupt.
ep_slow_task () at ep_hotspot_demo.c:15
15          for (i = 0; i < 200000000L; i++)
(gdb) bt
#0  ep_slow_task () at ep_hotspot_demo.c:15
#1  ep_work_cycle () at ep_hotspot_demo.c:21
#2  main () at ep_hotspot_demo.c:27
(gdb) continue

Repeat the Ctrl+C / bt / continue cycle ten or fifteen times and you will find that the overwhelming majority of your backtraces land inside ep_slow_task, even though it is a single call among three in ep_work_cycle. That is statistical profiling working exactly as designed — the function consuming the most CPU time gets caught the most often.

AspectPoor Man’s Profiler (GDB)perf (next lecture)
Setup costNone — GDB is already thereNeeds CONFIG_PERF_EVENTS + perf binary
Overhead per sampleHigh (full stop-the-world)Very low (PMU-driven)
Sample count achievableTens, by handThousands per second
Whole-system viewNo — one process at a timeYes — system, kernel, or process
Best forQuick sanity check, no tooling availableSerious performance investigation

Common Mistakes

  • Too few samples. Five backtraces prove nothing; aim for at least 20-30 before drawing conclusions.
  • Sampling only during idle periods. If you attach right as the program starts, you may only be watching one-time setup code, not steady-state behavior.
  • Building without debug symbols. Without -g, backtraces show raw addresses instead of function names, making the samples nearly useless.
  • Forgetting the intrusiveness. On latency-sensitive code, this technique can itself trigger the very timing problems you are trying to diagnose.

Best Practices

  • Always compile the target binary with -g (and avoid full -O2/-O3 if you want backtraces to map cleanly to source lines).
  • Sample across the full lifetime of a workload, not just its startup burst.
  • Use this technique as a five-minute first check, then move to perf when you need statistical confidence or system-wide visibility.
  • On embedded targets, prefer gdbserver over installing a full GDB on the board — it keeps the target’s storage and memory footprint small.

Summary and Key Takeaways

The poor man’s profiler turns an ordinary debugger into a rough-and-ready statistical profiler: attach, record the stack, resume, repeat. It costs nothing to set up, works on any target with GDB, and is intrusive enough that you should treat its results as a starting hint rather than a final answer. In the next lecture of this free linux kernel development course, we build on the same statistical idea using the kernel’s own perf_events subsystem, which samples with far lower overhead and far higher sample rates.

Frequently Asked Questions

Is the poor man’s profiler accurate?

It is a rough estimate, not a precise measurement. With enough samples it reliably points at the right hotspot, but the exact percentages it implies are not trustworthy — for that, use a dedicated sampling profiler like perf.

Does this technique work on a live production system?

It is not recommended. Each attach-and-stop cycle pauses the process, which is disruptive on production or real-time systems. Use it on a test or development instance instead.

Can I automate the Ctrl+C / backtrace / continue loop?

Yes — GDB Python scripting or an external wrapper script can drive the loop automatically and log each backtrace, removing the need to type commands by hand for every sample.

What if my program isn’t multi-threaded — does profiling still work?

Yes. The technique works identically on single-threaded and multi-threaded programs; for multi-threaded programs, use GDB’s thread apply all bt to capture every thread’s stack in one sample.

Why not just use perf from the start?

perf needs a kernel built with CONFIG_PERF_EVENTS and a matching perf binary on the target. On a board where that isn’t available yet, GDB sampling gives you an immediate first answer with zero extra setup.

Does this technique require root access?

You need permission to attach a debugger to the target process — root if it belongs to another user, but no special privileges beyond normal ptrace permissions for your own processes.

Keep Learning Linux Kernel Development — Free

This lecture is part of EmbeddedPathashala’s free linux kernel development course and free embedded systems course. Continue to the next lecture to learn statistical profiling the kernel’s own way, with perf.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *