Статьи

Как я исправил анонимный бесконечный цикл в JS Bin

Если вы часто пользуетесь JS Bin, вы могли заметить, что в нечетном случае он был недоступен. У меня есть сигналы тревоги, которые сообщают мне о проблемах, но основная причина была мне неизвестна. Результат выглядел как бесконечный цикл, но как найти , что код был моей миссии.

http://d.pr/i/Tvcn+

Получать помощь

Я несколько раз писал в Твиттере, чтобы попытаться получить некоторую помощь, но на самом деле, 140 символов не могут выразить проблему вообще.

Проблема сводится к следующему: как найти бесконечный цикл в коде на стороне сервера, когда невозможно воспроизвести ошибку. Такие инструменты, как Nodetime и New Relic, не будут работать, потому что код будет зацикливаться, поэтому у этих инструментов не будет свободного места для запроса снимка памяти. Т.е. это сложно!

Наконец, я разместил сообщение в Google Plus (а не здесь, потому что это был скорее простой вопрос), и именно тогда ответил Вячеслав Егоров (который, как я понимаю, работал на движке V8).

Он предложил подключиться к процессу и запустить дамп ядра. В сущности, я не совсем понял, что такое ниндзя-сервер вуду, но он опубликовал указания!

Огромное, огромное спасибо Вячеславу за его решение — какая звезда!

Получение Stacktrace из бесконечного цикла

Вам нужно gdbприсоединиться к процессу, установить точку останова в следующей защите стека времени выполнения (я считаю, что это окно Chrome «kill / wait» ), а затем продолжить выполнение.

Процесс завершается корректно, затем вы вставляете исключение, которое вызывает полную (и супер детализированную) трассировку стека (это в STDERR — или STDOUT — но его нет в gdb, поэтому я нашел его в журналах своего сервера).

Это суть Вячеслава, который показал процесс, и его объяснение (в комментариях к концу):

$ cat test.js
function foo () { while (true) { } }
function bar () { return foo(); }
bar();
$ node test.js &
$ gdb attach $(pidof node)
0x00000bf778c63d5f in ?? ()
(gdb) b v8::internal::Runtime_StackGuard
Breakpoint 1 at 0x84a1f0
(gdb) print 'v8::V8::TerminateExecution'(0)
$2 = 0
(gdb) c
Continuing.

Breakpoint 1, 0x000000000084a1f0 in v8::internal::Runtime_StackGuard(v8::internal::Arguments, v8::internal::Isolate*) ()
(gdb) print V8_Fatal("a", 11, "c")


#
# Fatal error in a, line 11
# c
#


==== Stack trace ============================================

Security context: 0x11330e857229 <JS Object>#0#
    1: foo(aka foo) [/home/mraleph/test.js:~1] (this=0)
    2: bar [/home/mraleph/test.js:2] (this=0x11330e857349 <JS Global Object>#1#)
    3: /* anonymous */ [/home/mraleph/test.js:3] (this=0xb6d64f79f11 <an Object>#2#,exports=0xb6d64f79f11 <an Object>#2#,require=0xb6d64f7c651 <JS Function require>#3#,module=0xb6d64f79e19 <a Module>#4#,__filename=0xb6d64f76a69 <String[38]: /home/mraleph/test.js>,__dirname=0xb6d64f7cfb1 <String[30]: /home/mraleph>)

/// and so forth

Причина в JS Bin

Я нашел очень подробную трассировку стека в моих журналах (в настоящее время я использую Forever с JS Bin в производстве).

Моя трассировка стека началась так:

#
# Fatal error in a, line 11
# c
#


==== Stack trace ============================================

Security context: 0x21783c406b71 <JS Object>#0#
    1: new constructor(aka Token) [/WWW/jsbin/node_modules/stylus/lib/token.js:~22] (this=0x1bf223f440d1 <a Token>#1#,type=0x2cb2b9cfbc39 <String[3]: eos>,val=0x21783c404121 <undefined>)
    2: arguments adaptor frame: 1->2
    4: eos [/WWW/jsbin/node_modules/stylus/lib/lexer.js:216] (this=0x1bdbdd256941 <a Lexer>#2#)
    5: advance [/WWW/jsbin/node_modules/stylus/lib/lexer.js:~155] (this=0x1bdbdd256941 <a Lexer>#2#)

… и, как я подозревал, он был связан с процессором (в данном случае Stylus).

Прокручивая дальше по трассировке стека, я также обнаружил заголовок «Ключ», как мне повезло, в следующей записи:

str: 0x25b2767e3809 <String[22]\: h1{color:red;}\n\nblink{>

Затем, далее, я нашел строку SQL, которая привела бы меня к корзине, которая могла бы последовательно воспроизвести ошибку:

sql: 0x25b2767e6d29 <String[280]: UPDATE `sandbox` SET `css`='h1{color:red;}\n\nblink{', `settings`='{\"processors\":{\"css\":\"stylus\"}}', `created`='2013-09-11T09:27:12.613Z' WHERE `url`='aHOVoMe' AND `revision`='2' AND `streaming_key`='8c47219c1d3f1e25c368fa82687b00f2' AND `streaming_key`!='' AND `active`='y'>

Итак, я знал, что процессор — это Stylus, и панель CSS будет аварийно завершать работу, если он будет содержать, довольно просто:

blink {

Поэтому я исправил JS Bin, удалив поддержку Stylus (которая в любом случае оказалась не особенно популярной), и исправлю ее должным образом, создав процесс-демон для обработки процессоров (и добавив поддержку SASS).

Хотя интересный урок состоит в том, как обнаружить супер хитрые ошибки в Node.