Или в других проектах, которые включают 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 и Андреасу Хаасу за вычитку и ценные исправления.