Debugging with GDB or LLDB

Warning

Some of the facilities described here are only available in builds from April 11, 2013 or later.

Warning

On macOS, we recommend using lldb to debug rather than gdb.

Which compiler backend are you using?

If you are using the C backend, then the information discussed here will be useful for debugging. We use the C backend on 64 bit Linux, 64 bit FreeBSD and all macOS versions.

If you’re using the HARP backend (32 bit Linux or 32 bit FreeBSD), then you’ll be limited to getting stack traces.

Debugging with the C backend

Debugging with the C backend is not a perfect experience but it is improving.

Finding the generated C files

The C files generated by the compiler will be found under the _build/build directory with a directory in there for each library that was built as part of the project.

Finding the corresponding Dylan code

The generated C files contain the file name and line number of the corresponding Dylan source. However, this may be confusing in the presence of inlined functions and macro expansions.

Understanding name mangling

Fill this in, particularly with a link to the Hacker’s Guide!

Understanding stack traces

Let’s look at part of a representative stack trace:

#0  0x92b41b06 in read$NOCANCEL$UNIX2003 ()
#1  0x0042c509 in Kunix_readYio_internalsVioI ()
#2  0x000bc8e7 in xep_4 ()
#3  0x0042ba99 in Kaccessor_read_intoXYstreams_internalsVioMM0I ()
#4  0x000c0805 in key_mep_6 ()
#5  0x000c43a4 in implicit_keyed_single_method_engine_4 ()
#6  0x000c1dd5 in gf_optional_xep_4 ()
#7  0x004139fb in Kload_bufferYstreams_internalsVioI ()
#8  0x0041334a in Kdo_next_input_bufferYstreamsVioMM1I ()
#9  0x000c04ab in key_mep_4 ()
#10 0x000c3eaf in implicit_keyed_single_method_engine_1 ()
#11 0x0040520f in Kread_lineYstreamsVioMM0I ()
#12 0x000c0321 in key_mep_3 ()
#13 0x000c3eaf in implicit_keyed_single_method_engine_1 ()
#14 0x0079dcf6 in Kcommand_line_loopYcommand_linesVenvironment_commandsMM0I ()
#15 0x000bf6b3 in rest_key_xep_5 ()
#16 0x00007abe in Kdo_execute_commandVcommandsMdylan_compilerM0I ()
#17 0x000bb9bb in primitive_engine_node_apply_with_optionals ()
#18 0x0002b9ba in Khandle_missed_dispatchVKgI ()
#19 0x0002aaef in KPgf_dispatch_absentVKgI ()
#20 0x000c25e8 in general_engine_node_n_engine ()
#21 0x004b19a8 in Kexecute_commandVcommandsMM0I ()
#22 0x000bb9bb in primitive_engine_node_apply_with_optionals ()
#23 0x0002b9ba in Khandle_missed_dispatchVKgI ()
#24 0x0002aaef in KPgf_dispatch_absentVKgI ()
#25 0x000c25e8 in general_engine_node_n_engine ()
#26 0x000c11ab in gf_xep_1 ()
#27 0x0000aa9e in KmainYconsole_environmentVdylan_compilerI ()
#28 0x0000abb3 in _Init_dylan_compiler__X_start_for_user ()

Some things to notice:

  • Method dispatch takes up the bulk of the stack frames with function calls like gf_xep_1, general_engine_node_n_engine, rest_key_xep_5, key_mep_4, xep_4, implicit_keyed_single_method_engine_1 and so on.
  • Methods representing Dylan code are mangled as discussed in a section above.
  • It may seem alarming to see methods like KPgf_dispatch_absentVKgI or Khandle_missed_dispatchVKgI (where did dispatch go?!?). These function calls indicate that the dispatch data for a method hadn’t been set up yet as this is the first invocation of that method. The method dispatch data is set up lazily, so this is normal and expected.
  • There isn’t any information on arguments or what file and line number contains the corresponding code. This means that you don’t have the debug data for the compiler around. An easy way to address this is to build your own copy of the compiler.

Breaking on main

Unfortunately, you can’t simply set a breakpoint on main. This is because the generated code runs from shared library constructor functions so the entire Dylan application runs and exits prior to main being invoked.

A reasonable alternative is to determine the C name of your entry point function and set a breakpoint on that instead.

Inspecting Dylan objects in LLDB

In LLDB, you may import our Dylan support library:

command script import /path/to/opendylan/share/opendylan/lldb/dylan

Do not import the scripts under that directory directly as that will not work. Once this has been done, variables that are representing Dylan values will automatically be shown with additional data about their actual values.

Inspecting Dylan objects in GDB

We do not yet have support for Dylan in GDB as we do for LLDB.

The C runtime contains a number of helper functions specifically for improving the debugging process. These can be invoked from gdb or lldb and will assist you in analyzing values.

D dylan_object_class(D* instance)

Returns the class instance for the given instance object.

bool dylan_boolean_p(D instance)

Tests whether instance is a <boolean>.

bool dylan_true_p(D instance)

Tests whether instance is #t.

bool dylan_float_p(D instance)

Tests whether instance is a <float>.

bool dylan_single_float_p(D instance)

Tests whether instance is a <single-float>.

float dylan_single_float_data(D instance)

Returns the float data stored in the instance.

bool dylan_double_float_p(D instance)

Tests whether instance is a <double-float>.

double dylan_double_float_data(D instance)

Returns the double data stored in the instance.

bool dylan_symbol_p(D instance)

Tests whether instance is a <symbol>.

D dylan_symbol_name(D instance)

Returns the string form of the given symbol.

bool dylan_pair_p(D instance)

Tests whether instance is a <pair>.

bool dylan_empty_list_p(D instance)

Tests whether instance is an empty list.

D dylan_head(D instance)

Returns the head of the given <pair> instance.

D dylan_tail(D instance)

Returns the tail of the given <pair> instance.

bool dylan_vector_p(D instance)

Tests whether instance is a <vector>.

bool dylan_string_p(D instance)

Tests whether instance is a <string>.

char* dylan_string_data(D instance)

Returns the C string data stored in the given instance.

bool dylan_simple_condition_p(D instance)

Tests whether instance is a <simple-condition>.

D dylan_simple_condition_format_string(D instance)

Returns the format string stored in the given <simple-condition>.

D dylan_simple_condition_format_args(D instance)

Returns the format string arguments stored in the given <simple-condition>.

bool dylan_class_p(D instance)

Tests whether instance is a <class>.

D dylan_class_debug_name(D instance)

Returns the <string> object containing the class’s name.

bool dylan_function_p(D instance)

Tests whether instance is a <function>.

D dylan_function_debug_name(D instance)

Returns the <string> object containing the function’s name. Note that we do not store the name for all function objects.

void dylan_print_object(D object)

Print some information about the given object to stdout.

Debugging with the HARP backend

As mentioned previously, this is largely limited to getting stack traces. If you try to run a Dylan application built with the HARP backend under the debugger, you may need to adjust your debugger’s signal handling as the Memory Pool System GC that is used employs the SIGSEGV signal.

To do this on Linux and FreeBSD in gdb, use this command:

handle SIGSEGV pass nostop noprint

Add more notes about this later.