/* DF-5674 attribution: SIGSEGV handler that names the faulting symbol via dladdr */
#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <dlfcn.h>
#include <pthread.h>
#include <libprop/proplib.h>

#define NKEYS     96
#define NREMOVERS 3
#define NALLOCERS 2
#define ITERS     400000

static char keys[NKEYS][24];
static prop_string_t shared_str;
static int do_remove = 1;

static void
segv(int sig, siginfo_t *si, void *uc)
{
	ucontext_t *u = uc;
	Dl_info d;
	void *rip = (void *)(uintptr_t)u->uc_mcontext.mc_rip;

	if (dladdr(rip, &d) != 0 && d.dli_sname != NULL)
		fprintf(stderr, "SIGSEGV in %s (%s) addr=%p rip=%p\n",
		    d.dli_sname, d.dli_fname, si->si_addr, rip);
	else
		fprintf(stderr, "SIGSEGV addr=%p rip=%p (no symbol)\n",
		    si->si_addr, rip);
	_exit(139);
}

static void *
remover(void *arg)
{
	int it, i;
	for (it = 0; it < ITERS; it++) {
		prop_dictionary_t d = prop_dictionary_create();
		if (d == NULL) break;
		for (i = 0; i < NKEYS; i++)
			prop_dictionary_set(d, keys[i], shared_str);
		if (do_remove)
			for (i = 0; i < NKEYS; i++)
				prop_dictionary_remove(d, keys[i]);
		prop_object_release(d);
	}
	return NULL;
}

static void *
allocer(void *arg)
{
	int it, i;
	for (it = 0; it < ITERS; it++) {
		prop_dictionary_t d = prop_dictionary_create();
		if (d == NULL) break;
		for (i = 0; i < NKEYS; i++)
			prop_dictionary_set(d, keys[i], shared_str);
		prop_object_release(d);
	}
	return NULL;
}

int
main(int argc, char **argv)
{
	pthread_t thr[NREMOVERS + NALLOCERS];
	struct sigaction sa;
	int i;

	if (argc > 1 && strcmp(argv[1], "noremove") == 0)
		do_remove = 0;

	memset(&sa, 0, sizeof(sa));
	sa.sa_sigaction = segv;
	sa.sa_flags = SA_SIGINFO;
	sigaction(SIGSEGV, &sa, NULL);
	sigaction(SIGBUS, &sa, NULL);

	for (i = 0; i < NKEYS; i++)
		snprintf(keys[i], sizeof(keys[i]), "key%04d", i * 7 % NKEYS);
	shared_str = prop_string_create_cstring("value");

	{	/* warm the global keysym tree */
		prop_dictionary_t d = prop_dictionary_create();
		for (i = 0; i < NKEYS; i++)
			prop_dictionary_set(d, keys[i], shared_str);
		prop_object_release(d);
	}

	for (i = 0; i < NREMOVERS; i++)
		pthread_create(&thr[i], NULL, remover, NULL);
	for (i = 0; i < NALLOCERS; i++)
		pthread_create(&thr[NREMOVERS + i], NULL, allocer, NULL);
	for (i = 0; i < NREMOVERS + NALLOCERS; i++)
		pthread_join(thr[i], NULL);

	printf("survived\n");
	return 0;
}
