Learning Potent C-Debugging Over ssh With cgdb and vim.

We look over step debugging using vim, ssh, step-debugging, code-folding, multiple-field code folding!

Learning Potent  C-Debugging Over ssh With cgdb and vim.
Linux / Raspberry Pi / C / gdb - What's not to Love!

Please note: do not! expect to learn every command to vim, cgdb and C step-debugging in a day! They are not intuitive, they have literally hundreds of options, and that means the following:

  • Repetition, repetition, repetition, or use it, use it, and use it some more..  Something that is NOT friendly will take rout memory to learn. Get up the next day and use it some more. Expect to become maybe somewhat comfortably competent with this process after maybe a week or two!
  • That requires patience, and that is very difficult in a world that is used to limited-attention spaces.

Why learn this? Well, it's powerful, let me explain why.

  • Raspberry Pi Zero's / Pi 3 / Pi 4's are now too slow for larger IDE X-11 gui-over-ssh step-debugging.  AI-based coding is causing an explosion of bloat-ware, so software that used to run on 4-cores will probably need a Ryzen Threadripper soon the way that it all is expanding.   Just trying loading pycharm over ssh - it breaks, how about vscodium - it breaks too.  So you simply cannot step-debug a raspberry pi easily.
  • GDB split-view is glitchy.
  • DDD is slow (it is a X-11 based application..)
  • If you can master this you can step-debug just about anywhere, at any time, imagine a minimal 1-core VPS that doesn't have the ponies to even run vscode, or pycharm? This lets you do powerful step-debugging on something that is very limited now-days.
  • So enter cgdb! However it does not support code folding so we will show at the end how to utilize multiple different ssh windows - one with cgdb, one with vim and code-folding, and a third with a compilation window.

There is  currently literally 1 guide for your learning, we want to touch on it then go far farther, to become functionally proficient and efficient with the gdb / cgdb setup.  Go watch the one basic video on cgdb that is found:

Ready? Let's install cgdb, with it, we will install  a compiler and your typical build-essential (this guide was  all developed and tested on a Raspberry Pi / Linux over ssh setup - so it will work on your minimal system!).

sudo apt install cgdb gcc build-essential

Now let's start with a basic hello world program, again we want to master the basics and build a strong foundation.

  • Repeat, repeat, repeat, repeat.
#include <stdio.h>
int main()
{
  printf("Hello World!");
  return 0;
}

We want to compile this so that it has the following options:

gcc hello.c -g -O0 -o hello

-g: Enable debugging symbols

-O0: Disable optimizations. This is important so that lines of code are not eliminated away, this can cause step-debugging line-mismatches.

-o: Output hello

hello.c is the program that we are compiling.

We can now activate the stepping debugger:

cgdb hello

You should see a split screen like:

~│
1│ #include <stdio.h>
2│ int main()
3│ {
4│     printf("Hello World!");
5│     return 0;
6│ }
~││
~│
~│
~│
/home/c/csoftware/01_test/hello.c
GNU gdb (Debian 13.1-3) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "aarch64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello...
New UI allocated
(gdb)

<esc> - will jump up to move the cursor around the code,

<i> - will jump back to the (gdb) side.

Basic Command Examples:

In CGDB (a curses-based interface to GDB), the lower window is the GDB window (often referred to as the "GDB side" or "code side" for entering commands). This is where you interact directly with GDB by typing standard GDB commands.

CGDB does not support code folding.

When the focus is in the GDB window (entered via i or Ctrl+X from the source window, depending on configuration), you can execute any valid GDB command. CGDB passes these directly to the underlying GDB process.

The full set of available commands in the GDB window is therefore the complete set of GDB commands (several hundred in total), including all standard categories such as:

  • Program execution: run, start, continue (c), kill, quit
  • Stepping: step (s), next (n), finish, until, stepi, nexti
  • Breakpoints: break (b), tbreak, delete, clear, enable, disable, condition, ignore
  • Watchpoints: watch, rwatch, awatch
  • Stack & frames: backtrace (bt, where), frame, up, down, select-frame
  • Variables & expressions: print (p), display, undisplay, whatis, ptype, call
  • Memory: x (examine), set {type}address = value
  • Registers: info registers, print $rax (or other registers)
  • Threads: info threads, thread, thread apply all
  • Info commands: info breakpoints, info locals, info args, info functions, info variables
  • Symbols & source: list, directory, file, symbol-file
  • Convenience: set, show, help, apropos
  • And many others (e.g., attach, detach, signal, handle, maintenance, etc.)

To see the complete list or get details on any command while inside CGDB's GDB window, use:

  • (gdb) help
  • (gdb) help [class] (e.g., help breakpoints, help running, help data)
  • (gdb) help [command] (e.g., help break, help print)

CGDB itself adds only a few special conveniences in or related to the GDB window:

  • Ctrl+L — Redraw the screen
  • Page Up / Page Down — Scroll output history (or enter scroll mode in some configurations)
  • q or i or Enter — Often used to exit scroll mode and return to command input

If your question instead refers to CGDB-specific key bindings that send predefined GDB commands from the source window (e.g., F5 for run, F10 for next), please clarify, as those belong to CGDB mode rather than the GDB window itself.

For the most authoritative reference, consult the official CGDB manual (section on GDB mode) or type help directly in the GDB window during a session.

Learning VIM.

  • Code folding is essential if you build larger single-file applications and do not want to traverse dozens of library files or have complex structures of files open, vim is the one application that supports this.
  • Installing vim:
sudo apt install vim -y

vim has four modes, namely (a cheat-sheet is here)

  • Command Mode
  • Command-Line Mode (press : from command mode)
  • Insert Mode (press i from command mode, then <esc> to get back
  • Visual Mode (press v from command mode, then <esc> to get back)

Command Mode

  • vim starts in command mode.
  • pressing (i) will go to insert mode, then (esc) will bring you back. You can see if you are in – INSERT – mode or – VISUAL – mode by the bar at the bottom.  Remember <esc> will return you to – COMMAND – mode, which is BLANK at the bottom

We are able to edit our file, note - vim is intelligent if you end a line with ; it knows you are starting a new line fresh and indents to it.

We can edit our file as in:

#include <stdio.h>
int main()
{
printf("Hello World!");
int i = 0;
int x = 5;
int total = i + x;
printf("The value of i + x: %d\n", total);

return 0;
}

Keeping a second  terminal compile window open with the compile command recycling:

gcc -g hello.c -o hello

We can just hit <up> to recompile it,

In our third window we can get cgdb to reload the file by simply going down to the gdb and typing:

(gdb) file hello

We can see all three working together:

  • Searching inside vim (from command mode):
/string
  • Changing vim tab indents can be done by editing ~/.vimrc and adding:
set ts=2 sw=2

Standard Code Folding (From Command Mode)

  • za (toggle)
  • zo (open)
  • zc (close)
  • zR (Open all)

Adding Pycharm like //region / //endreigon code folding.

  • If you have ever used pycharm code folding around //region / //endregion - it is is a game changer, you can have giant programs that span 1000's of lines of code, instead of scrolling for 20 minutes you can simply fold them into small concentric grouping blocks.

To enable code folding delimited by //region and //endregion markers in Vim, configure the editor to use marker-based folding. This method treats any line containing the exact start marker as the beginning of a fold and the corresponding end marker as its termination, supporting nesting where applicable.

Add the following configuration to your ~/.vimrc (or the equivalent per-user configuration file) for global application:

" Enable marker-based folding
set foldmethod=marker

" Define custom fold markers
set foldmarker=//region,//endregion

For file-type-specific application (recommended to avoid unintended folding in other languages), use an autocommand group instead:

augroup region_folding
    autocmd!
    autocmd FileType c,cpp,cs,js,ts,py setlocal foldmethod=marker
    autocmd FileType c,cpp,cs,js,ts,py setlocal foldmarker=#region,#endregion
augroup END

After saving the file, reload your configuration with :source $MYVIMRC (or restart Vim). Ensure folding is active by setting set foldenable (it is enabled by default).

In practice, insert the markers in your source file as follows:

#region Example Section
int main(void) {
    /* code to be folded */
    return 0;
}
#endregion

The region will fold automatically when foldlevel permits. Standard fold commands apply:

  • za toggles the fold under the cursor.
  • zo opens the fold.
  • zc closes the fold.
  • zR opens all folds.
  • zM closes all folds.

Adjust foldlevelstart or foldnestmax in your configuration if deeper nesting or initial expansion is required. This setup is lightweight, requires no plugins, and functions reliably with Vim 7.0 and later. Test in a buffer containing the markers to verify behavior.

Once we have set this up we can modify our C program as:

#include <stdio.h>
//region Test Region
int test(int a, int b)
{
int c = a + b;
return c;


}
//endregion
//region main
int main()
{
printf("Hello World!");
int i = 0;
int x = 5;
int b = 14;

int total = i + x;
int zebra = test(x, b);
printf("this is a test of the broadcast system");
printf("The value of i + x: %d\n", total);
return 0;

}
//endregion

We can then use za to fold the regions and they will look like:

#include <stdio.h>
+--  9 lines: Test Region----------------------------------------------------------------------------------------
+-- 16 lines: main-----------------------------------------------------------------------------------------------

Setting Compound and Multi-Variant Folding in VIM

To enable automatic code folding on both curly braces {} (syntactic blocks) and custom #region / #endregion markers simultaneously, note that Vim supports only a single foldmethod at any given time. Combining marker and syntax (or any other pair) directly is not possible without custom scripting.

The recommended, plugin-free solution is to switch to foldmethod=syntax. This method already provides robust support for language-specific constructs and can be extended via syntax definitions to include folding for #region / #endregion while preserving (or explicitly enabling) folding on {} blocks. The extension is performed in an after/syntax file, which Vim loads automatically after the base syntax for the file type.

Step 1: Update your ~/.vimrc (or equivalent configuration)

Replace any prior marker-based settings with the following:

augroup syntax_folding
    autocmd!
    autocmd FileType c,cpp,cs,js,ts setlocal foldmethod=syntax
    autocmd FileType c,cpp,cs,js,ts setlocal foldlevelstart=0  " Optional: start with top-level folds closed
augroup END

Reload the configuration with :source $MYVIMRC (or restart Vim).

Step 2: Create syntax extension files

Create the directory if it does not exist:

mkdir -p ~/.vim/after/syntax

For C, C++, and C# files (~/.vim/after/syntax/c.vim)

" Custom folding for #region / #endregion
syntax region cRegionFold
    \ start="#region"
    \ end="#endregion"
    \ fold
    \ transparent
    \ keepend
    \ contains=TOP

" Enable folding on every { } block (brace folding)
syntax region cFoldBraces
    \ start="{"
    \ end="}"
    \ transparent
    \ fold
    \ keepend
    \ extend
    \ contains=TOP

For JavaScript / TypeScript files (~/.vim/after/syntax/javascript.vim and ~/.vim/after/syntax/typescript.vim)

Use identical content to the c.vim example above, replacing the group name if desired (e.g., javascriptRegionFold).

After creating these files, open a source file of the relevant type and ensure syntax highlighting is active (:syntax on). The folds will be computed automatically.

Usage notes

  • Standard fold commands continue to apply: za (toggle), zo (open), zc (close), zR (open all), zM (close all).
  • Regions support nesting: a #region inside a {} block (or vice versa) will fold correctly.
  • If you need to adjust fold depth or initial state, add setlocal foldnestmax=20 or setlocal foldlevel=1 as needed in the autocommand group.
  • To verify or debug, use :set foldmethod? and :syntax in the buffer.

This configuration is lightweight, respects existing syntax highlighting, and requires no external plugins. It works reliably in Vim 7.0 and later. If your workflow involves additional file types or more complex nesting requirements, the same pattern can be extended by adding further syntax region definitions in the appropriate after/syntax/*.vim files.

Linux Rocks Every Day