From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 28104 invoked by alias); 2 Jan 2004 07:25:06 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 28097 invoked from network); 2 Jan 2004 07:25:04 -0000 Received: from unknown (HELO mwinf0401.wanadoo.fr) (193.252.22.27) by sources.redhat.com with SMTP; 2 Jan 2004 07:25:04 -0000 Received: from takamaka.act-europe.fr (AStDenis-101-1-4-19.w80-13.abo.wanadoo.fr [80.13.18.19]) by mwinf0401.wanadoo.fr (SMTP Server) with ESMTP id BCA215800130 for ; Fri, 2 Jan 2004 08:25:02 +0100 (CET) Received: by takamaka.act-europe.fr (Postfix, from userid 507) id 5019447D62; Fri, 2 Jan 2004 11:25:00 +0400 (RET) Date: Fri, 02 Jan 2004 07:25:00 -0000 From: Joel Brobecker To: gdb-patches@sources.redhat.com Subject: [RFC/dwarf-2] Add support for included files Message-ID: <20040102072500.GS826@gnat.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="lEGEL1/lMxI0MVQ2" Content-Disposition: inline User-Agent: Mutt/1.4i X-SW-Source: 2004-01/txt/msg00015.txt.bz2 --lEGEL1/lMxI0MVQ2 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-length: 4290 Hello, Ada95 defines the notion of "separates", which allows a developper to write the implementation of either a function or a package in a separate file. Here is a small example: foo.ads: package Foo is procedure Proc; end Foo; foo.adb: Package body Foo is procedure Proc is separate; ^^^^^^^^^^^ End Foo; And then the body for procedure "Proc", as a separate in foo-proc.adb: separate (Foo) ^^^^^^^^^^^^^^ procedure Proc is begin null; end Proc; This capability can be reproduced in C as well, using #include. The following example is somewhat ugly with respect to the usual coding practices, but does reproduce well the capability above: hello.c: void say_hello (void) { printf ("Hello world.\n"); } foo.c: #include #include "hello.c" <--- Reproduces the "is separate"... int main (void) { say_hello (); return 0; } We have noticed that, if compiled with dwarf-2, GDB is unable to set a breakpoint inside hello.c using the file:lineno syntax: % gcc -gdwarf-2 foo.c -o foo % gdb foo (gdb) b hello.c:4 No source file named hello.c. (gdb) list hello.c:4 No source file named hello.c. However, we can set the breakpoint using the function name: (gdb) b say_hello Breakpoint 1 at 0x804833e: file hello.c, line 4. And then once this breakpoint is set, we can now insert the breakpoint using the source location: (gdb) b hello.c:4 Note: breakpoint 1 also set at pc 0x804833e. Breakpoint 2 at 0x804833e: file hello.c, line 4. (gdb) list hello.c:4 1 void 2 say_hello (void) 3 { 4 printf ("Hello world.\n"); 5 } In Dwarf-2, the list of included files is linked to the line table. First, we have "Line Number Program Header" which contains the "file names" table. And then the program itself which uses the DW_LNS_set_file opcode to change from file to file. There is also the DW_LNE_defile_file opcode that can be used to define new files instead of using the "file names" table. Our problem here is that we don't process this line table when building the partial symbols. Hence, we miss the list of included files, and therefore do not create any partial symtab for them. The problem does not exist with stabs because the N_SOL and line number information are embedded inside the rest of the stabs data. If we want to support the case above, I don't see any other way but to scan the line table as well. I suggest a fast scan, although I don't see a simple way to skip the opcodes and their data that are ignored during our scan without having a good knowledge of the opcode we are skipping. This makes the function that does the quick scan of the line table almost as fat as the one that is used to build the GDB line table, almost a copy except that we ignore most of what we read. Basically, I just copy/pasted the code from dwarf_decode_lines(), simplified it for partial symtab processing, and then called it right after the compilation unit psymtab has been built. Currently, the function scans the line number program, and records which files of the "file names" table have really been included in the line program (that's the "file_included" array). Here is a patch against GDB 6.0 that I used as a proof of concept. It does not handle the case where the compiler uses DW_LNE_defile_file instead of the the "file names" table yet, but that will be taken care of. Would that be an acceptable approach to suggest for inclusion? 2004-01-02 Joel Brobecker * dwarf2read.c (read_partial_die): Add new parameter. (scan_partial_symbols): Update call to read_partial_die. (dwarf2_add_include_psymtab): New function. (dwarf2_build_include_psymtabs): New function. (dwarf2_build_psymtabs_hard): Build the psymtabs for the included files as well. (psymtab_to_symtab_1): Build the symtab of all dependencies as well. Thank you. -- Joel --lEGEL1/lMxI0MVQ2 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="dwarf2read.c.diff" Content-length: 10811 Index: dwarf2read.c =================================================================== RCS file: /nile.c/cvs/Dev/gdb/gdb-6.0/gdb/dwarf2read.c,v retrieving revision 1.5 diff -u -p -r1.5 dwarf2read.c --- dwarf2read.c 19 Nov 2003 18:02:23 -0000 1.5 +++ dwarf2read.c 2 Jan 2004 07:10:08 -0000 @@ -693,7 +693,8 @@ static struct abbrev_info *dwarf2_lookup static char *read_partial_die (struct partial_die_info *, bfd *, char *, - const struct comp_unit_head *); + const struct comp_unit_head *, + unsigned int *); static char *read_full_die (struct die_info **, bfd *, char *, const struct comp_unit_head *); @@ -1196,6 +1197,195 @@ read_comp_unit_head (struct comp_unit_he return info_ptr; } +static void +dwarf2_add_include_psymtab (char *name, + struct partial_symtab *pst, + struct objfile *objfile) +{ + struct partial_symtab *subpst = allocate_psymtab (name, objfile); + + /* Copy the private data from the main psymtab. */ + subpst->section_offsets = pst->section_offsets; + PST_PRIVATE (subpst) = + (char *) obstack_alloc (&objfile->psymbol_obstack, + sizeof (struct dwarf2_pinfo)); + *PST_PRIVATE (subpst) = *PST_PRIVATE (pst); + + subpst->textlow = 0; + subpst->texthigh = 0; + + /* We could save slight bits of space by only making one of these, + shared by the entire set of include files. FIXME-someday. */ + subpst->dependencies = (struct partial_symtab **) + obstack_alloc (&objfile->psymbol_obstack, + sizeof (struct partial_symtab *)); + subpst->dependencies[0] = pst; + subpst->number_of_dependencies = 1; + + subpst->globals_offset = 0; + subpst->n_global_syms = 0; + subpst->statics_offset = 0; + subpst->n_static_syms = 0; + + subpst->readin = 0; + subpst->symtab = 0; + subpst->read_symtab = pst->read_symtab; +} + +static void +dwarf2_build_include_psymtabs (struct comp_unit_head *cu_header, + const unsigned int line_offset, + struct partial_symtab *pst, + struct objfile *objfile) +{ + bfd *abfd = objfile->obfd; + struct line_header *lh; + int file_index; + char *line_ptr; + char *line_end; + unsigned int bytes_read; + unsigned char op_code, extended_op, adj_opcode; // All used??? FIXME:JOEL + char *file_included; + + lh = dwarf_decode_line_header (line_offset, abfd, cu_header); + + if (lh == NULL) + return; /* No includes... */ + + line_ptr = lh->statement_program_start; + line_end = lh->statement_program_end; + + /* FIXME: brobecker: Add comments. */ + file_included = alloca (lh->num_file_names * sizeof (char)); + memset (file_included, 0, lh->num_file_names * sizeof (char)); + + /* Read the statement sequences until there's nothing left. */ + while (line_ptr < line_end) + { + /* state machine registers */ + int end_sequence = 0; + + /* Decode the table. */ + while (!end_sequence) + { + op_code = read_1_byte (abfd, line_ptr); + line_ptr += 1; + + if (op_code >= lh->opcode_base) + { /* Special operand. Nothing else to read. */ + } + else switch (op_code) + { + case DW_LNS_extended_op: + line_ptr += 1; /* ignore length */ + extended_op = read_1_byte (abfd, line_ptr); + line_ptr += 1; + switch (extended_op) + { + case DW_LNE_end_sequence: + end_sequence = 1; + break; + case DW_LNE_set_address: + read_address (abfd, line_ptr, cu_header, &bytes_read); + line_ptr += bytes_read; + break; + case DW_LNE_define_file: + { + // char *cur_file; + // unsigned int dir_index, mod_time, length; + + // cur_file = read_string (abfd, line_ptr, &bytes_read); + // line_ptr += bytes_read; + // dir_index = + // read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + // line_ptr += bytes_read; + // mod_time = + // read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + // line_ptr += bytes_read; + // length = + // read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + // line_ptr += bytes_read; + // add_file_name (lh, cur_file, dir_index, mod_time, length); + /* Ignore for now, but the compiler is allowed to + create the list of files via DW_LNE_define_file + entries too. We will likely need to take this + into account someday. */ + read_string (abfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + } + break; + default: + complaint (&symfile_complaints, + "mangled .debug_line section"); + return; + } + break; + case DW_LNS_copy: + break; + case DW_LNS_advance_pc: + read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + break; + case DW_LNS_advance_line: + read_signed_leb128 (abfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + break; + case DW_LNS_set_file: + { + /* lh->include_dirs and lh->file_names are 0-based, + but the directory and file name numbers in the + statement program are 1-based. */ + unsigned int file; + + file = read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + file_included[file - 1] = 1; + } + break; + case DW_LNS_set_column: + read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + break; + case DW_LNS_negate_stmt: + break; + case DW_LNS_set_basic_block: + break; + case DW_LNS_const_add_pc: + break; + case DW_LNS_fixed_advance_pc: + line_ptr += 2; + break; + default: + { /* Unknown standard opcode, ignore it. */ + int i; + for (i = 0; i < lh->standard_opcode_lengths[op_code]; i++) + { + (void) read_unsigned_leb128 (abfd, line_ptr, &bytes_read); + line_ptr += bytes_read; + } + } + } + } + } + + for (file_index = 0; file_index < lh->num_file_names; file_index++) + if (file_included[file_index] == 1) + { + char *include_name = lh->file_names [file_index].name; + + if (strcmp (include_name, pst->filename) != 0) + dwarf2_add_include_psymtab (include_name, pst, objfile); + } + + free_line_header (lh); +} + /* Build the partial symbol table by doing a quick pass through the .debug_info and .debug_abbrev sections. */ @@ -1262,6 +1452,8 @@ dwarf2_build_psymtabs_hard (struct objfi while (info_ptr < dwarf_info_buffer + dwarf_info_size) { struct comp_unit_head cu_header; + unsigned int line_offset = 0; + beg_of_comp_unit = info_ptr; info_ptr = read_comp_unit_head (&cu_header, info_ptr, abfd); @@ -1307,7 +1499,7 @@ dwarf2_build_psymtabs_hard (struct objfi /* Read the compilation unit die */ info_ptr = read_partial_die (&comp_unit_die, abfd, info_ptr, - &cu_header); + &cu_header, &line_offset); /* Set the language we're debugging */ set_cu_language (comp_unit_die.language); @@ -1373,6 +1565,10 @@ dwarf2_build_psymtabs_hard (struct objfi info_ptr = beg_of_comp_unit + cu_header.length + cu_header.initial_length_size; + + /* Get the list of files included in the current compilation unit, + and build a psymtab for each of them. */ + dwarf2_build_include_psymtabs (&cu_header, line_offset, pst, objfile); } do_cleanups (back_to); } @@ -1421,7 +1617,7 @@ scan_partial_symbols (char *info_ptr, st while (nesting_level) { - info_ptr = read_partial_die (&pdi, abfd, info_ptr, cu_header); + info_ptr = read_partial_die (&pdi, abfd, info_ptr, cu_header, NULL); /* Anonymous namespaces have no name but are interesting. */ @@ -1677,6 +1873,32 @@ psymtab_to_symtab_1 (struct partial_symt struct symtab *symtab; struct cleanup *back_to; struct attribute *attr; + int i; + + for (i = 0; i < pst->number_of_dependencies; i++) + if (!pst->dependencies[i]->readin) + { + /* Inform about additional files that need to be read in. */ + if (info_verbose) + { + fputs_filtered (" ", gdb_stdout); + wrap_here (""); + fputs_filtered ("and ", gdb_stdout); + wrap_here (""); + printf_filtered ("%s...", pst->dependencies[i]->filename); + wrap_here (""); /* Flush output */ + gdb_flush (gdb_stdout); + } + psymtab_to_symtab_1 (pst->dependencies[i]); + } + + if (pst->textlow == 0 && pst->texthigh == 0) + { + /* It's an include file, no symbols to read for it. + Everything is in the parent symtab. */ + pst->readin = 1; + return; + } /* Set local variables from the partial symbol table info. */ offset = DWARF_INFO_OFFSET (pst); @@ -4001,7 +4223,8 @@ dwarf2_lookup_abbrev (unsigned int numbe static char * read_partial_die (struct partial_die_info *part_die, bfd *abfd, - char *info_ptr, const struct comp_unit_head *cu_header) + char *info_ptr, const struct comp_unit_head *cu_header, + unsigned int *line_offset) { unsigned int abbrev_number, bytes_read, i; struct abbrev_info *abbrev; @@ -4096,6 +4319,9 @@ read_partial_die (struct partial_die_inf part_die->sibling = dwarf_info_buffer + dwarf2_get_ref_die_offset (&attr); break; + case DW_AT_stmt_list: + if (line_offset != NULL) + *line_offset = DW_UNSND (&attr); default: break; } @@ -4111,7 +4337,7 @@ read_partial_die (struct partial_die_inf int dummy; spec_ptr = dwarf_info_buffer + dwarf2_get_ref_die_offset (&spec_attr); - read_partial_die (&spec_die, abfd, spec_ptr, cu_header); + read_partial_die (&spec_die, abfd, spec_ptr, cu_header, NULL); if (spec_die.name) { part_die->name = spec_die.name; --lEGEL1/lMxI0MVQ2--