Spec-Zone.ru › Node.js

Отладчик

Стабильность: 2 — Стабильный

Node.js включает в себя утилиту отладки командной строки. Клиент отладчика Node.js не является полнофункциональным отладчиком, но позволяет выполнять пошаговую отладку и проверку.

Отладчик поддерживает два режима работы: интерактивный режим и неинтерактивный режим зондирования.

Интерактивный режим

$ node inspect [--port=<port>] [<node-option> ...] [<script> [<script-args>] | <host>:<port> | -p <pid>]
consolecopy

Чтобы использовать его, запустите Node.js с аргументом inspect, за которым укажите путь к отлаживаемому скрипту.

$ node inspect myscript.js
< Debugger listening on ws://127.0.0.1:9229/621111f9-ffcb-4e82-b718-48a145fa5db8
< For help, see: https://nodejs.org/learn/getting-started/debugging
<
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
<
 ok
Break on start in myscript.js:2
  1 // myscript.js
> 2 global.x = 5;
  3 setTimeout(() => {
  4   debugger;
debug>
consolecopy

Отладчик автоматически останавливается на первой исполняемой строке. Чтобы вместо этого выполнить код до первой точки останова (задаваемой оператором debugger), задайте для переменной окружения NODE_INSPECT_RESUME_ON_START значение 1.

$ cat myscript.js
// myscript.js
global.x = 5;
setTimeout(() => {
  debugger;
  console.log('world');
}, 1000);
console.log('hello');
$ NODE_INSPECT_RESUME_ON_START=1 node inspect myscript.js
< Debugger listening on ws://127.0.0.1:9229/f1ed133e-7876-495b-83ae-c32c6fc319c2
< For help, see: https://nodejs.org/learn/getting-started/debugging
<
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
<
< hello
<
break in myscript.js:4
  2 global.x = 5;
  3 setTimeout(() => {
> 4   debugger;
  5   console.log('world');
  6 }, 1000);
debug> next
break in myscript.js:5
  3 setTimeout(() => {
  4   debugger;
> 5   console.log('world');
  6 }, 1000);
  7 console.log('hello');
debug> repl
Press Ctrl+C to leave debug repl
> x
5
> 2 + 2
4
debug> next
< world
<
break in myscript.js:6
  4   debugger;
  5   console.log('world');
> 6 }, 1000);
  7 console.log('hello');
  8
debug> .exit
$
consolecopy

Команда repl позволяет удалённо выполнять код. Команда next переходит к следующей строке. Введите help, чтобы увидеть список остальных доступных команд.

Нажатие enter без ввода команды повторяет предыдущую команду отладчика.

Наблюдение

Во время отладки можно наблюдать за значениями выражений и переменных. При каждой точке останова каждое выражение из списка наблюдения вычисляется в текущем контексте и отображается непосредственно перед листингом исходного кода в точке останова.

Чтобы начать наблюдение за выражением, введите watch('my_expression'). Команда watchers выводит активные наблюдения. Чтобы удалить наблюдение, введите unwatch('my_expression').

Справочник по командам

Пошаговое выполнение

  • cont, c: Продолжить выполнение
  • next, n: Перейти к следующей строке
  • step, s: Войти в функцию
  • out, o: Выйти из функции
  • pause: Приостановить выполнение кода (как кнопка паузы в инструментах разработчика)
Точки останова
  • setBreakpoint(), sb(): Установить точку останова на текущей строке
  • setBreakpoint(line), sb(line): Установить точку останова на указанной строке
  • setBreakpoint('fn()'), sb(...): Установить точку останова на первом операторе в теле функции
  • setBreakpoint('script.js', 1), sb(...): Установить точку останова на первой строке script.js
  • setBreakpoint('script.js', 1, 'num < 4'), sb(...): Установить условную точку останова на первой строке script.js, которая срабатывает, только если выражение num < 4 принимает значение true
  • clearBreakpoint('script.js', 1), cb(...): Удалить точку останова в script.js на строке 1

Также можно установить точку останова в файле (модуле), который ещё не загружен:

$ node inspect main.js
< Debugger listening on ws://127.0.0.1:9229/48a5b28a-550c-471b-b5e1-d13dd7165df9
< For help, see: https://nodejs.org/learn/getting-started/debugging
<
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
<
Break on start in main.js:1
> 1 const mod = require('./mod.js');
  2 mod.hello();
  3 mod.hello();
debug> setBreakpoint('mod.js', 22)
Warning: script 'mod.js' was not loaded yet.
debug> c
break in mod.js:22
 20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
 21
>22 exports.hello = function() {
 23   return 'hello from module';
 24 };
debug>
consolecopy

Также можно установить условную точку останова, которая срабатывает, только если заданное выражение принимает значение true:

$ node inspect main.js
< Debugger listening on ws://127.0.0.1:9229/ce24daa8-3816-44d4-b8ab-8273c8a66d35
< For help, see: https://nodejs.org/learn/getting-started/debugging
<
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
Break on start in main.js:7
  5 }
  6
> 7 addOne(10);
  8 addOne(-1);
  9
debug> setBreakpoint('main.js', 4, 'num < 0')
  1 'use strict';
  2
  3 function addOne(num) {
> 4   return num + 1;
  5 }
  6
  7 addOne(10);
  8 addOne(-1);
  9
debug> cont
break in main.js:4
  2
  3 function addOne(num) {
> 4   return num + 1;
  5 }
  6
debug> exec('num')
-1
debug>
consolecopy
Информация
  • backtrace, bt: Вывести стек вызовов текущего кадра выполнения
  • list(5): Вывести исходный код скриптов с контекстом в 5 строк (по 5 строк до и после)
  • watch(expr): Добавить выражение в список наблюдения
  • unwatch(expr): Удалить выражение из списка наблюдения
  • unwatch(index): Удалить выражение с указанным индексом из списка наблюдения
  • watchers: Вывести все наблюдения и их значения (автоматически выводятся при каждой точке останова)
  • repl: Открыть REPL отладчика для выполнения выражений в контексте отлаживаемого скрипта
  • exec expr, p expr: Выполнить выражение в контексте отлаживаемого скрипта и вывести его значение
  • profile: Начать сеанс профилирования ЦП
  • profileEnd: Остановить текущий сеанс профилирования ЦП
  • profiles: Вывести список всех завершённых сеансов профилирования ЦП
  • profiles[n].save(filepath = 'node.cpuprofile'): Сохранить сеанс профилирования ЦП на диск в формате JSON
  • takeHeapSnapshot(filepath = 'node.heapsnapshot'): Создать снимок кучи и сохранить его на диск в формате JSON
Управление выполнением
  • run: Запустить скрипт (запускается автоматически при старте отладчика)
  • restart: Перезапустить скрипт
  • kill: Завершить скрипт
Прочее
  • scripts: Вывести список всех загруженных скриптов
  • version: Показать версию V8

Режим зондирования

Добавлено в версии: v26.1.0
История
Версия Изменения
v26.3.0

Добавлено терминальное событие probe_failure error для ошибок, возникающих на стороне инспектора в ходе сеанса, а также error.details для дополнительного контекста ошибок отдельных попаданий и терминальных ошибок.

v26.2.0

Схема отчёта JSON обновлена до версии 2. Значение target в режиме зондирования теперь является { suffix, line, column? }, а не массивом. Каждое событие «попадания» содержит поле location, указывающее фактическое место вычисления. Если в зонде не указана колонка, он привязывается к первой исполняемой колонке, а не к колонке 1 по умолчанию.

Стабильность: 1 — Экспериментальный

node inspect поддерживает неинтерактивный режим зондирования для проверки значений во время выполнения приложения с помощью флага --probe.

В настоящее время режим зондирования поддерживает только запуск нового процесса из скрипта точки входа, указанного в командной строке.

Режим зондирования устанавливает одну или несколько точек останова в исходном коде, вычисляет заданные выражения при каждом достижении точки останова и по завершении сеанса (при нормальном завершении, ошибке или истечении времени ожидания) выводит итоговый отчёт со всеми вычисленными выражениями. Это позволяет разработчикам выполнять отладку в стиле printf, не изменяя код приложения и не очищая его после этого. Также поддерживается структурированный вывод JSON для использования инструментами.

$ node inspect --probe <file>:<line>[:<col>] --expr <expr>
              [--probe <file>:<line>[:<col>] --expr <expr> ...]
              [--json] [--preview] [--timeout=<ms>] [--port=<port>]
              [--] [<node-option> ...] <script> [<script-args> ...]
consolecopy
  • --probe <file>:<line>[:<col>]: Местоположение зонда в исходном коде. При достижении этого места выполняются указанные выражения, а их результаты выводятся в отчёте. <file> соответствует окончанию URL скрипта, который нужно проверить. Номера <line> и <col> отсчитываются от 1. Если <col> не задан, зонд привязывается к первой исполняемой колонке в строке.
  • --expr <expr>: Выражение JavaScript, которое нужно вычислять при каждом достижении места, заданного предшествующим --probe. Оно должно непосредственно следовать за соответствующим ему --probe.
  • --timeout=<ms>: Глобальный срок выполнения всего сеанса зондирования по часам. Значение по умолчанию — 30000. Это можно использовать для зондирования долго работающего приложения, которое можно завершить извне.
  • --json: Если указан, вместо текстового отчёта по умолчанию выводится структурированный отчёт JSON.
  • --preview: Если указан, для непримитивных значений включаются предварительные данные свойств CDP для JSON-значений зонда, подобных объектам.
  • --port=<port>: Задаёт локальный порт инспектора, на котором будет ожидать подключения сеанс зондирования. По умолчанию используется 0, запрашивающий случайный порт.
  • -- необязателен, если только дочернему процессу не требуются собственные флаги Node.js.

Дополнительные правила для аргументов --probe и --expr:

  • --probe <file>:<line>[:<col>] и --expr <expr> должны использоваться строго парами. За каждым --probe должен непосредственно следовать ровно один --expr.
  • --timeout, --json, --preview и --port являются глобальными параметрами зондирования для всего сеанса. Они могут располагаться до пар зондов или между ними, но не между --probe и соответствующим ему --expr.
  • Если дочернему скрипту нужно передать дополнительные аргументы запуска Node.js, необходимо использовать --, чтобы отделить параметры зондирования от параметров Node.js для дочернего скрипта.

Пример:

$ node inspect --probe app.js:10 --expr "user"
               --probe src/utils.js:5:15 --expr "config.options"
               --json --preview -- --no-warnings app.js --arg-for-app=foo
consolecopy

Формат вывода зондирования

По завершении сеанса зондирования процесс зондирования выводит итоговый отчёт обо всех срабатываниях зонда и результатах.

Рассмотрим следующий скрипт:

// cli.js
let maxRSS = 0;
for (let i = 0; i < 2; i++) {
  const { rss } = process.memoryUsage();
  maxRSS = Math.max(maxRSS, rss);
}
jscopy

По умолчанию, без --json, вывод печатается в удобном для чтения формате:

$ node inspect --probe cli.js:5 --expr 'rss' cli.js
Hit 1 at file:///path/to/cli.js:5:3
  rss = 54935552
Hit 2 at file:///path/to/cli.js:5:3
  rss = 55083008
Completed
consolecopy

Исходное значение <file>:<line>[:<col>], переданное в --probe, может быть разрешено в другое местоположение, чтобы обеспечить возможность приостановки, либо ему могут соответствовать несколько загруженных скриптов, поэтому фактическое место вычисления помогает однозначно интерпретировать результаты.

Примитивные результаты выводятся напрямую, а для объектов и массивов, если они доступны, используются данные предварительного просмотра из протокола Chrome DevTools. Для остальных непримитивных значений используется строка description протокола Chrome DevTools. Ошибки выражений записываются как строки [error] ... и не приводят к сбою всего сеанса. Если требуется более гибкое форматирование текста, оберните выражение в JSON.stringify(...) или util.inspect(...).

При использовании --json вывод имеет следующую структуру:

$ node inspect --json --probe cli.js:5 --expr 'rss' cli.js
{"v":2,"probes":[{"expr":"rss","target":{"suffix":"cli.js","line":5}}],"results":[{"probe":0,"event":"hit","hit":1,"location":{"url":"file:///path/to/cli.js","line":5,"column":3},"result":{"type":"number","value":55443456,"description":"55443456"}},{"probe":0,"event":"hit","hit":2,"location":{"url":"file:///path/to/cli.js","line":5,"column":3},"result":{"type":"number","value":55574528,"description":"55574528"}},{"event":"completed"}]}
consolecopy
{
  "v": 2, // Probe JSON schema version.
  "probes": [
    {
      "expr": "rss", // The expression paired with --probe.
      "target": {
        // The user's probe specification. `suffix` is the raw <file> passed
        // to --probe and is matched as a path-separator-anchored suffix
        // against every loaded script's URL. `column` is present only if the
        // user supplied `:col`. The actual evaluation location may differ
        // from the target and will be reported in each hit's `location` field.
        "suffix": "cli.js",
        "line": 5
      }
    }
  ],
  "results": [
    {
      "probe": 0, // Index into probes[].
      "event": "hit", // Hit events are recorded in observation order.
      "hit": 1, // 1-based hit count for this probe.
      "location": {
        // The actual location where the execution is paused to evaluate
        // the expression of the probe. This may differ from the probe's
        // target due to pausability adjustments or multiple matches.
        "url": "file:///path/to/cli.js",
        "line": 5,
        "column": 3
      },
      "result": {
        "type": "number",
        "value": 55443456,
        "description": "55443456"
      }
      // If the probe expression throws, fails, or never completes, the entry
      // carries an `error` field instead of `result` with the shape
      // `{ message: string, details?: object }`. The `message` and `details`
      // content is informational only and may change between releases.
    },
    {
      "probe": 0,
      "event": "hit",
      "hit": 2,
      "location": { "url": "file:///path/to/cli.js", "line": 5, "column": 3 },
      "result": {
        "type": "number",
        "value": 55574528,
        "description": "55574528"
      }
    },
    {
      "event": "completed"
      // The final entry is always a terminal event, for example:
      // 1. { "event": "completed" }
      // 2. { "event": "miss", "pending": [0, 1] }
      // 3. {
      //      "event": "timeout",
      //      "pending": [0],
      //      "error": {
      //       "code": "probe_timeout",
      //       "message": "Timed out after 30000ms waiting for probes: app.js:10"
      //      }
      //    }
      // 4. {
      //      "event": "error",
      //      "pending": [0],
      //      "error": {
      //       "code": "probe_target_exit",
      //       "exitCode": 1,
      //       "stderr": "Error: boom",
      //       "message": "Target exited with code 1 before probes: app.js:10"
      //      }
      //    }
      // 5. {
      //      "event": "error",
      //      "pending": [1],
      //      "error": {
      //       "code": "probe_failure",
      //       "probe": 0,
      //       "stderr": "...",
      //       "message": "Target process exited during probe evaluation before probes: app.js:12. If the failure repeats, review the probe expression.",
      //       "details": { "lastCdpMethod": "Debugger.evaluateOnCallFrame" }
      //      }
      //    }
    }
  ]
}
jsoncopy

Вывод и коды завершения

В режиме зондирования в stdout выводится только итоговый отчёт зондирования; вывод stdout/stderr дочернего процесса подавляется. По завершении сеанса зондирования процесс зондирования обычно завершается с кодом 0 и выводит итоговый отчёт в stdout. Если дочерний процесс завершается с ненулевым кодом до окончания сеанса зондирования или сеанс не удаётся завершить по другой причине, в итоговом отчёте фиксируется терминальное событие error.

Если значение error.code равно 'probe_failure' или 'probe_timeout', процесс зондирования завершается с ненулевым кодом, что указывает на возможную неполноту записанных срабатываний. В этом случае error.message будет содержать подсказки по восстановлению, а error.probe, если он присутствует, будет индексом в массиве probes отчёта, который в качестве подсказки указывает на возможный проблемный зонд.

Недопустимые аргументы, а также критические ошибки запуска или подключения могут привести к завершению процесса зондирования с ненулевым кодом и выводу сообщения об ошибке в stderr без итогового отчёта зондирования.

Зондирование нескольких выражений в одной точке выполнения

Если несколько пар --probe/--expr имеют одинаковое значение --probe, выражения будут вычислены при одной и той же приостановке в порядке их появления в командной строке.

// app.js
const x = { x: 42 };       // line 2
const y = { y: 35 };       // line 3
const z = { ...x, ...y };  // line 4
jscopy
$ node inspect --probe app.js:4 --expr 'x' --probe app.js:4 --expr 'y' -- app.js
consolecopy

Выводит

Hit 1 at file:///path/to/app.js:4:1
  x = {x: 42}
Hit 1 at file:///path/to/app.js:4:1
  y = {y: 35}
Completed
textcopy
$ node inspect --probe app.js:4 --expr 'x' --probe app.js:4 --expr 'y' --json --preview -- app.js
consolecopy

Выводит

{"v":2,"probes":[{"expr":"x","target":{"suffix":"app.js","line":4}},{"expr":"y","target":{"suffix":"app.js","line":4}}],"results":[{"probe":0,"event":"hit","hit":1,"location":{"url":"file:///path/to/app.js","line":4,"column":1},"result":{"type":"object","description":"Object","preview":{"type":"object","description":"Object","overflow":false,"properties":[{"name":"x","type":"number","value":"42"}]}}},{"probe":1,"event":"hit","hit":1,"location":{"url":"file:///path/to/app.js","line":4,"column":1},"result":{"type":"object","description":"Object","preview":{"type":"object","description":"Object","overflow":false,"properties":[{"name":"y","type":"number","value":"35"}]}}},{"event":"completed"}]}
jsoncopy

Выбор местоположения зонда

При достижении местоположения зонда выражения вычисляются в его лексической области видимости. Не следует зондировать переменную, объявленную с помощью let или const, непосредственно в месте её объявления: это приведёт к ReferenceError из-за обращения к переменной в её временной мёртвой зоне (TDZ).

// app.js
const x = 42;        // line 2
console.log(x);      // line 3
jscopy
$ node inspect --probe app.js:1 --expr 'x' app.js
Hit 1 at file:///path/to/app.js:1:1
  [error] x = ReferenceError: Cannot access 'x' from debugger
  ...
Completed
consolecopy

Вместо этого установите зонд в месте, где переменная уже инициализирована:

$ node inspect --probe app.js:3 --expr 'x' app.js
Hit 1 at file:///path/to/app.js:3:1
  x = 42
Completed
consolecopy

Аргумент <file> сопоставляется с окончанием пути каждого URL загруженного скрипта; границей служит разделитель пути. Если передать только базовое имя файла, ему будут соответствовать все загруженные скрипты с таким именем — аналогично тому, как обычно сопоставляются точки останова в нативных отладчиках. Частичный путь позволяет сузить поиск. Например:

project/
  - src/utils.js
  - lib/utils.js
textcopy

--probe utils.js:10 привязывается к обоим файлам и создаёт по одному срабатыванию для каждого совпадения. Каждое срабатывание содержит собственное поле location, указывающее, где именно было выполнено выражение, что позволяет потребителям точно соотнести результат с одним из двух файлов. Чтобы однозначно определить файл при привязке, укажите более полный путь, соответствующий только нужному файлу:

$ node inspect --probe src/utils.js:10 --expr 'x' main.js   # matches only src/utils.js
consolecopy

Дополнительные сведения об использовании

Интеграция инспектора V8 с Node.js

Интеграция инспектора V8 позволяет подключать Chrome DevTools к экземплярам Node.js для отладки и профилирования. Она использует протокол Chrome DevTools.

Инспектор V8 можно включить, передав флаг --inspect при запуске приложения Node.js. С помощью этого флага также можно указать другой порт, например --inspect=9222 будет принимать подключения DevTools на порту 9222.

Флаг --inspect запускает выполнение кода непосредственно перед подключением отладчика. Это означает, что код начнёт выполняться до того, как вы сможете начать отладку, что может быть нежелательно, если нужно отлаживать программу с самого начала.

В таких случаях можно выбрать один из двух вариантов:

  1. Флаг --inspect-wait: этот флаг приостанавливает выполнение кода, пока не подключится отладчик. Это позволяет начать отладку с самого начала выполнения.
  2. Флаг --inspect-brk: в отличие от --inspect, этот флаг останавливает выполнение на первой строке кода сразу после подключения отладчика. Это полезно, если нужно пошагово отлаживать код с самого начала, не допуская его выполнения до начала отладки.

Поэтому при выборе между --inspect, --inspect-wait и --inspect-brk учитывайте, нужно ли вам немедленно начать выполнение кода, дождаться подключения отладчика или остановиться на первой строке для пошаговой отладки.

$ node --inspect index.js
Debugger listening on ws://127.0.0.1:9229/dc9010dd-f8b8-4ac5-a510-c1a114ec7d29
For help, see: https://nodejs.org/learn/getting-started/debugging
consolecopy

(В приведённом выше примере UUID dc9010dd-f8b8-4ac5-a510-c1a114ec7d29 в конце URL генерируется динамически и меняется от одного сеанса отладки к другому.)

© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/api/debugger.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API