From: Heather McIntyre <h...@rice.edu> * tests/eu_search_cfi.c: New file. * tests/eu_search_die.c: New file. * tests/eu_search_lines.c: New file. * tests/eu_search_macros.c: New file. * tests/run-eu-search-tests.sh: New test. * tests/Makefile.am: Add USE_LOCKS condition for -pthread. (check_PROGRAMS): Add eu_search_cfi, eu_search_die, eu_search_lines, and eu_search_macros. (TESTS, EXTRA_DIST): Add run-eu-search-tests.sh (eu_search_cfi_LDADD): New variable. (eu_search_die_LDADD): New variable. (eu_search_lines_LDADD): New variable. (eu_search_macros_LDADD): New variable. (eu_search_cfi_LDFLAGS): New variable. Add -pthread if USE_LOCKS is not defined. (eu_search_die_LDFLAGS): Likewise. (eu_search_lines_LDFLAGS): Likewise. (eu_search_macros_LDFLAGS): Likewise.
Signed-off-by: Heather S. McIntyre <h...@rice.edu> Signed-off-by: Aaron Merey <ame...@redhat.com> Signed-off-by: Mark Wielaard <m...@klomp.org> --- v2 changes: Rebase and add run-eu-search-tests.sh to EXTRA_DIST. The elfutils testsuite (including the tests added in this patch) passes for me on the sourceware buildbots. However the buildbots do not configure elfutils with --enable-thread-safety. I think we should include this configure option when the buildbots run elfutils tests. tests/.gitignore | 4 + tests/Makefile.am | 19 ++- tests/eu_search_cfi.c | 234 +++++++++++++++++++++++++++++++ tests/eu_search_die.c | 262 +++++++++++++++++++++++++++++++++++ tests/eu_search_lines.c | 228 ++++++++++++++++++++++++++++++ tests/eu_search_macros.c | 216 +++++++++++++++++++++++++++++ tests/run-eu-search-tests.sh | 168 ++++++++++++++++++++++ 7 files changed, 1129 insertions(+), 2 deletions(-) create mode 100644 tests/eu_search_cfi.c create mode 100644 tests/eu_search_die.c create mode 100644 tests/eu_search_lines.c create mode 100644 tests/eu_search_macros.c create mode 100755 tests/run-eu-search-tests.sh diff --git a/tests/.gitignore b/tests/.gitignore index 30f5800b..360c13a3 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -67,6 +67,10 @@ /elfstrtab /elf-print-reloc-syms /emptyfile +/eu_search_cfi +/eu_search_die +/eu_search_lines +/eu_search_macros /fillfile /find-prologues /funcretval diff --git a/tests/Makefile.am b/tests/Makefile.am index cfed54b7..1642d4e9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -32,6 +32,10 @@ else tests_rpath = no endif +if USE_LOCKS + AM_CFLAGS += -pthread +endif + check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ showptable update1 update2 update3 update4 test-nlist \ show-die-info get-files next-files get-lines next-lines \ @@ -64,6 +68,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ msg_tst system-elf-libelf-test system-elf-gelf-test \ nvidia_extended_linemap_libdw elf-print-reloc-syms \ cu-dwp-section-info declfiles \ + eu_search_cfi eu_search_die eu_search_lines eu_search_macros \ $(asm_TESTS) asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ @@ -216,7 +221,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-readelf-dw-form-indirect.sh run-strip-largealign.sh \ run-readelf-Dd.sh run-dwfl-core-noncontig.sh run-cu-dwp-section-info.sh \ run-declfiles.sh \ - run-sysroot.sh + run-sysroot.sh run-eu-search-tests.sh if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -669,7 +674,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ testfile-dwp-4-cu-index-overflow.dwp.bz2 \ testfile-dwp-cu-index-overflow.source \ testfile-define-file.bz2 \ - testfile-sysroot.tar.bz2 run-sysroot.sh + testfile-sysroot.tar.bz2 run-sysroot.sh \ + run-eu-search-tests.sh if USE_VALGRIND @@ -849,6 +855,15 @@ nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw) elf_print_reloc_syms_LDADD = $(libelf) cu_dwp_section_info_LDADD = $(libdw) declfiles_LDADD = $(libdw) +eu_search_cfi_LDFLAGS = $(if $(filter undefined,$(origin USE_LOCKS)),-pthread) $(AM_LDFLAGS) +eu_search_die_LDFLAGS = $(if $(filter undefined,$(origin USE_LOCKS)),-pthread) $(AM_LDFLAGS) +eu_search_lines_LDFLAGS = $(if $(filter undefined,$(origin USE_LOCKS)),-pthread) $(AM_LDFLAGS) +eu_search_macros_LDFLAGS = $(if $(filter undefined,$(origin USE_LOCKS)),-pthread) $(AM_LDFLAGS) +eu_search_cfi_LDADD = $(libeu) $(libelf) $(libdw) +eu_search_die_LDADD = $(libdw) +eu_search_lines_LDADD = $(libdw) $(libelf) +eu_search_macros_LDADD = $(libdw) # We want to test the libelf headers against the system elf.h header. # Don't include any -I CPPFLAGS. Except when we install our own elf.h. diff --git a/tests/eu_search_cfi.c b/tests/eu_search_cfi.c new file mode 100644 index 00000000..0b63b213 --- /dev/null +++ b/tests/eu_search_cfi.c @@ -0,0 +1,234 @@ +/*Test program for eu_search_cfi + Copyright (C) 2023 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see<http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <assert.h> +#include <inttypes.h> +#include ELFUTILS_HEADER(dw) +#include <dwarf.h> +#include <argp.h> +#include <stdbool.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "system.h" +#include <pthread.h> + +static void handle_section(char *name, const unsigned char e_ident[], Elf_Scn *scn, const bool is_eh); +static void *thread_work(void *arg); + +typedef struct +{ + char *name; + const unsigned char *e_ident; + Elf_Scn * scn; + bool is_eh; +} + +ThreadData; + +static void *thread_work(void *arg) +{ + ThreadData *data = (ThreadData*) arg; + handle_section(data->name, data->e_ident, data->scn, data->is_eh); + free(data); + return NULL; +} + +static void handle_section(char *name, const unsigned char e_ident[], + Elf_Scn *scn, const bool is_eh) +{ + if (is_eh) + printf(".eh_frame\n"); + else + printf(".debug_frame\n"); + + GElf_Shdr mem; + GElf_Shdr *shdr = gelf_getshdr(scn, &mem); + if (shdr == NULL) + error(EXIT_FAILURE, 0, "Couldn't get section header: %s", + elf_errmsg(-1)); + if ((shdr->sh_flags &SHF_COMPRESSED) != 0) + { + if (elf_compress(scn, 0, 0) < 0) + error(EXIT_FAILURE, 0, "Couldn't decompress section: %s", + elf_errmsg(-1)); + } + else if (name[0] == '.' && name[1] == 'z') + { + if (elf_compress_gnu(scn, 0, 0) < 0) + error(EXIT_FAILURE, 0, "Couldn't decompress section: %s", + elf_errmsg(-1)); + } + + Elf_Data *data = elf_getdata(scn, NULL); + if (data == NULL || data->d_buf == NULL) + error(EXIT_FAILURE, 0, "no section data"); + + int res; + Dwarf_Off off; + Dwarf_Off next_off = 0; + Dwarf_CFI_Entry entry; + while ((res = dwarf_next_cfi(e_ident, data, is_eh, off = next_off, &next_off, &entry)) == 0) + { + printf("[%" PRId64 "] ", off); + if (dwarf_cfi_cie_p(&entry)) + printf("CIE augmentation=\"%s\"\n", entry.cie.augmentation); + else + { + printf("FDE cie=[%" PRId64 "]\n", entry.fde.CIE_pointer); + + Dwarf_Off cie_off = entry.fde.CIE_pointer; + Dwarf_Off cie_off_next; + Dwarf_CFI_Entry cie_entry; + if (dwarf_next_cfi(e_ident, data, is_eh, cie_off, &cie_off_next, &cie_entry) != 0 || + !dwarf_cfi_cie_p(&cie_entry)) + error(EXIT_FAILURE, 0, "FDE doesn't point to CIE"); + } + } + + if (res < 0) + error(EXIT_FAILURE, 0, "dwarf_next_cfi failed: %s\n", + dwarf_errmsg(-1)); +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) + error(EXIT_FAILURE, 0, "need file name argument"); + + const char *file = argv[1]; + printf("%s\n", file); + + int fd = open(file, O_RDONLY); + if (fd == -1) + error(EXIT_FAILURE, errno, "cannot open input file `%s'", file); + + elf_version(EV_CURRENT); + + Elf *elf = elf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) + error(EXIT_FAILURE, 0, "cannot create ELF descriptor: %s", elf_errmsg(-1)); + + size_t esize; + const unsigned char *ident = (const unsigned char *) elf_getident(elf, &esize); + if (ident == NULL || esize < EI_NIDENT) + error(EXIT_FAILURE, 0, "no, or too small, ELF ident"); + + GElf_Ehdr ehdr; + if (gelf_getehdr(elf, &ehdr) == NULL) + error(EXIT_FAILURE, 0, "cannot get the ELF header: %s\n", elf_errmsg(-1)); + + size_t strndx = ehdr.e_shstrndx; + + int num_threads = 4; + pthread_t *threads = (pthread_t*) malloc(num_threads * sizeof(pthread_t)); + ThreadData **thread_data = (ThreadData**) malloc(num_threads * sizeof(ThreadData*)); + int thread_count = 0; + + if (!threads || !thread_data) + { + fprintf(stderr, "Failed to allocate memory for threads.\n"); + free(threads); + free(thread_data); + return 1; + } + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn(elf, scn)) != NULL) + { + GElf_Shdr shdr; + if (gelf_getshdr(scn, &shdr) != NULL) + { + char *name = elf_strptr(elf, strndx, (size_t) shdr.sh_name); + if (name != NULL && shdr.sh_type == SHT_PROGBITS) + { + bool is_eh = false; + if (strcmp(name, ".eh_frame") == 0) + { + is_eh = true; + } + else if (strcmp(name, ".debug_frame") == 0 || strcmp(name, ".zdebug_frame") == 0) + { + is_eh = false; + } + else + { + continue; + } + + if (thread_count >= num_threads) + { + num_threads *= 2; + threads = realloc(threads, num_threads * sizeof(pthread_t)); + thread_data = realloc(thread_data, num_threads * sizeof(ThreadData*)); + } + + thread_data[thread_count] = malloc(sizeof(ThreadData)); + thread_data[thread_count]->name = name; + thread_data[thread_count]->e_ident = ident; + thread_data[thread_count]->scn = scn; + thread_data[thread_count]->is_eh = is_eh; + + if (pthread_create(&threads[thread_count], NULL, thread_work, thread_data[thread_count]) != 0) + { + perror("Failed to create thread"); + for (int j = 0; j < thread_count; j++) + { + pthread_cancel(threads[j]); + } + free(threads); + free(thread_data); + return 1; + } + else + { + thread_count++; + } + } + } + } + + for (int i = 0; i < thread_count; i++) + { + if (pthread_join(threads[i], NULL) != 0) + { + perror("Failed to join thread"); + free(threads); + free(thread_data); + return 1; + } + } + + for (int i = 0; i < thread_count; i++) + { + free(thread_data[i]); + } + + free(threads); + free(thread_data); + + elf_end(elf); + close(fd); + + return 0; +} \ No newline at end of file diff --git a/tests/eu_search_die.c b/tests/eu_search_die.c new file mode 100644 index 00000000..a7f75521 --- /dev/null +++ b/tests/eu_search_die.c @@ -0,0 +1,262 @@ +/*Test program for eu_search_die. + Copyright (C) 2023 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see<http://www.gnu.org/licenses/>. */ +#include <config.h> +#include ELFUTILS_HEADER(dw) +#include <dwarf.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <assert.h> +#include <inttypes.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <pthread.h> + +static void *thread_work(void *arg); +static int check_die(Dwarf_Die *die); +static int check_dbg(Dwarf *dbg); + +/*The main Dwarf file. */ +static Dwarf * dwarf; + +typedef struct +{ + Dwarf * dbg; + Dwarf_Off start_offset; + Dwarf_Off end_offset; + int result; +} + +ThreadData; + +static void *thread_work(void *arg) +{ + ThreadData *data = (ThreadData*) arg; + data->result = check_dbg(data->dbg); + return NULL; +} + +static int check_die(Dwarf_Die *die) +{ + if (dwarf_tag(die) == DW_TAG_invalid) + { + printf("Invalid die\n"); + return -1; + } + + int res = 0; + void *addr = die->addr; + Dwarf_Die die2; + if (dwarf_die_addr_die(dwarf, addr, &die2) == NULL) + { + printf("Bad die addr die at offset %" PRIx64 "\n", dwarf_dieoffset(die)); + res = -1; + } + + if (dwarf_tag(die) != dwarf_tag(&die2)) + { + printf("Tags differ for die at offset %" PRIx64 "\n", dwarf_dieoffset(die)); + res = -1; + } + + if (dwarf_cuoffset(die) != dwarf_cuoffset(&die2)) + { + printf("CU offsets differ for die at offset %" PRIx64 "\n", dwarf_dieoffset(die)); + res = -1; + } + + Dwarf_Die child; + if (dwarf_child(die, &child) == 0) + res |= check_die(&child); + + Dwarf_Die sibling; + if (dwarf_siblingof(die, &sibling) == 0) + res |= check_die(&sibling); + + return res; +} + +static int check_dbg(Dwarf *dbg) +{ + int res = 0; + Dwarf_Off off = 0; + Dwarf_Off old_off = 0; + size_t hsize; + Dwarf_Off abbrev; + uint8_t addresssize; + uint8_t offsetsize; + + while (dwarf_nextcu(dbg, off, &off, &hsize, &abbrev, &addresssize, &offsetsize) == 0) + { + Dwarf_Die die; + if (dwarf_offdie(dbg, old_off + hsize, &die) != NULL) + { + printf("checking CU at %" PRIx64 "\n", old_off); + res |= check_die(&die); + } + + old_off = off; + } + + // Same for type... + Dwarf_Half version; + uint64_t typesig; + Dwarf_Off typeoff; + off = 0; + old_off = 0; + + while (dwarf_next_unit(dbg, off, &off, &hsize, &version, &abbrev, &addresssize, &offsetsize, &typesig, &typeoff) == 0) + { + Dwarf_Die die; + if (dwarf_offdie_types(dbg, old_off + hsize, &die) != NULL) + { + printf("checking TU at %" PRIx64 "\n", old_off); + res |= check_die(&die); + } + + // We should have seen this already, but double check... + if (dwarf_offdie_types(dbg, old_off + typeoff, &die) != NULL) + { + printf("checking Type DIE at %" PRIx64 "\n", old_off + hsize + typeoff); + res |= check_die(&die); + } + + old_off = off; + } + + Dwarf *alt = dwarf_getalt(dbg); + + if (alt != NULL) + { + printf("checking alt debug\n"); + res |= check_dbg(alt); + } + + // Split or Type Dwarf_Dies gotten through dwarf_get_units. + Dwarf_CU *cu = NULL; + Dwarf_Die subdie; + uint8_t unit_type; + while (dwarf_get_units(dbg, cu, &cu, NULL, &unit_type, NULL, &subdie) == 0) + { + if (dwarf_tag(&subdie) != DW_TAG_invalid) + { + printf("checking %" + PRIx8 " subdie\n", unit_type); + res |= check_die(&subdie); + } + } + + return res; +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + printf("No file given.\n"); + return -1; + } + + const char *name = argv[1]; + int fd = open(name, O_RDONLY); + if (fd < 0) + { + printf("Cannot open '%s': %s\n", name, strerror(errno)); + return -1; + } + + dwarf = dwarf_begin(fd, DWARF_C_READ); + if (dwarf == NULL) + { + printf("Not a Dwarf file '%s': %s\n", name, dwarf_errmsg(-1)); + close(fd); + return -1; + } + + printf("checking %s\n", name); + + int num_threads = 4; + pthread_t *threads = (pthread_t*) malloc(num_threads* sizeof(pthread_t)); + ThreadData *thread_data = (ThreadData*) malloc(num_threads* sizeof(ThreadData)); + + if (!threads || !thread_data) + { + fprintf(stderr, "Failed to allocate memory for threads.\n"); + free(threads); + free(thread_data); + return 1; + } + + Dwarf_Off total_off = 0; + Dwarf_Off unit_off = 0; + size_t hsize; + Dwarf_Off abbrev; + uint8_t addresssize; + uint8_t offsetsize; + + while (dwarf_nextcu(dwarf, unit_off, &unit_off, &hsize, &abbrev, &addresssize, &offsetsize) == 0) + { + thread_data[total_off % num_threads].start_offset = unit_off; + thread_data[total_off % num_threads].end_offset = unit_off + hsize; + total_off++; + } + + for (int i = 0; i < num_threads; i++) + { + thread_data[i].dbg = dwarf; + if (pthread_create(&threads[i], NULL, thread_work, (void*) &thread_data[i]) != 0) + { + perror("Failed to create thread"); + for (int j = 0; j < i; j++) + { + pthread_cancel(threads[j]); + } + free(threads); + free(thread_data); + return 1; + } + } + + for (int i = 0; i < num_threads; i++) + { + if (pthread_join(threads[i], NULL) != 0) + { + perror("Failed to join thread"); + free(threads); + free(thread_data); + return 1; + } + } + + int res = 0; + for (int i = 0; i < num_threads; i++) + { + res |= thread_data[i].result; + } + + free(threads); + free(thread_data); + + dwarf_end(dwarf); + close(fd); + + return res; +} \ No newline at end of file diff --git a/tests/eu_search_lines.c b/tests/eu_search_lines.c new file mode 100644 index 00000000..b7a875d8 --- /dev/null +++ b/tests/eu_search_lines.c @@ -0,0 +1,228 @@ +/*Test program for eu_search_lines. + Copyright (C) 2023 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see<http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <fcntl.h> +#include <inttypes.h> +#include <libelf.h> +#include ELFUTILS_HEADER(dw) +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <pthread.h> + +typedef struct +{ + const char *filename; + int result; +} + +ThreadData; + +static void *thread_work(void *arg) +{ + ThreadData *data = (ThreadData*) arg; + + int fd = open(data->filename, O_RDONLY); + + Dwarf *dbg = dwarf_begin(fd, DWARF_C_READ); + if (dbg == NULL) + { + printf("%s not usable: %s\n", data->filename, dwarf_errmsg(-1)); + close(fd); + free(data); + pthread_exit(NULL); + } + + Dwarf_Off cuoff = 0; + Dwarf_Off old_cuoff = 0; + size_t hsize; + Dwarf_Off ao; + uint8_t asz; + uint8_t osz; + while (dwarf_nextcu(dbg, cuoff, &cuoff, &hsize, &ao, &asz, &osz) == 0) + { + printf("cuhl = %zu, o = %llu, asz = %hhu, osz = %hhu, ncu = %llu\n", + hsize, (unsigned long long int) ao, + asz, osz, (unsigned long long int) cuoff); + + // Get the DIE for the CU. + Dwarf_Die die; + if (dwarf_offdie(dbg, old_cuoff + hsize, &die) == NULL) + { + printf("%s: cannot get CU die\n", data->filename); + data->result = 1; + break; + } + + old_cuoff = cuoff; + + Dwarf_Lines * lb; + size_t nlb; + if (dwarf_getsrclines(&die, &lb, &nlb) != 0) + { + printf("%s: cannot get lines\n", data->filename); + data->result = 1; + break; + } + + printf(" %zu lines\n", nlb); + + for (size_t i = 0; i < nlb; ++i) + { + Dwarf_Line *l = dwarf_onesrcline(lb, i); + if (l == NULL) + { + printf("%s: cannot get individual line\n", data->filename); + data->result = 1; + break; + } + + Dwarf_Addr addr; + if (dwarf_lineaddr(l, &addr) != 0) + addr = 0; + const char *file = dwarf_linesrc(l, NULL, NULL); + int line; + if (dwarf_lineno(l, &line) != 0) + line = 0; + + printf("%" PRIx64 ": %s:%d:", (uint64_t) addr, file ? : "???", line); + + // Getting the file path through the Dwarf_Files should + // result in the same path. + Dwarf_Files * files; + size_t idx; + if (dwarf_line_file(l, &files, &idx) != 0) + { + printf("%s: cannot get file from line (%zd): %s\n", + data->filename, i, dwarf_errmsg(-1)); + data->result = 1; + break; + } + + const char *path = dwarf_filesrc(files, idx, NULL, NULL); + if ((path == NULL && file != NULL) || + (path != NULL && file == NULL) || + (strcmp(file, path) != 0)) + { + printf("%s: line %zd srcline (%s) != file srcline (%s)\n", + data->filename, i, file ? : "???", path ? : "???"); + data->result = 1; + break; + } + + int column; + if (dwarf_linecol(l, &column) != 0) + column = 0; + if (column >= 0) + printf("%d:", column); + + bool is_stmt; + if (dwarf_linebeginstatement(l, &is_stmt) != 0) + is_stmt = false; + bool end_sequence; + if (dwarf_lineendsequence(l, &end_sequence) != 0) + end_sequence = false; + bool basic_block; + if (dwarf_lineblock(l, &basic_block) != 0) + basic_block = false; + bool prologue_end; + if (dwarf_lineprologueend(l, &prologue_end) != 0) + prologue_end = false; + bool epilogue_begin; + if (dwarf_lineepiloguebegin(l, &epilogue_begin) != 0) + epilogue_begin = false; + + printf(" is_stmt:%s, end_seq:%s, bb:%s, prologue:%s, epilogue:%s\n", + is_stmt ? "yes" : "no", end_sequence ? "yes" : "no", + basic_block ? "yes" : "no", prologue_end ? "yes" : "no", + epilogue_begin ? "yes" : "no"); + } + } + + dwarf_end(dbg); + close(fd); + free(data); + + pthread_exit(NULL); +} + +int main(int argc, char *argv[]) +{ + int result = 0; + int cnt; + + if (argc < 2) + { + printf("Usage: %s<filename1>[<filename2> ...]\n", argv[0]); + return 1; + } + + pthread_t *threads = (pthread_t*) malloc((argc - 1) *sizeof(pthread_t)); + ThreadData **thread_data = (ThreadData **) malloc((argc - 1) *sizeof(ThreadData*)); + + if (!threads || !thread_data) + { + fprintf(stderr, "Failed to allocate memory for threads.\n"); + free(threads); + free(thread_data); + return 1; + } + + for (cnt = 1; cnt < argc; ++cnt) + { + thread_data[cnt - 1] = (ThreadData*) malloc(sizeof(ThreadData)); + thread_data[cnt - 1]->filename = argv[cnt]; + thread_data[cnt - 1]->result = 0; + + if (pthread_create(&threads[cnt - 1], NULL, thread_work, thread_data[cnt - 1]) != 0) + { + perror("Failed to create thread"); + for (int j = 0; j < cnt; j++) + { + pthread_cancel(threads[j]); + } + free(threads); + free(thread_data); + return 1; + } + } + + for (cnt = 0; cnt < argc - 1; ++cnt) + { + if (pthread_join(threads[cnt], NULL) != 0) + { + perror("Failed to join thread"); + free(threads); + free(thread_data); + return 1; + } + + if (thread_data[cnt]->result != 0) + { + result = 1; + } + + free(thread_data[cnt]); + } + + free(threads); + free(thread_data); + + return result; +} \ No newline at end of file diff --git a/tests/eu_search_macros.c b/tests/eu_search_macros.c new file mode 100644 index 00000000..6954ca79 --- /dev/null +++ b/tests/eu_search_macros.c @@ -0,0 +1,216 @@ +/*Test program for eu_search_macros + Copyright (C) 2023 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see<http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include ELFUTILS_HEADER(dw) +#include <dwarf.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <assert.h> +#include <inttypes.h> +#include <stdatomic.h> +#include <pthread.h> + +static void *thread_work(void *arg); +static int mac(Dwarf_Macro *macro, void *dbg); +static void include(Dwarf *dbg, Dwarf_Off macoff, ptrdiff_t token); + +typedef struct +{ + Dwarf * dbg; + Dwarf_Die * cudie; + bool new_style; +} + +ThreadData; + +static void *thread_work(void *arg) +{ + ThreadData *data = (ThreadData*) arg; + Dwarf *dbg = data->dbg; + Dwarf_Die *cudie = data->cudie; + bool new_style = data->new_style; + + for (ptrdiff_t off = new_style ? DWARF_GETMACROS_START : 0; + (off = dwarf_getmacros(cudie, mac, dbg, off));) + { + if (off == -1) + { + puts(dwarf_errmsg(dwarf_errno())); + break; + } + } + + return NULL; +} + +static void include(Dwarf *dbg, Dwarf_Off macoff, ptrdiff_t token) +{ + while ((token = dwarf_getmacros_off(dbg, macoff, mac, dbg, token)) != 0) + { + if (token == -1) + { + puts(dwarf_errmsg(dwarf_errno())); + break; + } + } +} + +static int +mac(Dwarf_Macro *macro, void *dbg) +{ + static atomic_int level = 0; + + unsigned int opcode; + dwarf_macro_opcode(macro, &opcode); + switch (opcode) + { + case DW_MACRO_import: + { + Dwarf_Attribute at; + int r = dwarf_macro_param(macro, 0, &at); + assert(r == 0); + + Dwarf_Word w; + r = dwarf_formudata(&at, &w); + assert(r == 0); + + printf ("%dinclude %#" PRIx64 "\n", atomic_load (&level), w); + + atomic_fetch_add(&level, 1); + + include(dbg, w, DWARF_GETMACROS_START); + + atomic_fetch_sub(&level, 1); + + printf ("%d/include\n", atomic_load (&level)); + break; + } + + case DW_MACRO_start_file: + { + Dwarf_Files * files; + size_t nfiles; + if (dwarf_macro_getsrcfiles(dbg, macro, &files, &nfiles) < 0) + printf("dwarf_macro_getsrcfiles: %s\n", dwarf_errmsg(dwarf_errno())); + + Dwarf_Word w = 0; + dwarf_macro_param2(macro, &w, NULL); + + const char *name = dwarf_filesrc (files, (size_t) w, NULL, NULL); + printf ("%dfile %s\n", atomic_load (&level), name); + atomic_fetch_add(&level, 1); + break; + } + + case DW_MACRO_end_file: + { + atomic_fetch_sub(&level, 1); + printf ("%d/file\n", atomic_load (&level)); + break; + } + + case DW_MACINFO_define: + case DW_MACRO_define_strp: + { + const char *value; + dwarf_macro_param2(macro, NULL, &value); + printf ("%d%s\n", atomic_load (&level), value); + break; + } + + case DW_MACINFO_undef: + case DW_MACRO_undef_strp: + break; + + default: + { + size_t paramcnt; + dwarf_macro_getparamcnt(macro, ¶mcnt); + printf ("%dopcode %u with %zd arguments\n", atomic_load (&level), opcode, paramcnt); + break; + } + } + + return DWARF_CB_ABORT; +} + +int main(int argc, char *argv[]) +{ + assert(argc >= 3); + const char *name = argv[1]; + ptrdiff_t cuoff = strtol(argv[2], NULL, 0); + bool new_style = argc > 3; + + int fd = open(name, O_RDONLY); + Dwarf *dbg = dwarf_begin(fd, DWARF_C_READ); + + Dwarf_Die cudie_mem, *cudie = dwarf_offdie(dbg, cuoff, &cudie_mem); + + int num_threads = 4; + pthread_t *threads = malloc(num_threads* sizeof(pthread_t)); + ThreadData *thread_data = malloc(num_threads* sizeof(ThreadData)); + + if (!threads || !thread_data) + { + fprintf(stderr, "Failed to allocate memory for threads.\n"); + free(threads); + free(thread_data); + return 1; + } + + for (int i = 0; i < num_threads; i++) + { + thread_data[i].dbg = dbg; + thread_data[i].cudie = cudie; + thread_data[i].new_style = new_style; + + if (pthread_create(&threads[i], NULL, thread_work, (void*) &thread_data[i]) != 0) + { + perror("Failed to create thread"); + for (int j = 0; j < i; j++) + { + pthread_cancel(threads[j]); + } + free(threads); + free(thread_data); + return 1; + } + } + + for (int i = 0; i < num_threads; i++) + { + if (pthread_join(threads[i], NULL) != 0) + { + perror("Failed to join thread"); + free(threads); + free(thread_data); + return 1; + } + } + + free(threads); + free(thread_data); + + dwarf_end(dbg); + + return 0; +} diff --git a/tests/run-eu-search-tests.sh b/tests/run-eu-search-tests.sh new file mode 100755 index 00000000..84edc234 --- /dev/null +++ b/tests/run-eu-search-tests.sh @@ -0,0 +1,168 @@ +#! /bin/sh +# Copyright (C) 2015, 2018 Red Hat, Inc. +# This file is part of elfutils. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# elfutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. $srcdir/test-subr.sh + +# Extract the value of USE_ADDRESS_SANITIZER_TRUE from config.status +# Cannot use Helgrind and Address Sanitizer together. +# Test will be skipped if Address Sanitizer is enabled. +USE_ADDRESS_SANITIZER=$(grep 'USE_ADDRESS_SANITIZER_TRUE' ${abs_builddir}/../config.status | awk -F'=' '{print $2}') + +if [[ "$USE_ADDRESS_SANITIZER" == "\"#\"" ]]; then + echo "Address Sanitizer is disabled." +else + echo "Address Sanitizer is enabled. Skipping test." + exit 77 +fi + +# Extract the value of USE_MEMORY_SANITIZER_TRUE from config.status +# Cannot use Helgrind and Memory Sanitizer together. +# Test will be skipped if Memory Sanitizer is enabled. +USE_MEMORY_SANITIZER=$(grep 'USE_MEMORY_SANITIZER_TRUE' ${abs_builddir}/../config.status | awk -F'=' '{print $2}') + +if [[ "$USE_MEMORY_SANITIZER" == "\"#\"" ]]; then + echo "Memory Sanitizer is disabled." +else + echo "Memory Sanitizer is enabled. Skipping test." + exit 77 +fi + +# Extract the value of USE_LOCKS from config.h +# Test will only be run if USE_LOCKS is defined. Otherwise, skip. +USE_LOCKS=$(grep '^#define USE_LOCKS' ${abs_builddir}/../config.h | awk '{print $3}') + +if [[ "$USE_LOCKS" -eq 1 ]]; then + echo "USE_LOCKS is defined." +else + echo "USE_LOCKS is not defined. Skipping test." + exit 77 +fi + +# Disable valgrind if configured, since we are already using it here. +SAVED_VALGRIND_CMD="$VALGRIND_CMD" +unset VALGRIND_CMD + +echo "Begin tests..." + +# Begin data race test for parallelized dwarf-die-addr-die +# Tests thread safety for updated libdw_findcu.c and libdw_find_split_unit.c +testfiles testfile-debug-types +testfiles testfile_multi_main testfile_multi.dwz +testfiles testfilebazdbgppc64.debug +testfiles testfile-dwarf-4 testfile-dwarf-5 +testfiles testfile-splitdwarf-4 testfile-hello4.dwo testfile-world4.dwo +testfiles testfile-splitdwarf-5 testfile-hello5.dwo testfile-world5.dwo + +die_test_files=("testfile-debug-types" + "testfile_multi_main" "testfile_multi.dwz" + "testfilebazdbgppc64.debug" + "testfile-dwarf-4" "testfile-dwarf-5" + "testfile-splitdwarf-4" "testfile-hello4.dwo" "testfile-world4.dwo" + "testfile-splitdwarf-5" "testfile-hello5.dwo" "testfile-world5.dwo") + +echo -e "\nStarting data race test for dwarf-die-addr-die" + +for file in "${die_test_files[@]}"; do + helgrind_output=$(valgrind --tool=helgrind "${abs_builddir}/eu_search_die" "$file" 2>&1) + + if grep -q "ERROR SUMMARY: 0 errors" <<< "$helgrind_output"; then + echo "No data races found for $file. Test passed." + else + echo "Data races found for $file. Test failed." + echo "$helgrind_output" + exit 1 + fi +done + +# Begin data race test for parallelized next_cfi +# Tests thread safety for updated cie.c and fde.c +testfiles testfile11 testfile12 +testfiles testfilearm testfileaarch64 +testfiles testfileppc32 testfileppc64 + +cfi_test_files=("testfile11" "testfile12" + "testfilearm" "testfileaarch64" + "testfileppc32" "testfileppc64") + +echo -e "\nStarting data race test for next_cfi" + +for file in "${cfi_test_files[@]}"; do + + helgrind_output=$(valgrind --tool=helgrind "${abs_builddir}/eu_search_cfi" $file 2>&1) + + if grep -q "ERROR SUMMARY: 0 errors" <<< "$helgrind_output"; then + echo "No data races found for $file. Test passed." + else + echo "Data races found for $file. Test failed." + echo "$helgrind_output" + exit 1 + fi + +done + +# Begin data race test for parallelizd dwarf-getmacros +# Tests thread safety for updated dwarf_getmacros.c +testfiles testfile51 +testfiles testfile-macros +testfiles testfile-macros-0xff + +macro_test_files=("testfile51 0xb" + "testfile51 0x84" + "testfile-macrosm 0xb" + "testfile-macros-0xff 0xb") + +echo -e "\nStarting data race test for dwarf-getmacros" + +for file in "${macro_test_files[@]}"; do + + helgrind_output=$(valgrind --tool=helgrind "${abs_builddir}/eu_search_macros" $file 2>&1) + + if grep -q "ERROR SUMMARY: 0 errors" <<< "$helgrind_output"; then + echo "No data races found for $file. Test passed." + else + echo "Data races found for $file. Test failed." + echo "$helgrind_output" + exit 1 + fi + +done + +# Begin data race test for parallelized get-lines +# Tests thread safety for updated dwarf_getsrclines.c +testfiles testfile testfile2 testfilenolines + +lines_test_files=("testfile" "testfile2" "testfilenolines") + +echo -e "\nStarting data race test for get-lines" + +for file in "${lines_test_files[@]}"; do + + helgrind_output=$(valgrind --tool=helgrind "${abs_builddir}/eu_search_lines" $file 2>&1) + + if grep -q "ERROR SUMMARY: 0 errors" <<< "$helgrind_output"; then + echo "No data races found for $file. Test passed." + else + echo -e "$helgrind_output" + echo "Data races found for $file. Test failed." + exit 1 + fi + +done + +# This line is reached only if no data races were found in any test +# Exit with success status. +exit 0 \ No newline at end of file -- 2.45.2