tap-driver.sh: flush stdout after each test result
[babeltrace.git] / plugins / lttng-utils / debug-info / bin-info.c
index c3e16cec9b97c3ff38ec5c8315f4acc548f27360..bad796d225dd7aa43921ca0e49bd3865f857b986 100644 (file)
 #define BT_LOG_TAG "PLUGIN-CTF-LTTNG-UTILS-DEBUG-INFO-FLT-BIN-INFO"
 #include "logging.h"
 
+#include <dwarf.h>
+#include <errno.h>
 #include <fcntl.h>
-#include <math.h>
+#include <inttypes.h>
 #include <libgen.h>
+#include <math.h>
 #include <stdio.h>
-#include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <dwarf.h>
+
 #include <glib.h>
-#include <errno.h>
-#include "dwarf.h"
+
+#include <babeltrace2/common-internal.h>
+
 #include "bin-info.h"
 #include "crc32.h"
+#include "dwarf.h"
 #include "utils.h"
 
 /*
@@ -68,12 +72,14 @@ int bin_info_init(void)
 }
 
 BT_HIDDEN
-struct bin_info *bin_info_create(const char *path, uint64_t low_addr,
-               uint64_t memsz, bool is_pic, const char *debug_info_dir,
-               const char *target_prefix)
+struct bin_info *bin_info_create(struct bt_fd_cache *fdc, const char *path,
+               uint64_t low_addr, uint64_t memsz, bool is_pic,
+               const char *debug_info_dir, const char *target_prefix)
 {
        struct bin_info *bin = NULL;
 
+       BT_ASSERT(fdc);
+
        if (!path) {
                goto error;
        }
@@ -84,8 +90,7 @@ struct bin_info *bin_info_create(const char *path, uint64_t low_addr,
        }
 
        if (target_prefix) {
-               bin->elf_path = g_build_path("/", target_prefix,
-                                               path, NULL);
+               bin->elf_path = g_build_filename(target_prefix, path, NULL);
        } else {
                bin->elf_path = g_strdup(path);
        }
@@ -108,6 +113,7 @@ struct bin_info *bin_info_create(const char *path, uint64_t low_addr,
        bin->build_id = NULL;
        bin->build_id_len = 0;
        bin->file_build_id_matches = false;
+       bin->fd_cache = fdc;
 
        return bin;
 
@@ -133,8 +139,8 @@ void bin_info_destroy(struct bin_info *bin)
 
        elf_end(bin->elf_file);
 
-       close(bin->elf_fd);
-       close(bin->dwarf_fd);
+       bt_fd_cache_put_handle(bin->fd_cache, bin->elf_handle);
+       bt_fd_cache_put_handle(bin->fd_cache, bin->dwarf_handle);
 
        g_free(bin);
 }
@@ -148,79 +154,80 @@ void bin_info_destroy(struct bin_info *bin)
 static
 int bin_info_set_elf_file(struct bin_info *bin)
 {
-       int elf_fd = -1;
+       struct bt_fd_cache_handle *elf_handle = NULL;
        Elf *elf_file = NULL;
 
        if (!bin) {
                goto error;
        }
 
-       elf_fd = open(bin->elf_path, O_RDONLY);
-       if (elf_fd < 0) {
-               elf_fd = -errno;
-               BT_LOGE("Failed to open %s\n", bin->elf_path);
+       elf_handle = bt_fd_cache_get_handle(bin->fd_cache, bin->elf_path);
+       if (!elf_handle) {
+               BT_LOGD("Failed to open %s", bin->elf_path);
                goto error;
        }
+       bin->elf_handle = elf_handle;
 
-       elf_file = elf_begin(elf_fd, ELF_C_READ, NULL);
+       elf_file = elf_begin(bt_fd_cache_handle_get_fd(bin->elf_handle),
+               ELF_C_READ, NULL);
        if (!elf_file) {
-               BT_LOGE("elf_begin failed: %s\n", elf_errmsg(-1));
+               BT_LOGE("elf_begin failed: %s", elf_errmsg(-1));
                goto error;
        }
 
+       bin->elf_file = elf_file;
+
        if (elf_kind(elf_file) != ELF_K_ELF) {
-               BT_LOGE("Error: %s is not an ELF object\n",
-                               bin->elf_path);
+               BT_LOGE("Error: %s is not an ELF object", bin->elf_path);
                goto error;
        }
 
-       bin->elf_fd = elf_fd;
-       bin->elf_file = elf_file;
        return 0;
 
 error:
-       if (elf_fd >= 0) {
-               close(elf_fd);
-               elf_fd = -1;
-       }
+       bt_fd_cache_put_handle(bin->fd_cache, elf_handle);
        elf_end(elf_file);
-       return elf_fd;
+       return -1;
 }
 
 /**
- * From a note section data buffer, check if it is a build id note.
+ * From a note section data struct, check if it is a build id note.
  *
- * @param buf                  Pointer to a note section
+ * @param note_data            Pointer to a note section
  *
  * @returns                    1 on match, 0 if `buf` does not contain a
  *                             valid build id note
  */
 static
-int is_build_id_note_section(uint8_t *buf)
+int is_build_id_note_section(Elf_Data *note_data)
 {
+       size_t name_offset, desc_offset;
+       GElf_Nhdr note_header;
        int ret = 0;
-       uint32_t name_sz, desc_sz, note_type;
 
-       /* The note section header has 3 32bit integer for the following:
-        * - Section name size
-        * - Description size
-        * - Note type
+       /*
+        * Discard the return value as it contains the size of the note section
+        * and we don't need it.
         */
-       name_sz = (uint32_t) *buf;
-       buf += sizeof(name_sz);
-
-       buf += sizeof(desc_sz);
+       (void) gelf_getnote(note_data, 0, &note_header, &name_offset,
+               &desc_offset);
 
-       note_type = (uint32_t) *buf;
-       buf += sizeof(note_type);
+       /*
+        * Check the note name length. The name_sz field includes the
+        * terminating null byte.
+        */
+       if (note_header.n_namesz != sizeof(BUILD_ID_NOTE_NAME)) {
+               goto invalid;
+       }
 
        /* Check the note type. */
-       if (note_type != NT_GNU_BUILD_ID) {
+       if (note_header.n_type != NT_GNU_BUILD_ID) {
                goto invalid;
        }
 
        /* Check the note name. */
-       if (memcmp(buf, BUILD_ID_NOTE_NAME, name_sz) != 0) {
+       if (memcmp(note_data->d_buf + name_offset, BUILD_ID_NOTE_NAME,
+                       note_header.n_namesz) != 0) {
                goto invalid;
        }
 
@@ -231,46 +238,38 @@ invalid:
 }
 
 /**
- *  From a build id note section data buffer, check if the build id it contains
+ *  From a build id note section data struct, check if the build id it contains
  *  is identical to the build id passed as parameter.
  *
- * @param file_build_id_note   Pointer to the file build id note section.
+ * @param note_data            Pointer to the file build id note section.
  * @param build_id             Pointer to a build id to compare to.
  * @param build_id_len         length of the build id.
  *
  * @returns                    1 on match, 0 otherwise.
  */
 static
-int is_build_id_note_section_matching(uint8_t *file_build_id_note,
+int is_build_id_note_section_matching(Elf_Data *note_data,
                uint8_t *build_id, size_t build_id_len)
 {
-       uint32_t name_sz, desc_sz, note_type;
+       size_t name_offset, desc_offset;
+       GElf_Nhdr note_header;
 
        if (build_id_len <= 0) {
                goto end;
        }
 
-       /* The note section header has 3 32bit integer for the following:
-        * - Section name size
-        * - Description size
-        * - Note type
-        */
-       name_sz = (uint32_t) *file_build_id_note;
-       file_build_id_note += sizeof(name_sz);
-       file_build_id_note += sizeof(desc_sz);
-       file_build_id_note += sizeof(note_type);
-
        /*
-        * Move the pointer pass the name char array. This corresponds to the
-        * beginning of the description section. The description is the build
-        * id in the case of a build id note.
+        * Discard the return value as it contains the size of the note section
+        * and we don't need it.
         */
-       file_build_id_note += name_sz;
+       (void) gelf_getnote(note_data, 0, &note_header, &name_offset,
+               &desc_offset);
 
        /*
         * Compare the binary build id with the supplied build id.
         */
-       if (memcmp(build_id, file_build_id_note, build_id_len) == 0) {
+       if (memcmp(build_id, note_data->d_buf + desc_offset,
+                       build_id_len) == 0) {
                return 1;
        }
 end:
@@ -294,8 +293,7 @@ int is_build_id_matching(struct bin_info *bin)
 {
        int ret, is_build_id, is_matching = 0;
        Elf_Scn *curr_section = NULL, *next_section = NULL;
-       Elf_Data *note_data = NULL;
-       GElf_Shdr *curr_section_hdr = NULL;
+       GElf_Shdr curr_section_hdr;
 
        if (!bin->build_id) {
                goto error;
@@ -310,37 +308,35 @@ int is_build_id_matching(struct bin_info *bin)
                }
        }
 
-       curr_section_hdr = g_new0(GElf_Shdr, 1);
-       if (!curr_section_hdr) {
-               goto error;
-       }
-
        next_section = elf_nextscn(bin->elf_file, curr_section);
        if (!next_section) {
                goto error;
        }
 
        while (next_section) {
+               Elf_Data *note_data = NULL;
+
                curr_section = next_section;
                next_section = elf_nextscn(bin->elf_file, curr_section);
 
-               curr_section_hdr = gelf_getshdr(curr_section, curr_section_hdr);
-
-               if (!curr_section_hdr) {
+               if (!gelf_getshdr(curr_section, &curr_section_hdr)) {
                        goto error;
                }
 
-               if (curr_section_hdr->sh_type != SHT_NOTE) {
+               if (curr_section_hdr.sh_type != SHT_NOTE) {
                        continue;
                }
 
+               /*
+                * elf_getdata() translates the data to native byte order.
+                */
                note_data = elf_getdata(curr_section, NULL);
                if (!note_data) {
                        goto error;
                }
 
                /* Check if the note is of the build-id type. */
-               is_build_id = is_build_id_note_section(note_data->d_buf);
+               is_build_id = is_build_id_note_section(note_data);
                if (!is_build_id) {
                        continue;
                }
@@ -349,14 +345,13 @@ int is_build_id_matching(struct bin_info *bin)
                 * Compare the build id of the on-disk file and
                 * the build id recorded in the trace.
                 */
-               is_matching = is_build_id_note_section_matching(note_data->d_buf,
-                               bin->build_id, bin->build_id_len);
+               is_matching = is_build_id_note_section_matching(
+                       note_data, bin->build_id, bin->build_id_len);
                if (!is_matching) {
                        break;
                }
        }
 error:
-       g_free(curr_section_hdr);
        return is_matching;
 }
 
@@ -441,7 +436,8 @@ error:
 static
 int bin_info_set_dwarf_info_from_path(struct bin_info *bin, char *path)
 {
-       int fd = -1, ret = 0;
+       int ret = 0;
+       struct bt_fd_cache_handle *dwarf_handle = NULL;
        struct bt_dwarf_cu *cu = NULL;
        Dwarf *dwarf_info = NULL;
 
@@ -449,13 +445,13 @@ int bin_info_set_dwarf_info_from_path(struct bin_info *bin, char *path)
                goto error;
        }
 
-       fd = open(path, O_RDONLY);
-       if (fd < 0) {
-               fd = -errno;
+       dwarf_handle = bt_fd_cache_get_handle(bin->fd_cache, path);
+       if (!dwarf_handle) {
                goto error;
        }
 
-       dwarf_info = dwarf_begin(fd, DWARF_C_READ);
+       dwarf_info = dwarf_begin(bt_fd_cache_handle_get_fd(dwarf_handle),
+               DWARF_C_READ);
        if (!dwarf_info) {
                goto error;
        }
@@ -474,7 +470,7 @@ int bin_info_set_dwarf_info_from_path(struct bin_info *bin, char *path)
                goto error;
        }
 
-       bin->dwarf_fd = fd;
+       bin->dwarf_handle = dwarf_handle;
        bin->dwarf_path = g_strdup(path);
        if (!bin->dwarf_path) {
                goto error;
@@ -485,15 +481,12 @@ int bin_info_set_dwarf_info_from_path(struct bin_info *bin, char *path)
        return 0;
 
 error:
-       if (fd >= 0) {
-               close(fd);
-               fd = -1;
-       }
+       bt_fd_cache_put_handle(bin->fd_cache, dwarf_handle);
        dwarf_end(dwarf_info);
        g_free(dwarf_info);
        free(cu);
 
-       return fd;
+       return -1;
 }
 
 /**
@@ -508,9 +501,9 @@ static
 int bin_info_set_dwarf_info_build_id(struct bin_info *bin)
 {
        int i = 0, ret = 0;
-       char *path = NULL, *build_id_file = NULL;
+       char *path = NULL, *build_id_prefix_dir = NULL, *build_id_file = NULL;
        const char *dbg_dir = NULL;
-       size_t build_id_file_len;
+       size_t build_id_char_len, build_id_suffix_char_len, build_id_file_len;
 
        if (!bin || !bin->build_id) {
                goto error;
@@ -518,23 +511,49 @@ int bin_info_set_dwarf_info_build_id(struct bin_info *bin)
 
        dbg_dir = bin->debug_info_dir ? bin->debug_info_dir : DEFAULT_DEBUG_DIR;
 
-       /* 2 characters per byte printed in hex, +1 for '/' and +1 for '\0' */
-       build_id_file_len = (2 * bin->build_id_len) + 1 +
-                       strlen(BUILD_ID_SUFFIX) + 1;
+       /*
+        * The prefix dir is the first byte of the build id, represented in
+        * lowercase hex as two characters per byte, +1 for '\0'.
+        */
+       build_id_prefix_dir = g_new0(gchar, BUILD_ID_PREFIX_DIR_LEN + 1);
+       if (!build_id_prefix_dir) {
+               goto error;
+       }
+       g_snprintf(build_id_prefix_dir, BUILD_ID_PREFIX_DIR_LEN + 1, "%02x", bin->build_id[0]);
+
+       /*
+        * The build id file is the remaining bytes of the build id,
+        * represented in lowercase hex, as two characters per byte.
+        */
+       build_id_char_len = (2 * (bin->build_id_len - 1));
+
+       /* To which the build id suffix is added, +1 for '\0'. */
+       build_id_suffix_char_len = strlen(BUILD_ID_SUFFIX) + 1;
+
+       /*
+        * The resulting filename string is the concatenation of the
+        * hex build id and the suffix.
+        */
+       build_id_file_len =  build_id_char_len + build_id_suffix_char_len;
        build_id_file = g_new0(gchar, build_id_file_len);
        if (!build_id_file) {
                goto error;
        }
 
-       g_snprintf(build_id_file, 4, "%02x/", bin->build_id[0]);
+       /*
+        * For each byte, starting at offset 1, append two characters
+        * in lowercase hex.
+        */
        for (i = 1; i < bin->build_id_len; ++i) {
-               int path_idx = 3 + 2 * (i - 1);
+               int path_idx = 2 * (i - 1);
 
                g_snprintf(&build_id_file[path_idx], 3, "%02x", bin->build_id[i]);
        }
-       g_strconcat(build_id_file, BUILD_ID_SUFFIX, NULL);
+       /* Append the suffix to the generated string, including the '\0'. */
+       g_snprintf(&build_id_file[build_id_char_len], build_id_suffix_char_len,
+               BUILD_ID_SUFFIX);
 
-       path = g_build_path("/", dbg_dir, BUILD_ID_SUBDIR, build_id_file, NULL);
+       path = g_build_filename(dbg_dir, BUILD_ID_SUBDIR, build_id_prefix_dir, build_id_file, NULL);
        if (!path) {
                goto error;
        }
@@ -549,8 +568,9 @@ int bin_info_set_dwarf_info_build_id(struct bin_info *bin)
 error:
        ret = -1;
 end:
-       free(build_id_file);
-       free(path);
+       g_free(build_id_prefix_dir);
+       g_free(build_id_file);
+       g_free(path);
 
        return ret;
 }
@@ -570,21 +590,22 @@ end:
  *             0 otherwise
  */
 static
-int is_valid_debug_file(char *path, uint32_t crc)
+int is_valid_debug_file(struct bin_info *bin, char *path, uint32_t crc)
 {
-       int ret = 0, fd = -1;
+       int ret = 0;
+       struct bt_fd_cache_handle *debug_handle = NULL;
        uint32_t _crc = 0;
 
        if (!path) {
-               goto end_noclose;
+               goto end;
        }
 
-       fd = open(path, O_RDONLY);
-       if (fd < 0) {
-               goto end_noclose;
+       debug_handle = bt_fd_cache_get_handle(bin->fd_cache, path);
+       if (!debug_handle) {
+               goto end;
        }
 
-       ret = crc32(fd, &_crc);
+       ret = crc32(bt_fd_cache_handle_get_fd(debug_handle), &_crc);
        if (ret) {
                ret = 0;
                goto end;
@@ -593,8 +614,7 @@ int is_valid_debug_file(char *path, uint32_t crc)
        ret = (crc == _crc);
 
 end:
-       close(fd);
-end_noclose:
+       bt_fd_cache_put_handle(bin->fd_cache, debug_handle);
        return ret;
 }
 
@@ -611,47 +631,42 @@ int bin_info_set_dwarf_info_debug_link(struct bin_info *bin)
 {
        int ret = 0;
        const gchar *dbg_dir = NULL;
-       gchar *bin_dir = NULL, *dir_name = NULL, *path = NULL;
+       gchar *bin_dir = NULL, *path = NULL;
 
        if (!bin || !bin->dbg_link_filename) {
                goto error;
        }
 
        dbg_dir = bin->debug_info_dir ? bin->debug_info_dir : DEFAULT_DEBUG_DIR;
-       dir_name = g_path_get_dirname(bin->elf_path);
-       if (!dir_name) {
-               goto error;
-       }
-
-       bin_dir = g_strconcat(dir_name, "/", NULL);
+       bin_dir = g_path_get_dirname(bin->elf_path);
 
        /* First look in the executable's dir */
-       path = g_strconcat(bin_dir, bin->dbg_link_filename, NULL);
+       path = g_build_filename(bin_dir, bin->dbg_link_filename, NULL);
 
-       if (is_valid_debug_file(path, bin->dbg_link_crc)) {
+       if (is_valid_debug_file(bin, path, bin->dbg_link_crc)) {
                goto found;
        }
 
        /* If not found, look in .debug subdir */
        g_free(path);
-       path = g_strconcat(bin_dir, DEBUG_SUBDIR, bin->dbg_link_filename, NULL);
+       path = g_build_filename(bin_dir, DEBUG_SUBDIR, bin->dbg_link_filename, NULL);
 
-       if (is_valid_debug_file(path, bin->dbg_link_crc)) {
+       if (is_valid_debug_file(bin, path, bin->dbg_link_crc)) {
                goto found;
        }
 
        /* Lastly, look under the global debug directory */
        g_free(path);
 
-       path = g_strconcat(dbg_dir, bin_dir, bin->dbg_link_filename, NULL);
-       if (is_valid_debug_file(path, bin->dbg_link_crc)) {
+       path = g_build_filename(dbg_dir, bin_dir, bin->dbg_link_filename, NULL);
+       if (is_valid_debug_file(bin, path, bin->dbg_link_crc)) {
                goto found;
        }
 
 error:
        ret = -1;
 end:
-       g_free(dir_name);
+       g_free(bin_dir);
        g_free(path);
 
        return ret;
@@ -739,7 +754,6 @@ int bin_info_append_offset_str(const char *base_str, uint64_t low_addr,
        uint64_t offset;
        char *_result = NULL;
 
-
        if (!base_str || !result) {
                goto error;
        }
@@ -1101,7 +1115,8 @@ int bin_info_lookup_function_name(struct bin_info *bin,
        if (!bin->dwarf_info && !bin->is_elf_only) {
                ret = bin_info_set_dwarf_info(bin);
                if (ret) {
-                       BT_LOGD_STR("Failed to set bin dwarf info, falling back to ELF lookup.");
+                       BT_LOGD_STR("Failed to set bin dwarf info, falling "
+                                       "back to ELF lookup.");
                        /* Failed to set DWARF info, fallback to ELF. */
                        bin->is_elf_only = true;
                }
@@ -1367,7 +1382,9 @@ error:
  * @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
+ *                     line number) for the address. Set only if the address
+ *                     is found and resolved successfully
+ *
  * @returns            0 on success, -1 on failure
  */
 static
@@ -1379,7 +1396,7 @@ int bin_info_lookup_cu_src_loc_no_inl(struct bt_dwarf_cu *cu, uint64_t addr,
        const char *filename = NULL;
        Dwarf_Line *line = NULL;
        Dwarf_Addr line_addr;
-       int ret, line_no;
+       int ret = 0, line_no;
 
        if (!cu || !src_loc) {
                goto error;
@@ -1392,7 +1409,8 @@ int bin_info_lookup_cu_src_loc_no_inl(struct bt_dwarf_cu *cu, uint64_t addr,
 
        line = dwarf_getsrc_die(die->dwarf_die, addr);
        if (!line) {
-               goto error;
+               /* This is not an error. The caller needs to keep looking. */
+               goto end;
        }
 
        ret = dwarf_lineaddr(line, &line_addr);
@@ -1420,18 +1438,18 @@ int bin_info_lookup_cu_src_loc_no_inl(struct bt_dwarf_cu *cu, uint64_t addr,
                _src_loc->filename = g_strdup(filename);
        }
 
-       bt_dwarf_die_destroy(die);
-
        if (_src_loc) {
                *src_loc = _src_loc;
        }
 
-       return 0;
+       goto end;
 
 error:
        source_location_destroy(_src_loc);
+       ret = -1;
+end:
        bt_dwarf_die_destroy(die);
-       return -1;
+       return ret;
 }
 
 /**
This page took 0.032409 seconds and 4 git commands to generate.