I’ll show you the terminal version first, then how I think you can do the same in CLion.
Not sure specifically how you’re getting the print to happen, but I added -Wall
, shouldn’t matter as long as we both reach that function.
$ cat /tmp/test.c
int main() { return 0; }
$ ./bin/clang /tmp/test.c -o /tmp/test.o -g -Wall
CFG::buildCFG
Note that this build is without debug symbols but generally you can at least break on most functions despite that. For anything more than “did the code run”, I do recommenced you use the Debug
build type.
Here I’m loading it into lldb but the same principle applies to other tools. In fact CLion might be using lldb anyway, gdb will work the same way too.
(the --
separates the program file I’m telling lldb to debug, from the options I want to pass to that program file)
$ ./bin/lldb ./bin/clang -- /tmp/test.c -o /tmp/test.o -g -Wall
(lldb) target create "./bin/clang"
Current executable set to '/home/david.spickett/build-llvm-aarch64/bin/clang' (aarch64).
(lldb) settings set -- target.run-args "/tmp/test.c" "-o" "/tmp/test.o" "-g" "-Wall"
(lldb) b CFG::buildCFG
Breakpoint 1: where = clang`clang::CFG::buildCFG(clang::Decl const*, clang::Stmt*, clang::ASTContext*, clang::CFG::BuildOptions const&), address = 0x000000000676dec0
(lldb) run
Process 1334109 launched: '/home/david.spickett/build-llvm-aarch64/bin/clang' (aarch64)
CFG::buildCFG
Process 1334109 stopped and restarted: thread 1 received signal: SIGCHLD
Process 1334109 stopped and restarted: thread 1 received signal: SIGCHLD
Process 1334109 exited with status = 0 (0x00000000)
(lldb) q
Note that the print did happen, but apparently not inside the process we were debugging, so we didn’t stop. It did let us place the breakpoint, because all this code is in the same binary.
Now I’ll add -###
to the original command line to get the cc1 command. Here is where I should have given you an example, because it prints a wall of text that’s not easy to parse.
$ ./bin/clang /tmp/test.c -o /tmp/test.o -g -Wall -###
clang version 20.0.0git (https://github.com/llvm/llvm-project.git ea9204505cf1099b98b1fdcb898f0bd35e463984)
Target: aarch64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/david.spickett/build-llvm-aarch64/bin
Build config: +assertions
"/home/david.spickett/build-llvm-aarch64/bin/clang-20" "-cc1" "-triple" "aarch64-unknown-linux-gnu" <...>
"/usr/bin/ld" "-EL" "-z" "relro" "--hash-style=gnu" <....>
The line you want is the one that begins "/home/.../clang-20" "-cc1"
. Debug that command instead:
$ ./bin/lldb "/home/david.spickett/build-llvm-aarch64/bin/clang-20" -- "-cc1" "-triple" <...>
(lldb) target create "/home/david.spickett/build-llvm-aarch64/bin/clang-20"
Current executable set to '/home/david.spickett/build-llvm-aarch64/bin/clang-20' (aarch64).
(lldb) settings set -- target.run-args "-cc1" "-triple" <...>
(lldb) b CFG::buildCFG
Breakpoint 1: where = clang-20`clang::CFG::buildCFG(clang::Decl const*, clang::Stmt*, clang::ASTContext*, clang::CFG::BuildOptions const&), address = 0x000000000676dec0
(lldb) run
Process 1334225 launched: '/home/david.spickett/build-llvm-aarch64/bin/clang-20' (aarch64)
Process 1334225 stopped
* thread #1, name = 'clang-20', stop reason = breakpoint 1.1
frame #0: 0x0000aaaab1217ec0 clang-20`clang::CFG::buildCFG(clang::Decl const*, clang::Stmt*, clang::ASTContext*, clang::CFG::BuildOptions const&)
clang-20`clang::CFG::buildCFG:
-> 0xaaaab1217ec0 <+0>: str d8, [sp, #-0x70]!
0xaaaab1217ec4 <+4>: stp x29, x30, [sp, #0x10]
0xaaaab1217ec8 <+8>: stp x28, x27, [sp, #0x20]
0xaaaab1217ecc <+12>: stp x26, x25, [sp, #0x30]
Now we’re debugging the process where the print happens, which is cc1 which is the “compiler” vs. the “driver” that first runs.
Note that I’ve got no source display here because this is a release build I happened to have already. Again I reccomend using the cmake option -DCMAKE_BUILD_TYPE=Debug
if you’re doing any serious debugging.
I tried the -fintegrated-cc1
options but it made no difference here. I suspect because this just controls whether a new process is used, and even with integrated cc1 it’s replacing itself with the cc1 process? I’m not an expert here though.
What does work is lldb’s follow fork child mode using the original clang command:
$ ./bin/lldb ./bin/clang -- /tmp/test.c -o /tmp/test.o -g -Wall
(lldb) target create "./bin/clang"
s Current executable set to '/home/david.spickett/build-llvm-aarch64/bin/clang' (aarch64).
(lldb) settings set -- target.run-args "/tmp/test.c" "-o" "/tmp/test.o" "-g" "-Wall"
(lldb) settings set target.process.follow-fork-mode child
(lldb) b CFG::buildCFG
Breakpoint 1: where = clang`clang::CFG::buildCFG(clang::Decl const*, clang::Stmt*, clang::ASTContext*, clang::CFG::BuildOptions const&), address = 0x000000000676dec0
(lldb) run
Process 1334571 launched: '/home/david.spickett/build-llvm-aarch64/bin/clang' (aarch64)
Process 1334666 stopped
* thread #2, name = 'clang-20', stop reason = exec
frame #0: 0x0000fffff7fd9c40 ld-linux-aarch64.so.1`_start
ld-linux-aarch64.so.1`_start:
-> 0xfffff7fd9c40 <+0>: bti c
0xfffff7fd9c44 <+4>: mov x0, sp
0xfffff7fd9c48 <+8>: bl 0xfffff7fda610 ; _dl_start at rtld.c:527:1
0xfffff7fd9c4c <+12>: mov x21, x0
1 location added to breakpoint 1
lldb will stop on exec which is clang starting the cc1 process. There’s probably a way to continue automatically here but I couldn’t find it. Just continue to then hit the breakpoint as expected:
(lldb) c
Process 1334666 resuming
Process 1334666 stopped
* thread #2, name = 'clang-20', stop reason = breakpoint 1.2
frame #0: 0x0000aaaab1217ec0 clang-20`clang::CFG::buildCFG(clang::Decl const*, clang::Stmt*, clang::ASTContext*, clang::CFG::BuildOptions const&)
clang-20`clang::CFG::buildCFG:
-> 0xaaaab1217ec0 <+0>: str d8, [sp, #-0x70]!
0xaaaab1217ec4 <+4>: stp x29, x30, [sp, #0x10]
0xaaaab1217ec8 <+8>: stp x28, x27, [sp, #0x20]
0xaaaab1217ecc <+12>: stp x26, x25, [sp, #0x30]
If you are using GDB it’s follow fork settings are documented here: Forks (Debugging with GDB)
I am not a CLion user but I found Debug forked processes | CLion Documentation. I think you will want the “Follow Child on Fork” option.
Otherwise you may be able to make CLion run settings set ...
or set follow-fork-mode...
for you, depending on whether it’s launching LLDB or GDB.
Using this follow fork setting will mean you don’t have to go get the cc1 command every time.