summaryrefslogtreecommitdiff
path: root/libdw
diff options
context:
space:
mode:
authorJohn M Mellor-Crummey <johnmc@rice.edu>2021-10-20 18:19:38 -0500
committerMark Wielaard <mark@klomp.org>2021-11-10 11:03:32 +0100
commitdbd44e96f101a81f387ca6ff46910ab74ed7b1dc (patch)
tree062f1ea987223df99a9dae87aaa4a9e25c294b00 /libdw
parent5b21e70216b853065fa2fef34273db5f7dcdc88b (diff)
downloadelfutils-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/ChangeLog19
-rw-r--r--libdw/Makefile.am1
-rw-r--r--libdw/dwarf.h4
-rw-r--r--libdw/dwarf_getsrclines.c36
-rw-r--r--libdw/dwarf_linecontext.c45
-rw-r--r--libdw/dwarf_linefunctionname.c52
-rw-r--r--libdw/libdw.h9
-rw-r--r--libdw/libdw.map6
-rw-r--r--libdw/libdwP.h3
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