diff options
author | John M Mellor-Crummey <johnmc@rice.edu> | 2021-10-20 18:19:38 -0500 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2021-11-10 11:03:32 +0100 |
commit | dbd44e96f101a81f387ca6ff46910ab74ed7b1dc (patch) | |
tree | 062f1ea987223df99a9dae87aaa4a9e25c294b00 /libdw | |
parent | 5b21e70216b853065fa2fef34273db5f7dcdc88b (diff) | |
download | elfutils-dbd44e96f101a81f387ca6ff46910ab74ed7b1dc.tar.gz |
libdw, readelf: Read inlining info in NVIDIA extended line map
As of CUDA 11.2, NVIDIA added extensions to the line map section
of CUDA binaries to represent inlined functions. These extensions
include
- two new fields in a line table row to represent inline
information: context, and functionname,
- two new DWARF extended opcodes: DW_LNE_NVIDIA_inlined_call,
DW_LNE_NVIDIA_set_function_name,
- an additional word in the line table header that indicates
the offset in the .debug_str function where the function
names for this line table begin, and
A line table row for an inlined function contains a non-zero "context"
value. The “context” field indicates the index of the line table row
that serves as the call site for an inlined context.
The "functionname" field in a line table row is only meaningful if the
"context" field of the row is non-zero. A meaningful "functionname"
field contains an index into the .debug_str section relative to the
base offset established in the line table header; the position in the
.debug_str section indicates the name of the inlined function.
These extensions resemble the proposed DWARF extensions
(http://dwarfstd.org/ShowIssue.php?issue=140906.1) by Cary Coutant,
but are not identical.
This commit integrates support for handling NVIDIA's extended line
maps into elfutil's libdw library, by adding two functions
dwarf_linecontext and dwarf_linefunctionname, and the readelf
--debug-dump=line command line utility.
Signed-off-by: John M Mellor-Crummey <johnmc@rice.edu>
Signed-off-by: Mark Wielaard <mark@klomp.org>
Diffstat (limited to 'libdw')
-rw-r--r-- | libdw/ChangeLog | 19 | ||||
-rw-r--r-- | libdw/Makefile.am | 1 | ||||
-rw-r--r-- | libdw/dwarf.h | 4 | ||||
-rw-r--r-- | libdw/dwarf_getsrclines.c | 36 | ||||
-rw-r--r-- | libdw/dwarf_linecontext.c | 45 | ||||
-rw-r--r-- | libdw/dwarf_linefunctionname.c | 52 | ||||
-rw-r--r-- | libdw/libdw.h | 9 | ||||
-rw-r--r-- | libdw/libdw.map | 6 | ||||
-rw-r--r-- | libdw/libdwP.h | 3 |
9 files changed, 173 insertions, 2 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 38e6efb2..ca742e6b 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,22 @@ +2021-10-20 John M Mellor-Crummey <johnmc@rice.edu> + + * dwarf_linecontext.c: New file. + * dwarf_linefunctionname.c: Likewise. + * Makefile.am (libdw_a_SOURCES): Add dwarf_linecontext.c and + dwarf_linefunctionname.c + * dwarf.h: Add DW_LNE_NVIDIA_inlined_call and + DW_LNE_NVIDIA_set_function_name. + * dwarf_getsrclines.c (struct line_state): Add context and + function_name fields. + (add_new_line): Set context and function_name. + (MAX_STACK_LINES): Reduce to MAX_STACK_ALLOC / 2. + (read_srclines): Initialize context and function_name. Try to + read debug_str_offset if available. Handle + DW_LNE_NVIDIA_inlined_call and DW_LNE_NVIDIA_set_function_name. + * libdw.h (dwarf_linecontext): New declaration. + (dwarf_linefunctionname): Likewise. + * libdw.map (ELFUTILS_0.186): New section. + 2021-11-08 Mark Wielaard <mark@klomp.org> * dwarf_begin_elf.c (scn_dwarf_type): New function. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index 6b7834af..4fda33bd 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -63,6 +63,7 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_linesrc.c dwarf_lineno.c dwarf_lineaddr.c \ dwarf_linecol.c dwarf_linebeginstatement.c \ dwarf_lineendsequence.c dwarf_lineblock.c \ + dwarf_linecontext.c dwarf_linefunctionname.c \ dwarf_lineprologueend.c dwarf_lineepiloguebegin.c \ dwarf_lineisa.c dwarf_linediscriminator.c \ dwarf_lineop_index.c dwarf_line_file.c \ diff --git a/libdw/dwarf.h b/libdw/dwarf.h index 19a4be96..3ce7f236 100644 --- a/libdw/dwarf.h +++ b/libdw/dwarf.h @@ -844,6 +844,10 @@ enum DW_LNE_set_discriminator = 4, DW_LNE_lo_user = 128, + + DW_LNE_NVIDIA_inlined_call = 144, + DW_LNE_NVIDIA_set_function_name = 145, + DW_LNE_hi_user = 255 }; diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c index 8fc48e1d..2c1d7a40 100644 --- a/libdw/dwarf_getsrclines.c +++ b/libdw/dwarf_getsrclines.c @@ -93,6 +93,8 @@ struct line_state struct linelist *linelist; size_t nlinelist; unsigned int end_sequence; + unsigned int context; + unsigned int function_name; }; static inline void @@ -139,6 +141,8 @@ add_new_line (struct line_state *state, struct linelist *new_line) SET (epilogue_begin); SET (isa); SET (discriminator); + SET (context); + SET (function_name); #undef SET @@ -161,7 +165,7 @@ read_srclines (Dwarf *dbg, the stack. Stack allocate some entries, only dynamically malloc when more than MAX. */ #define MAX_STACK_ALLOC 4096 -#define MAX_STACK_LINES MAX_STACK_ALLOC +#define MAX_STACK_LINES (MAX_STACK_ALLOC / 2) #define MAX_STACK_FILES (MAX_STACK_ALLOC / 4) #define MAX_STACK_DIRS (MAX_STACK_ALLOC / 16) @@ -180,7 +184,9 @@ read_srclines (Dwarf *dbg, .prologue_end = false, .epilogue_begin = false, .isa = 0, - .discriminator = 0 + .discriminator = 0, + .context = 0, + .function_name = 0 }; /* The dirs normally go on the stack, but if there are too many @@ -648,6 +654,13 @@ read_srclines (Dwarf *dbg, } } + unsigned int debug_str_offset = 0; + if (unlikely (linep == header_start + header_length - 4)) + { + /* CUBINs contain an unsigned 4-byte offset */ + debug_str_offset = read_4ubyte_unaligned_inc (dbg, linep); + } + /* Consistency check. */ if (unlikely (linep != header_start + header_length)) { @@ -753,6 +766,8 @@ read_srclines (Dwarf *dbg, state.epilogue_begin = false; state.isa = 0; state.discriminator = 0; + state.context = 0; + state.function_name = 0; break; case DW_LNE_set_address: @@ -831,6 +846,23 @@ read_srclines (Dwarf *dbg, get_uleb128 (state.discriminator, linep, lineendp); break; + case DW_LNE_NVIDIA_inlined_call: + if (unlikely (linep >= lineendp)) + goto invalid_data; + get_uleb128 (state.context, linep, lineendp); + if (unlikely (linep >= lineendp)) + goto invalid_data; + get_uleb128 (state.function_name, linep, lineendp); + state.function_name += debug_str_offset; + break; + + case DW_LNE_NVIDIA_set_function_name: + if (unlikely (linep >= lineendp)) + goto invalid_data; + get_uleb128 (state.function_name, linep, lineendp); + state.function_name += debug_str_offset; + break; + default: /* Unknown, ignore it. */ if (unlikely ((size_t) (lineendp - (linep - 1)) < len)) diff --git a/libdw/dwarf_linecontext.c b/libdw/dwarf_linecontext.c new file mode 100644 index 00000000..84572e22 --- /dev/null +++ b/libdw/dwarf_linecontext.c @@ -0,0 +1,45 @@ +/* Return context in line. + This file is part of elfutils. + Written by John Mellor-Crummey <johnmc@rice.edu>, 2021. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "libdwP.h" + + +Dwarf_Line* +dwarf_linecontext (Dwarf_Lines* lines, Dwarf_Line *line) +{ + if (lines == NULL || line == NULL) + return NULL; + if (line->context == 0 || line->context >= lines->nlines) + return NULL; + + return lines->info + (line->context - 1); +} diff --git a/libdw/dwarf_linefunctionname.c b/libdw/dwarf_linefunctionname.c new file mode 100644 index 00000000..e194d212 --- /dev/null +++ b/libdw/dwarf_linefunctionname.c @@ -0,0 +1,52 @@ +/* Return function name in line. + This file is part of elfutils. + Written by John Mellor-Crummey <johnmc@rice.edu>, 2021. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> +#include "libdwP.h" + + +const char * +dwarf_linefunctionname (Dwarf *dbg, Dwarf_Line *line) +{ + if (dbg == NULL || line == NULL) + return NULL; + if (line->context == 0) + return NULL; + + Elf_Data *str_data = dbg->sectiondata[IDX_debug_str]; + if (str_data == NULL || line->function_name >= str_data->d_size + || memchr (str_data->d_buf + line->function_name, '\0', + str_data->d_size - line->function_name) == NULL) + return NULL; + + return (char *) str_data->d_buf + line->function_name; +} diff --git a/libdw/libdw.h b/libdw/libdw.h index 77174d28..64d1689a 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -701,6 +701,15 @@ extern int dwarf_linediscriminator (Dwarf_Line *line, unsigned int *discp) extern const char *dwarf_linesrc (Dwarf_Line *line, Dwarf_Word *mtime, Dwarf_Word *length); +/* Return the caller of this line if inlined. If not inlined, + return NULL. */ +extern Dwarf_Line *dwarf_linecontext (Dwarf_Lines *lines, Dwarf_Line *line); + +/* Return the function name in this line record. If this line is + inlined, this is the name of the function that was inlined. If this line + is not inlined, return NULL. */ +extern const char *dwarf_linefunctionname (Dwarf *dbg, Dwarf_Line *line); + /* Return file information. The returned string is NULL when an error occurred, or the file path. The file path is either absolute or relative to the compilation directory. See dwarf_decl_file. */ diff --git a/libdw/libdw.map b/libdw/libdw.map index 8ab0a2a0..4f530378 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -360,3 +360,9 @@ ELFUTILS_0.177 { # presume that NULL is only returned on error (otherwise ELF_K_NONE). dwelf_elf_begin; } ELFUTILS_0.175; + +ELFUTILS_0.186 { + global: + dwarf_linecontext; + dwarf_linefunctionname; +} ELFUTILS_0.177; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 48f3a943..360ad01a 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -303,6 +303,9 @@ struct Dwarf_Line_s unsigned int op_index:8; unsigned int isa:8; unsigned int discriminator:24; + /* These are currently only used for the NVIDIA extensions. */ + unsigned int context; + unsigned int function_name; }; struct Dwarf_Lines_s |