Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include debug_helper to llnode #571

Closed
kvakil opened this issue Jul 17, 2022 · 21 comments
Closed

Include debug_helper to llnode #571

kvakil opened this issue Jul 17, 2022 · 21 comments

Comments

@kvakil
Copy link

kvakil commented Jul 17, 2022

llnode was a great tool to have. Unfortunately it is currently
unmaintained and so it has been removed from the diagnostic tooling
list, see nodejs/node#43289.

Why llnode is good

  1. V8 heapdumps are slow. It traverses every live object and
    constructs the graph. In my experiences, heapdumps are often much
    slower than a coredump, making them unusable for live production
    debugging.

  2. V8 heapdumps do not provide visibility into external memory usage.

  3. To some extent, diagnostic report has subsumed the need for llnode
    postmortem debugging. However, the ability to inspect all active
    objects and state is still more powerful than what diagnostic report can
    feasibly provide.

Problems with llnode now

  1. llnode relies heavily on V8 internals, which means that it breaks
    every once in a while. See Not showing details of JS frames on node v16.14.0 llnode#399 for the most recent
    example. Resolving these issues is hairy; it essentially requires
    somebody who understands the V8 change and can replicate the change for
    llnode.

  2. llnode works by walking every byte in the process space and looking
    for things which look like V8 objects. In addition to often producing
    false positives ("ghost objects"), this is extremely slow for
    larger heap dumps.

Alternative proposal

I do not know if llnode is actually something that needs to be
maintained. There seems to be a decent amount of community interest in
it based on the Github repo, but perhaps those people would be better
served by diagnostic report anyway. Perhaps somebody should try to
figure out why people are still using llnode?

If an llnode-alternative is desirable, I think it would make the most
sense to build it on top of V8's debug_helper library, which is what
v8windbg uses. The debug module includes GetStackFrame and
GetObjectProperties functions. Presumably V8 would be receptive to
patches to implement other functionality.

I tried to write a gdb extension which used debug_helper.
Unfortunately it did not seem to work that well and gave some wrong
results; it's possible that debug_helper does not work with Node's
configuration, or that I just didn't work out the build system correctly.

@Qard
Copy link
Member

Qard commented Jul 17, 2022

I still use llnode constantly, even in its relatively broken state. I would like to find time to contribute to it, but it's been difficult being a one person team currently. 😐

@kvakil
Copy link
Author

kvakil commented Jul 17, 2022

I still use llnode constantly, even in its relatively broken state. I would like to find time to contribute to it, but it's been difficult being a one person team currently. neutral_face

Thanks for the input! I'd love to hear a little about why you use llnode
so frequently & what gaps it fills; I can work that into the "Why llnode
is good" section.

Also, which features do you tend to use? I am curious if there is a
desire for Node-specific functionality, or if something simple
implemented atop debug_helper (v8 bt + v8 inspect) would suffice.

@tony-go
Copy link
Member

tony-go commented Jul 18, 2022

I'd like to help too. My concern is more regarding my knowledge in the field than the bandwidth ^^

But maybe could we write down a list of things that could address the broken state of the project and see how could we improve it incrementally? Maybe with small/scoped tasks, it could be easier to tackle them? (even for me, then I could help)

Also, note that during our last meeting we mention the idea to find a new principal maintainer for the project.

@RaisinTen
Copy link

If an llnode-alternative is desirable

I'm not sure about the limitations or how often it's used but there is lldb_commands.py from V8. It can be used to get the javascript stack trace like this:

$ lldb --one-line "command script import deps/v8/tools/lldb_commands.py" -- node
> run
  ...
> jst # javascript stack trace
  ...
$ lldb --one-line "command script import node/deps/v8/tools/lldb_commands.py" -- $(command -v node) --eval 'process.abort()'
(lldb) target create "/usr/local/bin/node"
Current executable set to '/usr/local/bin/node' (x86_64).
(lldb) settings set -- target.run-args  "--eval" "process.abort()"
(lldb) command script import node/deps/v8/tools/lldb_commands.py
(lldb) run
Process 19660 launched: '/usr/local/bin/node' (x86_64)
 1: 0x1013ddef5 node::Abort() (.cold.1) [/usr/local/bin/node]
 2: 0x1000d1869 node::Abort() [/usr/local/bin/node]
 3: 0x1001437a9 node::Abort(v8::FunctionCallbackInfo<v8::Value> const&) [/usr/local/bin/node]
 4: 0x1002bc939 v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) [/usr/local/bin/node]
 5: 0x1002bc406 v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) [/usr/local/bin/node]
 6: 0x1002bbb7f v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [/usr/local/bin/node]
 7: 0x100b2f079 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit [/usr/local/bin/node]
Process 19660 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x00007ff80f42800e libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
->  0x7ff80f42800e <+10>: jae    0x7ff80f428018            ; <+20>
    0x7ff80f428010 <+12>: mov    rdi, rax
    0x7ff80f428013 <+15>: jmp    0x7ff80f4221c5            ; cerror_nocancel
    0x7ff80f428018 <+20>: ret
Target 0: (node) stopped.
(lldb) jst

==== JS stack trace =========================================

Security context: 0x29ef686a7fb1 <JSGlobalObject>#0#
    0: builtin exit frame: abort(this=0x29ef68682729 <process map = 0x29ef00b81479>#1#,0x29ef68682729 <process map = 0x29ef00b81479>#1#)

    1: /* anonymous */ [0x29ef268d9da1] [[eval]:1] [bytecode=0x29ef268d9859 offset=9](this=0x29ef68681119 <JSGlobalProxy>#2#)
    2: InternalFrame [pc: 0x100abfc78]
    3: EntryFrame [pc: 0x100abfa03]
    4: builtin exit frame: runInThisContext(this=0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#,0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#,-1,0x29efa4d01709 <true>,0x29efa4d01769 <false>,0x29efa4d01769 <false>)

    5: runInThisContext [0x29efbc5161b1] [node:vm:129] [bytecode=0x29ef268d9991 offset=93](this=0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#,0x29ef5944b4f1 <Object map = 0x29ef00bb4971>#4#)
    6: runInThisContext [0x29efbc516369] [node:vm:305] [bytecode=0x29ef268d8dc9 offset=36](this=0x29efbc515c01 <Object map = 0x29ef00b8f141>#5#,0x29eff200aa19 <String[15]: "process.abort()">,0x29ef5944b4f1 <Object map = 0x29ef00bb4971>#4#)
    7: /* anonymous */(aka /* anonymous */) [0x29ef5944b4b9] [node:internal/process/execution:76] [bytecode=0x29ef268d8c31 offset=59](this=0x29efa4d01599 <undefined>)
    8: /* anonymous */ [0x29ef5944b481] [[eval]-wrapper:6] [bytecode=0x29ef268d8a99 offset=0](this=0x29ef68681119 <JSGlobalProxy>#2#,0x29ef5944b4b9 <JSFunction (sfi = 0x29ef268cdee1)>#6#)
    9: evalScript(aka evalScript) [0x29efbc50ebb1] [node:internal/process/execution:75] [bytecode=0x29ef268cdff9 offset=225](this=0x29efa4d01599 <undefined>,0x29efc2382ab9 <String[6]: #[eval]>,0x29eff200aa19 <String[15]: "process.abort()">,0x29efa4d01769 <false>,0x29efa4d01769 <false>)
   10: /* anonymous */(aka /* anonymous */) [0x29eff20058d1] [node:internal/main/eval_string:27] [bytecode=0x29efc2382d21 offset=131](this=0x29efa4d01599 <undefined>,0x29ef68682729 <process map = 0x29ef00b81479>#1#,0x29ef68683489 <JSFunction nativeModuleRequire (sfi = 0x29efc73cfa09)>#7#,0x29ef68683a49 <JSFunction internalBinding (sfi = 0x29efc73cf489)>#8#,0x29efbc519d81 <Object map = 0x29ef00ba4b31>#9#,0x29efc23825d9 <JSFunction (sfi = 0x29efc23825a1)>#10#)
   11: InternalFrame [pc: 0x100abfc78]
   12: EntryFrame [pc: 0x100abfa03]

==== Details ================================================

[0]: builtin exit frame: abort(this=0x29ef68682729 <process map = 0x29ef00b81479>#1#,0x29ef68682729 <process map = 0x29ef00b81479>#1#)

[1]: /* anonymous */ [0x29ef268d9da1] [[eval]:1] [bytecode=0x29ef268d9859 offset=9](this=0x29ef68681119 <JSGlobalProxy>#2#) {
  // expression stack (top to bottom)
  [07] : 0x29efa4d01599 <undefined>
  [06] : 0x29ef6868a5d1 <JSFunction abort (sfi = 0x29efc3fef389)>#11#
  [05] : 5
  [04] : 0x29efa4d01669 <the_hole>
  [03] : 0x29ef68682729 <process map = 0x29ef00b81479>#1#
  [02] : 0x29ef68682729 <process map = 0x29ef00b81479>#1#
  [01] : 0x29ef6868a5d1 <JSFunction abort (sfi = 0x29efc3fef389)>#11#
  [00] : 0x29efa4d01599 <undefined>
--------- s o u r c e   c o d e ---------
process.abort()
-----------------------------------------
}

[2]: InternalFrame [pc: 0x100abfc78]
[3]: EntryFrame [pc: 0x100abfa03]
[4]: builtin exit frame: runInThisContext(this=0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#,0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#,-1,0x29efa4d01709 <true>,0x29efa4d01769 <false>,0x29efa4d01769 <false>)

[5]: runInThisContext [0x29efbc5161b1] [node:vm:129] [bytecode=0x29ef268d9991 offset=93](this=0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#,0x29ef5944b4f1 <Object map = 0x29ef00bb4971>#4#) {
  // expression stack (top to bottom)
  [14] : 0x29efa4d01599 <undefined>
  [13] : 0x29efbb04c1b1 <JSFunction runInThisContext (sfi = 0x29efc3fe0641)>#12#
  [12] : 9
  [11] : 0x29efa4d01669 <the_hole>
  [10] : 0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#
  [09] : -1
  [08] : 0x29efa4d01709 <true>
  [07] : 0x29efa4d01769 <false>
  [06] : 0x29efa4d01769 <false>
  [05] : 0x29ef5944c6e9 <JSArray[4]>#13#
  [04] : 0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#
  [03] : 0x29efbb04c1b1 <JSFunction runInThisContext (sfi = 0x29efc3fe0641)>#12#
  [02] : 0x29ef686841a1 <JSFunction apply (sfi = 0x29efc3fe3951)>#14#
  [01] : 0x29ef5944c6e9 <JSArray[4]>#13#
  [00] : 0x29efa4d01769 <false>
--------- s o u r c e   c o d e ---------
function runInThisContext(options) {\x0a    const { breakOnSigint, args } = getRunInContextArgs(options);\x0a    if (breakOnSigint && process.listenerCount('SIGINT') > 0) {\x0a      return sigintHandlersWrap(super.runInThisContext, this, args);\x0a    }\x0a    return ReflectApply(super.runInThisContext, this, args);\x0a  }
-----------------------------------------
}

[6]: runInThisContext [0x29efbc516369] [node:vm:305] [bytecode=0x29ef268d8dc9 offset=36](this=0x29efbc515c01 <Object map = 0x29ef00b8f141>#5#,0x29eff200aa19 <String[15]: "process.abort()">,0x29ef5944b4f1 <Object map = 0x29ef00bb4971>#4#) {
  // expression stack (top to bottom)
  [03] : 0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#
  [02] : 0x29ef5944b4f1 <Object map = 0x29ef00bb4971>#4#
  [01] : 0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>#3#
  [00] : 0x29efbc5161b1 <JSFunction runInThisContext (sfi = 0x29efc3fcb1c1)>#15#
--------- s o u r c e   c o d e ---------
function runInThisContext(code, options) {\x0a  if (typeof options === 'string') {\x0a    options = { filename: options };\x0a  }\x0a  return createScript(code, options).runInThisContext(options);\x0a}
-----------------------------------------
}

[7]: /* anonymous */(aka /* anonymous */) [0x29ef5944b4b9] [node:internal/process/execution:76] [bytecode=0x29ef268d8c31 offset=59](this=0x29efa4d01599 <undefined>) {
  // expression stack (top to bottom)
  [07] : 0x29efbc515c01 <Object map = 0x29ef00b8f141>#5#
  [06] : 0x29eff200aa19 <String[15]: "process.abort()">
  [05] : 0x29ef5944b4f1 <Object map = 0x29ef00bb4971>#4#
  [04] : 0x29ef268c4681 <String[23]: #importModuleDynamically>
  [03] : 0x29ef5944b4f1 <Object map = 0x29ef00bb4971>#4#
  [02] : 0x29eff200aa19 <String[15]: "process.abort()">
  [01] : 0x29efbc515c01 <Object map = 0x29ef00b8f141>#5#
  [00] : 0x29efbc516369 <JSFunction runInThisContext (sfi = 0x29efc3fcb5e1)>#16#
--------- s o u r c e   c o d e ---------
function () =>\x0a    require('vm').runInThisContext(body, {\x0a      filename: name,\x0a      displayErrors: true,\x0a      [kVmBreakFirstLineSymbol]: !!breakFirstLine,\x0a      importModuleDynamically(specifier, _, importAssertions) {\x0a        const loader = asyncESM.esmLoader;\x0a        return loader.import(specifier, base...

-----------------------------------------
}

[8]: /* anonymous */ [0x29ef5944b481] [[eval]-wrapper:6] [bytecode=0x29ef268d8a99 offset=0](this=0x29ef68681119 <JSGlobalProxy>#2#,0x29ef5944b4b9 <JSFunction (sfi = 0x29ef268cdee1)>#6#) {
  // expression stack (top to bottom)
  [00] : 0x29efa4d01599 <undefined>
--------- s o u r c e   c o d e ---------
function (main) => main()
-----------------------------------------
}

[9]: evalScript(aka evalScript) [0x29efbc50ebb1] [node:internal/process/execution:75] [bytecode=0x29ef268cdff9 offset=225](this=0x29efa4d01599 <undefined>,0x29efc2382ab9 <String[6]: #[eval]>,0x29eff200aa19 <String[15]: "process.abort()">,0x29efa4d01769 <false>,0x29efa4d01769 <false>) {
  // heap-allocated locals
  var breakFirstLine = 0x29efa4d01769 <false>
  var body = 0x29eff200aa19 <String[15]: "process.abort()">
  var name = 0x29efc2382ab9 <String[6]: #[eval]>
  var kVmBreakFirstLineSymbol = 0x29efc3ff9f89 <Symbol: kVmBreakFirstLineSymbol>#17#
  var asyncESM = 0x29efbc50ea21 <Object map = 0x29ef00bb2de1>#18#
  var baseUrl = 0x29ef5944a829 <String[42]: c"file:///Users/raisinten/Desktop/git/[eval]">
  // expression stack (top to bottom)
  [15] : 0x29ef68681119 <JSGlobalProxy>#2#
  [14] : 0x29ef5944b4b9 <JSFunction (sfi = 0x29ef268cdee1)>#6#
  [13] : 0x29ef5944a849 <String[14]: c"[eval]-wrapper">
  [12] : 0x29efa4d01599 <undefined>
  [11] : 0x29ef68688131 <Object map = 0x29ef00b84719>#19#
  [10] : 0x29ef5944b4b9 <JSFunction (sfi = 0x29ef268cdee1)>#6#
  [09] : 0x29ef5944b481 <JSFunction (sfi = 0x29ef268d8039)>#20#
  [08] : 0x29ef68688069 <FunctionContext[21]>#21#
  [07] : 0x29efa4d01599 <undefined>
  [06] : 0x29efa4d01599 <undefined>
  [05] : 0x29ef268cdcb9 <String[170]: #\n    globalThis.module = module;\n    globalThis.exports = exports;\n    globalThis.__dirname = __dirname;\n    globalThis.require = require;\n    return (main) => main();\n  >
  [04] : 0x29ef59448e09 <Module map = 0x29ef00bb3e79>#22#
  [03] : 0x29eff2017d71 <JSFunction Module (sfi = 0x29efc23965e1)>#23#
  [02] : 0x29ef59446e31 <String[28]: "/Users/raisinten/Desktop/git">
  [01] : 0x29efbc510b01 <JSFunction pathToFileURL (sfi = 0x29ef4afad161)>#24#
  [00] : 0x29eff2017d71 <JSFunction Module (sfi = 0x29efc23965e1)>#23#
--------- s o u r c e   c o d e ---------
function evalScript(name, body, breakFirstLine, print) {\x0a  const CJSModule = require('internal/modules/cjs/loader').Module;\x0a  const { kVmBreakFirstLineSymbol } = require('internal/util');\x0a  const { pathToFileURL } = require('url');\x0a\x0a  const cwd = tryGetCwd();\x0a  const origModule = globalThis.module;  // Set e.g. when c...

-----------------------------------------
}

[10]: /* anonymous */(aka /* anonymous */) [0x29eff20058d1] [node:internal/main/eval_string:27] [bytecode=0x29efc2382d21 offset=131](this=0x29efa4d01599 <undefined>,0x29ef68682729 <process map = 0x29ef00b81479>#1#,0x29ef68683489 <JSFunction nativeModuleRequire (sfi = 0x29efc73cfa09)>#7#,0x29ef68683a49 <JSFunction internalBinding (sfi = 0x29efc73cf489)>#8#,0x29efbc519d81 <Object map = 0x29ef00ba4b31>#9#,0x29efc23825d9 <JSFunction (sfi = 0x29efc23825a1)>#10#) {
  // expression stack (top to bottom)
  [17] : 0x29efa4d01599 <undefined>
  [16] : 0x29efc2382ab9 <String[6]: #[eval]>
  [15] : 0x29eff200aa19 <String[15]: "process.abort()">
  [14] : 0x29efa4d01769 <false>
  [13] : 0x29efa4d01769 <false>
  [12] : 0x29efa4d01769 <false>
  [11] : 0x29efa4d01769 <false>
  [10] : 0x29eff200aa19 <String[15]: "process.abort()">
  [09] : 0x29efc2382ab9 <String[6]: #[eval]>
  [08] : 0x29efa4d017b1 <String[0]: #>
  [07] : 0x29efa4d01769 <false>
  [06] : 0x29eff200aa19 <String[15]: "process.abort()">
  [05] : 0x29efbc50d921 <JSFunction getOptionValue (sfi = 0x29efc3fd47e1)>#25#
  [04] : 0x29eff2006f81 <JSFunction addBuiltinLibsToObject (sfi = 0x29efc2386199)>#26#
  [03] : 0x29efbc50ebb1 <JSFunction evalScript (sfi = 0x29efcf917301)>#27#
  [02] : 0x29efbc50eb59 <JSFunction evalModule (sfi = 0x29efcf917291)>#28#
  [01] : 0x29eff2005be1 <JSFunction prepareMainThreadExecution (sfi = 0x29efc2384151)>#29#
  [00] : 0x29ef68681119 <JSGlobalProxy>#2#
--------- s o u r c e   c o d e ---------
function 'use strict';\x0a\x0a// User passed `-e` or `--eval` arguments to Node without `-i` or\x0a// `--interactive`.\x0a\x0aconst {\x0a  globalThis,\x0a} = primordials;\x0a\x0aconst {\x0a  prepareMainThreadExecution\x0a} = require('internal/bootstrap/pre_execution');\x0aconst { evalModule, evalScript } = require('internal/process/execution')...

-----------------------------------------
}

[11]: InternalFrame [pc: 0x100abfc78]
[12]: EntryFrame [pc: 0x100abfa03]
-- ObjectCacheKey --

 #0# 0x29ef686a7fb1: 0x29ef686a7fb1 <JSGlobalObject>
 #1# 0x29ef68682729: 0x29ef68682729 <process map = 0x29ef00b81479>
 #2# 0x29ef68681119: 0x29ef68681119 <JSGlobalProxy>
 #3# 0x29ef5944b869: 0x29ef5944b869 <ContextifyScript map = 0x29ef00bb49b9>
 #4# 0x29ef5944b4f1: 0x29ef5944b4f1 <Object map = 0x29ef00bb4971>
          filename: 0x29efc2382ab9 <String[6]: #[eval]>
     displayErrors: 0x29efa4d01709 <true>
importModuleDynamically: 0x29ef5944b601 <JSFunction importModuleDynamically (sfi = 0x29ef268d8b59)>#30#
 #5# 0x29efbc515c01: 0x29efbc515c01 <Object map = 0x29ef00b8f141>
            Script: 0x29efbc515c61 <JSFunction Script (sfi = 0x29efc3fcabd9)>#31#
     createContext: 0x29efbc515f99 <JSFunction createContext (sfi = 0x29efc3fcb451)>#32#
      createScript: 0x29efbc515ff1 <JSFunction createScript (sfi = 0x29efc3fcb4a1)>#33#
      runInContext: 0x29efbc5162b9 <JSFunction runInContext (sfi = 0x29efc3fcb541)>#34#
   runInNewContext: 0x29efbc516311 <JSFunction runInNewContext (sfi = 0x29efc3fcb591)>#35#
  runInThisContext: 0x29efbc516369 <JSFunction runInThisContext (sfi = 0x29efc3fcb5e1)>#16#
         isContext: 0x29efbc515f41 <JSFunction isContext (sfi = 0x29efc3fcb401)>#36#
   compileFunction: 0x29efbc5163c1 <JSFunction compileFunction (sfi = 0x29efc3fcb631)>#37#
     measureMemory: 0x29efbc516419 <JSFunction measureMemory (sfi = 0x29efc3fcb681)>#38#
 #6# 0x29ef5944b4b9: 0x29ef5944b4b9 <JSFunction (sfi = 0x29ef268cdee1)>
 #7# 0x29ef68683489: 0x29ef68683489 <JSFunction nativeModuleRequire (sfi = 0x29efc73cfa09)>
 #8# 0x29ef68683a49: 0x29ef68683a49 <JSFunction internalBinding (sfi = 0x29efc73cf489)>
 #9# 0x29efbc519d81: 0x29efbc519d81 <Object map = 0x29ef00ba4b31>
 #10# 0x29efc23825d9: 0x29efc23825d9 <JSFunction (sfi = 0x29efc23825a1)>
 #11# 0x29ef6868a5d1: 0x29ef6868a5d1 <JSFunction abort (sfi = 0x29efc3fef389)>
 #12# 0x29efbb04c1b1: 0x29efbb04c1b1 <JSFunction runInThisContext (sfi = 0x29efc3fe0641)>
 #13# 0x29ef5944c6e9: 0x29ef5944c6e9 <JSArray[4]>
                 0: -1
                 1: 0x29efa4d01709 <true>
                 2: 0x29efa4d01769 <false>
                 3: 0x29efa4d01769 <false>
 #14# 0x29ef686841a1: 0x29ef686841a1 <JSFunction apply (sfi = 0x29efc3fe3951)>
 #15# 0x29efbc5161b1: 0x29efbc5161b1 <JSFunction runInThisContext (sfi = 0x29efc3fcb1c1)>
 #16# 0x29efbc516369: 0x29efbc516369 <JSFunction runInThisContext (sfi = 0x29efc3fcb5e1)>
 #17# 0x29efc3ff9f89: 0x29efc3ff9f89 <Symbol: kVmBreakFirstLineSymbol>
 #18# 0x29efbc50ea21: 0x29efbc50ea21 <Object map = 0x29ef00bb2de1>
initializeImportMetaObject: 0x29ef268c8f99 <JSFunction exports.initializeImportMetaObject (sfi = 0x29efc239a019)>#39#
importModuleDynamicallyCallback: 0x29ef268c9349 <JSFunction importModuleDynamicallyCallback (sfi = 0x29efc239a0f1)>#40#
         esmLoader: 0x29eff2037959 <ESMLoader map = 0x29ef00bb2d51>#41#
           loadESM: 0x29ef268c9559 <JSFunction loadESM (sfi = 0x29efc239a171)>#42#
 #19# 0x29ef68688131: 0x29ef68688131 <Object map = 0x29ef00b84719>
           resolve: 0x29ef68688221 <JSFunction resolve (sfi = 0x29ef4afba549)>#43#
         normalize: 0x29ef68688271 <JSFunction normalize (sfi = 0x29ef4afba599)>#44#
        isAbsolute: 0x29ef686882c1 <JSFunction isAbsolute (sfi = 0x29ef4afba5e9)>#45#
              join: 0x29ef68688311 <JSFunction join (sfi = 0x29ef4afba639)>#46#
          relative: 0x29ef68688361 <JSFunction relative (sfi = 0x29ef4afba689)>#47#
  toNamespacedPath: 0x29ef686881e9 <JSFunction toNamespacedPath (sfi = 0x29ef4afba6d9)>#48#
           dirname: 0x29ef686883b1 <JSFunction dirname (sfi = 0x29ef4afba729)>#49#
          basename: 0x29ef68688401 <JSFunction basename (sfi = 0x29ef4afba779)>#50#
           extname: 0x29ef68688451 <JSFunction extname (sfi = 0x29ef4afba7c9)>#51#
            format: 0x29ef686884a1 <JSBoundFunction (BoundTargetFunction 0x29ef686884d1)>#52#
             parse: 0x29ef68688529 <JSFunction parse (sfi = 0x29ef4afba819)>#53#
               sep: 0x29efc73c21d9 <String[1]: #/>
         delimiter: 0x29efc3fede51 <String[1]: #:>
             win32: 0x29ef68688579 <Object map = 0x29ef00b84719>#54#
             posix: 0x29ef68688131 <Object map = 0x29ef00b84719>#19#
         _makeLong: 0x29ef686881e9 <JSFunction toNamespacedPath (sfi = 0x29ef4afba6d9)>#48#
 #20# 0x29ef5944b481: 0x29ef5944b481 <JSFunction (sfi = 0x29ef268d8039)>
 #21# 0x29ef68688069: 0x29ef68688069 <FunctionContext[21]>
 #22# 0x29ef59448e09: 0x29ef59448e09 <Module map = 0x29ef00bb3e79>
                id: 0x29efc2382ab9 <String[6]: #[eval]>
              path: 0x29efa4d047f1 <String[1]: #.>
           exports: 0x29ef59448e91 <Object map = 0x29ef00b82679>#55#
          filename: 0x29ef59449101 <String[35]: c"/Users/raisinten/Desktop/git/[eval]">
            loaded: 0x29efa4d01769 <false>
          children: 0x29ef59448ec9 <JSArray[0]>#56#
             paths: 0x29ef594492e1 <JSArray[5]>#57#
 #23# 0x29eff2017d71: 0x29eff2017d71 <JSFunction Module (sfi = 0x29efc23965e1)>
    builtinModules: 0x29eff2038531 <JSArray[65]>#58#
            _cache: 0x29ef59441251 <Object map = 0x29ef00b81749>#59#
        _pathCache: 0x29ef59441391 <Object map = 0x29ef00b81749>#60#
       _extensions: 0x29ef594414e9 <Object map = 0x29ef00b81749>#61#
       globalPaths: 0x29ef59443b79 <JSArray[3]>#62#
            _debug: 0x29ef59441ff1 <JSFunction deprecated (sfi = 0x29ef80a81bb9)>#63#
         _findPath: 0x29ef268c9c71 <JSFunction Module._findPath (sfi = 0x29efc2396d01)>#64#
  _nodeModulePaths: 0x29ef268c9cb1 <JSFunction Module._nodeModulePaths (sfi = 0x29efc2396e41)>#65#
_resolveLookupPaths: 0x29ef268c9cf1 <JSFunction Module._resolveLookupPaths (sfi = 0x29efc2396ed1)>#66#
             _load: 0x29ef268c9d91 <JSFunction Module._load (sfi = 0x29efc23970c9)>#67#
  _resolveFilename: 0x29ef268c9dd1 <JSFunction Module._resolveFilename (sfi = 0x29efc2397139)>#68#
     createRequire: 0x29eff2018231 <JSFunction createRequire (sfi = 0x29efc2397759)>#69#
        _initPaths: 0x29ef268c9f91 <JSFunction Module._initPaths (sfi = 0x29efc23977c9)>#70#
   _preloadModules: 0x29ef268c9fd1 <JSFunction Module._preloadModules (sfi = 0x29efc2397859)>#71#
syncBuiltinESMExports: 0x29ef268ca011 <JSFunction syncBuiltinESMExports (sfi = 0x29efc2397909)>#72#
            Module: 0x29eff2017d71 <JSFunction Module (sfi = 0x29efc23965e1)>#23#
           runMain: 0x29ef59443e71 <JSFunction executeUserEntryPoint (sfi = 0x29ef268cbb91)>#73#
     findSourceMap: 0x29eff2016121 <JSFunction findSourceMap (sfi = 0x29efc2390d69)>#74#
         SourceMap: 0x29ef59447b81 <JSFunction SourceMap (sfi = 0x29ef268d19b9)>#75#
 #24# 0x29efbc510b01: 0x29efbc510b01 <JSFunction pathToFileURL (sfi = 0x29ef4afad161)>
 #25# 0x29efbc50d921: 0x29efbc50d921 <JSFunction getOptionValue (sfi = 0x29efc3fd47e1)>
 #26# 0x29eff2006f81: 0x29eff2006f81 <JSFunction addBuiltinLibsToObject (sfi = 0x29efc2386199)>
 #27# 0x29efbc50ebb1: 0x29efbc50ebb1 <JSFunction evalScript (sfi = 0x29efcf917301)>
 #28# 0x29efbc50eb59: 0x29efbc50eb59 <JSFunction evalModule (sfi = 0x29efcf917291)>
 #29# 0x29eff2005be1: 0x29eff2005be1 <JSFunction prepareMainThreadExecution (sfi = 0x29efc2384151)>
 #30# 0x29ef5944b601: 0x29ef5944b601 <JSFunction importModuleDynamically (sfi = 0x29ef268d8b59)>
 #31# 0x29efbc515c61: 0x29efbc515c61 <JSFunction Script (sfi = 0x29efc3fcabd9)>
 #32# 0x29efbc515f99: 0x29efbc515f99 <JSFunction createContext (sfi = 0x29efc3fcb451)>
 #33# 0x29efbc515ff1: 0x29efbc515ff1 <JSFunction createScript (sfi = 0x29efc3fcb4a1)>
 #34# 0x29efbc5162b9: 0x29efbc5162b9 <JSFunction runInContext (sfi = 0x29efc3fcb541)>
 #35# 0x29efbc516311: 0x29efbc516311 <JSFunction runInNewContext (sfi = 0x29efc3fcb591)>
 #36# 0x29efbc515f41: 0x29efbc515f41 <JSFunction isContext (sfi = 0x29efc3fcb401)>
 #37# 0x29efbc5163c1: 0x29efbc5163c1 <JSFunction compileFunction (sfi = 0x29efc3fcb631)>
 #38# 0x29efbc516419: 0x29efbc516419 <JSFunction measureMemory (sfi = 0x29efc3fcb681)>
 #39# 0x29ef268c8f99: 0x29ef268c8f99 <JSFunction exports.initializeImportMetaObject (sfi = 0x29efc239a019)>
 #40# 0x29ef268c9349: 0x29ef268c9349 <JSFunction importModuleDynamicallyCallback (sfi = 0x29efc239a0f1)>
 #41# 0x29eff2037959: 0x29eff2037959 <ESMLoader map = 0x29ef00bb2d51>
          cjsCache: 0x29eff2037c61 <JSWeakMap>#76#
         evalIndex: 0
         moduleMap: 0x29eff2038009 <Map map = 0x29ef00bb2c79>#77#
       translators: 0x29eff2035819 <Map map = 0x29ef00b82121>#78#
 #42# 0x29ef268c9559: 0x29ef268c9559 <JSFunction loadESM (sfi = 0x29efc239a171)>
 #43# 0x29ef68688221: 0x29ef68688221 <JSFunction resolve (sfi = 0x29ef4afba549)>
 #44# 0x29ef68688271: 0x29ef68688271 <JSFunction normalize (sfi = 0x29ef4afba599)>
 #45# 0x29ef686882c1: 0x29ef686882c1 <JSFunction isAbsolute (sfi = 0x29ef4afba5e9)>
 #46# 0x29ef68688311: 0x29ef68688311 <JSFunction join (sfi = 0x29ef4afba639)>
 #47# 0x29ef68688361: 0x29ef68688361 <JSFunction relative (sfi = 0x29ef4afba689)>
 #48# 0x29ef686881e9: 0x29ef686881e9 <JSFunction toNamespacedPath (sfi = 0x29ef4afba6d9)>
 #49# 0x29ef686883b1: 0x29ef686883b1 <JSFunction dirname (sfi = 0x29ef4afba729)>
 #50# 0x29ef68688401: 0x29ef68688401 <JSFunction basename (sfi = 0x29ef4afba779)>
 #51# 0x29ef68688451: 0x29ef68688451 <JSFunction extname (sfi = 0x29ef4afba7c9)>
 #52# 0x29ef686884a1: 0x29ef686884a1 <JSBoundFunction (BoundTargetFunction 0x29ef686884d1)>
 #53# 0x29ef68688529: 0x29ef68688529 <JSFunction parse (sfi = 0x29ef4afba819)>
 #54# 0x29ef68688579: 0x29ef68688579 <Object map = 0x29ef00b84719>
           resolve: 0x29ef68688631 <JSFunction resolve (sfi = 0x29ef4afba119)>#79#
         normalize: 0x29ef68688669 <JSFunction normalize (sfi = 0x29ef4afba169)>#80#
        isAbsolute: 0x29ef686886a1 <JSFunction isAbsolute (sfi = 0x29ef4afba1d9)>#81#
              join: 0x29ef686886d9 <JSFunction join (sfi = 0x29ef4afba249)>#82#
          relative: 0x29ef68688711 <JSFunction relative (sfi = 0x29ef4afba299)>#83#
  toNamespacedPath: 0x29efbb05d321 <JSFunction toNamespacedPath (sfi = 0x29ef4afba301)>#84#
           dirname: 0x29ef68688749 <JSFunction dirname (sfi = 0x29ef4afba371)>#85#
          basename: 0x29ef68688781 <JSFunction basename (sfi = 0x29ef4afba3d9)>#86#
           extname: 0x29ef686887b9 <JSFunction extname (sfi = 0x29ef4afba441)>#87#
            format: 0x29ef686887f1 <JSBoundFunction (BoundTargetFunction 0x29ef686884d1)>#88#
             parse: 0x29ef68688821 <JSFunction parse (sfi = 0x29ef4afba4a9)>#89#
               sep: 0x29efc73c23b9 <String[1]: #\\>
         delimiter: 0x29efc3fede69 <String[1]: #;>
             win32: 0x29ef68688579 <Object map = 0x29ef00b84719>#54#
             posix: 0x29ef68688131 <Object map = 0x29ef00b84719>#19#
         _makeLong: 0x29efbb05d321 <JSFunction toNamespacedPath (sfi = 0x29ef4afba301)>#84#
 #55# 0x29ef59448e91: 0x29ef59448e91 <Object map = 0x29ef00b82679>
 #56# 0x29ef59448ec9: 0x29ef59448ec9 <JSArray[0]>
 #57# 0x29ef594492e1: 0x29ef594492e1 <JSArray[5]>
                 0: 0x29ef59449331 <String[41]: c"/Users/raisinten/Desktop/git/node_modules">
                 1: 0x29ef59449409 <String[37]: c"/Users/raisinten/Desktop/node_modules">
                 2: 0x29ef59449449 <String[29]: c"/Users/raisinten/node_modules">
                 3: 0x29ef59449481 <String[19]: c"/Users/node_modules">
                 4: 0x29ef268d3591 <String[13]: #/node_modules>
 #58# 0x29eff2038531: 0x29eff2038531 <JSArray[65]>
 #59# 0x29ef59441251: 0x29ef59441251 <Object map = 0x29ef00b81749>
 #60# 0x29ef59441391: 0x29ef59441391 <Object map = 0x29ef00b81749>
 #61# 0x29ef594414e9: 0x29ef594414e9 <Object map = 0x29ef00b81749>
 #62# 0x29ef59443b79: 0x29ef59443b79 <JSArray[3]>
                 0: 0x29ef59443b31 <String[30]: c"/Users/raisinten/.node_modules">
                 1: 0x29ef594438c1 <String[32]: c"/Users/raisinten/.node_libraries">
                 2: 0x29ef59443681 <String[19]: c"/usr/local/lib/node">
 #63# 0x29ef59441ff1: 0x29ef59441ff1 <JSFunction deprecated (sfi = 0x29ef80a81bb9)>
 #64# 0x29ef268c9c71: 0x29ef268c9c71 <JSFunction Module._findPath (sfi = 0x29efc2396d01)>
 #65# 0x29ef268c9cb1: 0x29ef268c9cb1 <JSFunction Module._nodeModulePaths (sfi = 0x29efc2396e41)>
 #66# 0x29ef268c9cf1: 0x29ef268c9cf1 <JSFunction Module._resolveLookupPaths (sfi = 0x29efc2396ed1)>
 #67# 0x29ef268c9d91: 0x29ef268c9d91 <JSFunction Module._load (sfi = 0x29efc23970c9)>
 #68# 0x29ef268c9dd1: 0x29ef268c9dd1 <JSFunction Module._resolveFilename (sfi = 0x29efc2397139)>
 #69# 0x29eff2018231: 0x29eff2018231 <JSFunction createRequire (sfi = 0x29efc2397759)>
 #70# 0x29ef268c9f91: 0x29ef268c9f91 <JSFunction Module._initPaths (sfi = 0x29efc23977c9)>
 #71# 0x29ef268c9fd1: 0x29ef268c9fd1 <JSFunction Module._preloadModules (sfi = 0x29efc2397859)>
 #72# 0x29ef268ca011: 0x29ef268ca011 <JSFunction syncBuiltinESMExports (sfi = 0x29efc2397909)>
 #73# 0x29ef59443e71: 0x29ef59443e71 <JSFunction executeUserEntryPoint (sfi = 0x29ef268cbb91)>
 #74# 0x29eff2016121: 0x29eff2016121 <JSFunction findSourceMap (sfi = 0x29efc2390d69)>
 #75# 0x29ef59447b81: 0x29ef59447b81 <JSFunction SourceMap (sfi = 0x29ef268d19b9)>
 #76# 0x29eff2037c61: 0x29eff2037c61 <JSWeakMap>
 #77# 0x29eff2038009: 0x29eff2038009 <Map map = 0x29ef00bb2c79>
 #78# 0x29eff2035819: 0x29eff2035819 <Map map = 0x29ef00b82121>
 #79# 0x29ef68688631: 0x29ef68688631 <JSFunction resolve (sfi = 0x29ef4afba119)>
 #80# 0x29ef68688669: 0x29ef68688669 <JSFunction normalize (sfi = 0x29ef4afba169)>
 #81# 0x29ef686886a1: 0x29ef686886a1 <JSFunction isAbsolute (sfi = 0x29ef4afba1d9)>
 #82# 0x29ef686886d9: 0x29ef686886d9 <JSFunction join (sfi = 0x29ef4afba249)>
 #83# 0x29ef68688711: 0x29ef68688711 <JSFunction relative (sfi = 0x29ef4afba299)>
 #84# 0x29efbb05d321: 0x29efbb05d321 <JSFunction toNamespacedPath (sfi = 0x29ef4afba301)>
 #85# 0x29ef68688749: 0x29ef68688749 <JSFunction dirname (sfi = 0x29ef4afba371)>
 #86# 0x29ef68688781: 0x29ef68688781 <JSFunction basename (sfi = 0x29ef4afba3d9)>
 #87# 0x29ef686887b9: 0x29ef686887b9 <JSFunction extname (sfi = 0x29ef4afba441)>
 #88# 0x29ef686887f1: 0x29ef686887f1 <JSBoundFunction (BoundTargetFunction 0x29ef686884d1)>
 #89# 0x29ef68688821: 0x29ef68688821 <JSFunction parse (sfi = 0x29ef4afba4a9)>
=====================

@Qard
Copy link
Member

Qard commented Jul 18, 2022

My use-case is mainly the live debugging aspect. Setting breakpoints, running, getting the backtrace where it breaks, inspecting objects in the frame to see if they are what they are supposed to be, etc. Heapdump stuff alone is insufficient for most of my uses. 😅

@kvakil
Copy link
Author

kvakil commented Jul 18, 2022

But maybe could we write down a list of things that could address the broken state of the project and see how could we improve it incrementally? Maybe with small/scoped tasks, it could be easier to tackle them? (even for me, then I could help)

I think at every point the incremental maintenance burden feels pretty
low, but breakages are pretty common. For me, the obvious first thing
for a new maintainer would be to get tests passing for 16 & 18 and push
out a new release. Currently those releases are completely untested
(source), so I'm not sure how much work that is.

Also, note that during our last meeting we mention the idea to find a new principal maintainer for the project.

My feeling is it will be hard to find someone willing to maintain the project.

Here are all the commands llnode has and their dependencies:

Command Dependency Importance
backtrace V8 High
print/inspect V8 High
findjsinstances V8 Medium
findjsobjects V8 Medium
findrefs V8 Medium
getactivehandles Node.js & V8 Medium
getactiverequests Node.js & V8 Low
nodeinfo Node.js & V8 Low
source list V8 Low

I think the maintenance difficulty is that all of these commands depend
pretty heavily on V8 and/or Node.js internals, but the actual code is
maintained out-of-tree of both of them. llnode essentially has its own
internal representation of all of V8's concepts. And so breakages
(especially for V8) are fairly common, and somebody will need to go and
keep fixing them...

If it was built on top of debug_helper, then at least breakages on V8
would be less common, and potentially (somebody should ask the V8
people) we could upstream some helpers like findjsinstances into
debug_helper proper.

If an llnode-alternative is desirable

I'm not sure about the limitations or how often it's used but there is lldb_commands.py from V8.

nodejs/node#32402 has some color. I don't think exposing V8 internal
functions is a great precedent.

@Qard
Copy link
Member

Qard commented Jul 19, 2022

Whatever we do, it should probably be contributed upstream to V8 to avoid it getting out of sync.

@tony-go
Copy link
Member

tony-go commented Jul 20, 2022

Thanks, @kvakil & @Qard, it's a good start :)

If I got you well there are two approaches:

  • build something (new?) on the top of debug_helper
    or
  • maintaining abstract directly in v8 instead of having our representation of all of V8's concepts (internally)?

@Qard
Copy link
Member

Qard commented Jul 20, 2022

Yeah, whatever we do, it should probably be maintained in V8 codebase so it stays in-sync. Probably just make it directly usable in d8 and have Node.js just inherit the functionality that way. Might not be able to have Node.js-specific stuff though. 🤔

@tony-go
Copy link
Member

tony-go commented Jul 20, 2022

Neat!

On my side, I cloned the project and saw that some tests were failing. Maybe a good way to become more familiar with the codebase.

@mhdawson
Copy link
Member

As an FYI as possibly this did not make it back to the Diagnostics WG. @No9 has volunteered to help maintain llnode and was added as a maintainer. He will only have so many cycles but at least we have a maintainer who can review/land PRs if those who are interested send them in.

@No9
Copy link
Member

No9 commented Jul 23, 2022

Thanks @mhdawson for the ping.
This seems like a good thread to provide a quick update.

  1. CI is now back to green https://github.com/nodejs/llnode/actions/runs/2721848039
  2. There has been further work on the LLVM support https://github.com/nodejs/llnode/pull/389/commits
  3. We could really use some help on the v16 issues Not showing details of JS frames on node v16.14.0 llnode#399
    Edit:
    If folks would like I'd be happy to co-ordinate a call to align efforts and discuss expectations.

@RafaelGSS
Copy link
Member

Can we add it to the support list again, then? https://github.com/nodejs/node/blob/main/doc/contributing/diagnostic-tooling-support-tiers.md

@Qard
Copy link
Member

Qard commented Jul 24, 2022

As someone that hasn't contributed yet but would be interested in helping, I'd be down for a call to learn what the current state is and to understand what I can do to help. 😊

@tony-go
Copy link
Member

tony-go commented Jul 24, 2022

As someone that hasn't contributed yet but would be interested in helping, I'd be down for a call to learn what the current state is and to understand what I can do to help. 😊

+1

@RafaelGSS
Copy link
Member

As someone that hasn't contributed yet but would be interested in helping, I'd be down for a call to learn what the current state is and to understand what I can do to help. blush

Maybe we can have it in one of our Diagnostics meetings

@kvakil
Copy link
Author

kvakil commented Jul 24, 2022

I commented on nodejs/llnode#399 with some more specific info on why
Node 16 backtraces are broken. Anybody can grab it if they'd like; my
plan is to pick it back up around two weeks from now.

I still think that incrementally maintaining llnode is hard -- you can
contrast the stack printing code in llnode with the implementation in
debug_helper
. The latter just has a lot nicer access to
internals. But obviously nobody has the appetite to create a whole new
llnode, so perhaps it's better to just keep pushing the existing
codebase along.

@No9
Copy link
Member

No9 commented Jul 24, 2022

@RafaelGSS Good suggestion on taking a slot in diagnostics meeting - I'll look out for the next session.
@kvakil Agree with the incremental maintenance being hard.
I'll try and get myself upto speed with debug_helper so we can discuss further.

@No9
Copy link
Member

No9 commented Jul 25, 2022

@Qard @tony-go @kvakil
Looks like the Diagnostics session is tomorrow #574 sorry for the short notice but only just found out. I'll probably attend the one after too if you can't make this one.

HT @RafaelGSS

@RafaelGSS
Copy link
Member

We are interested in providing support for this feature. If anybody wants to take the lead o that, feel free to move on. Removing from the agenda.

@github-actions
Copy link

This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants