1# v8windbg
2
3V8windbg is a WinDbg extension for the V8 engine. It adjusts the behavior of the
4Locals pane and corresponding `dx` commands to display useful data when
5inspecting V8 object types. It is intended to be as robust as possible in dumps
6with limited memory, and should work equally well in live sessions, crash dumps,
7and time travel debugging.
8
9## Building
10
11Run `autoninja v8windbg` in your output directory.
12
13## Using
14
15In WinDbgX, run `.load path\to\your\output\dir\v8windbg.dll` to load the
16extension. To inspect V8 objects, use the Locals window or the `dx` command as
17usual.
18
19**Important notes:**
20
21- The version of v8windbg must exactly match the version and build configuration
22  of the process you're debugging. (To find the version number of a module in a
23  crash dump, enter `lm` and click the module name, or run `lmDvm modulename`.)
24- V8windbg relies on detailed symbols (symbol_level = 2).
25- Ensure also that WinDbg can load the symbols (.pdb file) for the module
26  containing V8.
27- Cross-architecture debugging is possible in some cases:
28  - To debug an x86 process on x64, load the x86 build of v8windbg.
29  - To debug an ARM64 process on x64, load the ARM64 simulator build of v8windbg
30    (built with target_cpu="x64" and v8_target_cpu="arm64").
31
32As well as improving the Locals pane behavior, v8windbg also provides a few
33functions that can be called from within `dx` commands:
34
35- `@$v8object()` returns information about the fields of a tagged V8 value,
36  passed in as a plain number like `dx @$v8object(0x34f49880471)`. This invokes
37  the same logic that is used for the locals pane. You may also pass a type hint
38  as an optional second parameter if you find that v8windbg is not inferring the
39  correct type (which can happen when the memory for the object's Map wasn't
40  collected in a crash dump). The type hint is a fully-qualified C++ class name,
41  like `dx @$v8object(0x34f49880471, "v8::internal::JSArray")`.
42- `@$curisolate()` gets the Isolate pointer for the current thread, if the
43  current thread has a JavaScript Isolate associated.
44- `@$listchunks()` returns a list of the memory chunks in the Heap for the
45  current Isolate.
46
47*Tip:*: to see what objects are present in a chunk of heap memory, you can cast
48it to an array of `TaggedValue`, like this:
49
50`dx (v8::internal::TaggedValue(*)[64])0x34f49880450`
51
52## Architecture
53
54V8windbg uses the [DataModel] as much as possible as opposed to the older
55[DbgEng] APIs. It uses the [WRL COM] APIs due to limitations in Clang's support
56for [C++/WinRT COM].
57
58Where possible, v8windbg uses the cross-platform v8_debug_helper library to
59avoid depending on V8 internals.
60
61The source in `./base` is a generic starting point for implementing a WinDbg
62extension. The V8-specific implementation under `./src` then implements the two
63functions declared in `dbgext.h` to create and destroy the extension instance.
64
65`./src` file index:
66
67- `cur-isolate.{cc,h}` implements the `IModelMethod` for `@$curisolate()`.
68- `list-chunks.{cc,h}` implements the `IModelMethod` for `@$listchunks()`. Its
69  result is a custom object that supports iteration and indexing.
70- `local-variables.{cc,h}` implements the `IModelPropertyAccessor` that provides
71  content to show in the Locals pane for stack frames corresponding to builtins
72  or runtime-generated code.
73- `object-inspection.{cc,h}` contains various classes that allow the debugger to
74  show fields within V8 objects.
75- `v8-debug-helper-interop.{cc,h}` makes requests to the V8 postmortem debugging
76  API, and converts the results into simple C++ structs.
77- `v8windbg-extension.{cc,h}` is responsible for initializing the extension and
78  cleaning up when the extension is unloaded.
79
80When the extension is initialized (`Extension::Initialize()`):
81
82- It registers a "parent model" for all known V8 object types, such as
83  `v8::internal::HeapObject` and `v8::internal::Symbol`. Any time WinDbg needs
84  to represent a value with one of these types, it creates an `IModelObject`
85  representing the value and attaches the parent model. This particular parent
86  model supports `IStringDisplayableConcept` and `IDynamicKeyProviderConcept`,
87  meaning the debugger will call a custom method every time it wants to get a
88  description string or a list of fields for any of these objects.
89- It registers a different parent model, with a single property getter named
90  "Value", for handle types such as `v8::internal::Handle<*>`. The "Value"
91  getter returns the correctly-typed tagged pointer contained by the handle.
92- It overrides the getter functions for "LocalVariables" and "Parameters" on the
93  parent model for stack frames. When the user selects a stack frame, WinDbg
94  calls these getter functions to determine what it should show in the Locals
95  pane.
96- It registers the function aliases such as `@$curisolate()`.
97
98The `./test` directory contains a test function that exercises v8windbg. It does
99not require WinDbg, but uses DbgEng.dll and DbgModel.dll from the Windows SDK
100(these are slightly older versions of the same modules used by WinDbg). The test
101function launches a separate d8 process, attaches to that process as a debugger,
102lets d8 run until it hits a breakpoint, and then checks the output of a few `dx`
103commands.
104
105## Debugging the extension
106
107To debug the extension, launch a WinDbgx instance to debug with an active
108target, e.g.
109
110`windbgx \src\github\v8\out\x64.debug\d8.exe -e "console.log('hello');"`
111
112or
113
114`windbgx \src\github\v8\out\x64.debug\d8.exe c:\temp\test.js`
115
116The WinDbgx process itself does not host the extensions, but uses a helper
117process. Attach another instance of WinDbgx to the `enghost.exe` helper process,
118e.g.
119
120`windbgx -pn enghost.exe`
121
122Set a breakpoint in this second session for when the extension initializes, e.g.
123
124`bm v8windbg!DebugExtensionInitialize`
125
126..and/or whenever a function of interest is invoked, e.g.
127
128 - `bp v8windbg!CurrIsolateAlias::Call` for the invocation of `@$curisolate()`
129 - `bp v8windbg!GetHeapObject` for the interpretation of V8 objects.
130
131Load the extension in the target debugger (the first WinDbg session), which
132should trigger the breakpoint.
133
134`.load "C:\\src\\github\\v8windbg\\x64\\v8windbg.dll"`
135
136Note: For D8, the below is a good breakpoint to set just before any script is
137run:
138
139`bp d8_exe!v8::Shell::ExecuteString`
140
141..or the below for once the V8 engine is entered (for component builds):
142
143`bp v8!v8::Script::Run`
144
145Then trigger the extension code of interest via something like `dx source` or
146`dx @$curisolate()`.
147
148[DataModel]: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/data-model-cpp-overview
149[DbgEng]: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/writing-dbgeng-extension-code
150[C++/WinRT COM]: https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/consume-com
151[WRL COM]: https://docs.microsoft.com/en-us/cpp/cppcx/wrl/windows-runtime-cpp-template-library-wrl?view=vs-2019
152