From b5a8598f01c8e40163449bff173909eff824647b Mon Sep 17 00:00:00 2001 From: Antoine Busque Date: Wed, 9 Sep 2015 05:05:27 -0400 Subject: [PATCH] Initial implementation of the debuginfo API MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This patchset adds so_info and durin, which are used in debug info or callsite analysis. The developer is expected to use the SO info API documented in `include/babeltrace/so-info.h`. Durin is only used internally by so_info as a wrapper around `libdw`. The analysis itself is implemented in the new `lib/debuginfo.c`. This introduces a dependency on `libelf` and `libdw` from elfutils. The feature can be disabled at configure time with the `--disable-debuginfo` flag. Signed-off-by: Antoine Busque Signed-off-by: Jérémie Galarneau --- configure.ac | 13 + converter/Makefile.am | 4 + converter/babeltrace.c | 21 +- formats/ctf-text/Makefile.am | 4 + formats/ctf-text/ctf-text.c | 4 +- formats/ctf-text/types/integer.c | 3 + formats/ctf/Makefile.am | 4 + formats/ctf/ctf.c | 14 + include/Makefile.am | 5 + include/babeltrace/babeltrace-internal.h | 4 +- include/babeltrace/crc32.h | 55 + include/babeltrace/ctf-ir/metadata.h | 9 + include/babeltrace/debuginfo.h | 72 ++ include/babeltrace/dwarf.h | 225 ++++ include/babeltrace/so-info.h | 202 ++++ include/babeltrace/trace-debuginfo.h | 142 +++ include/babeltrace/types.h | 17 + lib/Makefile.am | 10 + lib/crc32.c | 134 +++ lib/debuginfo.c | 714 +++++++++++++ lib/dwarf.c | 369 +++++++ lib/so-info.c | 1231 ++++++++++++++++++++++ types/integer.c | 4 +- 23 files changed, 3253 insertions(+), 7 deletions(-) create mode 100644 include/babeltrace/crc32.h create mode 100644 include/babeltrace/debuginfo.h create mode 100644 include/babeltrace/dwarf.h create mode 100644 include/babeltrace/so-info.h create mode 100644 include/babeltrace/trace-debuginfo.h create mode 100644 lib/crc32.c create mode 100644 lib/debuginfo.c create mode 100644 lib/dwarf.c create mode 100644 lib/so-info.c diff --git a/configure.ac b/configure.ac index eb2fc9e4..0851f63a 100644 --- a/configure.ac +++ b/configure.ac @@ -231,6 +231,19 @@ else fi +# Optional debuginfo feature (enabled by default) +AC_ARG_ENABLE([debuginfo], [AC_HELP_STRING([--disable-debuginfo], [disable the debuginfo feature])], [], [enable_debuginfo=yes]) + +AM_CONDITIONAL([ENABLE_DEBUGINFO], [test "x$enable_debuginfo" = xyes]) +AS_IF([test "x$enable_debuginfo" = xyes], [ + AC_CHECK_LIB([elf], [elf_version], [], []) + AC_CHECK_LIB([dw], [dwarf_begin], [], []) + AS_IF([test "x$ac_cv_lib_elf_elf_version" = xno || test "x$ac_cv_lib_dw_dwarf_begin" = xno], + [AC_MSG_ERROR(Missing library from elfutils required for debuginfo. You can disable this feature with --disable-debuginfo.)] + ) + AC_DEFINE([ENABLE_DEBUGINFO], [1], [Define to 1 if you enable the 'debuginfo' feature]) +], []) + pkg_modules="gmodule-2.0 >= 2.0.0" PKG_CHECK_MODULES(GMODULE, [$pkg_modules]) AC_SUBST(PACKAGE_LIBS) diff --git a/converter/Makefile.am b/converter/Makefile.am index af95627d..aba3333d 100644 --- a/converter/Makefile.am +++ b/converter/Makefile.am @@ -19,6 +19,10 @@ babeltrace_LDADD = \ $(top_builddir)/formats/bt-dummy/libbabeltrace-dummy.la \ $(top_builddir)/formats/lttng-live/libbabeltrace-lttng-live.la +if ENABLE_DEBUGINFO +babeltrace_LDADD += $(top_builddir)/lib/libdebuginfo.la +endif + babeltrace_log_SOURCES = babeltrace-log.c babeltrace_log_LDADD = \ diff --git a/converter/babeltrace.c b/converter/babeltrace.c index 01e034c4..e5aaab6b 100644 --- a/converter/babeltrace.c +++ b/converter/babeltrace.c @@ -36,6 +36,8 @@ #include #include #include +#include + #include #include #include @@ -70,7 +72,7 @@ static char *opt_input_format, *opt_output_format; */ static GPtrArray *opt_input_paths; static char *opt_output_path; -int opt_stream_intersection; +static int opt_stream_intersection; static struct bt_format *fmt_read; @@ -103,6 +105,7 @@ enum { OPT_CLOCK_GMT, OPT_CLOCK_FORCE_CORRELATE, OPT_STREAM_INTERSECTION, + OPT_DEBUG_DIR, }; /* @@ -133,6 +136,9 @@ static struct poptOption long_options[] = { { "clock-gmt", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL }, { "clock-force-correlate", 0, POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL }, { "stream-intersection", 0, POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL }, +#ifdef ENABLE_DEBUGINFO + { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_DIR, NULL, NULL }, +#endif { NULL, 0, 0, NULL, 0, NULL, NULL }, }; @@ -179,6 +185,10 @@ static void usage(FILE *fp) fprintf(fp, " --clock-force-correlate Assume that clocks are inherently correlated\n"); fprintf(fp, " across traces.\n"); fprintf(fp, " --stream-intersection Only print events when all streams are active.\n"); +#ifdef ENABLE_DEBUGINFO + fprintf(fp, " --debug-info-dir Directory in which to look for debugging information\n"); + fprintf(fp, " files. (default: /usr/lib/debug/)\n"); +#endif list_formats(fp); fprintf(fp, "\n"); } @@ -401,7 +411,13 @@ static int parse_options(int argc, char **argv) case OPT_STREAM_INTERSECTION: opt_stream_intersection = 1; break; - + case OPT_DEBUG_DIR: + opt_debug_dir = (char *) poptGetOptArg(pc); + if (!opt_debug_dir) { + ret = -EINVAL; + goto end; + } + break; default: ret = -EINVAL; goto end; @@ -857,6 +873,7 @@ end: free(opt_input_format); free(opt_output_format); free(opt_output_path); + free(opt_debug_dir); g_ptr_array_free(opt_input_paths, TRUE); if (partial_error) exit(EXIT_FAILURE); diff --git a/formats/ctf-text/Makefile.am b/formats/ctf-text/Makefile.am index 39f8f389..d8180e85 100644 --- a/formats/ctf-text/Makefile.am +++ b/formats/ctf-text/Makefile.am @@ -14,3 +14,7 @@ libbabeltrace_ctf_text_la_LDFLAGS = \ libbabeltrace_ctf_text_la_LIBADD = \ $(top_builddir)/lib/libbabeltrace.la \ $(top_builddir)/formats/ctf/libbabeltrace-ctf.la + +if ENABLE_DEBUGINFO +libbabeltrace_ctf_text_la_LIBADD += $(top_builddir)/lib/libdebuginfo.la +endif diff --git a/formats/ctf-text/ctf-text.c b/formats/ctf-text/ctf-text.c index 2ba08230..00876233 100644 --- a/formats/ctf-text/ctf-text.c +++ b/formats/ctf-text/ctf-text.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -237,7 +238,6 @@ const char *print_loglevel(int value) static int ctf_text_write_event(struct bt_stream_pos *ppos, struct ctf_stream_definition *stream) - { struct ctf_text_stream_pos *pos = container_of(ppos, struct ctf_text_stream_pos, parent); @@ -266,6 +266,8 @@ int ctf_text_write_event(struct bt_stream_pos *ppos, struct ctf_stream_definitio return -EINVAL; } + handle_debug_info_event(stream_class, event); + if (stream->has_timestamp) { set_field_names_print(pos, ITEM_HEADER); if (pos->print_names) diff --git a/formats/ctf-text/types/integer.c b/formats/ctf-text/types/integer.c index 7363b524..34df4fc9 100644 --- a/formats/ctf-text/types/integer.c +++ b/formats/ctf-text/types/integer.c @@ -31,6 +31,7 @@ #include #include #include +#include int ctf_text_integer_write(struct bt_stream_pos *ppos, struct bt_definition *definition) { @@ -124,5 +125,7 @@ int ctf_text_integer_write(struct bt_stream_pos *ppos, struct bt_definition *def return -EINVAL; } + ctf_text_integer_write_debug_info(ppos, definition); + return 0; } diff --git a/formats/ctf/Makefile.am b/formats/ctf/Makefile.am index cfb2bc7e..19207bd7 100644 --- a/formats/ctf/Makefile.am +++ b/formats/ctf/Makefile.am @@ -21,3 +21,7 @@ libbabeltrace_ctf_la_LIBADD = \ metadata/libctf-parser.la \ metadata/libctf-ast.la \ writer/libctf-writer.la + +if ENABLE_DEBUGINFO +libbabeltrace_ctf_la_LIBADD += $(top_builddir)/lib/libdebuginfo.la +endif diff --git a/formats/ctf/ctf.c b/formats/ctf/ctf.c index 4dcec903..8e967098 100644 --- a/formats/ctf/ctf.c +++ b/formats/ctf/ctf.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -84,6 +85,7 @@ uint64_t opt_clock_offset; uint64_t opt_clock_offset_ns; extern int yydebug; +char *opt_debug_dir; /* * TODO: babeltrace_ctf_console_output ensures that we only print @@ -2353,8 +2355,14 @@ struct bt_trace_descriptor *ctf_open_trace(const char *path, int flags, goto error; } + ret = trace_debug_info_create(td); + if (ret) { + goto error; + } + return &td->parent; error: + trace_debug_info_destroy(td); g_free(td); return NULL; } @@ -2523,6 +2531,11 @@ struct bt_trace_descriptor *ctf_open_mmap_trace( if (ret) goto error_free; + ret = trace_debug_info_create(td); + if (ret) { + goto error_free; + } + return &td->parent; error_free: @@ -2674,6 +2687,7 @@ int ctf_close_trace(struct bt_trace_descriptor *tdp) } } free(td->metadata_string); + trace_debug_info_destroy(td); g_free(td); return 0; } diff --git a/include/Makefile.am b/include/Makefile.am index 6b81b234..1f2b558a 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -32,6 +32,11 @@ noinst_HEADERS = \ babeltrace/trace-collection.h \ babeltrace/prio_heap.h \ babeltrace/types.h \ + babeltrace/crc32.h \ + babeltrace/debuginfo.h \ + babeltrace/trace-debuginfo.h \ + babeltrace/dwarf.h \ + babeltrace/so-info.h \ babeltrace/ctf-ir/metadata.h \ babeltrace/ctf/events-internal.h \ babeltrace/ctf/metadata.h \ diff --git a/include/babeltrace/babeltrace-internal.h b/include/babeltrace/babeltrace-internal.h index b879544d..abe1a7f0 100644 --- a/include/babeltrace/babeltrace-internal.h +++ b/include/babeltrace/babeltrace-internal.h @@ -191,11 +191,11 @@ extern int opt_all_field_names, opt_clock_seconds, opt_clock_date, opt_clock_gmt, - opt_clock_force_correlate, - opt_stream_intersection; + opt_clock_force_correlate; extern uint64_t opt_clock_offset; extern uint64_t opt_clock_offset_ns; extern int babeltrace_ctf_console_output; +extern char *opt_debug_dir; #endif diff --git a/include/babeltrace/crc32.h b/include/babeltrace/crc32.h new file mode 100644 index 00000000..4a229d63 --- /dev/null +++ b/include/babeltrace/crc32.h @@ -0,0 +1,55 @@ +#ifndef _BABELTRACE_CRC32_H +#define _BABELTRACE_CRC32_H + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +/** + * Compute a 32-bit cyclic redundancy checksum for a given file. + * + * On success, the out parameter crc is set with the computed checksum + * value, + * + * @param fd File descriptor for the file for which to compute the CRC + * @param crc Out parameter, the computed checksum + * @returns 0 on success, -1 on failure. + */ +BT_HIDDEN +int crc32(int fd, uint32_t *crc); + +#endif /* _BABELTRACE_CRC32_H */ diff --git a/include/babeltrace/ctf-ir/metadata.h b/include/babeltrace/ctf-ir/metadata.h index 0e16f621..80033ff4 100644 --- a/include/babeltrace/ctf-ir/metadata.h +++ b/include/babeltrace/ctf-ir/metadata.h @@ -187,6 +187,10 @@ struct ctf_tracer_env { char tracer_name[TRACER_ENV_LEN]; }; +#ifdef ENABLE_DEBUGINFO +struct debug_info; +#endif + struct ctf_trace { struct bt_trace_descriptor parent; @@ -225,6 +229,11 @@ struct ctf_trace { DIR *dir; int dirfd; int flags; /* open flags */ + +#ifdef ENABLE_DEBUGINFO + /* Debug information for this trace */ + struct debug_info *debug_info; +#endif }; #define CTF_STREAM_SET_FIELD(ctf_stream, field) \ diff --git a/include/babeltrace/debuginfo.h b/include/babeltrace/debuginfo.h new file mode 100644 index 00000000..9d5a9ddb --- /dev/null +++ b/include/babeltrace/debuginfo.h @@ -0,0 +1,72 @@ +#ifndef _BABELTRACE_DEBUGINFO_H +#define _BABELTRACE_DEBUGINFO_H + +/* + * Babeltrace - Debug information state tracker + * + * Copyright (c) 2015 EfficiOS Inc. + * Copyright (c) 2015 Philippe Proulx + * Copyright (c) 2015 Antoine Busque + * Copyright (c) 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +struct debug_info; +struct ctf_event_definition; + +#ifdef ENABLE_DEBUGINFO + +#include +#include +#include +#include + +struct debug_info_source { + /* Strings are owned by debug_info_source. */ + char *func; + uint64_t line_no; + char *filename; +}; + +BT_HIDDEN +struct debug_info *debug_info_create(void); + +BT_HIDDEN +void debug_info_destroy(struct debug_info *debug_info); + +BT_HIDDEN +void debug_info_handle_event(struct debug_info *debug_info, + struct ctf_event_definition *event); + +#else /* ifdef ENABLE_DEBUGINFO */ + +static inline +struct debug_info *debug_info_create(void) { return malloc(1); } + +static inline +void debug_info_destroy(struct debug_info *debug_info) { free(debug_info); } + +static inline +void debug_info_handle_event(struct debug_info *debug_info, + struct ctf_event_definition *event) { } + +#endif /* ENABLE_DEBUGINFO */ + +#endif /* _BABELTRACE_DEBUGINFO_H */ diff --git a/include/babeltrace/dwarf.h b/include/babeltrace/dwarf.h new file mode 100644 index 00000000..4baa5918 --- /dev/null +++ b/include/babeltrace/dwarf.h @@ -0,0 +1,225 @@ +#ifndef _BABELTRACE_DWARF_H +#define _BABELTRACE_DWARF_H + +/* + * Babeltrace - DWARF Information Reader + * + * Copyright 2015 Antoine Busque + * + * Author: Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +/* + * bt_dwarf is a wrapper over libdw providing a nicer, higher-level + * interface, to access basic debug information. + */ + +/* + * This structure corresponds to a single compilation unit (CU) for a + * given set of debug information (Dwarf type). + */ +struct bt_dwarf_cu { + Dwarf *dwarf_info; + /* Offset in bytes in the DWARF file to current CU header. */ + Dwarf_Off offset; + /* Offset in bytes in the DWARF file to next CU header. */ + Dwarf_Off next_offset; + /* Size in bytes of CU header */ + size_t header_size; +}; + +/* + * This structure represents a single debug information entry (DIE), + * within a compilation unit (CU). + */ +struct bt_dwarf_die { + struct bt_dwarf_cu *cu; + Dwarf_Die *dwarf_die; + /* + * A depth of 0 represents a root DIE, located in the DWARF + * layout on the same level as its corresponding CU entry. Its + * children DIEs will have a depth of 1, and so forth. + */ + unsigned int depth; +}; + +/** + * Instantiate a structure to access compile units (CU) from a given + * `dwarf_info`. + * + * @param dwarf_info Dwarf instance + * @returns Pointer to the new bt_dwarf_cu on success, + * NULL on failure. + */ +BT_HIDDEN +struct bt_dwarf_cu *bt_dwarf_cu_create(Dwarf *dwarf_info); + +/** + * Destroy the given bt_dwarf_cu instance. + * + * @param cu bt_dwarf_cu instance + */ +BT_HIDDEN +void bt_dwarf_cu_destroy(struct bt_dwarf_cu *cu); + +/** + * Advance the compile unit `cu` to the next one. + * + * On success, `cu`'s offset is set to that of the current compile + * unit in the executable. On failure, `cu` remains unchanged. + * + * @param cu bt_dwarf_cu instance + * @returns 0 on success, 1 if no next CU is available, + * -1 on failure + */ +BT_HIDDEN +int bt_dwarf_cu_next(struct bt_dwarf_cu *cu); + +/** + * Instantiate a structure to access debug information entries (DIE) + * for the given compile unit `cu`. + * + * @param cu bt_dwarf_cu instance + * @returns Pointer to the new bt_dwarf_die on success, + * NULL on failure. + */ +BT_HIDDEN +struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu); + +/** + * Destroy the given bt_dwarf_die instance. + * + * @param die bt_dwarf_die instance + */ +BT_HIDDEN +void bt_dwarf_die_destroy(struct bt_dwarf_die *die); + +/** + * Advance the debug information entry `die` to its first child, if + * any. + * + * @param die bt_dwarf_die instance + * @returns 0 on success, 1 if no child DIE is available, + * -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_child(struct bt_dwarf_die *die); + +/** + * Advance the debug information entry `die` to the next one. + * + * The next DIE is considered to be its sibling on the same level. The + * only exception is when the depth of the given DIE is 0, i.e. a + * newly created bt_dwarf_die, in which case next returns the first + * DIE at depth 1. + * + * The reason for staying at a depth of 1 is that this is where all + * the function DIEs (those with a tag value of DW_TAG_subprogram) are + * located, from which more specific child DIEs can then be accessed + * if needed via bt_dwarf_die_child. + * + * @param die bt_dwarf_die instance + * @returns 0 on success, 1 if no other siblings are available, -1 on + * failure + */ +BT_HIDDEN +int bt_dwarf_die_next(struct bt_dwarf_die *die); + +/** + * Get a DIE's tag. + * + * On success, the `tag` out parameter is set to the `die`'s tag's + * value. It remains unchanged on failure. + * + * @param die bt_dwarf_die instance + * @param tag Out parameter, the DIE's tag value + * @returns 0 on success, -1 on failure. + */ +BT_HIDDEN +int bt_dwarf_die_get_tag(struct bt_dwarf_die *die, int *tag); + +/** + * Get a DIE's name. + * + * On success, the `name` out parameter is set to the DIE's name. It + * remains unchanged on failure. + * + * @param die bt_dwarf_die instance + * @param name Out parameter, the DIE's name + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_get_name(struct bt_dwarf_die *die, char **name); + +/** + * Get the full path to the DIE's callsite file. + * + * Only applies to DW_TAG_inlined_subroutine entries. The out + * parameter `filename` is set on success, unchanged on failure. + * + * @param die bt_dwarf_die instance + * @param filename Out parameter, the filename for the subroutine's + * callsite + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_get_call_file(struct bt_dwarf_die *die, char **filename); + +/** + * Get line number for the DIE's callsite. + * + * Only applies to DW_TAG_inlined_subroutine entries. The out + * parameter `line_no` is set on success, unchanged on failure. + * + * @param die bt_dwarf_die instance + * @param line_no Out parameter, the line number for the + * subroutine's callsite + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_get_call_line(struct bt_dwarf_die *die, + uint64_t *line_no); + +/** + * Verifies whether a given DIE contains the virtual memory address + * `addr`. + * + * On success, the out parameter `contains` is set with the boolean + * value indicating whether the DIE's range covers `addr`. On failure, + * it remains unchanged. + * + * @param die bt_dwarf_die instance + * @param addr The memory address to verify + * @param contains Out parameter, 1 if addr is contained, + * 0 if not + * @returns 0 on succes, -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_contains_addr(struct bt_dwarf_die *die, uint64_t addr, + int *contains); + +#endif /* _BABELTRACE_DWARF_H */ diff --git a/include/babeltrace/so-info.h b/include/babeltrace/so-info.h new file mode 100644 index 00000000..453aa8a7 --- /dev/null +++ b/include/babeltrace/so-info.h @@ -0,0 +1,202 @@ +#ifndef _BABELTRACE_SO_INFO_H +#define _BABELTRACE_SO_INFO_H + +/* + * Babeltrace - Executable and Shared Object Debug Info Reader + * + * Copyright 2015 Antoine Busque + * + * Author: Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#define DEFAULT_DEBUG_DIR "/usr/lib/debug" +#define DEBUG_SUBDIR ".debug/" +#define BUILD_ID_SUBDIR ".build-id/" +#define BUILD_ID_SUFFIX ".debug" + +struct so_info { + /* Base virtual memory address. */ + uint64_t low_addr; + /* Upper bound of exec address space. */ + uint64_t high_addr; + /* Size of exec address space. */ + uint64_t memsz; + /* Paths to ELF and DWARF files. */ + char *elf_path; + char *dwarf_path; + /* libelf and libdw objects representing the files. */ + Elf *elf_file; + Dwarf *dwarf_info; + /* Optional build ID info. */ + uint8_t *build_id; + size_t build_id_len; + /* Optional debug link info. */ + char *dbg_link_filename; + uint32_t dbg_link_crc; + /* FDs to ELF and DWARF files. */ + int elf_fd; + int dwarf_fd; + /* Denotes whether the executable is position independent code. */ + bool is_pic:1; + /* Denotes whether the SO only has ELF symbols and no DWARF info. */ + bool is_elf_only:1; +}; + +struct source_location { + uint64_t line_no; + char *filename; +}; + +/** + * Initializes the so_info framework. Call this before calling + * anything else. + * + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int so_info_init(void); + +/** + * Instantiate a structure representing an ELF executable, possibly + * with DWARF info, located at the given path. + * + * @param path Path to the ELF file + * @param low_addr Base address of the executable + * @param memsz In-memory size of the executable + * @returns Pointer to the new so_info on success, + * NULL on failure. + */ +BT_HIDDEN +struct so_info *so_info_create(const char *path, uint64_t low_addr, + uint64_t memsz); + +/** + * Destroy the given so_info instance + * + * @param so so_info instance to destroy + */ +BT_HIDDEN +void so_info_destroy(struct so_info *so); + +/** + * Sets the build ID information for a given so_info instance. + * + * @param so The so_info instance for which to set + * the build ID + * @param build_id Array of bytes containing the actual ID + * @param build_id_len Length in bytes of the build_id + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int so_info_set_build_id(struct so_info *so, uint8_t *build_id, + size_t build_id_len); + +/** + * Sets the debug link information for a given so_info instance. + * + * @param so The so_info instance for which to set + * the debug link + * @param filename Name of the separate debug info file + * @param crc Checksum for the debug info file + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int so_info_set_debug_link(struct so_info *so, char *filename, uint32_t crc); + +/** + * Returns whether or not the given SO info \p so contains the address + * \p addr. + * + * @param so so_info instance + * @param addr Address to lookup + * @returns 1 if \p so contains \p addr, 0 if it does not, + * -1 on failure + */ +static inline +int so_info_has_address(struct so_info *so, uint64_t addr) +{ + if (!so) { + return -1; + } + + return addr >= so->low_addr && addr < so->high_addr; +} + +/** + * Get the name of the function containing a given address within an + * executable. + * + * If no DWARF info is available, the function falls back to ELF + * symbols and the "function name" is in fact the name of the closest + * symbol, followed by the offset between the symbol and the address. + * + * On success, if found, the out parameter `func_name` is set. The ownership + * of `func_name` is passed to the caller. On failure, `func_name` remains + * unchanged. + * + * @param so so_info instance for the executable containing + * the address + * @param addr Virtual memory address for which to find the + * function name + * @param func_name Out parameter, the function name. + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int so_info_lookup_function_name(struct so_info *so, uint64_t addr, + char **func_name); + +/** + * Get the source location (file name and line number) for a given + * address within an executable. + * + * If no DWARF info is available, the source location cannot be found + * and the function will return unsuccesfully. + * + * On success, if found, the out parameter `src_loc` is set. The ownership + * of `src_loc` is passed to the caller. On failure, `src_loc` remains + * unchanged. + * + * @param so so_info instance for the executable containing + * the address + * @param addr Virtual memory address for which to find the + * source location + * @param src_loc Out parameter, the source location + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int so_info_lookup_source_location(struct so_info *so, uint64_t addr, + struct source_location **src_loc); + +/** + * Destroy the given source_location instance + * + * @param src_loc source_location instance to destroy + */ +BT_HIDDEN +void source_location_destroy(struct source_location *src_loc); + +#endif /* _BABELTRACE_SO_INFO_H */ diff --git a/include/babeltrace/trace-debuginfo.h b/include/babeltrace/trace-debuginfo.h new file mode 100644 index 00000000..adbad09d --- /dev/null +++ b/include/babeltrace/trace-debuginfo.h @@ -0,0 +1,142 @@ +#ifndef _BABELTRACE_TRACE_DEBUGINFO_H +#define _BABELTRACE_TRACE_DEBUGINFO_H + +/* + * Babeltrace - Debug information state tracker wrapper + * + * Copyright (c) 2015 EfficiOS Inc. + * Copyright (c) 2015 Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#ifdef ENABLE_DEBUGINFO + +#include +#include +#include + +static inline +void ctf_text_integer_write_debug_info(struct bt_stream_pos *ppos, + struct bt_definition *definition) +{ + struct definition_integer *integer_definition = + container_of(definition, struct definition_integer, p); + struct ctf_text_stream_pos *pos = ctf_text_pos(ppos); + struct debug_info_source *debug_info_src = + integer_definition->debug_info_src; + + /* Print debug info if available */ + if (debug_info_src) { + if (debug_info_src->func || debug_info_src->filename) { + bool add_comma = false; + + fprintf(pos->fp, ", debug_info = { "); + + if (debug_info_src->func) { + fprintf(pos->fp, "func = \"%s\"", + debug_info_src->func); + add_comma = true; + } + + if (debug_info_src->filename) { + if (add_comma) { + fprintf(pos->fp, ", "); + } + + fprintf(pos->fp, "source_loc = \"%s:%" PRIu64 + "\"", + debug_info_src->filename, + debug_info_src->line_no); + } + + fprintf(pos->fp, " }"); + } + } +} + +static inline +int trace_debug_info_create(struct ctf_trace *trace) +{ + int ret = 0; + + if (strcmp(trace->env.domain, "ust") != 0) { + goto end; + } + + if (strcmp(trace->env.tracer_name, "lttng-ust") != 0) { + goto end; + } + + trace->debug_info = debug_info_create(); + if (!trace->debug_info) { + ret = -1; + goto end; + } + +end: + return ret; +} + +static inline +void trace_debug_info_destroy(struct ctf_trace *trace) +{ + debug_info_destroy(trace->debug_info); +} + +static inline +void handle_debug_info_event(struct ctf_stream_declaration *stream_class, + struct ctf_event_definition *event) +{ + debug_info_handle_event(stream_class->trace->debug_info, event); +} + +#else /* #ifdef ENABLE_DEBUGINFO */ + +static inline +void ctf_text_integer_write_debug_info(struct bt_stream_pos *ppos, + struct bt_definition *definition) +{ + /* Do nothing. */ +} + +static inline +int trace_debug_info_create(struct ctf_trace *trace) +{ + return 0; +} + +static inline +void trace_debug_info_destroy(struct ctf_trace *trace) +{ + /* Do nothing. */ +} + +static inline +void handle_debug_info_event(struct ctf_stream_declaration *stream_class, + struct ctf_event_definition *event) +{ + /* Do nothing. */ +} + +#endif /* #else #ifdef ENABLE_DEBUGINFO */ + +#endif /* _BABELTRACE_TRACE_DEBUGINFO_H */ diff --git a/include/babeltrace/types.h b/include/babeltrace/types.h index 677818b9..3d040d4f 100644 --- a/include/babeltrace/types.h +++ b/include/babeltrace/types.h @@ -148,6 +148,10 @@ struct declaration_integer { struct ctf_clock *clock; }; +#ifdef ENABLE_DEBUGINFO +struct debug_info_source; +#endif + struct definition_integer { struct bt_definition p; struct declaration_integer *declaration; @@ -156,6 +160,17 @@ struct definition_integer { uint64_t _unsigned; int64_t _signed; } value; + +#ifdef ENABLE_DEBUGINFO + /* + * Debug infos (NULL if not set). + * + * This is extended debug informations set by the CTF input plugin + * itself when available. If it's set, then this integer definition + * is the "_ip" field of the stream event context. + */ + struct debug_info_source *debug_info_src; +#endif }; struct declaration_float { @@ -527,6 +542,8 @@ void bt_append_scope_path(const char *path, GArray *q); */ struct bt_definition *bt_lookup_definition(const struct bt_definition *definition, const char *field_name); +struct bt_definition *bt_lookup_definition_by_quark(const struct bt_definition *definition, + GQuark quark); struct definition_integer *bt_lookup_integer(const struct bt_definition *definition, const char *field_name, int signedness); diff --git a/lib/Makefile.am b/lib/Makefile.am index 348b0a9b..42a94346 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -13,6 +13,16 @@ libbabeltrace_la_SOURCES = babeltrace.c \ libbabeltrace_la_LDFLAGS = -version-info $(BABELTRACE_LIBRARY_VERSION) +if ENABLE_DEBUGINFO +noinst_LTLIBRARIES = libdebuginfo.la + +libdebuginfo_la_SOURCES = debuginfo.c \ + so-info.c \ + dwarf.c \ + crc32.c +libdebuginfo_la_LDFLAGS = -lelf -ldw +endif + libbabeltrace_la_LIBADD = \ prio_heap/libprio_heap.la \ $(top_builddir)/types/libbabeltrace_types.la \ diff --git a/lib/crc32.c b/lib/crc32.c new file mode 100644 index 00000000..397697f7 --- /dev/null +++ b/lib/crc32.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) + +/* generated using the AUTODIN II polynomial + * x^32 + x^26 + x^23 + x^22 + x^16 + + * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 + */ +static const uint32_t crctab[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +int crc32(int fd, uint32_t *crc) +{ + int nr; + uint32_t _crc = ~0; + char buf[BUFSIZ], *p; + + if (fd < 0 || !crc) { + goto error; + } + + while ((nr = read(fd, buf, sizeof(buf))) > 0) { + for (p = buf; nr--; ++p) { + CRC(_crc, *p); + } + } + + if (nr < 0) { + goto error; + } + + *crc = ~_crc; + return 0; + +error: + return -1; +} diff --git a/lib/debuginfo.c b/lib/debuginfo.c new file mode 100644 index 00000000..480d5b29 --- /dev/null +++ b/lib/debuginfo.c @@ -0,0 +1,714 @@ +/* + * Babeltrace - Debug Information State Tracker + * + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015 Philippe Proulx + * Copyright (c) 2015 Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +struct proc_debug_info_sources { + /* + * Hash table: base address to so info; owned by + * proc_debug_info_sources. + */ + GHashTable *baddr_to_so_info; + + /* + * Hash table: IP to (struct debug_info_source *); owned by + * proc_debug_info_sources. + */ + GHashTable *ip_to_debug_info_src; +}; + +struct debug_info { + /* + * Hash table of VPIDs (int64_t) to (struct ctf_proc_debug_infos*); + * owned by debug_info. + */ + GHashTable *vpid_to_proc_dbg_info_src; + GQuark q_statedump_soinfo; + GQuark q_statedump_debug_link; + GQuark q_statedump_build_id; + GQuark q_statedump_start; + GQuark q_dl_open; +}; + +static +int debug_info_init(struct debug_info *info) +{ + info->q_statedump_soinfo = g_quark_from_string( + "lttng_ust_statedump:soinfo"); + info->q_statedump_debug_link = g_quark_from_string( + "lttng_ust_statedump:debug_link)"); + info->q_statedump_build_id = g_quark_from_string( + "lttng_ust_statedump:build_id"); + info->q_statedump_start = g_quark_from_string( + "lttng_ust_statedump:start"); + info->q_dl_open = g_quark_from_string("lttng_ust_dl:dlopen"); + + return so_info_init(); +} + +static +void debug_info_source_destroy(struct debug_info_source *debug_info_src) +{ + if (!debug_info_src) { + return; + } + + free(debug_info_src->func); + free(debug_info_src->filename); + g_free(debug_info_src); +} + +static +struct debug_info_source *debug_info_source_create_from_so(struct so_info *so, + uint64_t ip) +{ + int ret; + struct debug_info_source *debug_info_src = NULL; + struct source_location *src_loc = NULL; + + debug_info_src = g_new0(struct debug_info_source, 1); + + if (!debug_info_src) { + goto end; + } + + /* Lookup function name */ + ret = so_info_lookup_function_name(so, ip, &debug_info_src->func); + if (ret) { + goto error; + } + + /* Can't retrieve src_loc from ELF only, skip it */ + if (so->is_elf_only) { + goto end; + } + + /* Lookup source location */ + ret = so_info_lookup_source_location(so, ip, &src_loc); + if (ret) { + goto error; + } + + if (src_loc) { + debug_info_src->line_no = src_loc->line_no; + + if (src_loc->filename) { + debug_info_src->filename = strdup(src_loc->filename); + + if (!debug_info_src->filename) { + goto error; + } + } + + source_location_destroy(src_loc); + } + +end: + return debug_info_src; + +error: + debug_info_source_destroy(debug_info_src); + return NULL; +} + +static +void proc_debug_info_sources_destroy( + struct proc_debug_info_sources *proc_dbg_info_src) +{ + if (!proc_dbg_info_src) { + return; + } + + if (proc_dbg_info_src->baddr_to_so_info) { + g_hash_table_destroy(proc_dbg_info_src->baddr_to_so_info); + } + + if (proc_dbg_info_src->ip_to_debug_info_src) { + g_hash_table_destroy(proc_dbg_info_src->ip_to_debug_info_src); + } + + g_free(proc_dbg_info_src); +} + +static +struct proc_debug_info_sources *proc_debug_info_sources_create(void) +{ + struct proc_debug_info_sources *proc_dbg_info_src = NULL; + + proc_dbg_info_src = g_new0(struct proc_debug_info_sources, 1); + if (!proc_dbg_info_src) { + goto end; + } + + proc_dbg_info_src->baddr_to_so_info = g_hash_table_new_full( + NULL, NULL, NULL, (GDestroyNotify) so_info_destroy); + if (!proc_dbg_info_src->baddr_to_so_info) { + goto error; + } + + proc_dbg_info_src->ip_to_debug_info_src = g_hash_table_new_full( + NULL, NULL, NULL, + (GDestroyNotify) debug_info_source_destroy); + if (!proc_dbg_info_src->ip_to_debug_info_src) { + goto error; + } + +end: + return proc_dbg_info_src; + +error: + proc_debug_info_sources_destroy(proc_dbg_info_src); + return NULL; +} + +static +struct proc_debug_info_sources *proc_debug_info_sources_ht_get_entry( + GHashTable *ht, int64_t vpid) +{ + gpointer key = (gpointer) vpid; + struct proc_debug_info_sources *proc_dbg_info_src = NULL; + + /* Exists? Return it */ + proc_dbg_info_src = g_hash_table_lookup(ht, key); + if (proc_dbg_info_src) { + goto end; + } + + /* Otherwise, create and return it */ + proc_dbg_info_src = proc_debug_info_sources_create(); + if (!proc_dbg_info_src) { + goto end; + } + + g_hash_table_insert(ht, key, proc_dbg_info_src); +end: + return proc_dbg_info_src; +} + +static +struct debug_info_source *proc_debug_info_sources_get_entry( + struct proc_debug_info_sources *proc_dbg_info_src, uint64_t ip) +{ + struct debug_info_source *debug_info_src = NULL; + gint64 key = (gint64) ip; + GHashTableIter iter; + gpointer baddr, value; + + /* Look in IP to debug infos hash table first. */ + debug_info_src = g_hash_table_lookup( + proc_dbg_info_src->ip_to_debug_info_src, + (gpointer) key); + if (debug_info_src) { + goto end; + } + + /* Check in all so_infos. */ + g_hash_table_iter_init(&iter, proc_dbg_info_src->baddr_to_so_info); + + while (g_hash_table_iter_next(&iter, &baddr, &value)) + { + struct so_info *so = value; + + if (!so_info_has_address(value, ip)) { + continue; + } + + /* Found; add it to cache. */ + debug_info_src = debug_info_source_create_from_so(so, ip); + if (debug_info_src) { + g_hash_table_insert( + proc_dbg_info_src->ip_to_debug_info_src, + (gpointer) key, debug_info_src); + } + break; + } + +end: + return debug_info_src; +} + +BT_HIDDEN +struct debug_info_source *debug_info_query(struct debug_info *debug_info, + int64_t vpid, uint64_t ip) +{ + struct debug_info_source *dbg_info_src = NULL; + struct proc_debug_info_sources *proc_dbg_info_src; + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + dbg_info_src = proc_debug_info_sources_get_entry( + proc_dbg_info_src, ip); + if (!dbg_info_src) { + goto end; + } + +end: + return dbg_info_src; +} + +BT_HIDDEN +struct debug_info *debug_info_create(void) +{ + int ret; + struct debug_info *debug_info; + + debug_info = g_new0(struct debug_info, 1); + if (!debug_info) { + goto end; + } + + debug_info->vpid_to_proc_dbg_info_src = g_hash_table_new_full(NULL, + NULL, NULL, + (GDestroyNotify) proc_debug_info_sources_destroy); + if (!debug_info->vpid_to_proc_dbg_info_src) { + goto error; + } + + ret = debug_info_init(debug_info); + if (ret) { + goto error; + } + +end: + return debug_info; +error: + g_free(debug_info); + return NULL; +} + +BT_HIDDEN +void debug_info_destroy(struct debug_info *debug_info) +{ + if (!debug_info) { + goto end; + } + + if (debug_info->vpid_to_proc_dbg_info_src) { + g_hash_table_destroy(debug_info->vpid_to_proc_dbg_info_src); + } + + g_free(debug_info); +end: + return; +} + +static +void handle_statedump_build_id_event(struct debug_info *debug_info, + struct ctf_event_definition *event_def) +{ + struct proc_debug_info_sources *proc_dbg_info_src; + struct bt_definition *event_fields_def = NULL; + struct bt_definition *sec_def = NULL; + struct bt_definition *baddr_def = NULL; + struct bt_definition *vpid_def = NULL; + struct bt_definition *build_id_def = NULL; + struct definition_sequence *build_id_seq; + struct so_info *so = NULL; + int i; + int64_t vpid; + uint64_t baddr; + uint8_t *build_id = NULL; + uint64_t build_id_len; + + event_fields_def = (struct bt_definition *) event_def->event_fields; + sec_def = (struct bt_definition *) + event_def->stream->stream_event_context; + + if (!event_fields_def || !sec_def) { + goto end; + } + + baddr_def = bt_lookup_definition(event_fields_def, "_baddr"); + if (!baddr_def) { + goto end; + } + + vpid_def = bt_lookup_definition(sec_def, "_vpid"); + if (!vpid_def) { + goto end; + } + + build_id_def = bt_lookup_definition(event_fields_def, "_build_id"); + if (!build_id_def) { + goto end; + } + + if (baddr_def->declaration->id != CTF_TYPE_INTEGER) { + goto end; + } + + if (vpid_def->declaration->id != CTF_TYPE_INTEGER) { + goto end; + } + + if (build_id_def->declaration->id != CTF_TYPE_SEQUENCE) { + goto end; + } + + baddr = bt_get_unsigned_int(baddr_def); + vpid = bt_get_signed_int(vpid_def); + build_id_seq = container_of(build_id_def, + struct definition_sequence, p); + build_id_len = build_id_seq->length->value._unsigned; + + build_id = g_malloc(build_id_len); + if (!build_id) { + goto end; + } + + for (i = 0; i < build_id_len; ++i) { + struct bt_definition **field; + + field = (struct bt_definition **) &g_ptr_array_index( + build_id_seq->elems, i); + build_id[i] = bt_get_unsigned_int(*field); + } + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + so = g_hash_table_lookup(proc_dbg_info_src->baddr_to_so_info, + (gpointer) &baddr); + if (!so) { + /* + * The build_id event comes after the so has been + * created. If it isn't found, just ignore this event. + */ + goto end; + } + + so_info_set_build_id(so, build_id, build_id_len); + +end: + free(build_id); + return; +} + +static +void handle_statedump_debug_link_event(struct debug_info *debug_info, + struct ctf_event_definition *event_def) +{ + struct proc_debug_info_sources *proc_dbg_info_src; + struct bt_definition *event_fields_def = NULL; + struct bt_definition *sec_def = NULL; + struct bt_definition *baddr_def = NULL; + struct bt_definition *vpid_def = NULL; + struct bt_definition *filename_def = NULL; + struct bt_definition *crc32_def = NULL; + struct so_info *so = NULL; + int64_t vpid; + uint64_t baddr; + char *filename = NULL; + uint32_t crc32; + + event_fields_def = (struct bt_definition *) event_def->event_fields; + sec_def = (struct bt_definition *) + event_def->stream->stream_event_context; + + if (!event_fields_def || !sec_def) { + goto end; + } + + baddr_def = bt_lookup_definition(event_fields_def, "_baddr"); + if (!baddr_def) { + goto end; + } + + vpid_def = bt_lookup_definition(sec_def, "_vpid"); + if (!vpid_def) { + goto end; + } + + filename_def = bt_lookup_definition(event_fields_def, "_filename"); + if (!filename_def) { + goto end; + } + + crc32_def = bt_lookup_definition(event_fields_def, "_crc32"); + if (!crc32_def) { + goto end; + } + + if (baddr_def->declaration->id != CTF_TYPE_INTEGER) { + goto end; + } + + if (vpid_def->declaration->id != CTF_TYPE_INTEGER) { + goto end; + } + + if (filename_def->declaration->id != CTF_TYPE_STRING) { + goto end; + } + + if (crc32_def->declaration->id != CTF_TYPE_INTEGER) { + goto end; + } + + baddr = bt_get_unsigned_int(baddr_def); + vpid = bt_get_signed_int(vpid_def); + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + so = g_hash_table_lookup(proc_dbg_info_src->baddr_to_so_info, + (gpointer) &baddr); + if (!so) { + /* + * The debug_link event comes after the so has been + * created. If it isn't found, just ignore this event. + */ + goto end; + } + + filename = bt_get_string(filename_def); + crc32 = bt_get_unsigned_int(crc32_def); + + so_info_set_debug_link(so, filename, crc32); + +end: + return; +} + +static +void handle_statedump_soinfo_event(struct debug_info *debug_info, + struct ctf_event_definition *event_def) +{ + struct bt_definition *baddr_def = NULL; + struct bt_definition *memsz_def = NULL; + struct bt_definition *sopath_def = NULL; + struct bt_definition *vpid_def = NULL; + struct bt_definition *event_fields_def = NULL; + struct bt_definition *sec_def = NULL; + struct proc_debug_info_sources *proc_dbg_info_src; + struct so_info *so; + uint64_t baddr, memsz; + int64_t vpid; + const char *sopath; + + event_fields_def = (struct bt_definition *) event_def->event_fields; + sec_def = (struct bt_definition *) + event_def->stream->stream_event_context; + + if (!event_fields_def || !sec_def) { + goto end; + } + + baddr_def = bt_lookup_definition(event_fields_def, "_baddr"); + if (!baddr_def) { + goto end; + } + + memsz_def = bt_lookup_definition(event_fields_def, "_memsz"); + if (!memsz_def) { + goto end; + } + + sopath_def = bt_lookup_definition(event_fields_def, "_sopath"); + if (!sopath_def) { + goto end; + } + + vpid_def = bt_lookup_definition(sec_def, "_vpid"); + if (!vpid_def) { + goto end; + } + + if (baddr_def->declaration->id != CTF_TYPE_INTEGER) { + goto end; + } + + if (memsz_def->declaration->id != CTF_TYPE_INTEGER) { + goto end; + } + + if (sopath_def->declaration->id != CTF_TYPE_STRING) { + goto end; + } + + if (vpid_def->declaration->id != CTF_TYPE_INTEGER) { + goto end; + } + + baddr = bt_get_unsigned_int(baddr_def); + memsz = bt_get_unsigned_int(memsz_def); + sopath = bt_get_string(sopath_def); + vpid = bt_get_signed_int(vpid_def); + + if (!sopath) { + goto end; + } + + if (memsz == 0) { + /* Ignore VDSO. */ + goto end; + } + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + so = g_hash_table_lookup(proc_dbg_info_src->baddr_to_so_info, + (gpointer) baddr); + if (so) { + goto end; + } + + so = so_info_create(sopath, baddr, memsz); + if (!so) { + goto end; + } + + g_hash_table_insert(proc_dbg_info_src->baddr_to_so_info, + (gpointer) baddr, so); + +end: + return; +} + +static +void handle_statedump_start(struct debug_info *debug_info, + struct ctf_event_definition *event_def) +{ + struct bt_definition *vpid_def = NULL; + struct bt_definition *sec_def = NULL; + struct proc_debug_info_sources *proc_dbg_info_src; + int64_t vpid; + + sec_def = (struct bt_definition *) + event_def->stream->stream_event_context; + if (!sec_def) { + goto end; + } + + vpid_def = bt_lookup_definition(sec_def, "_vpid"); + if (!vpid_def) { + goto end; + } + + vpid = bt_get_signed_int(vpid_def); + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + g_hash_table_remove_all(proc_dbg_info_src->baddr_to_so_info); + g_hash_table_remove_all(proc_dbg_info_src->ip_to_debug_info_src); + +end: + return; +} + +static +void register_event_debug_infos(struct debug_info *debug_info, + struct ctf_event_definition *event) +{ + struct bt_definition *ip_def, *vpid_def; + int64_t vpid; + uint64_t ip; + struct bt_definition *sec_def; + + /* Get stream event context definition. */ + sec_def = (struct bt_definition *) event->stream->stream_event_context; + if (!sec_def) { + goto end; + } + + /* Get "ip" and "vpid" definitions. */ + vpid_def = bt_lookup_definition((struct bt_definition *) sec_def, + "_vpid"); + ip_def = bt_lookup_definition((struct bt_definition *) sec_def, "_ip"); + + if (!vpid_def || !ip_def) { + goto end; + } + + vpid = bt_get_signed_int(vpid_def); + ip = bt_get_unsigned_int(ip_def); + + /* Get debug info for this context. */ + ((struct definition_integer *) ip_def)->debug_info_src = + debug_info_query(debug_info, vpid, ip); + +end: + return; +} + +BT_HIDDEN +void debug_info_handle_event(struct debug_info *debug_info, + struct ctf_event_definition *event) +{ + struct ctf_event_declaration *event_class; + struct ctf_stream_declaration *stream_class; + + if (!debug_info || !event) { + goto end; + } + + stream_class = event->stream->stream_class; + event_class = g_ptr_array_index(stream_class->events_by_id, + event->stream->event_id); + + if (event_class->name == debug_info->q_statedump_soinfo || + event_class->name == debug_info->q_dl_open) { + /* State dump/dlopen() */ + handle_statedump_soinfo_event(debug_info, event); + } else if (event_class->name == debug_info->q_statedump_start) { + /* Start state dump */ + handle_statedump_start(debug_info, event); + } else if (event_class->name == debug_info->q_statedump_debug_link) { + /* Debug link info */ + handle_statedump_debug_link_event(debug_info, event); + } else if (event_class->name == debug_info->q_statedump_build_id) { + /* Build ID info */ + handle_statedump_build_id_event(debug_info, event); + } else { + /* Other events: register debug infos */ + register_event_debug_infos(debug_info, event); + } + +end: + return; +} diff --git a/lib/dwarf.c b/lib/dwarf.c new file mode 100644 index 00000000..c7e2f448 --- /dev/null +++ b/lib/dwarf.c @@ -0,0 +1,369 @@ +/* + * dwarf.c + * + * Babeltrace - DWARF Information Reader + * + * Copyright 2015 Antoine Busque + * + * Author: Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +BT_HIDDEN +struct bt_dwarf_cu *bt_dwarf_cu_create(Dwarf *dwarf_info) +{ + struct bt_dwarf_cu *cu; + + if (!dwarf_info) { + goto error; + } + + cu = g_new0(struct bt_dwarf_cu, 1); + if (!cu) { + goto error; + } + cu->dwarf_info = dwarf_info; + return cu; + +error: + return NULL; +} + +BT_HIDDEN +void bt_dwarf_cu_destroy(struct bt_dwarf_cu *cu) +{ + g_free(cu); +} + +BT_HIDDEN +int bt_dwarf_cu_next(struct bt_dwarf_cu *cu) +{ + int ret; + Dwarf_Off next_offset; + size_t cu_header_size; + + if (!cu) { + ret = -1; + goto end; + } + + ret = dwarf_nextcu(cu->dwarf_info, cu->next_offset, &next_offset, + &cu_header_size, NULL, NULL, NULL); + if (ret) { + /* ret is -1 on error, 1 if no next CU. */ + goto end; + } + + cu->offset = cu->next_offset; + cu->next_offset = next_offset; + cu->header_size = cu_header_size; + +end: + return ret; +} + +BT_HIDDEN +struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu) +{ + Dwarf_Die *dwarf_die = NULL; + struct bt_dwarf_die *die = NULL; + + if (!cu) { + goto error; + } + + dwarf_die = g_new0(Dwarf_Die, 1); + if (!dwarf_die) { + goto error; + } + + dwarf_die = dwarf_offdie(cu->dwarf_info, cu->offset + cu->header_size, + dwarf_die); + if (!dwarf_die) { + goto error; + } + + die = g_new0(struct bt_dwarf_die, 1); + if (!die) { + goto error; + } + + die->cu = cu; + die->dwarf_die = dwarf_die; + die->depth = 0; + + return die; + +error: + g_free(dwarf_die); + g_free(die); + return NULL; +} + +BT_HIDDEN +void bt_dwarf_die_destroy(struct bt_dwarf_die *die) +{ + if (!die) { + return; + } + + g_free(die->dwarf_die); + g_free(die); +} + +BT_HIDDEN +int bt_dwarf_die_child(struct bt_dwarf_die *die) +{ + int ret; + Dwarf_Die *child_die = NULL; + + if (!die) { + ret = -1; + goto error; + } + + child_die = g_new0(Dwarf_Die, 1); + if (!child_die) { + ret = -1; + goto error; + } + + ret = dwarf_child(die->dwarf_die, child_die); + if (ret) { + /* ret is -1 on error, 1 if no child DIE. */ + goto error; + } + + g_free(die->dwarf_die); + die->dwarf_die = child_die; + die->depth++; + return 0; + +error: + g_free(child_die); + return ret; +} + +BT_HIDDEN +int bt_dwarf_die_next(struct bt_dwarf_die *die) +{ + int ret; + Dwarf_Die *next_die = NULL; + + if (!die) { + ret = -1; + goto error; + } + + next_die = g_new0(Dwarf_Die, 1); + if (!next_die) { + ret = -1; + goto error; + } + + if (die->depth == 0) { + ret = dwarf_child(die->dwarf_die, next_die); + if (ret) { + /* ret is -1 on error, 1 if no child DIE. */ + goto error; + } + + die->depth = 1; + } else { + ret = dwarf_siblingof(die->dwarf_die, next_die); + if (ret) { + /* ret is -1 on error, 1 if we reached end of + * DIEs at this depth. */ + goto error; + } + } + + g_free(die->dwarf_die); + die->dwarf_die = next_die; + return 0; + +error: + g_free(next_die); + return ret; +} + +BT_HIDDEN +int bt_dwarf_die_get_tag(struct bt_dwarf_die *die, int *tag) +{ + int _tag; + + if (!die || !tag) { + goto error; + } + + _tag = dwarf_tag(die->dwarf_die); + if (_tag == DW_TAG_invalid) { + goto error; + } + + *tag = _tag; + return 0; + +error: + return -1; +} + +BT_HIDDEN +int bt_dwarf_die_get_name(struct bt_dwarf_die *die, char **name) +{ + const char *_name; + + if (!die || !name) { + goto error; + } + + _name = dwarf_diename(die->dwarf_die); + if (!_name) { + goto error; + } + + *name = strdup(_name); + if (!*name) { + goto error; + } + + return 0; + +error: + return -1; +} + +BT_HIDDEN +int bt_dwarf_die_get_call_file(struct bt_dwarf_die *die, char **filename) +{ + int ret; + Dwarf_Sword file_no; + const char *_filename = NULL; + Dwarf_Files *src_files = NULL; + Dwarf_Attribute *file_attr = NULL; + struct bt_dwarf_die *cu_die = NULL; + + if (!die || !filename) { + goto error; + } + + file_attr = g_new0(Dwarf_Attribute, 1); + if (!file_attr) { + goto error; + } + + file_attr = dwarf_attr(die->dwarf_die, DW_AT_call_file, file_attr); + if (!file_attr) { + goto error; + } + + ret = dwarf_formsdata(file_attr, &file_no); + if (ret) { + goto error; + } + + cu_die = bt_dwarf_die_create(die->cu); + if (!cu_die) { + goto error; + } + + ret = dwarf_getsrcfiles(cu_die->dwarf_die, &src_files, NULL); + if (ret) { + goto error; + } + + _filename = dwarf_filesrc(src_files, file_no, NULL, NULL); + if (!_filename) { + goto error; + } + + *filename = strdup(_filename); + + bt_dwarf_die_destroy(cu_die); + g_free(file_attr); + + return 0; + +error: + bt_dwarf_die_destroy(cu_die); + g_free(file_attr); + + return -1; +} + +BT_HIDDEN +int bt_dwarf_die_get_call_line(struct bt_dwarf_die *die, + uint64_t *line_no) +{ + int ret = 0; + Dwarf_Attribute *line_attr = NULL; + uint64_t _line_no; + + if (!die || !line_no) { + goto error; + } + + line_attr = g_new0(Dwarf_Attribute, 1); + if (!line_attr) { + goto error; + } + + line_attr = dwarf_attr(die->dwarf_die, DW_AT_call_line, line_attr); + if (!line_attr) { + goto error; + } + + ret = dwarf_formudata(line_attr, &_line_no); + if (ret) { + goto error; + } + + *line_no = _line_no; + g_free(line_attr); + + return 0; + +error: + g_free(line_attr); + + return -1; +} + +BT_HIDDEN +int bt_dwarf_die_contains_addr(struct bt_dwarf_die *die, uint64_t addr, + int *contains) +{ + int ret; + + ret = dwarf_haspc(die->dwarf_die, addr); + if (ret == -1) { + goto error; + } + + *contains = ret; + + return 0; + +error: + return -1; +} diff --git a/lib/so-info.c b/lib/so-info.c new file mode 100644 index 00000000..f1124b34 --- /dev/null +++ b/lib/so-info.c @@ -0,0 +1,1231 @@ +/* + * so-info.c + * + * Babeltrace - Executable and Shared Object Debug Info Reader + * + * Copyright 2015 Antoine Busque + * + * Author: Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * An address printed in hex is at most 20 bytes (16 for 64-bits + + * leading 0x + optional leading '+' if addr is an offset + null + * character). + */ +#define ADDR_STR_LEN 20 + +BT_HIDDEN +int so_info_init(void) +{ + int ret = 0; + + if (elf_version(EV_CURRENT) == EV_NONE) { + fprintf(stderr, "ELF library initialization failed: %s\n", + elf_errmsg(-1)); + ret = -1; + } + + return ret; +} + +BT_HIDDEN +struct so_info *so_info_create(const char *path, uint64_t low_addr, + uint64_t memsz) +{ + struct so_info *so = NULL; + GElf_Ehdr *ehdr = NULL; + + if (!path) { + goto error; + } + + so = g_new0(struct so_info, 1); + if (!so) { + goto error; + } + + so->elf_path = strdup(path); + if (!so->elf_path) { + goto error; + } + + so->elf_fd = open(path, O_RDONLY); + if (so->elf_fd < 0) { + fprintf(stderr, "Failed to open %s\n", path); + goto error; + } + + so->elf_file = elf_begin(so->elf_fd, ELF_C_READ, NULL); + if (!so->elf_file) { + fprintf(stderr, "elf_begin failed: %s\n", elf_errmsg(-1)); + goto error; + } + + if (elf_kind(so->elf_file) != ELF_K_ELF) { + fprintf(stderr, "Error: %s is not an ELF object\n", + so->elf_path); + goto error; + } + + ehdr = g_new0(GElf_Ehdr, 1); + if (!ehdr) { + goto error; + } + + if (!gelf_getehdr(so->elf_file, ehdr)) { + fprintf(stderr, "Error: couldn't get ehdr for %s\n", + so->elf_path); + goto error; + } + + /* Position independent code has an e_type value of ET_DYN. */ + so->is_pic = ehdr->e_type == ET_DYN; + so->memsz = memsz; + so->low_addr = low_addr; + so->high_addr = so->low_addr + so->memsz; + + g_free(ehdr); + return so; + +error: + g_free(ehdr); + so_info_destroy(so); + return NULL; +} + +BT_HIDDEN +void so_info_destroy(struct so_info *so) +{ + if (!so) { + return; + } + + dwarf_end(so->dwarf_info); + + free(so->elf_path); + free(so->dwarf_path); + free(so->build_id); + free(so->dbg_link_filename); + + elf_end(so->elf_file); + + close(so->elf_fd); + close(so->dwarf_fd); + + g_free(so); +} + +BT_HIDDEN +int so_info_set_build_id(struct so_info *so, uint8_t *build_id, + size_t build_id_len) +{ + if (!so || !build_id) { + goto error; + } + + so->build_id = malloc(build_id_len); + if (!so->build_id) { + goto error; + } + + memcpy(so->build_id, build_id, build_id_len); + so->build_id_len = build_id_len; + + /* + * Reset the is_elf_only flag in case it had been set + * previously, because we might find separate debug info using + * the new build id information. + */ + so->is_elf_only = false; + + return 0; + +error: + + return -1; +} + +BT_HIDDEN +int so_info_set_debug_link(struct so_info *so, char *filename, uint32_t crc) +{ + if (!so || !filename) { + goto error; + } + + so->dbg_link_filename = strdup(filename); + if (!so->dbg_link_filename) { + goto error; + } + + so->dbg_link_crc = crc; + + /* + * Reset the is_elf_only flag in case it had been set + * previously, because we might find separate debug info using + * the new build id information. + */ + so->is_elf_only = false; + + return 0; + +error: + + return -1; +} + +/** + * Tries to read DWARF info from the location given by path, and + * attach it to the given so_info instance if it exists. + * + * @param so so_info instance for which to set DWARF info + * @param path Presumed location of the DWARF info + * @returns 0 on success, -1 on failure + */ +static +int so_info_set_dwarf_info_from_path(struct so_info *so, char *path) +{ + int fd = -1, ret = 0; + struct bt_dwarf_cu *cu = NULL; + Dwarf *dwarf_info = NULL; + + if (!so || !path) { + goto error; + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + goto error; + } + + dwarf_info = dwarf_begin(fd, DWARF_C_READ); + if (!dwarf_info) { + goto error; + } + + /* + * Check if the dwarf info has any CU. If not, the SO's object + * file contains no DWARF info. + */ + cu = bt_dwarf_cu_create(dwarf_info); + if (!cu) { + goto error; + } + + ret = bt_dwarf_cu_next(cu); + if (ret) { + goto error; + } + + so->dwarf_fd = fd; + so->dwarf_path = strdup(path); + if (!so->dwarf_path) { + goto error; + } + so->dwarf_info = dwarf_info; + free(cu); + + return 0; + +error: + close(fd); + dwarf_end(dwarf_info); + g_free(dwarf_info); + free(cu); + + return -1; +} + +/** + * Try to set the dwarf_info for a given so_info instance via the + * build ID method. + * + * @param so so_info instance for which to retrieve the + * DWARF info via build ID + * @returns 0 on success (i.e. dwarf_info set), -1 on failure + */ +static +int so_info_set_dwarf_info_build_id(struct so_info *so) +{ + int i = 0, ret = 0, dbg_dir_trailing_slash = 0; + char *path = NULL, *build_id_file = NULL; + const char *dbg_dir = NULL; + size_t build_id_file_len, path_len; + + if (!so || !so->build_id) { + goto error; + } + + dbg_dir = opt_debug_dir ? : DEFAULT_DEBUG_DIR; + + dbg_dir_trailing_slash = dbg_dir[strlen(dbg_dir) - 1] == '/'; + + /* 2 characters per byte printed in hex, +2 for '/' and '\0' */ + build_id_file_len = (2 * so->build_id_len) + 2; + build_id_file = malloc(build_id_file_len); + if (!build_id_file) { + goto error; + } + + snprintf(build_id_file, 4, "%02x/", so->build_id[0]); + for (i = 1; i < so->build_id_len; ++i) { + int path_idx = 3 + 2 * (i - 1); + + snprintf(&build_id_file[path_idx], 3, "%02x", so->build_id[i]); + } + + path_len = strlen(dbg_dir) + strlen(BUILD_ID_SUBDIR) + + strlen(build_id_file) + strlen(BUILD_ID_SUFFIX) + 1; + if (!dbg_dir_trailing_slash) { + path_len += 1; + } + + path = malloc(path_len); + if (!path) { + goto error; + } + + strcpy(path, dbg_dir); + if (!dbg_dir_trailing_slash) { + strcat(path, "/"); + } + strcat(path, BUILD_ID_SUBDIR); + strcat(path, build_id_file); + strcat(path, BUILD_ID_SUFFIX); + + ret = so_info_set_dwarf_info_from_path(so, path); + if (ret) { + goto error; + } + + goto end; + +error: + ret = -1; +end: + free(build_id_file); + free(path); + + return ret; +} + +/** + * Tests whether the file located at path exists and has the expected + * checksum. + * + * This predicate is used when looking up separate debug info via the + * GNU debuglink method. The expected crc can be found .gnu_debuglink + * section in the original ELF file, along with the filename for the + * file containing the debug info. + * + * @param path Full path at which to look for the debug file + * @param crc Expected checksum for the debug file + * @returns 1 if the file exists and has the correct checksum, + * 0 otherwise + */ +static +int is_valid_debug_file(char *path, uint32_t crc) +{ + int ret = 0, fd = -1; + uint32_t _crc = 0; + + if (!path) { + goto end; + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + goto end; + } + + ret = crc32(fd, &_crc); + if (ret) { + ret = 0; + goto end; + } + + ret = (crc == _crc); + +end: + close(fd); + return ret; +} + +/** + * Try to set the dwarf_info for a given so_info instance via the + * build ID method. + * + * @param so so_info instance for which to retrieve the + * DWARF info via debug link + * @returns 0 on success (i.e. dwarf_info set), -1 on failure + */ +static +int so_info_set_dwarf_info_debug_link(struct so_info *so) +{ + int ret = 0; + const char *dbg_dir = NULL; + char *dir_name = NULL, *so_dir = NULL, *path = NULL; + size_t max_path_len = 0; + + if (!so || !so->dbg_link_filename) { + goto error; + } + + dbg_dir = opt_debug_dir ? : DEFAULT_DEBUG_DIR; + + dir_name = dirname(so->elf_path); + if (!dir_name) { + goto error; + } + + /* so_dir is just dir_name with a trailing slash */ + so_dir = malloc(strlen(dir_name) + 2); + if (!so_dir) { + goto error; + } + + strcpy(so_dir, dir_name); + strcat(so_dir, "/"); + + max_path_len = strlen(dbg_dir) + strlen(so_dir) + + strlen(DEBUG_SUBDIR) + strlen(so->dbg_link_filename) + + 1; + path = malloc(max_path_len); + if (!path) { + goto error; + } + + /* First look in the SO's dir */ + strcpy(path, so_dir); + strcat(path, so->dbg_link_filename); + + if (is_valid_debug_file(path, so->dbg_link_crc)) { + goto found; + } + + /* If not found, look in .debug subdir */ + strcpy(path, so_dir); + strcat(path, DEBUG_SUBDIR); + strcat(path, so->dbg_link_filename); + + if (is_valid_debug_file(path, so->dbg_link_crc)) { + goto found; + } + + /* Lastly, look under the global debug directory */ + strcpy(path, dbg_dir); + strcat(path, so_dir); + strcat(path, so->dbg_link_filename); + + if (is_valid_debug_file(path, so->dbg_link_crc)) { + goto found; + } + +error: + ret = -1; +end: + free(path); + free(so_dir); + + return ret; + +found: + ret = so_info_set_dwarf_info_from_path(so, path); + if (ret) { + goto error; + } + + goto end; +} + +/** + * Initialize the DWARF info for a given executable. + * + * @param so so_info instance + * @returns 0 on success, -1 on failure + */ +static +int so_info_set_dwarf_info(struct so_info *so) +{ + int ret = 0; + + if (!so) { + goto error; + } + + /* First try to set the DWARF info from the ELF file */ + ret = so_info_set_dwarf_info_from_path(so, so->elf_path); + if (!ret) { + goto end; + } + + /* + * If that fails, try to find separate debug info via build ID + * and debug link. + */ + ret = so_info_set_dwarf_info_build_id(so); + if (!ret) { + goto end; + } + + ret = so_info_set_dwarf_info_debug_link(so); + if (!ret) { + goto end; + } + +error: + ret = -1; +end: + return ret; +} + +BT_HIDDEN +void source_location_destroy(struct source_location *src_loc) +{ + if (!src_loc) { + return; + } + + free(src_loc->filename); + g_free(src_loc); +} + +/** + * Try to find the symbol closest to an address within a given ELF + * section. + * + * Only function symbols are taken into account. The symbol's address + * must precede `addr`. A symbol with a closer address might exist + * after `addr` but is irrelevant because it cannot encompass `addr`. + * + * On success, if found, the out parameters `sym` and `shdr` are + * set. On failure or if none are found, they remain unchanged. + * + * @param scn ELF section in which to look for the address + * @param addr Virtual memory address for which to find the + * nearest function symbol + * @param sym Out parameter, the nearest function symbol + * @param shdr Out parameter, the section header for scn + * @returns 0 on success, -1 on failure + */ +static +int so_info_get_nearest_symbol_from_section(Elf_Scn *scn, uint64_t addr, + GElf_Sym **sym, GElf_Shdr **shdr) +{ + int i; + size_t symbol_count; + Elf_Data *data = NULL; + GElf_Shdr *_shdr = NULL; + GElf_Sym *nearest_sym = NULL; + + if (!scn || !sym || !shdr) { + goto error; + } + + _shdr = g_new0(GElf_Shdr, 1); + if (!_shdr) { + goto error; + } + + _shdr = gelf_getshdr(scn, _shdr); + if (!_shdr) { + goto error; + } + + if (_shdr->sh_type != SHT_SYMTAB) { + /* + * We are only interested in symbol table (symtab) + * sections, skip this one. + */ + goto end; + } + + data = elf_getdata(scn, NULL); + if (!data) { + goto error; + } + + symbol_count = _shdr->sh_size / _shdr->sh_entsize; + + for (i = 0; i < symbol_count; ++i) { + GElf_Sym *cur_sym = NULL; + + cur_sym = g_new0(GElf_Sym, 1); + if (!cur_sym) { + goto error; + } + cur_sym = gelf_getsym(data, i, cur_sym); + if (!cur_sym) { + goto error; + } + if (GELF_ST_TYPE(cur_sym->st_info) != STT_FUNC) { + /* We're only interested in the functions. */ + g_free(cur_sym); + continue; + } + + if (cur_sym->st_value <= addr && + (!nearest_sym || + cur_sym->st_value > nearest_sym->st_value)) { + g_free(nearest_sym); + nearest_sym = cur_sym; + } else { + g_free(cur_sym); + } + } + +end: + if (nearest_sym) { + *sym = nearest_sym; + *shdr = _shdr; + } else { + g_free(_shdr); + } + + return 0; + +error: + g_free(nearest_sym); + g_free(_shdr); + return -1; +} + +/** + * Get the name of the function containing a given address within an + * executable using ELF symbols. + * + * The function name is in fact the name of the nearest ELF symbol, + * followed by the offset in bytes between the address and the symbol + * (in hex), separated by a '+' character. + * + * If found, the out parameter `func_name` is set on success. On failure, + * it remains unchanged. + * + * @param so so_info instance for the executable containing + * the address + * @param addr Virtual memory address for which to find the + * function name + * @param func_name Out parameter, the function name + * @returns 0 on success, -1 on failure + */ +static +int so_info_lookup_elf_function_name(struct so_info *so, uint64_t addr, + char **func_name) +{ + /* + * TODO (possible optimisation): if an ELF has no symtab + * section, it has been stripped. Therefore, it would be wise + * to store a flag indicating the stripped status after the + * first iteration to prevent subsequent ones. + */ + int ret = 0; + Elf_Scn *scn = NULL; + GElf_Sym *sym = NULL; + GElf_Shdr *shdr = NULL; + char *sym_name = NULL; + char *_func_name = NULL; + char offset_str[ADDR_STR_LEN]; + + scn = elf_nextscn(so->elf_file, scn); + if (!scn) { + goto error; + } + + while (scn && !sym) { + ret = so_info_get_nearest_symbol_from_section( + scn, addr, &sym, &shdr); + if (ret) { + goto error; + } + + scn = elf_nextscn(so->elf_file, scn); + } + + if (sym) { + sym_name = elf_strptr(so->elf_file, shdr->sh_link, + sym->st_name); + if (!sym_name) { + goto error; + } + + snprintf(offset_str, ADDR_STR_LEN, "+%#0" PRIx64, + addr - sym->st_value); + _func_name = malloc(strlen(sym_name) + ADDR_STR_LEN); + if (!_func_name) { + goto error; + } + + strcpy(_func_name, sym_name); + strcat(_func_name, offset_str); + *func_name = _func_name; + } + + g_free(shdr); + g_free(sym); + return 0; + +error: + g_free(shdr); + g_free(sym); + free(_func_name); + return -1; +} + +/** + * Get the name of the function containing a given address within a + * given compile unit (CU). + * + * If found, the out parameter `func_name` is set on success. On + * failure, it remains unchanged. + * + * @param cu bt_dwarf_cu instance which may contain the address + * @param addr Virtual memory address for which to find the + * function name + * @param func_name Out parameter, the function name + * @returns 0 on success, -1 on failure + */ +static +int so_info_lookup_cu_function_name(struct bt_dwarf_cu *cu, uint64_t addr, + char **func_name) +{ + int ret = 0, found = 0; + char *_func_name = NULL; + struct bt_dwarf_die *die = NULL; + + if (!cu || !func_name) { + goto error; + } + + die = bt_dwarf_die_create(cu); + if (!die) { + goto error; + } + + while (bt_dwarf_die_next(die) == 0) { + int tag; + + ret = bt_dwarf_die_get_tag(die, &tag); + if (ret) { + goto error; + } + + if (tag == DW_TAG_subprogram) { + ret = bt_dwarf_die_contains_addr(die, addr, &found); + if (ret) { + goto error; + } + + if (found) { + break; + } + } + } + + if (found) { + ret = bt_dwarf_die_get_name(die, &_func_name); + if (ret) { + goto error; + } + + *func_name = _func_name; + } + + bt_dwarf_die_destroy(die); + return 0; + +error: + bt_dwarf_die_destroy(die); + return -1; +} + +/** + * Get the name of the function containing a given address within an + * executable using DWARF debug info. + * + * If found, the out parameter `func_name` is set on success. On + * failure, it remains unchanged. + * + * @param so so_info instance for the executable containing + * the address + * @param addr Virtual memory address for which to find the + * function name + * @param func_name Out parameter, the function name + * @returns 0 on success, -1 on failure + */ +static +int so_info_lookup_dwarf_function_name(struct so_info *so, uint64_t addr, + char **func_name) +{ + int ret = 0; + char *_func_name = NULL; + struct bt_dwarf_cu *cu = NULL; + + if (!so || !func_name) { + goto error; + } + + cu = bt_dwarf_cu_create(so->dwarf_info); + if (!cu) { + goto error; + } + + while (bt_dwarf_cu_next(cu) == 0) { + ret = so_info_lookup_cu_function_name(cu, addr, &_func_name); + if (ret) { + goto error; + } + + if (_func_name) { + break; + } + } + + if (_func_name) { + *func_name = _func_name; + } + + bt_dwarf_cu_destroy(cu); + return 0; + +error: + bt_dwarf_cu_destroy(cu); + return -1; +} + +BT_HIDDEN +int so_info_lookup_function_name(struct so_info *so, uint64_t addr, + char **func_name) +{ + int ret = 0; + char *_func_name = NULL; + + if (!so || !func_name) { + goto error; + } + + /* Set DWARF info if it hasn't been accessed yet. */ + if (!so->dwarf_info && !so->is_elf_only) { + ret = so_info_set_dwarf_info(so); + if (ret) { + /* Failed to set DWARF info, fallback to ELF. */ + so->is_elf_only = true; + } + } + + if (!so_info_has_address(so, addr)) { + goto error; + } + + /* + * Addresses in ELF and DWARF are relative to base address for + * PIC, so make the address argument relative too if needed. + */ + if (so->is_pic) { + addr -= so->low_addr; + } + + if (so->is_elf_only) { + ret = so_info_lookup_elf_function_name(so, addr, &_func_name); + } else { + ret = so_info_lookup_dwarf_function_name(so, addr, &_func_name); + } + + if (ret) { + goto error; + } + + if (_func_name) { + *func_name = _func_name; + } + + return 0; + +error: + return -1; +} + +/** + * Predicate used to determine whether the children of a given DIE + * contain a specific address. + * + * More specifically, the parameter `die` is expected to be a + * subprogram (function) DIE, and this predicate tells whether any + * subroutines are inlined within this function and would contain + * `addr`. + * + * Do note that this function advances the position of `die`. If the + * address is found within one of its children, `die` will be pointing + * to that child upon returning from the function, allowing to extract + * the information deemed necessary. + * + * @param die The parent DIE in whose children the address will be + * looked for + * @param addr The address for which to look for in the DIEs + * @returns Returns 1 if the address was found, 0 if not + */ +static +int so_info_child_die_has_address(struct bt_dwarf_die *die, uint64_t addr) +{ + int ret = 0, contains = 0; + + if (!die) { + goto error; + } + + ret = bt_dwarf_die_child(die); + if (ret) { + goto error; + } + + do { + int tag; + + ret = bt_dwarf_die_get_tag(die, &tag); + if (ret) { + goto error; + } + + if (tag == DW_TAG_inlined_subroutine) { + ret = bt_dwarf_die_contains_addr(die, addr, &contains); + if (ret) { + goto error; + } + + if (contains) { + ret = 1; + goto end; + } + } + } while (bt_dwarf_die_next(die) == 0); + +end: + return ret; + +error: + ret = 0; + goto end; +} + +/** + * Lookup the source location for a given address within a CU, making + * the assumption that it is contained within an inline routine in a + * function. + * + * @param cu bt_dwarf_cu instance in which to look for the address + * @param addr The address for which to look for + * @param src_loc Out parameter, the source location (filename and + * line number) for the address + * @returns 0 on success, -1 on failure + */ +static +int so_info_lookup_cu_src_loc_inl(struct bt_dwarf_cu *cu, uint64_t addr, + struct source_location **src_loc) +{ + int ret = 0, found = 0; + struct bt_dwarf_die *die = NULL; + struct source_location *_src_loc = NULL; + + if (!cu || !src_loc) { + goto error; + } + + die = bt_dwarf_die_create(cu); + if (!die) { + goto error; + } + + while (bt_dwarf_die_next(die) == 0) { + int tag; + + ret = bt_dwarf_die_get_tag(die, &tag); + if (ret) { + goto error; + } + + if (tag == DW_TAG_subprogram) { + int contains = 0; + + ret = bt_dwarf_die_contains_addr(die, addr, &contains); + if (ret) { + goto error; + } + + if (contains) { + /* + * Try to find an inlined subroutine + * child of this DIE containing addr. + */ + found = so_info_child_die_has_address( + die, addr); + goto end; + } + } + } + +end: + if (found) { + char *filename = NULL; + uint64_t line_no; + + _src_loc = g_new0(struct source_location, 1); + if (!_src_loc) { + goto error; + } + + ret = bt_dwarf_die_get_call_file(die, &filename); + if (ret) { + goto error; + } + ret = bt_dwarf_die_get_call_line(die, &line_no); + if (ret) { + free(filename); + goto error; + } + + _src_loc->filename = filename; + _src_loc->line_no = line_no; + *src_loc = _src_loc; + } + + bt_dwarf_die_destroy(die); + return 0; + +error: + source_location_destroy(_src_loc); + bt_dwarf_die_destroy(die); + return -1; +} + +/** + * Lookup the source location for a given address within a CU, + * assuming that it is contained within an inlined function. + * + * A source location can be found regardless of inlining status for + * this method, but in the case of an inlined function, the returned + * source location will point not to the callsite but rather to the + * definition site of the inline function. + * + * @param cu bt_dwarf_cu instance in which to look for the address + * @param addr The address for which to look for + * @param src_loc Out parameter, the source location (filename and + * line number) for the address + * @returns 0 on success, -1 on failure + */ +static +int so_info_lookup_cu_src_loc_no_inl(struct bt_dwarf_cu *cu, uint64_t addr, + struct source_location **src_loc) +{ + struct source_location *_src_loc = NULL; + struct bt_dwarf_die *die = NULL; + const char *filename = NULL; + Dwarf_Line *line = NULL; + Dwarf_Addr line_addr; + int ret, line_no; + + if (!cu || !src_loc) { + goto error; + } + + die = bt_dwarf_die_create(cu); + if (!die) { + goto error; + } + + line = dwarf_getsrc_die(die->dwarf_die, addr); + if (!line) { + goto error; + } + + ret = dwarf_lineaddr(line, &line_addr); + if (ret) { + goto error; + } + + filename = dwarf_linesrc(line, NULL, NULL); + if (!filename) { + goto error; + } + + if (addr == line_addr) { + _src_loc = g_new0(struct source_location, 1); + if (!_src_loc) { + goto error; + } + + ret = dwarf_lineno(line, &line_no); + if (ret) { + goto error; + } + + _src_loc->line_no = line_no; + _src_loc->filename = strdup(filename); + } + + bt_dwarf_die_destroy(die); + + if (_src_loc) { + *src_loc = _src_loc; + } + + return 0; + +error: + source_location_destroy(_src_loc); + bt_dwarf_die_destroy(die); + return -1; +} + +/** + * Get the source location (file name and line number) for a given + * address within a compile unit (CU). + * + * On success, the out parameter `src_loc` is set if found. On + * failure, it remains unchanged. + * + * @param so bt_dwarf_cu instance for the compile unit which + * may contain the address + * @param addr Virtual memory address for which to find the + * source location + * @param src_loc Out parameter, the source location + * @returns 0 on success, -1 on failure + */ +static +int so_info_lookup_cu_src_loc(struct bt_dwarf_cu *cu, uint64_t addr, + struct source_location **src_loc) +{ + int ret = 0; + struct source_location *_src_loc = NULL; + + if (!cu || !src_loc) { + goto error; + } + + ret = so_info_lookup_cu_src_loc_inl(cu, addr, &_src_loc); + if (ret) { + goto error; + } + + if (_src_loc) { + goto end; + } + + ret = so_info_lookup_cu_src_loc_no_inl(cu, addr, &_src_loc); + if (ret) { + goto error; + } + + if (_src_loc) { + goto end; + } + +end: + if (_src_loc) { + *src_loc = _src_loc; + } + + return 0; + +error: + source_location_destroy(_src_loc); + return -1; +} + +BT_HIDDEN +int so_info_lookup_source_location(struct so_info *so, uint64_t addr, + struct source_location **src_loc) +{ + struct bt_dwarf_cu *cu = NULL; + struct source_location *_src_loc = NULL; + + if (!so || !src_loc) { + goto error; + } + + /* Set DWARF info if it hasn't been accessed yet. */ + if (!so->dwarf_info && !so->is_elf_only) { + if (so_info_set_dwarf_info(so)) { + /* Failed to set DWARF info. */ + so->is_elf_only = true; + } + } + + if (so->is_elf_only) { + /* We cannot lookup source location without DWARF info. */ + goto error; + } + + if (!so_info_has_address(so, addr)) { + goto error; + } + + /* + * Addresses in ELF and DWARF are relative to base address for + * PIC, so make the address argument relative too if needed. + */ + if (so->is_pic) { + addr -= so->low_addr; + } + + cu = bt_dwarf_cu_create(so->dwarf_info); + if (!cu) { + goto error; + } + + while (bt_dwarf_cu_next(cu) == 0) { + int ret; + + ret = so_info_lookup_cu_src_loc(cu, addr, &_src_loc); + if (ret) { + goto error; + } + + if (_src_loc) { + break; + } + } + + bt_dwarf_cu_destroy(cu); + if (_src_loc) { + *src_loc = _src_loc; + } + + return 0; + +error: + source_location_destroy(_src_loc); + bt_dwarf_cu_destroy(cu); + return -1; +} diff --git a/types/integer.c b/types/integer.c index 73d4f2a4..52cc6bb6 100644 --- a/types/integer.c +++ b/types/integer.c @@ -56,7 +56,7 @@ struct declaration_integer * { struct declaration_integer *integer_declaration; - integer_declaration = g_new(struct declaration_integer, 1); + integer_declaration = g_new0(struct declaration_integer, 1); integer_declaration->p.id = CTF_TYPE_INTEGER; integer_declaration->p.alignment = alignment; integer_declaration->p.declaration_free = _integer_declaration_free; @@ -84,7 +84,7 @@ struct bt_definition * struct definition_integer *integer; int ret; - integer = g_new(struct definition_integer, 1); + integer = g_new0(struct definition_integer, 1); bt_declaration_ref(&integer_declaration->p); integer->p.declaration = declaration; integer->declaration = integer_declaration; -- 2.34.1