🐌
Yours truly is not an expert in compilers, but does enjoy diving into this topic and learning something new from time to time.

This post is split up into 3 pages.

Working with the codebase

Clang/LLVM’s codebase includes a lot of .inc and .def files. These are just normal C++ code. But by default VS doesn’t recognize them as such. Addressing this is really easy. Navigate to Tools > Options > Text Editor > File Extensions. Type in .inc into the Extension: filed, pick Microsoft Visual C++ from the Editor dropdown, click the Add button. Repeat for .def. To complete the configuration click OK. Done.

VS > Text Editor > File Extensions

Debugging

Inspecting data structures

Make sure you didn’t miss these portions of Hacking on Clang

Dump

  • Many LLVM and Clang data structures provide a dump() method which will print a description of the data structure to stderr.
  • The QualType structure is used pervasively. This is a simple value class for wrapping types with qualifiers; you can use the isConstQualified(), for example, to get one of the qualifiers, and the getTypePtr() method to get the wrapped Type* which you can then dump.

— Debugging

Let’s see an example of using dump().

A breakpoint

Hitting a breakpoint and invoking dump() from Immediate Window looks similar to this.

Invoking dump in Immediate Window A console window displaying the dump

Visualizers

The files llvm/utils/LLVMVisualizers/llvm.natvis and clang/utils/ClangVisualizers/clang.natvis provide debugger visualizers that make debugging of more complex data types much easier.

For Visual Studio 2013 only, put the files into %USERPROFILE%\Documents\Visual Studio 2013\Visualizers or create a symbolic link so they update automatically.

For later versions of Visual Studio, no installation is required. Note also that later versions of Visual Studio also display better visualizations.

— Debugging using Visual Studio

Attaching to a child process

Clang used to always start a child process to carry out actual compilation. This has changed, now whenever possible everything happens in the same process. Look for the following portion of code of clang_main in llvm-project/clang/tools/driver/driver.cpp:

if (!UseNewCC1Process) {
  TheDriver.CC1Main = [ToolContext](SmallVectorImpl<const char *> &ArgV) {
    return ExecuteCC1Tool(ArgV, ToolContext);
  };
  // Ensure the CC1Command actually catches cc1 crashes
  llvm::CrashRecoveryContext::Enable();
}

That said, there are compilation scenarios a child process has to be started for — translating source code into an executable is an example of one, actually. Or you may simply be working with an older codebase, where always starting a child process is still a thing.

Breakpoints placed in the code executed by child process aren’t going to be hit. So, here are a couple approaches to make VS actually stop at such breakpoints.

  1. Install the extension Microsoft Child Process Debugging Power Tool that makes VS automatically attach to any child process spawned from a process you’re already debugging.
  2. Look into calling the DebugBreak system function or placing the __debugbreak intrinsic into the code.
  3. See if changing clang_main such that it does not spawn a new process is possible and is an option for you.

Running tests

Hacking on Clang gives hints on running Clang’s test suite on Windows from Visual Studio or the command line.

Visual Studio

As the guide explains, a number of VS projects generated by CMake is configured to run tests. Building such a project results in running its group of tests. To run Clang’s test suite from VS, right-click the check-clang project under Clang tests and pick Build from the context menu. (Notice, Hacking on Clang says building clang-test triggers the execution of test suite. But that project doesn’t start any tests for me.) One thing to keep in mind is testing a Debug build of clang.exe with the full test suite takes hours, at least on my machine — I actually didn’t manage to wait through to its completion. Testing a Release build takes about 9 minutes though.

Start check-clang project&rsquo;s build to run Clang&rsquo;s test suite

Command line

Clang/LLVM’s codebase includes a testing tool written in Python called LLVM Integrated Tester or lit. We can use lit to run a single or multiple tests from the command line. A command to run the entire Clang’s test suite looks like this1:

PS > cd llvm-pod/vs-2022-1
PS > python ../llvm-project/llvm/utils/lit/lit.py -sv --param=build_mode=Release ./tools/clang/test

It departs from the command line given in the Hacking on Clang page in a couple of ways.

PS > python (path to llvm)\llvm\utils\lit\lit.py -sv `
 --param=build_mode=Win32 --param=build_config=Debug `
 --param=clang_site_config=(build dir)\tools\clang\test\lit.site.cfg `
 (path to llvm)\llvm\tools\clang\test

Let’s quickly go over the differences.

  1. It factors in the folder structure discussed earlier.
  2. build_config and clang_site_config are absent. I might be missing something, but lit seems to ignore these parameters as their values a) don’t affect test executions in any way and b) I could not find any traces of them in the source code of lit in llvm-project\llvm\utils\lit.
  3. The value of build_mode is used to form the path to the Clang executable under test. E.g., vs-2022-1/Debug/bin/clang.exe for build_mode=Debug; or vs-2022-1/RelWithDebInfo/bin/clang.exe for build_mode=RelWithDebInfo, etc.
  4. The path to the test suite (./tools/clang/test) points to the build tree (llvm-pod/llvm-project) in contrast to pointing to the source tree — (path to llvm)\llvm\tools\clang\test. The inner functions search, and search1 from getTestSuite in llvm-project\llvm\utils\lit\lit\discovery.py work their way upwards through the supplied path in the build tree from a child folder to its parent folder until they discover a lit.site.cfg.py test config file. Then they switch to the “symmetrical” path in the source tree (llvm-pod/llvm-project/...) and collect test cases there.

A command to run an individual test is:

PS > cd llvm-pod/vs-2022-1
PS > python ../llvm-project/llvm/utils/lit/lit.py -sv --param=build_mode=Debug ./tools/clang/test/Sema/wchar.c

It again differs from the documentation’s command line. The reasons for changes are the same as discussed above.

Conclusion

That’s it, and that’s all. Happy hacking!


  1. The cd commands are intended to indicate the location for executing subsequent commands. If you’ve already navigated there, ignore them, of cousre :) ↩︎