Online GDB Debugger: Breakpoints and Stepping Tutorial
To step through code line by line in OnlineGDB, pick your language (C, C++, Python 3 or Java), click the empty space left of a line number to set a breakpoint (a red dot appears), press Debug, then press start in the debug toolbar. The program stops at your breakpoint, and the step over, step into and step out buttons move it one line, into a function, or out of a function, while the Call Stack and Local Variables panels update as you go. For C and C++ the debugger is GDB; for Python it is pdb, which also works on OnlineGDB despite what many older guides (including an earlier version of this page) say.
This post is a hands-on stepping tutorial with three small buggy programs (C++, C and Python) and the real debugger output from each. For a broader overview of OnlineGDB itself (what the online GDB compiler supports, its limits, sharing, Classroom and alternatives), see our general OnlineGDB guide. The OnlineGDB official website is onlinegdb.com, whose homepage greets you with "Welcome to GDB Online"; GDB online and OnlineGDB are the same tool. By contrast, gdbonline.com is a different, parked domain that was listed for sale when we checked on November 4, 2026, so don't type your code there.
What you see in the OnlineGDB debugger
As of November 2026, the OnlineGDB editor has Run, Debug, Stop, Share, Save and Beautify in the top toolbar, and a language dropdown on the right. When you press Debug, a debugger area opens below the editor with:
| Control or panel | What it does | Plain GDB equivalent | pdb (Python) equivalent |
|---|---|---|---|
| start | Starts the program under the debugger | run |
not needed: pdb pauses on line 1 |
| pause | Interrupts a running program | Ctrl+C | n/a |
| continue | Runs until the next breakpoint or the end | continue |
c |
| step over | Runs the current line, without entering called functions | next |
n |
| step into | Runs the current line, entering a called function | step |
s |
| step out | Runs until the current function returns | finish |
r (type it; see the Python section) |
| Call Stack | The chain of function calls that led here | backtrace |
where |
| Local Variables | Values of locals in the current function | info locals |
p locals() |
| Display Expressions | Expressions re-evaluated at every stop | display |
display |
| Breakpoints and Watchpoints | Everything you have set, numbered | info breakpoints |
b with no arguments |
| Debug Console tab | Type debugger commands directly | the (gdb) prompt |
the (Pdb) prompt |
The button semantics come from OnlineGDB's own brief guide to its debugger; the commands behind them are standard GDB, documented in the GDB manual on stepping, and standard pdb, documented in the Python pdb docs.
One rule to remember: the highlighted line has not run yet. When the debugger stops on line 8, line 8 is the next line to execute, so its effect is not visible in Local Variables until you step.
Step over, step into, step out: which one when
All three run code and stop again; they differ in how far they go when the current line calls a function.
| You are on a line that... | step over (next) |
step into (step) |
step out (finish) |
|---|---|---|---|
| Calls your own function | Runs the whole call, stops on the next line | Stops on the first line inside the function | Finishes the function you are already in |
Calls a library function (printf, cout, vector::push_back) |
Runs it, stops on the next line | Enters library code if it has debug info, or behaves like step over | Same as above |
| Has no calls | Moves one line | Moves one line | Runs to the end of the current function |
| Is the last line of a function | Returns to the caller | Returns to the caller | Returns to the caller and prints the return value |
The GDB manual puts it precisely: step enters a called function only if it has line number information, otherwise it behaves like next, and next stops when control reaches a different line "at the original stack level". A good habit: step into your own functions, step over everything else, and step out as soon as you have seen what you came for.
The example program (with a bug)
Paste this into a new C++ project. The default file is main.cpp. The program should print Sum: 66 (4 + 8 + 15 + 16 + 23), but it prints something else.
#include <iostream>
#include <vector>
using namespace std;
int sumUpTo(const vector<int>& v, int n) {
int total = 0;
for (int i = 0; i <= n; i++) {
total += v[i];
}
return total;
}
int main() {
vector<int> scores = {4, 8, 15, 16, 23};
int count = scores.size();
int result = sumUpTo(scores, count);
cout << "Sum: " << result << endl;
return 0;
}
Line numbers used below: int total = 0; is line 6, the for loop is line 7, total += v[i]; is line 8, and the call to sumUpTo is line 16.
Step 1: Set a breakpoint and start debugging
- Choose C++ (or C++17/C++20) in the language dropdown.
- Click the blank gutter just left of the number 16. A red circle appears. That is your breakpoint.
- Click Debug in the top toolbar (or press F8). The debugger area opens and the Debug Console shows
Reading symbols from a.out...and a(gdb)prompt, but the program is not running yet. - Click start in the debug toolbar. Execution stops on line 16, highlighted.
Why line 16 and not line 14? If you stop on line 14 and press step into, you descend into the std::vector constructor in the standard library, which is not what you want to read. Breaking right before the call you care about saves time. (The section on skipping library code below shows how to make step into ignore the standard library entirely.)
Look at Local Variables now: scores holds five elements and count is 5. result shows a junk value because line 16 has not executed yet.
Step 2: Step into the function
- Click step into. The highlight jumps to line 6, the first line of
sumUpTo. - Call Stack now shows two rows:
sumUpToon top,mainbelow it. Clickmainto view its locals, and clicksumUpToto go back. - Local Variables shows
v,n(5) and an uninitializedtotal.
In the Debug Console, the same information comes from backtrace (short form bt):
(gdb) bt
#0 sumUpTo (v=std::vector of length 5, capacity 5 = {...}, n=5) at main.cpp:6
#1 0x... in main () at main.cpp:16
The exact formatting depends on the GDB version and pretty printers, but frame #0 is always where you are now and #1 is its caller. See Backtraces in the GDB manual.
Step 3: Step over, line by line
Click step over repeatedly and watch the highlight cycle through lines 7 and 8 while i and total change in Local Variables:
| Stop on line | i |
total (before this line runs) |
|---|---|---|
| 8 | 0 | 0 |
| 8 | 1 | 4 |
| 8 | 2 | 12 |
| 8 | 3 | 27 |
| 8 | 4 | 43 |
| 8 | 5 | 66 |
That last row is the bug. total is already 66, the correct answer, but the loop runs once more with i == 5, reading v[5], which is past the end of a five-element vector. That is undefined behavior, so the printed sum is wrong (and can differ between runs). The fix is i < n instead of i <= n.
Step over versus step into matters here: on line 8, step into would enter std::vector::operator[]. Use step over for library calls and step into only for your own functions.
Step 4: Use a conditional breakpoint instead of clicking
Stepping six times is fine for a five-element array, not for 10,000 iterations. Stop exactly where the interesting thing happens. Open the Debug Console tab and type:
(gdb) break 8 if i == 5
(gdb) continue
The program now runs until line 8 with i equal to 5, and stops there. The new breakpoint shows up in Breakpoints and Watchpoints. You can also add a condition to an existing breakpoint by number, for example condition 2 i == 5, or skip the first hits with ignore 2 4. Syntax details are in Break Conditions.
Three breakpoint variants worth knowing:
| Command | What it does |
|---|---|
tbreak sumUpTo |
A temporary breakpoint: deleted automatically the first time it stops (Setting Breakpoints) |
rbreak ^sum |
A breakpoint on every function whose name matches the regular expression |
info breakpoints |
Lists every breakpoint with its condition and how many times it was hit |
Step 5: Watch a variable change
A watchpoint stops the program whenever a value changes, without you knowing which line changes it. While stopped inside sumUpTo, type:
(gdb) watch total
(gdb) continue
Each stop prints the old and new value:
Hardware watchpoint 3: total
Old value = 27
New value = 43
A watchpoint on a local variable is deleted automatically when the function returns, because the variable no longer exists. GDB tells you so: Watchpoint 3 deleted because the program has left the block in which its expression is valid. See Setting Watchpoints, which also covers rwatch (stop on read) and awatch (read or write).
To keep a value on screen without stopping, use display:
(gdb) display i
(gdb) display total
Displayed expressions are printed at every stop (Automatic Display), and they also appear in the Display Expressions panel, where you can type an expression into Enter expression to watch instead of using the console. To check a value once, use print, e.g. print n or p total.
Step 6: Step out and finish the run
- Click step out (GDB:
finish). The function runs to its end and the debugger stops back inmainon line 16. In the console,finishprintsRun till exit from #0 ...and thenValue returned is $1 = ...with the return value. - Click step over once more so line 16 completes.
resultin Local Variables now holds the returned sum. - Click continue to run to the end. Output appears in the console, followed by
[Inferior 1 (process ...) exited normally].
Now change line 7 to for (int i = 0; i < n; i++), debug again, and the program prints Sum: 66.
Stepping through C: arrays, scope and until
The same buttons work in C mode. This second example counts scores above a limit, and shows three things the C++ walk-through didn't: printing an array through a pointer, why a loop variable "doesn't exist" yet, and how watchpoints end.
#include <stdio.h>
int count_above(const int *scores, int len, int limit) {
int count = 0;
for (int i = 0; i < len; i++) {
if (scores[i] > limit)
count++;
}
return count;
}
int main(void) {
int scores[] = {72, 95, 60, 88, 100};
int n = sizeof(scores) / sizeof(scores[0]);
int passed = count_above(scores, n, 70);
printf("%d of %d passed\n", passed, n);
return 0;
}
This is the real session from OnlineGDB's C mode on November 4, 2026 (addresses shortened):
(gdb) tbreak count_above
Temporary breakpoint 1 at 0x1147: file main.c, line 4.
(gdb) run
Temporary breakpoint 1, count_above (scores=0x7fffffffea60, len=5, limit=70)
at main.c:4
4 int count = 0;
(gdb) info args
scores = 0x7fffffffea60
len = 5
limit = 70
(gdb) print *scores@len
$1 = {72, 95, 60, 88, 100}
(gdb) display i
No symbol "i" in current context.
(gdb) display count
1: count = 0
(gdb) break 7 if scores[i] == 100
Breakpoint 2 at 0x555555555172: file main.c, line 7.
(gdb) continue
Breakpoint 2, count_above (scores=0x7fffffffea60, len=5, limit=70) at main.c:7
7 count++;
1: count = 3
(gdb) print i
$2 = 4
What it shows:
print *scores@lenprints the whole array. Insidecount_above,scoresis only a pointer, soprint scoresshows an address; the@operator tells GDB how many elements to read.No symbol "i" in current contextis not a bug:iis declared inside theforstatement, so it only exists once the loop starts. Set the display after the first step into the loop, or displaycountinstead.- The conditional breakpoint skipped the four uninteresting scores and stopped on the last one, with
countalready 3.
Then, to leave the loop and the function:
(gdb) delete 2
(gdb) next
5 for (int i = 0; i < len; i++) {
1: count = 4
(gdb) next
9 return count;
(gdb) until
10 }
(gdb) watch count
Hardware watchpoint 3: count
(gdb) continue
Watchpoint 3 deleted because the program has left the block in
which its expression is valid.
0x00005555555551cf in main () at main.c:15
15 int passed = count_above(scores, n, 70);
(gdb) continue
4 of 5 passed
[Inferior 1 (process 1850) exited normally]
If you type finish while already in main, GDB answers "finish" not meaningful in the outermost frame.: there is no caller to return to. Use continue instead.
Following recursion in the call stack
Recursive functions are where the Call Stack panel earns its place, because the same function appears several times with different arguments. Debug this in C++ mode:
#include <iostream>
#include <vector>
using namespace std;
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
int main() {
vector<int> v = {1, 2, 3};
int f = factorial(4);
cout << f << " " << v.size() << endl;
return 0;
}
The real console output, stepping in and walking the frames:
(gdb) break main
Breakpoint 1 at 0x120d: file main.cpp, line 11.
(gdb) run
Breakpoint 1, main () at main.cpp:11
11 vector<int> v = {1, 2, 3};
(gdb) next
12 int f = factorial(4);
(gdb) step
factorial (n=4) at main.cpp:6
6 if (n <= 1) return 1;
(gdb) step
7 return n * factorial(n - 1);
(gdb) step
factorial (n=3) at main.cpp:6
6 if (n <= 1) return 1;
(gdb) bt
#0 factorial (n=3) at main.cpp:6
#1 0x00005555555551fe in factorial (n=4) at main.cpp:7
#2 0x000055555555524d in main () at main.cpp:12
(gdb) up
#1 0x00005555555551fe in factorial (n=4) at main.cpp:7
(gdb) down
#0 factorial (n=3) at main.cpp:6
(gdb) finish
Run till exit from #0 factorial (n=3) at main.cpp:6
0x00005555555551fe in factorial (n=4) at main.cpp:7
Value returned is $1 = 6
(gdb) finish
Run till exit from #0 0x00005555555551fe in factorial (n=4) at main.cpp:7
0x000055555555524d in main () at main.cpp:12
Value returned is $2 = 24
Read it like this: each step on line 7 adds a frame, bt lists them newest first, and up / down (or clicking a row in Call Stack) changes which frame's variables you see without running anything. finish (the step out button) unwinds one frame at a time and prints what each call returned: factorial(3) returned 6, then factorial(4) returned 24. That is the fastest way to find which level of a recursion produces a wrong value.
Skipping library code when you step into
Step into on a line like vector<int> v = {1, 2, 3}; or cout << ... drops you into standard library headers. GDB's skip command stops that. In the Debug Console, before you press start:
(gdb) skip -gfile /usr/include/*
File(s) /usr/include/* will be skipped when stepping.
(gdb) info skip
Num Enb Glob File RE Function
1 y y /usr/include/* n <none>
From then on, step into treats functions defined in those files like step over while still entering your own code. You can also skip a single function by name (skip -function helper) or by regular expression (skip -rfunction ^std::). info skip lists the rules and skip delete removes them (GDB manual: Skipping Over Functions and Files). The glob above matched OnlineGDB's toolchain in our test; if a library still gets entered, run info source while inside it to see its real path and adjust the pattern.
Stepping through Python on OnlineGDB (pdb)
Python debugging works on OnlineGDB. On November 4, 2026 we opened the Online Python Debugger, clicked Debug, and got a live pdb session with Call Stack and Local Variables filled in. Printing sys.version inside the session showed Python 3.13.7, even though the FAQ still lists Python 3.8.10. The site's page code also still contains a "Debug mode for python program is not yet supported" message, which is the source of the old claim; it no longer reflects what happens when you press Debug. The sibling guide covers Python and Java debugging on OnlineGDB in overview; this section is about stepping.
How a Python session differs from C/C++:
- It pauses on line 1 immediately, so there is no start to press. Use continue to run to your first breakpoint.
- step over and step into work (they send pdb's
nextandstep). Step into first stops on thedefline of the called function, marked--Call--, then on its first statement. - step out sends GDB's
finish, which pdb does not know: the console prints*** NameError: name 'finish' is not definedand nothing moves. Typer(pdb'sreturn) in the Debug Console instead. - Call Stack lists frames oldest first (
<module>on row 0, the current function below it), the opposite of GDB. - pdb has no watchpoints. Use
display, which prints a value whenever it changed at a stop, or a conditional breakpoint.
Try it with a function that crashes on one input:
def average(nums):
total = 0
for n in nums:
total += n
return total / len(nums)
scores = [72, 95, 60]
print(average(scores))
print(average([]))
Click Debug, then type these commands at the (Pdb) prompt. This is the real session:
> /home/main.py(1)<module>()
-> def average(nums):
(Pdb) b 4, n == 95
Breakpoint 1 at /home/main.py:4
(Pdb) c
> /home/main.py(4)average()
-> total += n
(Pdb) p total
72
(Pdb) display total
display total: 72
(Pdb) n
> /home/main.py(3)average()
-> for n in nums:
display total: 167 [old: 72]
(Pdb) n
> /home/main.py(4)average()
-> total += n
(Pdb) r
--Return--
> /home/main.py(5)average()->75.66666666666667
-> return total / len(nums)
display total: 227 [old: 167]
(Pdb) retval
75.66666666666667
(Pdb) c
75.66666666666667
...
ZeroDivisionError: division by zero
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /home/main.py(5)average()
-> return total / len(nums)
Three things happened that matter:
b 4, n == 95is pdb's conditional breakpoint: line number, comma, condition. It stopped only whennwas 95.rran to the end ofaverageand showed the return value (->75.66666666666667);retvalprints it again.- When
average([])crashed, pdb entered post-mortem debugging at the failing line, with the crashed frame's variables still available. Typep nums, totalthere and you see([], 0): the empty list is the case you forgot.crestarts the program from line 1.
pdb commands for stepping, from the pdb documentation:
| Task | pdb command |
|---|---|
| Step over / step into / step out | n / s / r |
| Leave a loop | until (unt) |
| Breakpoint at line 4, or with a condition | b 4, b 4, n == 95 |
| One-time breakpoint | tbreak 4 |
| Show the call stack, move between frames | w, u, d |
| Arguments of the current function | a |
| Print, pretty-print, type of a value | p x, pp data, whatis x |
| Show a value at every stop when it changes | display x |
| Show the whole current function | ll |
| Jump to another line (bottom frame only) | j 7 |
Debugging Java: the closest thing to an online jdb
Java works the same way as C++ from the Online Java Debugger page: click Debug, then start, set breakpoints in Main.java with the gutter, and use the same step buttons while Call Stack shows JVM frames. If you are used to the JDK's command-line jdb, this is the closest thing to an online jdb with no install. Our OnlineGDB guide has the details of the Java setup.
GDB command cheat sheet for the Debug Console
| Task | Command | Short form |
|---|---|---|
| Break at a line | break 16 |
b 16 |
| Break at a function | break sumUpTo |
b sumUpTo |
| Conditional break | break 8 if i == 5 |
|
| One-time break | tbreak sumUpTo |
|
| Ignore the next 4 hits of breakpoint 2 | ignore 2 4 |
|
| List breakpoints | info breakpoints |
i b |
| Delete breakpoint 2 | delete 2 |
d 2 |
| Start the program | run |
r |
| Next line, skip calls | next |
n |
| Next line, enter calls | step |
s |
| Repeat a step 5 times | next 5 |
n 5 |
| Run to function return | finish |
fin |
| Run past the end of a loop | until |
u |
| Run to line 20 | advance 20 |
|
| Never step into these files | skip -gfile /usr/include/* |
|
| Show call stack | backtrace |
bt |
| Switch to frame 1 / move up or down | frame 1 / up / down |
f 1 |
| Print a value | print total |
p total |
| Print an array through a pointer | print *arr@len |
|
| Show all locals / arguments | info locals / info args |
|
| Show a value at every stop | display total |
|
| Stop when a value changes / is read | watch total / rwatch total |
Press Enter on an empty prompt to repeat the last command, which makes repeated next or step one key each.
Troubleshooting stepping problems
| What you see | Why | Fix |
|---|---|---|
| Breakpoint is never hit | You pressed Run, not Debug, or the line never executes | Use Debug, then start; put the breakpoint on a line that runs |
No symbol "i" in current context. |
The variable's scope hasn't started (or has ended) | Step into the block first, or print a variable that is in scope |
| Variable shows a strange value | The highlighted line hasn't run yet | Step over once |
Variables show <optimized out>, lines jump around |
Optimization flags such as -O2 in Extra Compiler Flags |
Remove optimization flags while debugging |
Step into lands in /usr/include/... |
The line calls standard library code | Step out, then add skip -gfile /usr/include/* |
"finish" not meaningful in the outermost frame. |
You are in main, which has no caller |
Use continue or step over |
Watchpoint ... deleted because the program has left the block |
The watched local went out of scope | Normal; set it again next time the function runs |
Python: NameError: name 'finish' is not defined |
The step out button sent a GDB command to pdb | Type r in the Debug Console |
| Program seems frozen while debugging | It is waiting for cin / scanf / input() |
Type the input in the console and press Enter |
| "Debug session is being stopped due to inactivity" | The session sat idle too long | Click Debug again; your breakpoints stay in the editor |
Limitations to know
- The debugger covers C, C++, Python 3 and Java. C and C++ use GDB, Python uses pdb, and Java uses a JVM debugger. Other languages on the menu run but can't be stepped through. Python's step out button doesn't work (type
r), and the site still shows an outdated "not yet supported" notice in its page code. - Published versions lag what actually runs. The FAQ lists gcc 11.4.0, gdb 12.1 and Python 3.8.10, but our November 4, 2026 test reported gcc 14.3.0 (C mode), GDB 16.3 (C and C++ mode) and Python 3.13.7. Check
show versionin the Debug Console, or printsys.version, if a feature depends on it. - Sessions time out. OnlineGDB stops a debug session after a period of inactivity, so don't leave it paused for long. In interactive console mode, a program runs until it finishes or 15 minutes of inactivity; in text mode, the run time limit is 10 seconds (FAQ).
- Input must be typed when the program asks. If your code reads from
cin, the debugger waits for you to type in the console at that point. - Optimization hurts stepping. If you add flags such as
-O2under Extra Compiler Flags, lines may be reordered and variables shown as<optimized out>. Keep optimization off while debugging. - It is a learning tool, not a full IDE. For multi-file projects, core dumps, or attaching to a running process, use GDB or LLDB locally, or a desktop IDE. You can compare options on our IDE and editor tools page and in DevHunt's comparison of popular IDEs. For JavaScript, the equivalent stepping tools are covered in Chrome DevTools debugging and Node.js debuggers.
FAQ
How do I set a breakpoint in OnlineGDB?
Click the blank area just left of the line number in the editor; a red circle marks the breakpoint. You can also type break <line> (C/C++) or b <line> (Python) in the Debug Console after pressing Debug. Breakpoints only take effect when you run with Debug, not Run.
What is the difference between step over and step into?
Both run the current line. Step over (GDB next) treats function calls on that line as one step. Step into (GDB step) stops at the first line inside the called function. Use step into for your own functions and step over for library calls like cout or vector methods.
What does step out do in the online GDB debugger?
It runs until the current function returns and stops in the caller, the same as GDB's finish. In C and C++ the console also prints Value returned is ..., which is handy for checking each level of a recursive function. In Python sessions, type r instead, because the button's finish command isn't a pdb command.
Why does my variable show a strange value at the breakpoint?
The highlighted line has not executed yet, so a variable it assigns still holds whatever was in memory. Click step over once and the value updates.
Can I use the OnlineGDB debugger for Python or Java?
Yes, both. Python sessions run on pdb (Python 3.13.7 in our November 2026 test): step over and step into work from the toolbar, and you type r to step out. Java runs under a JVM debugger with the same buttons. Only C and C++ use GDB itself.
Is there an online GDB for Python?
Not GDB itself, but OnlineGDB's Python mode gives you the Python equivalent, pdb, in the same interface: breakpoints in the gutter, the step buttons, Call Stack, Local Variables and a (Pdb) console. It also drops into post-mortem debugging when your program raises an uncaught exception.
How do I make GDB stop when a variable changes?
Type watch total in the Debug Console while the variable is in scope, then continue. GDB stops on every change and prints the old and new value. For locals, the watchpoint is deleted when the function returns.
What is the difference between until and next in GDB?
Inside a loop they behave the same, except at the loop's end: next jumps back to the top for another iteration, while until keeps going until it reaches a line after the loop. Use until to get out of a loop you've already understood.
What is an online GDB account?
It's a free OnlineGDB sign-in that lets you save projects and come back to them. You don't need one to run code, set breakpoints or step through a program.
Is OnlineGDB free?
Yes, the compiler and debugger are free to use in the browser without installing anything. Signing in lets you save projects to your account. If you are looking for other free developer tools, see our free alternatives directory.