-
Notifications
You must be signed in to change notification settings - Fork 51
Concord Architecture
Here is a simplified diagram of Concord's architecture:
At the top of this diagram, there is the Debugger UI & SDM, which make up the front end of the VS debugger architecture. The SDM/UI interact with Concord through the debug engine interfaces which Concord implements (IDebug* interfaces). These components interact with the AD7 AL, which is the adaptation layer between the debug engine APIs (known as AD7) and the Concord APIs.
On the left side of the diagram is the box for the Dispatcher. The Dispatcher knows nearly nothing about debugging, but instead it is a routing, loading, and remoting service used by the rest of Concord. The dispatcher exposes the Concord API. When a Concord API method is called, the dispatcher finds the best implementation of this API, loads up the implementation, and calls this component through an interface. If the caller and implementer are on different computers, the Dispatcher remotes the method call to the correct computer. If the method is implemented in managed code, the Dispatcher marshals the call from the native Concord API universe to the managed Concord API universe.
In the middle of the diagram are a bunch of blue boxes. Each blue box represents a Concord ‘Component’. Components are the basic unit of extensibility in Concord. Each component has a configuration file that describes to the dispatcher what interfaces it implements along with a filter to indicate when this interface should be used. As an example, there is an interface for setting breakpoints. This same interface is implemented by the Managed DM for setting breakpoints in managed code, the Script DM for setting breakpoints in JavaScript code, and the Win32 Base DM for setting breakpoints in native code. Each implementation has a different filter so that the dispatcher can pick the correct implementation depending on where the breakpoint is being set. Microsoft provides more components than could fit on a diagram or that would make sense to describe, but here is a quick overview of some of the more important components from an extensibility perspective. This description tries to illustrate the granularity that Microsoft used when creating components and therefore the granularity with which the API was created.
Responsible for building up the call stack to the form where it is ready to display to users. To do this, the stack provider makes requests to other components to walk the stack, filter the stack to include appropriate descriptions and to hide frames that should be hidden, and lastly obtain the frame description string in whatever language the frame was implemented.
Responsible for managing the various user-level breakpoint requests. It will match them up to symbols as modules are loaded and send information down to one of the Debug Monitors in order to add or clear breakpoints.
Responsible for walking call stacks through x86 code which has a PDB loaded. It will use data from the PDB and the stack walk APIs from the MSDIA dll to walk the call stack.
The Expression Evaluator (EE) allows the debugger to communicate with the user in the language the program being debugged was written against. The EE is responsible for populating data for the various inspections windows (Watch, Locals, Quick Watch, Immediate, and DataTips) and formats data such as stack frames, method names, variable names, and variable values.
Responsible for loading PDB files, implementing the Concord symbol provider API using information in these PDB files, and for providing the MSDIA API to other Concord components. The Concord symbol provider API covers a few common symbol operations that various other Concord components need such as mapping between source lines and symbols. For more advanced scenarios, such as the data inspection needs of the PDB stack walker and the C++ EE, the PDB Symbol Provider will instead provide the MSDIA interfaces.
In many cases, such as managed code running under the .NET runtime, or native code running in an ARM or x64 process, it is possible to walk the call stack without access to symbols. The stack merger is responsible for working with components that walk the stack using this runtime data, and sending back the complete results to the stack provider.
Responsible for coordinating stepping operations across the various runtime environments loaded in the target process.
The CLR Inspector is used when debugging .NET applications and is responsible for communicating with the CLR's debugging API (mscordbi). It also has intimate knowledge of IL, the runtime, and the .NET framework library. This enables many of the features available when debugging managed code. Some examples of these features are: Step into specific, exception decoding, stepping over await statements, and populating the Tasks window.
This is the Debug Monitor (DM) responsible for inspecting low-level state, and controlling execution of managed code running in the target process. Debug monitors are the lowest-level debugging components in the Concord architecture. There are two categories of debug monitor:
- Runtime debug monitors: There is a runtime debug monitor for each type of execution environment (known as a ‘runtime instance’ in the Concord API) which is loaded and debugged in the target process. So for, example, if a target process had loaded native and .NET Framework code, and both were being debugged at the same time, then the target process would have two runtime instances (native and managed), and two runtime debug monitors would be active.
- Base debug monitors: The base debug monitor is the component that owns the underlying connection to the target process. Unlike runtime debug monitors, there can be only one base debug monitor. Base debug monitors provide the lowest level primitives such as reading or writing memory or registers, and basic execution control.
When debugging with "managed only" code, the Managed DM functions as the base debug monitor. When debugging with "mixed" code, the Managed DM functions as one of the runtime monitors.
Runtime debug monitor for native code built using the Visual C++ compiler. It builds on top of the base DM APIs to implement source level stepping, and C++ exception decoding among other things.
Base debug monitor which is implemented on top of the Win32 debugging APIs (ReadProcessMemory/WriteProcessMemory/WaitForDebugEvent, etc). This is the base debug monitor for all native debugging and mixed mode debugging.
Concord Documentation:
- Overview
- Visual Studio 2022 support
- Concord Architecture
- Getting troubleshooting logs
- Tips for debugging extensions
- Component Discovery and Configuration
- Component Levels
- Navigating the Concord API
- Obtaining the Concord API headers, libraries, etc
- Concord Threading Model
- Data Container API
- Creating and Closing Objects
- Expression Evaluators (EEs)
- .NET language EEs
- Native language EEs
- Installing Extensions
- Cross Platform and VS Code scenarios:
- Implementing components in native code:
- Worker Process Remoting
Samples: