Или в других проектах, которые включают V8

Если вы вносите свой вклад в ядро ​​Node, вам в конечном итоге потребуется отлаживать код C ++ в / node / src /. Если вы делали это раньше, то могли заметить, что команда GDB print бесполезна при работе с кодом V8.

TL;DR Use .gdbinit from V8. 
(gdb) job obj   # Print v8::HeapObject*.
(gdb) jlh obj   # Print v8::Local handle.
# Somewhere in the code
v8::Local<v8::String> class_name =
    FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
# Looking at this variable in GDB
(gdb) print class_name
$1 = {val_ = 0x51eb3a0} 

Нам бы очень хотелось, чтобы в выходных данных упоминался ContextifyScript, а не адрес памяти.

Ядро узла использует V8 в качестве движка JavaScript. Многие переменные в V8 представляют собой либо v8 :: Values, заключенные в дескрипторы v8 :: Local, либо v8 :: internal :: HeapObjects, завернутые в дескрипторы v8 :: internal :: Handle. Ручки нужны для сборщика мусора. Вы найдете v8 :: Locals повсюду в исходных кодах ядра узла. Однако, если вы хотите отладить их, вам нужно сделать что-то более сложное, чем просто набрать print.

V8 предоставляет файл gdbinit с пользовательскими командами для проверки объектов кучи V8. Они позволяют нам легко отлаживать объекты V8. Давайте шаг за шагом рассмотрим, как использовать эти команды.

Получите файл gdbinit из репозитория V8 и сохраните его как .gdbinit в своем каталоге узлов или в домашнем каталоге. Вы также можете передать файл в GDB с помощью -x. Если вы сохраните его в папке узла, вы, вероятно, захотите добавить его в .git / info / exclude.

Далее вам понадобится отладочная сборка Node.

# Build the Node debug build. We assume you already have the Node sources in $NODE.
$ cd $NODE
$ ./configure --debug && make -j8

Допустим, мы работаем над модулем vm в node_contextify.cc и нам нужно выяснить, что там происходит. Мы вызываем узел в тестовом примере (или в любом другом файле JavaScript, если на то пошло), который использует код, который мы хотим отладить.

# Start gdb. Node_g is the debug build executable.
$ gdb --args node_g test/parallel/test-vm-context.js

Установим несколько точек останова в интересующих нас функциях.

# Set a breakpoint. You can use filenames or function names. Code completion works here!
(gdb) break node_contextify.cc:380
Breakpoint 1 at 0x2214955: file ../src/node_contextify.cc, line 380.
(gdb) break node::ContextifyContext::GlobalPropertyQueryCallback
Breakpoint 2 at 0x2214aa2: file ../src/node_contextify.cc, line 409.
# Run until you hit a breakpoint.
(gdb) run
Starting program: ...
Breakpoint 1, node::ContextifyContext::GlobalPropertySetterCallback (property=..., value=..., args=...) at ../src/node_contextify.cc:380
380     ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>());
# If you want to set a breakpoint inside V8, don't forget the v8::internal namespace.
(gdb) b v8::internal::JSReceiver::GetPropertyAttributes
Breakpoint 3 at 0x15c4032: v8::internal::JSReceiver::GetPropertyAttributes. (2 locations)

Теперь отладчик остановился на точке останова 1. Код использует API V8, и у нас есть несколько дескрипторов v8 :: Local. Посмотрим, каковы их ценности.

# A regular print on v8::Local handles is not very helpful.
(gdb) print property
$1 = {val_ = 0x7ffed1702eb0}
(gdb) print value
$2 = {val_ = 0x7ffed1702ea8}
# To see the value of a local handle, use the jlh command provided by gdbinit. If you get "Undefined command", double check the location and name (don't forget the dot!) of your .gdbinit.
(gdb) jlh property
#foo
(gdb) jlh value
3
# As expected, because the test is setting foo = 3.

Если мы используем jlh вместо print в нашем первом примере, мы увидим нижележащую строку (возможно, то, что нас интересует), а не адрес памяти.

# Somewhere in the code
v8::Local<v8::String> class_name =
    FIXED_ONE_BYTE_STRING(env->isolate(), "ContextifyScript");
# Looking at this variable in GDB
(gdb) print class_name
$1 = {val_ = 0x51eb3a0}
(gdb) jlh class_name
"ContextifyScript"

Подводя итог, если вы хотите распечатать содержимое дескриптора v8 :: Local, используйте jlh.

Если вам интересно, что означает jlh, это означает «локальный дескриптор задания». job - это определяемая пользователем команда GDB V8 для печати v8 :: internal :: HeapObjects, которые являются очень распространенными объектами в V8.

(gdb) job heap_object_ptr

Не специфично для V8, но вот еще несколько полезных команд GDB. Удачной отладки!

# Run until breakpoint or the end
(gdb) r
# Go to the next line.
(gdb) n
# Continue to the next breakpoint. 
(gdb) c
# Step into a function.
(gdb) s
# List all your breakpoints, enable and disable them.
(gdb) info b
(gdb) disable 3
(gdb) enable 3
# You can shorten all commands in gdb, they only need to be so long that they are unique. Like p for print. 
# Make use of tab completion, arrow up-down, reverse-i-search, and readline commands.
# Enter repeats the last command.

Спасибо Янгу Го за добавление его команды jlh в gdbinit V8 и Андреасу Хаасу за вычитку и ценные исправления.