/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2010-2014 Intel Corporation
 */

#include "test.h"

#include <stdio.h>
#include <stdint.h>

#ifdef RTE_EXEC_ENV_WINDOWS
int
test_panic(void)
{
	printf("debug not supported on Windows, skipping test\n");
	return TEST_SKIPPED;
}
int
test_exit(void)
{
	printf("debug not supported on Windows, skipping test\n");
	return TEST_SKIPPED;
}
static int
test_debug(void)
{
	printf("debug not supported on Windows, skipping test\n");
	return TEST_SKIPPED;
}

#else

#include <sys/resource.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>

#include <rte_debug.h>
#include <rte_common.h>
#include <rte_eal.h>
#include <rte_lcore.h>

#include "process.h"

/*
 * Debug test
 * ==========
 */

static const char *test_args[8];

int
test_panic(void)
{
	int status;

	if (getenv(RECURSIVE_ENV_VAR) != NULL) {
		struct rlimit rl;

		/* No need to generate a coredump when panicking. */
		rl.rlim_cur = rl.rlim_max = 0;
		setrlimit(RLIMIT_CORE, &rl);
		rte_panic("Test Debug\n");
	}
	status = process_dup(test_args, RTE_DIM(test_args), "test_panic");
	if(status == 0){
		printf("Child process terminated normally!\n");
		return -1;
	} else
		printf("Child process terminated as expected - Test passed!\n");

	return 0;
}

static int
test_exit_val(int exit_val)
{
	char buf[5];
	int status;

	sprintf(buf, "%d", exit_val);
	if (setenv("TEST_DEBUG_EXIT_VAL", buf, 1) == -1)
		rte_panic("Failed to set exit value in env\n");
	status = process_dup(test_args, RTE_DIM(test_args), "test_exit");
	printf("Child process status: %d\n", status);
	if(!WIFEXITED(status) || WEXITSTATUS(status) != (uint8_t)exit_val){
		printf("Child process terminated with incorrect status (expected = %d)!\n",
				exit_val);
		return -1;
	}
	return 0;
}

int
test_exit(void)
{
	int test_vals[] = { 0, 1, 2, 255, -1 };
	unsigned i;

	if (getenv(RECURSIVE_ENV_VAR) != NULL) {
		int exit_val;

		if (!getenv("TEST_DEBUG_EXIT_VAL"))
			rte_panic("No exit value set in env\n");

		exit_val = strtol(getenv("TEST_DEBUG_EXIT_VAL"), NULL, 0);
		rte_exit(exit_val, __func__);
	}

	for (i = 0; i < RTE_DIM(test_vals); i++) {
		if (test_exit_val(test_vals[i]) < 0)
			return -1;
	}
	printf("%s Passed\n", __func__);
	return 0;
}

static void
dummy_app_usage(const char *progname)
{
	RTE_SET_USED(progname);
}

static int
test_usage(void)
{
	if (rte_set_application_usage_hook(dummy_app_usage) != NULL) {
		printf("Non-NULL value returned for initial usage hook\n");
		return -1;
	}
	if (rte_set_application_usage_hook(NULL) != dummy_app_usage) {
		printf("Incorrect value returned for application usage hook\n");
		return -1;
	}
	return 0;
}

static int
test_debug(void)
{
#ifdef RTE_EXEC_ENV_FREEBSD
	/* BSD target doesn't support prefixes at this point, and we also need to
	 * run another primary process here.
	 */
	const char * prefix = "--no-shconf";
#else
	const char * prefix = "--file-prefix=debug";
#endif
	char core[10];

	sprintf(core, "%d", rte_get_main_lcore());

	test_args[0] = prgname;
	test_args[1] = prefix;
	test_args[2] = "-l";
	test_args[3] = core;

	if (rte_eal_has_hugepages() && RTE_EXEC_ENV_IS_LINUX) {
		test_args[4] = "";
		test_args[5] = "";
		test_args[6] = "";
		test_args[7] = "";
	} else {
		test_args[4] = "--no-huge";
		test_args[5] = "-m2048";
		test_args[6] = "--no-telemetry";
#ifdef RTE_ARCH_PPC_64
		/* iova=pa is the default, but fails on ppc64 with --no-huge */
		test_args[7] = "--iova-mode=va";
#else
		test_args[7] = "";
#endif
	}

	rte_dump_stack();
	if (test_panic() < 0)
		return -1;
	if (test_exit() < 0)
		return -1;
	if (test_usage() < 0)
		return -1;
	return 0;
}

#endif /* !RTE_EXEC_ENV_WINDOWS */

REGISTER_FAST_TEST(debug_autotest, true, true, test_debug);
