A native, user-mode, multi-process, graphical debugger.
Note: This README does not document usage instructions and tips for the
debugger itself, and is intended as a technical overview of the project. The
debugger’s README, which includes usage instructions and tips, can be found
packaged along with debugger releases, or within the build
folder after a
local copy has been built.
The RAD Debugger is a native, user-mode, multi-process, graphical debugger. It
currently only supports local-machine Windows x64 debugging with PDBs, with
plans to expand and port in the future. In the future we’ll expand to also
support native Linux debugging and DWARF debug info.
The RAD Debugger is currently in ALPHA. In order to get the debugger bullet-
proof, it’d greatly help out if you submitted the issues you find here, along
with any information you can gather, like dump files (along with the build you
used), instructions to reproduce, test executables, and so on.
You can download pre-built binaries for the debugger
here.
The RAD Debugger project aims to simplify the debugger by simplifying and
unifying the underlying debug info format. In that pursuit we’ve built the RAD
Debug Info (RDI) format, which is what the debugger parses and uses. To work
with existing toolchains, we convert PDB (and eventually PE/ELF files with
embedded DWARF) into the RDI format on-demand.
The RDI format is currently specified in code, in the files within the
src/lib_rdi_format
folder. The other relevant folders for working with the
format are:
lib_rdi_make
: The “RAD Debug Info Make” library, for making RDI debug info.rdi_from_pdb
: Our PDB-to-RDI converter. Can be used as a helper codebaserdi_from_dwarf
: Our in-progress DWARF-to-RDI converter.rdi_dump
: Our RDI textual dumping utility.Note: Currently, only x64 Windows development is supported.
In order to work with the codebase, you’ll need the Microsoft C/C++ Build Tools
v15 (2017) or later, for both
the Windows SDK and the MSVC compiler and linker.
If the Windows SDK is installed (e.g. via installation of the Microsoft C/C++
Build Tools), you may also build with Clang.
Building the codebase can be done in a terminal which is equipped with the
ability to call either MSVC or Clang from command line.
This is generally done by calling vcvarsall.bat x64
, which is included in the
Microsoft C/C++ Build Tools. This script is automatically called by the x64 Native Tools Command Prompt for VS <year>
variant of the vanilla cmd.exe
. If
you’ve installed the build tools, this command prompt may be easily located by
searching for Native
from the Windows Start Menu search.
You can ensure that the MSVC compiler is accessible from your command line by
running:
cl
If everything is set up correctly, you should have output very similar to the
following:
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30151 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
usage: cl [ option... ] filename... [ /link linkoption... ]
Within this terminal, cd
to the root directory of the codebase, and just run
the build.bat
script:
build
You should see the following output:
[debug mode]
[msvc compile]
metagen_main.c
searching C:\devel\raddebugger/src... 309 files found
parsing metadesk... 15 metadesk files parsed
gathering tables... 96 tables found
generating layer code...
raddbg_main.c
If everything worked correctly, there will be a build
folder in the root
level of the codebase, and it will contain a freshly-built raddbg.exe
.
The first priority for the project is to ensure that the most crucial debugger
components are functioning extremely reliably for local, x64, Windows
debugging. This would include parts like debug info conversion, debug info
loading, process control, stepping, evaluation (correct usage of both location
info and type info), and a robust frontend which ensures the lower level parts
are usable.
We feel that the debugger has already come a long way in all of these respects,
but given the massive set of possible combinations of languages, build
settings, toolchains, used language features, and patterns of generated code,
there are still cases where the debugger has not been tested, and so there are
still issues. So, we feel that the top priority is eliminating these issues,
such that the debugging experience is rock solid.
The next priority for the project is to take the rock solid x64 Windows
debugging experience, and port all of the relevant pieces to support local x64
Linux debugging also.
The debugger has been written to abstract over the parts that need to differ on
either Linux or Windows, and this is mainly going to be a task in building out
different backends for those abstraction layers.
The major parts of this phase are:
src/demon
layer to implement the Demon local process controlsrc/ctrl
layer.src/rdi_from_dwarf
.src/render
layer to implement all of the rendering features thesrc/font_provider
layer to a Linux-compatible fontsrc/os
layers to Linux. This includes core operating systemOnce the above list is complete, and once every part is rock solid, the Windows
debugging experience we’ll have worked diligently to create will also be
available natively on Linux machines.
There are several directions we might take after these two major phases,
like remote debugging, porting to different architectures, further improving
the debugger’s features (like improving the visualization engine), and so on.
But for now, we’re mostly focused on those first two phases.
The RAD Linker is a new performance linker for generating x64 PE/COFF binaries. It is designed to be very fast when creating gigantic executables. It generates standard PDB files for debugging, but it can also optionally create RAD Debugger debug info too (useful for huge executables that otherwise create broken PDBs that overflow internal 32-bit tables).
The RAD Linker is primarily optimized to handle huge linking projects - in our test cases (where debug info is multiple gigabytes), we see 50% faster link times.
The command line syntax is fully compatible with MSVC and you can get a full list of implemented switches from /help
.
Our current designed-for use case for the linker is to help with the compile-debug cycle of huge projects. We don’t yet have support for dead-code-elimination or link-time-optimizations, but these features are on the road map.
By default, the RAD linker spawns as many threads as there are cores, so if you plan to run multiple linkers in parallel, you can limit the number of thread workers via /rad_workers
.
We also have support for large memory pages, which, when enabled, reduce link time by
another 25%. To link with large pages, you need to explicitly request them via /rad_large_pages
. Large pages are off by default, since Windows support for large pages is a bit buggy - we recommend they only be used in Docker or VM images where the environment is reset after each link. In a standard Windows environment, using large pages otherwise will fragment memory quickly forcing a reboot. We are working on a Linux port of the linker that will be able to build with large pages robustly.
/opt:ref
.build radlink release
or if you have clang installed build radlink release clang
. We favor latter option for better code generation.If build was successful linker executable is placed in build
folder under radlink.exe
.
data
: Small binary files which are used when building, either to embedsrc
: All source code.After setting up the codebase and building, the following directories will
also exist:
build
: All build artifacts. Not checked in to version control.local
: Local files, used for local build configuration input files. NotThe codebase is organized into layers. Layers are separated either to isolate
certain problems, and to allow inclusion into various builds without needing to
pull everything in the codebase into a build. Layers correspond with folders
inside of the src
directory. Sometimes, one folder inside of the src
directory will include multiple sub-layers, but the structure is intended to be
fairly flat.
Layers correspond roughly 1-to-1 with namespaces. The term “namespaces” in
this context does not refer to specific namespace language features, but rather
a naming convention for C-style namespaces, which are written in the codebase as
a short prefix, usually 1-3 characters, followed by an underscore. These
namespaces are used such that the layer to which certain code belongs may be
quickly understood by glancing at code. The namespaces are generally quite short
to ensure that they aren’t much of a hassle to write. Sometimes, multiple sub-
layers will share a namespace. A few layers do not have a namespace, but most
do. Namespaces are either all-caps or lowercase depending on the context in
which they’re used. For types, enum values, and some macros, they are
capitalized. For functions and global variables, they are lowercase.
Layers depend on other layers, but circular dependencies would break the
separability and isolation utility of layers (in effect, forming one big layer),
so in other words, layers are arranged into a directed acyclic graph.
A few layers are built to be used completely independently from the rest of the
codebase, as libraries in other codebases and projects. As such, these layers do
not depend on any other layers in the codebase. The folders which contain these
layers are prefixed with lib_
, like lib_rdi_format
.
A list of the layers in the codebase and their associated namespaces is below:
async
(ASYNC_
): Implements a system for asynchronous work to be queuedbase
(no namespace): Universal, codebase-wide constructs. Strings, math,codeview
(CV_
): Code for parsing and/or writing the CodeView format.coff
(COFF_
): Code for parsing and/or writing the COFF (Common Object Filectrl
(CTRL_
): The debugger’s “control system” layer. Implementsdasm_cache
(DASM_
): An asynchronous disassembly decoder and cache. Usersdbgi
(DI_
): An asynchronous debug info loader and cache. Loads debug infodbg_engine
(D_
): Implements the core debugger system, without anydemon
(DMN_
): An abstraction layer for local-machine, low-level processctrl
.draw
(DR_
): Implements a high-level graphics drawing API for therender
abstraction layer. Provideseval
(E_
): Implements a compiler for an expression language built foreval_visualization
(EV_
): Implements the core non-graphical evaluationeval
layer) in a number of ways. Implements core data structures andWatch
view.file_stream
(FS_
): Provides asynchronous file loading, storing thehash_store
layer, andfont_cache
(FNT_
): Implements a cache of rasterized font data, both infont_provider
abstractionfont_provider
(FP_
): An abstraction layer for various font file decodingfuzzy_search
(FZY_
): Provides a fuzzy searching engine for doingProcedures
view, which search acrossgeo_cache
(GEO_
): Implements an asynchronously-filled cache for GPUhash_store
layer’s cache. Usedhash_store
(HS_
): Implements a cache for general data blobs, keyed by alib_raddbg_markup
(RADDBG_
): Standalone library for marking up userbase
, and can be independently relocated to other codebases.lib_rdi_format
(RDI_
): Standalone library which defines the core RDI typesbase
, and can be independently relocated to otherlib_rdi_make
(RDIM_
): Standalone library for constructing RDI debug infobase
, and can be independently relocatedmdesk
(MD_
): Code for parsing Metadesk files (stored as .mdesk
), whichmetagen
layer.metagen
(MG_
): A metaprogram which is used to generate primarily code and.mdesk
, andenum
s and a number ofbase
, because it may be used to generate code for those layers. Tobase
and os
layer features in the metagen
program, a separate,base
and os
are included in this layer. They aremsf
(MSF_
): Code for parsing and/or writing the MSF file format.mule
(no namespace): Test executables for battle testing debuggermutable_text
(MTX_
): Implements an asynchronously-filled-and-mutatedOutput
view.natvis
(no namespace): NatVis files for type visualization of the codebase’sos/core
(OS_
): An abstraction layer providing core, non-graphicalos/gfx
(OS_
): An abstraction layer, building on os/core
, providingpath
(PATH_
): Small helpers for manipulating file path strings.pdb
(PDB_
): Code for parsing and/or writing the PDB file format.pe
(PE_
): Code for parsing and/or writing the PE (Portable Executable)raddbg
(RD_
): The layer which ties everything together for the mainrdi_breakpad_from_pdb
(P2B_
): Our implementation, using the codebase’s RDIrdi_dump
(no namespace): A dumper utility program for dumpingrdi_format
(no namespace): A layer which includes the lib_rdi_format
layerrdi_from_dwarf
(D2R_
): Our in-progress implementation of DWARF-to-RDIrdi_from_pdb
(P2R_
): Our implementation of PDB-to-RDI conversion.rdi_make
(no namespace): A layer which includes the lib_rdi_make
layer andregs
(REGS_
): Types, helper functions, and metadata for registers ondemon
, or inrender
(R_
): An abstraction layer providing an abstract API for renderingdraw
scratch
(no namespace): Scratch space for small and transient test programs.texture_cache
(TEX_
): Implements an asynchronously-filled cache for GPUhash_store
layer’s cache. Usedtext_cache
(TXT_
): Implements an asynchronously-filled cache for textualhash_store
layer’s cache. Used for asynchronously preparing data forthird_party
(no namespace): External code from other projects, which someui
(UI_
): Machinery for building graphical user interfaces. Provides a