Notes for C++ Development on Windows

I’ve been writing a lot of C++ code at ArangoDB lately. Most people want their databases to run on top of some Linux distro, which makes perfect sense, but ArangoDB supports Windows as well (at least they did, until version 3.12). Even though that comes with extra maintenance costs, I think it’s a good idea. By building your project with different compilers, you’re making sure the code stays portable and uses only standard features. Clearly, multiple compilers can point out more potential issues together. By testing our project on different operating systems, we see how it behaves under various circumstances, thus making sure it doesn’t rely on platform specific behavior. I once stumbled across a stack overflow that was mostly reproduced when running the tests on Windows, because the stack size is by default smaller (1MB compared to 10MB on Linux). Even concurrency issues are often uncovered this way, because different operating systems use different schedulers.

Compilers

The staple compiler on Windows is MSVC. A good alternative is clang. If you care about MSVC compatibility, check out clang-cl, a driver program for clang that attempts to be compatible with MSVC’s cl.exe. There’s also a way to get GCC on Windows, but I never tried it, one can check out MinGW if he’s so inclined.
I find compiling on Windows slower compared to Linux, probably due to NTFS and ext4 differences. More so, I sometimes experienced lag when using the computer during longer builds, so I decreased the CPU and IO priority of the compiler and linker processes.

1
2
3
4
5
6
7
8
9
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\cl.exe\PerfOptions]
"CpuPriorityClass"=dword:00000005
"IoPriority"=dword:00000005

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\link.exe\PerfOptions]
"CpuPriorityClass"=dword:00000005
"IoPriority"=dword:00000005

In the example above, I am setting the priority to below normal, which works great for me. Here’s a list of hex values, so you can customize it:

  • Low: 00000001
  • Below Normal: 00000005
  • Normal: 00000002
  • Above Normal: 00000006
  • High: 00000003

In case that’s not enough, you could limit the number of cl.exe processes (to 8, for example) by passing the /p:CL_MPCount=8 argument to MSBuild. If you’re using an IDE, most probably there’s a setting for that.

Debuggers

On Windows, I usually go for lldb. You need LLVM for Windows, which is available on their GitHub releases page. You can also get GDB, but it has the disadvantage of not being able to load Windows crash dumps. Sometimes, during debugging, I have to observe the assembly instructions. In case of x86-64 programs, I prefer the Intel syntax, which can be configured from the .lldbinit file, placed in the user directory:

1
settings set target.x86-disassembly-flavor intel

These two debuggers are great for most use cases, but for debugging Windows drivers I had to use WinDbg. Although WinDbg can be used to debug any program, not just drivers, I used it solely for that.

Crash Dumps

Crash Dumps are the Windows counterpart of Linux Core Dumps. When a program crashes, a dump file (.dmp) is written to disk. This can be later used to debug and analyze the program (memory content, registers), at the moment of the crash. By default, crash dump generation is disabled, but it can be enabled through the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps registry key.

1
2
3
4
5
6
7
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps]
"DumpFolder"=hex(2):25,00,4c,00,4f,00,43,00,41,00,4c,00,41,00,50,00,50,00,44,00,41,00,54,00,41,00,25,00,5c,00,54,00,65,00,6d,00,70,00
"DumpCount"=dword:00000040
"DumpType"=dword:00000002
"CustomDumpFlags"=dword:00000000

How to configure it:

  • DumpFolder - where the dump files are to be stored (%LOCALAPPDATA%\Temp)
  • DumpCount - maximum number of dump files in the folder (64)
  • DumpType - Custom/Mini/Full (Full)
  • CustomDumpFlags - only used when DumpType is set to 0

Here’s a Python one-liner to generate a DumpFolder REG_EXPAND_SZ from a custom string:

1
',00,'.join([hex(ord(c))[2:] for c in '%LOCALAPPDATA%\Temp']) + ',00'

To test the settings, compile and run any broken C++ program.

1
2
3
4
5
6
7
int main(void) {
int *x = nullptr;
while (true) {
++*x++;
}
return 0;
}

Running this program will generate a crash dump in the folder pointed by DumpFolder. The dump will be named executable.pid.dmp. In my case, that was broken.exe.2940.dmp, because I called the executable broken.exe and the process ID was 2940. Load the executable alongside its core in lldb:

1
lldb broken.exe -c broken.exe.2940.dmp

If you’re used to GDB, but are new to lldb, check out this nice command map.

References and Further Reading