Show method names in callstacks

I went searching today to figure out why segfault handlers output method arguments, more than just method names, ex:

$ cat def go
  Exception::CallStack.print_backtrace
  pp caller
end
puts go
...
[0x55df0acdae16] *Exception::CallStack::print_backtrace:Nil +118 in ./me
[0x55df0acc9466] *go:Array(String) +6 in ./me
[0x55df0acb8cfe] __crystal_main +1230 in ./me
[0x55df0ad5cd96] *Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil +6 in ./me
[0x55df0ad5ccd9] *Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32 +41 in ./me
[0x55df0acc65e6] main +6 in ./me
[0x7f963e993b6b] __libc_start_main +235 in /lib/x86_64-linux-gnu/libc.so.6
[0x55df0acb876a] _start +42 in ./me

whereas normal exception stacktraces only list the method name

["me.cr:3:3 in 'go'",
 "me.cr:5:1 in '__crystal_main'",
 "src/crystal/main.cr:115:5 in 'main_user_code'",
 "src/crystal/main.cr:101:7 in 'main'",
 "src/crystal/main.cr:127:3 in 'main'",
 "/lib/x86_64-linux-gnu/libc.so.6 in '__libc_start_main'",
 "./me in '_start'",
 "???"]

This eventually led me to the CRYSTAL_CALLSTACK_FULL_INFO environment variable, which changes it:

$ CRYSTAL_CALLSTACK_FULL_INFO=1 ./me
...
["me.cr:3:3 in '*go:Array(String)' at 0x562e5cac446b",
 "me.cr:5:1 in '__crystal_main' at 0x562e5cab3cfe",
 "src/crystal/main.cr:115:5 in '*Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil' at 0x562e5cb57d96",
 "src/crystal/main.cr:101:7 in '*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32' at 0x562e5cb57cd9",
 "src/crystal/main.cr:127:3 in 'main' at 0x562e5cac15e6",
 "/lib/x86_64-linux-gnu/libc.so.6 in '__libc_start_main' at 0x7fd869c61b6b",
 "./me in '_start' at 0x562e5cab376a",
 "??? at 0x0"]

which I agree is too much.

But it doesn’t seem right that by default we don’t get “rich” call stacks that include argument types, those could be useful at times. Anybody averse to if I do a PR to have the default include method with parameter types (but not ip addresses)? Thoughts?

Cheers!

Fascinating, I had no idea this was a thing. The default stack traces appear to be lifted pretty much verbatim from Ruby, which would explain why method signature is omitted. I actually think it might be nice to have it return objects instead of strings to parse in error-reporting shards (which would almost certainly break if the format is changed in any way). Those objects could still serialize to the existing string formats or even use something similar to the Log::Formatter API.

lol Do you mean memory addresses here?

instruction pointer addresses, used internally. Definitely an ambiguous name LOL.

1 Like