Cynbe's Hints on Using Emacs + Gdb to Debug Gcc

Cynbe ru Taren

The gcc design and maintainers are not oriented towards strong compile-time verification of (for example) gcc machine descriptions. Gcc machine descriptions are essentially macro-expanded into C code without much checking by a series of genfoo.c programs, leaving most machine description debugging to be done at runtime, with gdb being the tool of choice, preferably under emacs.

This is a somewhat unfortunate state of affairs, but for the moment it is what it is.

It took me quite awhile to figure out how to debug gcc effectively with this toolset; perhaps the following hints will save you some time.

The main issue when debugging gcc with gdb is that the gcc executable itself is just a small driver; the real work is mostly done in the cc1 executable, and consequently that is where the bugs breed and that is the executable you usually want to be connected to in gdb.

The two critical tools for accomplishing this are

Following are detailed instructions for doing the above in just twenty easy steps. They reflect my way of doing things — your way will probably differ, but they should get you started.

Assume that you have a stimulus program stimulus.c which is crashing gcc and that you are trying to debug the crash.

  1. Set up a suitable working directory stim containing stimulus.c

  2. Make sure your gcc sourcetree is up to date.

  3. Start up emacs. Even if you prefer text-mode emacs in general (as I do — -nw switch), you'll find graphics-mode emacs better for debugging. Set up shell buffers in appropriate directories like stim and gcc plus open emacs buffers for any gcc files of interest — gcc/recog.c and gcc/passes are two good choices. I'll assume your gcc shellbuffer is named s gcc

  4. In the gcc Makefile set CFLAGS to include -g3 -O0. The -g3 flag records debugging information for C macros, which is critically important when debugging gcc because it makes very heavy use of macros. The -O0 flag keeps the optimizer from doing weird stuff to the gcc code that would make it harder to step through.

  5. It is a good idea to change
    printf ("#line %d \"%s\"\n", loc->lineno, loc->filename);
    
    to
    printf ("/* #line %d \"%s\" */\n", loc->lineno, loc->filename);
    
    in 766:read-rtl.c because the #lines do more harm than good in my experience. (they try to map the generated code back to the .md file, but the result just looks totally crazy when you're in gdb).

  6. Build gcc and if in doubt run the dejagnu test suite to be sure everything is functioning correctly.

  7. You may now wish to open gcc buffers on newly-generated files such as gcc/insn-recog.c, gcc/insn-emit.c and gcc/insn-preds.c. These contain your machine description in code-synthesized form. (In general all of these files will have names insn-foo.c for some suitable "foo".)

  8. So that gcc has an up-to-date index of where symbols are defined which includes the synthesized insn-*.c files, you should now rebuild the TAGS file in your gcc directory by a command like
    rm TAGS
    find .               | egrep '\.(texi|c|h|md)$' | xargs etags --append
    
    or perhaps instead
    rm TAGS
    etags gcc/*.[ch] libiberty/*.[ch] gcc/config/mymachine/*.[ch]
    
    where mymachine is the target architecture of interest.

  9. In your s gcc shell buffer do M-x visit-tags-table to load in the TAGS file you just generated.

  10. In your s stim shell buffer do gcc stimulus.c to reproduce the problem. If you are crosscompiling to architecture foo this may be foo-elf-gcc stimulus.c, say. Make sure you are running the right compiler image! If in doubt do which gcc. Depending on your problem, you may need to supply particular switches like -O2 -fPIC to gcc.

  11. To generate a preprocessed stimulus.i version of your stimulus.c file, in your s stim shell buffer recompile with an added--save-temps switch. The command may look something like foo-elf-gcc -save-temps -O2 -fPIC stimulus.c.

  12. To get the line needed to invoke cc1 directly, in your s stim shell buffer recompile with an added-### switch. The command may look something like foo-elf-gcc -### -O2 -fPIC stimulus.c.

    Save the full cc1 path, (say) in emacs register 'c' via C-x r s c.

    Save the rest of the line, say in emacs buffer 'xxx', and add run to the start, yielding something like

    run -fpreprocessed stimulus.i -quiet -dumpbase stimulus.i -auxbase stimulus -O2 -fPIC -o ccNHpl1b.s
    

  13. Set up emacs gdb flags per your taste:
    M-: (setq gdb-many-windows t)
    M-: (setq gdb-use-separate-io-buffer nil)
    
    (The emacs gdb docs are here.)

  14. Start up emacs gdb support by doing M-x gdb. Use C-x r i c to insert the full path to the cc1 executable that you saved in step 12.

  15. In the gdb window you may wish to do source gcc/.gdbinit to load in some gdb convenience settings generated by gcc.

  16. Set up any desired breakpoints in the gdb code. You can do this in the gdb buffer via commands like
    break _start
    break main
    break instantiate_virtual_regs
    break 'function.c':1705
    break 'passes.c':init_optimization_passes
    break tree_rest_of_compilation
    break c_expand_body
    break recog_for_combine
    break rest_of_handle_combine
    
    You can also set breakpoints in gcc sourcecode buffers just doing C-x <SPC>

    If line2 827-828 in your 'passes.c' file contain

    if (pass->execute)
       pass->execute();
    
    then one way to set up pass narration is to in the gdb buffer do
    break 'passes.c':828
    commands
    p/s pass->name
    end
    
    This will set a breakpoint on line 828 and print out the name of the pass when stopping at it.

    If you only want to stop at a given pass, say "ivopts", you might instead do

    break 'passes.c':828
    commands
    p/s pass->name
    end
    condition
    strcmp(pass->name,"ivopts")==0
    end
    
    (For more information on this sort of stuff check out the gdb manual.)

  17. You may wish to define some gdb convenience commands. In particular, the gcc maintainers provide a variety of debug_foo() functions specifically intended to be used to prettyprint gcc internal datastructures interactively from within gdb. The three you are most likely to use are
    call debug_rtx(insn)
    call debug_rtx_list(insn)
    call debug_tree(tree)
    
    so if you like to save keystrokes you might define gdb shortcuts
    define rx
    call debug_rtx($arg0)
    end
    
    define rxl
    call debug_rtx_list($arg0)
    end
    
    define tx
    call debug_tree($arg0)
    end
    
    so that you can then type just
    rx(insn)
    rxl(insn)
    tx(tree)
    
    at the gdb prompt. You might want to save these definitions in a file so that you can read them with a gdb source command. (For more about gdb functions see the User-defined Commands section of the gdb manual.)

  18. In your gdb window do
    cd /path/to/stim
    
    so that gdb's current working directory is the directory containing your stimulus.c program.

  19. In your gdb window do
    dir /path/to/gcc
    dir /path/to/gcc/config/mymachine
    dir /path/to/generated/code
    
    These commands tell gdb where to look for gcc source code. You'll probably want to specify three directories, as above, one each for the original gcc mainline code, the platform-specific gcc code, and the code generated from your mymachine.md machine description file.

  20. In your gdb window start cc1 running by cut-and-pasting the xxx buffer contents that you saved in step 12, something like
    run -fpreprocessed stimulus.i -quiet -dumpbase stimulus.i -auxbase stimulus -O2 -fPIC -o ccNHpl1b.s
    

And you're off single-stepping through the code using the gdb n(ext), s(tep), c(ontinue) commands etc. (The gnu manual section on them is here.) Wasn't that easy?

Two additional hints:

Finally, here is a complete list of debug_*() functions intended for interactive use from gdb. (You can of course also call other C functions interactively from gdb.) In general, you shouldn't try to memorize this list, just be aware that there a lot of such functions, and remember to look for them when you're in a particular part of the compiler where there is a datastructure you need to prettyprint interactively:

gcc/basic-block.h:      void debug_bb (basic_block);
gcc/basic-block.h:      basic_block debug_bb_n (int);
gcc/basic-block.h:      void debug_regset (regset);
gcc/bitmap.h:           void debug_bitmap (bitmap);

gcc/cp/cp-tree.h:       void debug_class                                (tree);
gcc/cp/cp-tree.h:       void debug_thunks                       (tree);
gcc/cp/cp-tree.h:       void debug_binfo                                (tree);

gcc/df.h:               void debug_df_insn (rtx);
gcc/df.h:               void debug_df_regno (unsigned int);
gcc/df.h:               void debug_df_reg (rtx);

gcc/df.h:               void debug_df_defno (unsigned int);
gcc/df.h:               void debug_df_useno (unsigned int);
gcc/df.h:               void debug_df_ref (struct ref *);
gcc/df.h:               void debug_df_chain (struct df_link *);

gcc/diagnostic.h:       void debug_generic_expr (tree);
gcc/diagnostic.h:       void debug_generic_stmt (tree);
gcc/diagnostic.h:       void debug_c_tree (tree);

gcc/dwarf2out.h:        void debug_dwarf (void);
gcc/dwarf2out.h:        void debug_dwarf_die (struct die_struct *);

gcc/flags.h:            enum debug_info_level debug_info_level;
gcc/reload.h:           void debug_reload_to_stream (FILE *);
gcc/reload.h:           void debug_reload (void);

gcc/rtl.h:              void debug_rtx (rtx);
gcc/rtl.h:              void debug_rtx_list (rtx, int);
gcc/rtl.h:              void debug_rtx_range (rtx, rtx);
gcc/rtl.h:              rtx debug_rtx_find (rtx, int);

gcc/sbitmap.h:          void debug_sbitmap (sbitmap);

gcc/tree-flow.h:        void debug_tree_bb (basic_block);
gcc/tree-flow.h:        basic_block debug_tree_bb_n (int);

gcc/tree-flow.h:        void debug_tree_cfg (int);
gcc/tree-flow.h:        void debug_cfg_stats (void);
gcc/tree-flow.h:        void debug_loop_ir (void);

gcc/tree-flow.h:        void debug_dfa_stats (void);
gcc/tree-flow.h:        void debug_referenced_vars (void);
gcc/tree-flow.h:        void debug_variable (tree);

gcc/tree-flow.h:        void debug_subvars_for (tree);
gcc/tree-flow.h:        void debug_may_aliases_for (tree);
gcc/tree-flow.h:        void debug_alias_info (void);

gcc/tree-flow.h:        void debug_points_to_info (void);
gcc/tree-flow.h:        void debug_points_to_info_for (tree);
gcc/tree-flow.h:        void debug_dominator_optimization_stats (void);
gcc/tree-flow.h:        void debug_value_expressions (tree);

gcc/tree.h:             void debug_tree (tree);
gcc/tree.h:             bool debug_find_tree (tree, tree);

gcc/tree-ssa-operands.h:        void debug_immediate_uses (void);
gcc/tree-ssa-operands.h:        void debug_immediate_uses_for (tree var);

gcc/tree-ssa-structalias.h:     void debug_constraint (constraint_t);
gcc/tree-ssa-structalias.h:     void debug_constraints (void);
gcc/tree-ssa-structalias.h:     void debug_solution_for_var (unsigned int);
gcc/tree-ssa-structalias.h:     void debug_sa_points_to_info (void);

gcc/debug.h:            void debug_nothing_void (void);
gcc/debug.h:            void debug_nothing_charstar (const char *);
gcc/debug.h:            void debug_nothing_int_charstar (unsigned int, const char *);

gcc/debug.h:            void debug_nothing_int (unsigned int);
gcc/debug.h:            void debug_nothing_int_int (unsigned int, unsigned int);
gcc/debug.h:            void debug_nothing_tree (tree);

gcc/debug.h:            void debug_nothing_tree_int (tree, int);
gcc/debug.h:            void debug_nothing_tree_tree (tree, tree);
gcc/debug.h:            bool debug_true_tree (tree);

gcc/debug.h:            void debug_nothing_rtx (rtx);
gcc/debug.h:            void debug_flush_symbol_queue (void);
gcc/debug.h:            void debug_queue_symbol (tree);

gcc/debug.h:            void debug_free_queue (void);
gcc/debug.h:            int debug_nesting;

[ Back to Cynbe's Gcc Debugging Hints ]


Cynbe ru Taren
Last modified: Sun Oct 7 21:50:21 CDT 2012