Python pretty-printer for gdb

From Verific Design Automation FAQ
Revision as of 11:10, 13 September 2022 by Vince (Talk | contribs)

Jump to: navigation, search

If gdb (GNU debugger) was compiled with Python support, it is possible to run Python scripts from within gdb. Below is an example of how to create a custom pretty-printer for Verific's VeriTreeNode using gdb's Python interface.

The Python script itself looks like this

    import gdb
    import re
 
    class VeriVhdlTreeNodePrinter:
        def __init__(self, val):
            self.val = val
 
        def to_string(self):
            eval_string = f"(({self.val.type.name}*){self.val.address})->GetPrettyPrintedString()"
            return gdb.parse_and_eval(eval_string).string()

    def verific_pp_func(val):
        lookup_tag = val.type.tag
        if lookup_tag is None:
            return None
   
        regex = re.compile("^(Verific::)?(Veri|Vhdl)(\w)+$")
        if regex.match(lookup_tag):
            return VeriVhdlTreeNodePrinter(val)
        return None
 
    gdb.pretty_printers.append(verific_pp_func)
 

To automate sourcing of the Python script on gdb startup, add the following line to your ~/.gdbinit :

   source ~/verific_pp.py

The script calls Verific's GetPrettyPrintedString() API which essentially pretty prints the VeriTreeNode. It takes an object and not a pointer. Printing the pointer will still print the value of the pointer as per usual gdb behavior.

Note that if a class has a name starting with Veri but is not derived from VeriTreeNode, gdb produces the following message :

   (gdb) print *pointer_to_VeriLibrary_which_is_not_derived_from_VeriTreeNode
   $2 = Python Exception <class 'gdb.error'> Couldn't find method VeriLibrary::GetPrettyPrintedString:
   (gdb)

The same will happen for VeriValue/VhdlValue/VeriBaseValue, etc, because these classes do not have the GetPrettyPrintedString() API. The pretty printer can be customized to exclude such classes, or modified to handle these other cases. This is just a regular regex rule.

Here is the output from an example run :

    gdb ./test-linux-g
    Reading symbols from test-linux-g...
    (gdb) break 62
    Breakpoint 1 at 0x306b50: file test.cpp, line 62.
    (gdb) run
    Starting program: test-linux-g
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    -- Analyzing Verilog file 'test.v' (VERI-1482)
    test.v(1): INFO: compiling module 'top' (VERI-1018)
    -- module: top
    --     param: P1
    --         initial expression: 8

    Breakpoint 1, main (argc=1, argv=0x7fffffffe0b8) at test.cpp:62
    62                    Message::PrintLine("        initial value: ", Strings::itoa(int_val)) ;
    (gdb) '''p *module_item'''
    $1 = {<Verific::VeriTreeNode> = {<Verific::VeriNode> = {
          _vptr.VeriNode = 0x5555565bcd18 <vtable for Verific::VeriDataDecl+16>, static _present_scope = 0x0,
          static _container_scope_arr = 0x0, static _sva_clock_expr = 0x0, static _default_clock = 0x0,
          static _default_disable_iff_cond = 0x0, static _has_disable_iff = 0, static _id_ref = 0x0,
          static _name_list = 0x0, static _inside_clocked_sequence = 0, static _contains_event_control = 0,
          static _contains_delay_control = 0, static _contains_even_id_in_body = 0, static _multiple_inferred_clock = 0,
          static _in_property_expr = 0, static _in_checker = 0, static _in_fork_join_none_or_any = 0,
          static _in_bind_directive = 0, static _hier_name_to_allow_mod_name = 0, static _do_not_recurse_packed_dims = 0,
          static _processing_param = 0x0, static _processing_expr = 0x0, static _msgs = 0x55555666b870,
          static _relaxed_msgs = 0x0, static _udp_edge_chars = 0x0, static _verilog_keywords = 0x0,
          static _verilog_tokens = 0x0, static _system_tasks = 0x0, static _ref_count_map = 0x0,
          static _attribute_map = 0x0, static _map_of_comment_arr = 0x0, static _global_clocking = 0x0,
          static _var_usage_running = 0, static _is_static_elab = 0, static _is_rtl_elab = 0,
          static _func_pointer_stack = 0x0, static _func_stack = 0x0, static _mutual_recursion_count = 0,
          static _max_mutual_recursion_count = 0, static _within_seq_area = 0, static _within_generate_endgenerate = 0,
          static _under_default_clock = 0, static _default_disable_defined = 0, static _has_delay_or_event_control = 0,
          static _processing_operand = 0, static _potential_always_loop = 0, static _reresolve_idrefs = 0,
          static _ignore_ams_in_rtl_elab = 0, static _follow_lrm_for_constant_func = 0, static _evaluated_vals = 0x0,
          static _evaluated_constraints = 0x0, static _p_evaluated_vals = 0x0, static _p_evaluated_constraints = 0x0,
          static _unnamedscope_vs_decl_ids = 0x0, static _check_linefile_of_import = 1}, _linefile = 8589934594},
      _qualifiers = 0}
    (gdb) '''p *val'''
    $2 = {_vptr.VeriBaseValue = 0x55555654b280 <vtable for Verific::VeriInteger+16>}
    (gdb) '''source verific_pp.py'''
    (gdb) '''p *module_item'''
    $3 = parameter P1 = 8 ;

    (gdb) '''p *val'''
    $4 = 8
    (gdb)
 

Notice how before activating the Python pretty-printer, gdb prints out a lot of information when calling 'p *module_item' or 'p *val' . With the pretty-printer, it now prints only the value from GetPrettyPrintedString().