Table of contents
Debugging Note
This post simply contains a debugging note for V8 development for myself.
Please let me know if you know a more efficient way.
This post will be updated when I got something useful.
TOOLS
https://v8.github.io/tools/head/
CSA
Handle<T>
To Print Handle<T>
in CSA, we can use HeapConstantNoHole
Handle<String> name = DoSomething();
auto temp = HeapConstantNoHole(name);
Print(temp);
Result:
DebugPrint: 0x4ca00004859: [String] in ReadOnlySpace: #global
0x4ca00000155: [Map] in ReadOnlySpace
- map: 0x04ca00000475 <MetaMap (0x04ca0000002d <null>)>
- type: INTERNALIZED_ONE_BYTE_STRING_TYPE
- instance size: variable
- elements kind: HOLEY_ELEMENTS
- enum length: invalid
- stable_map
- non-extensible
- back pointer: 0x04ca00000011 <undefined>
- prototype_validity cell: 0
- instance descriptors (own) #0: 0x04ca00000779 <DescriptorArray[0]>
- prototype: 0x04ca0000002d <null>
- constructor: 0x04ca0000002d <null>
- dependent code: 0x04ca00000755 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
TNode<Object>
We can just put it to Print
Print(node)
Results same with above Handle<T>
Torque (.tq file)
you can find the Print() functions in base.tq
It is same with CSA’s.
extern macro Print(constexpr string): void;
extern macro Print(constexpr string, Object): void;
extern macro Print(Object): void;
extern macro Print(constexpr string, uintptr): void;
extern macro Print(constexpr string, float64): void;
extern macro PrintErr(constexpr string): void;
extern macro PrintErr(constexpr string, Object): void;
extern macro PrintErr(Object): void;
Snapshot
snapshot compile error
BIND and Label
If BIND occur, it means different code chunk. What if the code chunk is not connected with label it will cause snapshot compile error.
// What if else? the flow is not connected.
GotoIf(IsNullOrUndefined(flags), &if_flags_not_exist);
// ...
// Start another block.
BIND(&loop);
#
# Fatal error in ../../src/compiler/raw-machine-assembler.cc, line 817
# Binding label without closing previous block:
# label: (&loop:../../src/builtins/builtins-regexp-gen.cc:1535)
So even though BIND is placed by the execution flow, we should jump to the BIND.
// What if else
GotoIf(IsNullOrUndefined(flags), &if_flags_not_exist);
// ...
Goto(&loop);
// Start another block.
BIND(&loop);
Runtime
StackTrace
Print stack trace:
#include "src/base/debug/stack_trace.h"
// ...
base::debug::StackTrace stack;
stack.Print(); // or std::string trace = stack.ToString();
// ...
example results:
==== C stack trace ===============================
0 libv8_libbase.dylib 0x0000000100fa2f70 v8::base::debug::StackTrace::StackTrace() + 32
1 libv8_libbase.dylib 0x0000000100fa2fac v8::base::debug::StackTrace::StackTrace() + 28
2 libv8.dylib 0x00000001146073d0 v8::internal::__RT_impl_Runtime_RegExpReplaceRT(v8::internal::Arguments<(v8::internal::ArgumentsType)0>, v8::internal::Isolate*) + 240
3 libv8.dylib 0x0000000114607094 v8::internal::Runtime_RegExpReplaceRT(int, unsigned long*, v8::internal::Isolate*) + 288
4 ??? 0x0000000307a37150 0x0 + 13013053776
5 ??? 0x0000000307cc72a4 0x0 + 13015741092
6 ??? 0x00000003077b2498 0x0 + 13010412696
Turbofan/TurboShaft
Entrypoint of each phases related in Turbolizer
Turbofan
Search DECL_PIPELINE_PHASE_CONSTANTS
in pipeline.cc.
TurboShaft
Search DECL_TURBOSHAFT_PHASE_CONSTANTS
or DECL_TURBOSHAFT_PHASE_CONSTANTS_WITH_LEGACY_NAME
.
LLDB/GDB
We can use lldb
or gdb
to debug v8 (and chromium too).
The source must be compiled in debug build to create Debugging Symbols.
By debugger, you can stop the specific position of the source code and print the value. Even you can execute method and print the result like:
(lldb) p this->ToString()
GDB/LLDB command map: https://lldb.llvm.org/use/map.html
Example in CLI
devsdk@MacBook-Pro ~/workspace/chromium/v8/v8/out/arm64.debug % lldb -- d8 ~/workspace/chromium/playground/temp.js
(lldb) target create "d8"
Current executable set to '/Users/devsdk/workspace/chromium/v8/v8/out/arm64.debug/d8' (arm64).
(lldb) settings set -- target.run-args "/Users/devsdk/workspace/chromium/playground/temp.js"
(lldb) b src/parsing/parser-base.h:5910
Breakpoint 1: 2 locations.
(lldb) r
Process 49637 launched: '/Users/devsdk/workspace/chromium/v8/v8/out/arm64.debug/d8' (arm64)
0
Process 49637 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2
frame #0: 0x0000000113664f94 libv8.dylib`v8::internal::ParserBase<v8::internal::PreParser>::ParseBlock(this=0x000000014ae04a30, labels=0x0000000000000000, block_scope=0x000000014b010430) at parser-base.h:5910:3
5907 body->set_scope(scope()->FinalizeBlockScope());
5908 }
5909
-> 5910 body->InitializeStatements(statements, zone());
5911 return body;
5912 }
5913
Target 0: (d8) stopped.
(lldb) p body
(v8::internal::ParserBase<v8::internal::PreParser>::BlockT) {
v8::internal::PreParserStatement = (code_ = kUnknownStatement)
scope_ = nullptr
}
VS Code Debugger Setup
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(lldb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/out/arm64.debug/d8",
"args": ["/Users/devsdk/workspace/chromium/playground/temp.js"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/out/arm64.debug",
"environment": [],
"sourceFileMap" : {"../../src/": "${workspaceFolder}/src/"},
"externalConsole": false,
"MIMode": "lldb"
}
]
}
src/base
ThreadedList<T>
ThreadedList is a linked list where each object acts as its own node—this is known as an intrusive linked list.
The linkage (i.e., next pointers) is embedded directly within the object rather than stored in a separate node wrapper.
⚠️ Note: Despite the name, ThreadedList is not related to thread safety or multithreading. It simply refers to “threading” objects together in a list.
To use an object type T
with ThreadedList<T>
, the class must expose the following static member functions:
static T** next(T* t) { return t->next(); }
static T** start(T** t) { return t; }
static T* const* start(T* const* t) { return t; }
These functions tell the list how to get and follow the next pointer in the object.
You can also customize the pointer accessor logic by specifying a second template parameter. For example:
base::ThreadedList<Variable, Variable::VarListNext>;
If you’re facing an issue where Add
fails because the tail is not nullptr
,
you’re likely trying to use the object in multiple lists at once.
For example,
class Something {
// ...
base::ThreadedList<Variable> local_;
base::ThreadedList<Variable> var_list_;
// ...
}
This can cause Add to fail and not work as expected,
because both lists are sharing the same next()
pointer from Variable.
To make this work correctly:
class Variable {
public:
// ...
struct VarListNext {
static Variable** next(Variable* t) { return &t->var_next_; }
static Variable** start(Variable** t) { return t; }
};
// ...
private:
Variable* next_; // Used by local_
Variable* var_next_; // Used by var_list_
}
class Something {
base::ThreadedList<Variable> local_;
base::ThreadedList<Variable, Variable::VarListNext> var_list_;
};
Now each list uses its own next pointer, and the object can safely belong to both lists.