[15/16] tests: Add eu-search tests

Message ID 20231010134300.53830-15-mark@klomp.org
State Superseded
Delegated to: Aaron Merey
Headers
Series [01/16] lib: Add new once_define and once macros to eu-config.h |

Commit Message

Mark Wielaard Oct. 10, 2023, 1:42 p.m. UTC
  From: Heather McIntyre <hsm2@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): 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 <hsm2@rice.edu>
Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 tests/Makefile.am            |  16 ++-
 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 ++++++++++++++++++++++
 6 files changed, 1123 insertions(+), 1 deletion(-)
 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
  

Comments

Mark Wielaard Oct. 13, 2023, 2:38 p.m. UTC | #1
Hi Heather,

On Tue, 2023-10-10 at 15:42 +0200, Mark Wielaard wrote:
> From: Heather McIntyre <hsm2@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): 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 <hsm2@rice.edu>
> Signed-off-by: Mark Wielaard <mark@klomp.org>
> ---
>  tests/Makefile.am            |  16 ++-
>  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 ++++++++++++++++++++++
>  6 files changed, 1123 insertions(+), 1 deletion(-)
>  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/Makefile.am b/tests/Makefile.am
> index 32b18e6e..a66b1033 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

Below you add explicit -pthread to the new eu_search tests.
Do we need this globally in AM_CFLAGS as well?

>  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 \
> @@ -63,6 +67,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
>  		  getphdrnum leb128 read_unaligned \
>  		  msg_tst system-elf-libelf-test system-elf-gelf-test \
>  		  nvidia_extended_linemap_libdw \
> +		  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 \
> @@ -211,7 +216,8 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
>  	$(asm_TESTS) run-disasm-bpf.sh run-low_high_pc-dw-form-indirect.sh \
>  	run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
>  	run-readelf-dw-form-indirect.sh run-strip-largealign.sh \
> -	run-readelf-Dd.sh
> +	run-readelf-Dd.sh \
> +	run-eu-search-tests.sh

Correct.
But run-eu-search-tests.sh also needs to be added to EXTRA_DISTS to
make sure it gets shipped when doing make dist (make distcheck would
point that out, although with an obscure error).

diff --git a/tests/Makefile.am b/tests/Makefile.am
index a66b1033..09bfe405 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -635,7 +631,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
             run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
             testfile_nvidia_linemap.bz2 \
             testfile-largealign.o.bz2 run-strip-largealign.sh \
-            run-funcretval++11.sh
+            run-funcretval++11.sh run-eu-search-tests.sh
 
 
 if USE_VALGRIND


>  if !BIARCH
>  export ELFUTILS_DISABLE_BIARCH = 1
> @@ -804,6 +810,14 @@ getphdrnum_LDADD = $(libelf) $(libdw)
>  leb128_LDADD = $(libelf) $(libdw)
>  read_unaligned_LDADD = $(libelf) $(libdw)
>  nvidia_extended_linemap_libdw_LDADD = $(libelf) $(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.

OK. Although you might not need to conditionalize it on USE_LOCKS. It
can always be compiled with -pthread, the test run will just be skipped
if USE_LOCKS isn't set.

> 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.

Forgot to update the copyright notice as we discussed offlist. Sorry.
Will do so.

And maybe want to reindent to follow code style.

Skipping actual test code inspection, but see below on usage.

> +   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..3dca828e
> --- /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 ("%*sinclude %#" PRIx64 "\n", level, "", w);
> +
> +				atomic_fetch_add(&level, 1);
> +
> +				include(dbg, w, DWARF_GETMACROS_START);
> +
> +				atomic_fetch_sub(&level, 1);
> +
> +				printf ("%*s/include\n", 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 ("%*sfile %s\n", level, "", name);
> +				atomic_fetch_add(&level, 1);
> +				break;
> +			}
> +
> +		case DW_MACRO_end_file:
> +			{
> +				atomic_fetch_sub(&level, 1);
> +				printf ("%*s/file\n", level, "");
> +				break;
> +			}
> +
> +		case DW_MACINFO_define:
> +		case DW_MACRO_define_strp:
> +			{
> +				const char *value;
> +				dwarf_macro_param2(macro, NULL, &value);
> +				printf ("%*s%s\n", level, "", value);
> +				break;
> +			}
> +
> +		case DW_MACINFO_undef:
> +		case DW_MACRO_undef_strp:
> +			break;
> +
> +		default:
> +			{
> +				size_t paramcnt;
> +				dwarf_macro_getparamcnt(macro, &paramcnt);
> +				printf ("%*sopcode %u with %zd arguments\n", 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;

See note below on usage in run-eu-search-tests.sh

> +	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;
> +}
> \ No newline at end of file
> 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

Smart.

But you can also exclude the test in the tests/Makefile.am
which may be slightly simpler.

if USE_LOCKS
if !USE_ADDRESS_SANITIZER
if !USE_MEMORY_SANITIZER
TESTS += run-eu-search-tests.sh
endif
endif
endif

> +# Disable valgrind if configured, since we are already using it here.
> +SAVED_VALGRIND_CMD="$VALGRIND_CMD"
> +unset VALGRIND_CMD

Yes, but... see below.

> +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")

Nice collection of test files.

> +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

This will run valgrind and the program under the system installed
libelf and libdw. So we will want to do something like 

Also you may use valgrind --error-exitcode=1 to check for errors.

So you probably want something like
VALGRIND_CMD="valgrind -q --tool=helgrind --error-exitcode=1"

and then simply do:
  
for file in "${die_test_files[@]}"; do
  testrun "${abs_builddir}/eu_search_die" "$file"
done

Same for the other tests.


> +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
> 

Note you are passing just $file as argument to eu_search_macros but
that seems to require 3 arguments.

Cheers,

Mark
  

Patch

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 32b18e6e..a66b1033 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 \
@@ -63,6 +67,7 @@  check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
 		  getphdrnum leb128 read_unaligned \
 		  msg_tst system-elf-libelf-test system-elf-gelf-test \
 		  nvidia_extended_linemap_libdw \
+		  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 \
@@ -211,7 +216,8 @@  TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
 	$(asm_TESTS) run-disasm-bpf.sh run-low_high_pc-dw-form-indirect.sh \
 	run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \
 	run-readelf-dw-form-indirect.sh run-strip-largealign.sh \
-	run-readelf-Dd.sh
+	run-readelf-Dd.sh \
+	run-eu-search-tests.sh
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
@@ -804,6 +810,14 @@  getphdrnum_LDADD = $(libelf) $(libdw)
 leb128_LDADD = $(libelf) $(libdw)
 read_unaligned_LDADD = $(libelf) $(libdw)
 nvidia_extended_linemap_libdw_LDADD = $(libelf) $(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..3dca828e
--- /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 ("%*sinclude %#" PRIx64 "\n", level, "", w);
+
+				atomic_fetch_add(&level, 1);
+
+				include(dbg, w, DWARF_GETMACROS_START);
+
+				atomic_fetch_sub(&level, 1);
+
+				printf ("%*s/include\n", 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 ("%*sfile %s\n", level, "", name);
+				atomic_fetch_add(&level, 1);
+				break;
+			}
+
+		case DW_MACRO_end_file:
+			{
+				atomic_fetch_sub(&level, 1);
+				printf ("%*s/file\n", level, "");
+				break;
+			}
+
+		case DW_MACINFO_define:
+		case DW_MACRO_define_strp:
+			{
+				const char *value;
+				dwarf_macro_param2(macro, NULL, &value);
+				printf ("%*s%s\n", level, "", value);
+				break;
+			}
+
+		case DW_MACINFO_undef:
+		case DW_MACRO_undef_strp:
+			break;
+
+		default:
+			{
+				size_t paramcnt;
+				dwarf_macro_getparamcnt(macro, &paramcnt);
+				printf ("%*sopcode %u with %zd arguments\n", 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;
+}
\ No newline at end of file
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