Если вы часто пользуетесь JS Bin, вы могли заметить, что в нечетном случае он был недоступен. У меня есть сигналы тревоги, которые сообщают мне о проблемах, но основная причина была мне неизвестна. Результат выглядел как бесконечный цикл, но как найти , что код был моей миссии.
Получать помощь
Я несколько раз писал в Твиттере, чтобы попытаться получить некоторую помощь, но на самом деле, 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.