This patch adds a new option, -gmlt, that produces level 1 debug info plus line number tables and inlined subroutine information. (The option is short for "minimum line tables," taken from a similar feature of HP's compilers.)
We've been using this option at Google for about a year now, and have found it useful for collecting stack traces with file names and line numbers without having to pay the much larger overhead for full debug info. It's also useful for sample-based profiling, as discriminator information is also available in the line number tables. For optimized code, we've measured binaries built with -g0, -gmlt, and -g2, and found that the total size of a binary compiled with -gmlt is about 2.5x larger than one compiled without debug, while a binary compiled with -g2 is about 6.7x larger. OK for trunk? -cary M gcc/common.opt M gcc/doc/invoke.texi M gcc/dwarf2out.c M gcc/opts.c A gcc/testsuite/gcc.dg/debug/dwarf2/mlt1.c A gcc/testsuite/gcc.dg/debug/dwarf2/mlt2.c M gcc/tree-ssa-live.c Tested: bootstrapped on x86_64. Added two new test cases. gcc/ChangeLog: * common.opt (generate_debug_line_table): New global var. (gmlt): New option * dwarf2out.c (GENERATE_MINIMUM_LINE_TABLE): New macro. (add_pubname_string): Test for -gmlt. (add_pubname): Likewise. (add_src_coords_attributes): Likewise. (decls_for_scope): Likewise. (dwarf2out_source_line): Likewise. (dwarf2out_finish): Likewise. * opts.c (finish_options): Force debug info to at least level 1 if -gmlt specified. (common_handle_option): Add OPT_gmlt. (set_debug_level): Set generate_debug_line_table flag. * tree-ssa-live.c (remove_unused_scope_block_p): Test for -gmlt. * doc/invoke.texi (-gmlt): New options gcc/testsuite/ChangeLog: * gcc.dg/debug/dwarf2/mlt1.c: New test. * gcc.dg/debug/dwarf2/mlt2.c: New test. diff --git a/gcc/common.opt b/gcc/common.opt index 83a61fc..2964d61 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -120,6 +120,11 @@ enum debug_info_type write_symbols = NO_DEBUG Variable enum debug_info_levels debug_info_level = DINFO_LEVEL_NONE +; Whether to generate line number table. Normally set at DINFO_LEVEL_NORMAL +; or above; can also be set for DINFO_LEVEL_TERSE with -gmlt. +Variable +bool generate_debug_line_table = false + ; Nonzero means use GNU-only extensions in the generated symbolic ; debugging information. Currently, this only has an effect when ; write_symbols is set to DBX_DEBUG, XCOFF_DEBUG, or DWARF_DEBUG. @@ -2150,6 +2155,10 @@ ggdb Common JoinedOrMissing Generate debug information in default extended format +gmlt +Common RejectNegative +Generate debug information at level 1 with minimal line table + gstabs Common JoinedOrMissing Negative(gstabs+) Generate debug information in STABS format diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 4377f34..b199253 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -320,7 +320,7 @@ Objective-C and Objective-C++ Dialects}. -fstack-usage -ftest-coverage -ftime-report -fvar-tracking @gol -fvar-tracking-assignments -fvar-tracking-assignments-toggle @gol -g -g@var{level} -gtoggle -gcoff -gdwarf-@var{version} @gol --ggdb -gstabs -gstabs+ -gstrict-dwarf -gno-strict-dwarf @gol +-ggdb -gmlt -gstabs -gstabs+ -gstrict-dwarf -gno-strict-dwarf @gol -gvms -gxcoff -gxcoff+ @gol -fno-merge-debug-strings -fno-dwarf2-cfi-asm @gol -fdebug-prefix-map=@var{old}=@var{new} @gol @@ -4679,6 +4679,11 @@ debug format is long obsolete, but the option cannot be changed now. Instead use an additional @option{-g@var{level}} option to change the debug level for DWARF. +@item -gmlt +@opindex gmlt +Produce a minimal line table, with level 1 debugging information plus +information about inlined functions and line numbers. + @item -gtoggle @opindex gtoggle Turn off generation of debug info, if leaving out this option would have diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index f3c4c09..4506f7f 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -113,6 +113,10 @@ int vms_file_stats_name (const char *, long long *, long *, char *, int *); #define DWARF2_INDIRECT_STRING_SUPPORT_MISSING_ON_TARGET 0 #endif +/* True if generating only the minimum line table (-gmlt). */ +#define GENERATE_MINIMUM_LINE_TABLE (debug_info_level == DINFO_LEVEL_TERSE \ + && generate_debug_line_table) + /* ??? Poison these here until it can be done generically. They've been totally replaced in this file; make sure it stays that way. */ #undef DWARF2_UNWIND_INFO @@ -11639,7 +11643,7 @@ dwarf2_name (tree decl, int scope) static void add_pubname_string (const char *str, dw_die_ref die) { - if (targetm.want_debug_pub_sections) + if (!GENERATE_MINIMUM_LINE_TABLE && targetm.want_debug_pub_sections) { pubname_entry e; @@ -11652,7 +11656,9 @@ add_pubname_string (const char *str, dw_die_ref die) static void add_pubname (tree decl, dw_die_ref die) { - if (targetm.want_debug_pub_sections && TREE_PUBLIC (decl)) + if (!GENERATE_MINIMUM_LINE_TABLE + && targetm.want_debug_pub_sections + && TREE_PUBLIC (decl)) { const char *name = dwarf2_name (decl, 1); if (name) @@ -17837,11 +17843,12 @@ add_src_coords_attributes (dw_die_ref die, tree decl) static void add_linkage_name (dw_die_ref die, tree decl) { - if ((TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == VAR_DECL) - && TREE_PUBLIC (decl) - && !DECL_ABSTRACT (decl) - && !(TREE_CODE (decl) == VAR_DECL && DECL_REGISTER (decl)) - && die->die_tag != DW_TAG_member) + if (!GENERATE_MINIMUM_LINE_TABLE + && (TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == VAR_DECL) + && TREE_PUBLIC (decl) + && !DECL_ABSTRACT (decl) + && !(TREE_CODE (decl) == VAR_DECL && DECL_REGISTER (decl)) + && die->die_tag != DW_TAG_member) { /* Defer until we have an assembler name set. */ if (!DECL_ASSEMBLER_NAME_SET_P (decl)) @@ -20911,14 +20918,18 @@ decls_for_scope (tree stmt, dw_die_ref context_die, int depth) declared directly within this block but not within any nested sub-blocks. Also, nested function and tag DIEs have been generated with a parent of NULL; fix that up now. */ - for (decl = BLOCK_VARS (stmt); decl != NULL; decl = DECL_CHAIN (decl)) - process_scope_var (stmt, decl, NULL_TREE, context_die); - for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (stmt); i++) - process_scope_var (stmt, NULL, BLOCK_NONLOCALIZED_VAR (stmt, i), - context_die); + if (debug_info_level > DINFO_LEVEL_TERSE) + { + for (decl = BLOCK_VARS (stmt); decl != NULL; decl = DECL_CHAIN (decl)) + process_scope_var (stmt, decl, NULL_TREE, context_die); + for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (stmt); i++) + process_scope_var (stmt, NULL, BLOCK_NONLOCALIZED_VAR (stmt, i), + context_die); + } - /* If we're at -g1, we're not interested in subblocks. */ - if (debug_info_level <= DINFO_LEVEL_TERSE) + /* If we're at -g1 and not generating minimal line tables, + we're not interested in subblocks. */ + if (!generate_debug_line_table && debug_info_level <= DINFO_LEVEL_TERSE) return; /* Output the DIEs to represent all sub-blocks (and the items declared @@ -22189,7 +22200,7 @@ dwarf2out_source_line (unsigned int line, const char *filename, unsigned int file_num; dw_line_info_table *table; - if (debug_info_level < DINFO_LEVEL_NORMAL || line == 0) + if (!generate_debug_line_table || line == 0) return; /* The discriminator column was added in dwarf4. Simplify the below @@ -23719,7 +23730,7 @@ dwarf2out_finish (const char *filename) } } - if (debug_info_level >= DINFO_LEVEL_NORMAL) + if (generate_debug_line_table) add_AT_lineptr (comp_unit_die (), DW_AT_stmt_list, debug_line_section_label); @@ -23746,7 +23757,7 @@ dwarf2out_finish (const char *filename) /* Add a pointer to the line table for the main compilation unit so that the debugger can make sense of DW_AT_decl_file attributes. */ - if (debug_info_level >= DINFO_LEVEL_NORMAL) + if (generate_debug_line_table) add_AT_lineptr (ctnode->root_die, DW_AT_stmt_list, debug_line_section_label); diff --git a/gcc/opts.c b/gcc/opts.c index cd581f6..72f4d51 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -609,6 +609,11 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, { enum unwind_info_type ui_except; + /* If -gmlt was specified, make sure debug level is at least 1. */ + if (opts->x_generate_debug_line_table + && opts->x_debug_info_level < DINFO_LEVEL_TERSE) + opts->x_debug_info_level = DINFO_LEVEL_TERSE; + if (opts->x_dump_base_name && ! IS_ABSOLUTE_PATH (opts->x_dump_base_name)) { /* First try to make OPTS->X_DUMP_BASE_NAME relative to the @@ -1649,6 +1654,16 @@ common_handle_option (struct gcc_options *opts, loc); break; + case OPT_gmlt: + set_debug_level (NO_DEBUG, DEFAULT_GDB_EXTENSIONS, "", opts, opts_set, + loc); + /* Clear the debug level to NONE so that a subsequent bare -g will + set it to NORMAL (level 2). If no subsequent option sets the + level explicitly, we will set it to TERSE in finish_options(). */ + opts->x_debug_info_level = DINFO_LEVEL_NONE; + opts->x_generate_debug_line_table = true; + break; + case OPT_gvms: set_debug_level (VMS_DEBUG, false, arg, opts, opts_set, loc); break; @@ -1856,6 +1871,8 @@ set_debug_level (enum debug_info_type type, int extended, const char *arg, else opts->x_debug_info_level = (enum debug_info_levels) argval; } + + generate_debug_line_table = debug_info_level >= DINFO_LEVEL_NORMAL; } /* Arrange to dump core on error for diagnostic context DC. (The diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/mlt1.c b/gcc/testsuite/gcc.dg/debug/dwarf2/mlt1.c new file mode 100644 index 0000000..7b0b2e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/mlt1.c @@ -0,0 +1,32 @@ +/* Test that -gmlt includes line tables and inlined subroutine entries, + and excludes types and variables. */ +/* Origin: Cary Coutant <ccout...@google.com> */ +/* { dg-do compile } */ +/* { dg-options "-O2 -gdwarf-2 -dA -gmlt" } */ +/* { dg-final { scan-assembler "DW_AT_stmt_list" } } */ +/* { dg-final { scan-assembler "DW_TAG_subprogram" } } */ +/* { dg-final { scan-assembler "DW_TAG_inlined_subroutine" } } */ +/* { dg-final { scan-assembler-not "DW_TAG_variable" } } */ +/* { dg-final { scan-assembler-not "DW_TAG_formal_parameter" } } */ +/* { dg-final { scan-assembler-not "DW_TAG_base_type" } } */ + +static inline __attribute__((always_inline)) int +a(int i, int j) +{ + return (i << 5) + j; +} + +int +b(int i, int j) +{ + return (i >> 5) + (j << 27); +} + +int +c(int i, int j) +{ + int r = a(i, j); + r = b(r, i); + r = b(r, j); + return r; +} diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/mlt2.c b/gcc/testsuite/gcc.dg/debug/dwarf2/mlt2.c new file mode 100644 index 0000000..bd7ad05 --- /dev/null +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/mlt2.c @@ -0,0 +1,31 @@ +/* Test that -g overrides -gmlt. */ +/* Origin: Cary Coutant <ccout...@google.com> */ +/* { dg-do compile } */ +/* { dg-options "-O2 -gdwarf-2 -dA -gmlt -g" } */ +/* { dg-final { scan-assembler "DW_AT_stmt_list" } } */ +/* { dg-final { scan-assembler "DW_TAG_subprogram" } } */ +/* { dg-final { scan-assembler "DW_TAG_inlined_subroutine" } } */ +/* { dg-final { scan-assembler "DW_TAG_variable" } } */ +/* { dg-final { scan-assembler "DW_TAG_formal_parameter" } } */ +/* { dg-final { scan-assembler "DW_TAG_base_type" } } */ + +static inline __attribute__((always_inline)) int +a(int i, int j) +{ + return (i << 5) + j; +} + +int +b(int i, int j) +{ + return (i >> 5) + (j << 27); +} + +int +c(int i, int j) +{ + int r = a(i, j); + r = b(r, i); + r = b(r, j); + return r; +} diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c index 4216b22..6a88236 100644 --- a/gcc/tree-ssa-live.c +++ b/gcc/tree-ssa-live.c @@ -548,8 +548,9 @@ remove_unused_scope_block_p (tree scope) else if (!nsubblocks) ; /* For terse debug info we can eliminate info on unused variables. */ - else if (debug_info_level == DINFO_LEVEL_NONE - || debug_info_level == DINFO_LEVEL_TERSE) + else if (!generate_debug_line_table + && (debug_info_level == DINFO_LEVEL_NONE + || debug_info_level == DINFO_LEVEL_TERSE)) { /* Even for -g0/-g1 don't prune outer scopes from artificial functions, otherwise diagnostics using tree_nonartificial_location -- This patch is available for review at http://codereview.appspot.com/4440072