summaryrefslogblamecommitdiffstats
path: root/userspace/src/exec.c
blob: 8a99437be4724129c1ab8dd5e8bc4fa288fe5e71 (plain) (tree)






















                                                                             



                      









                              











                                                                         


                                                                              
                                                 

                       










                                                                          
 
                                                                      
 
                                     








                                                                                
                                      
 

                                                                     


                                                        
                                                         


                                                                            
                                                           


                                                                  






































                                                                                
 
























                                                







                                                         
                                     
















                                                                               

                                                                  







                                                                     
 



                                                                          



                 
/*
 * Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>

#include <unit/io.h>
#include <unit/core.h>
#include <unit/unit.h>
#include <unit/module.h>
#include <unit/results.h>

#include <nvgpu/posix/probe.h>

/*
 * Sempaphore to limit the number of threads
 */
sem_t unit_thread_semaphore;

/*
 * C11 thread local storage, used to access test context when a signal is
 * received (ex: SIGSEGV) in a thread.
 */
_Thread_local struct unit_module *thread_local_module;
_Thread_local struct unit_module_test *thread_local_test;

/*
 * Execute a module and all its subtests. This function builds a gk20a for the
 * test to use by executing nvgpu_posix_probe() and nvgpu_posix_cleanup();
 */
static void *core_exec_module(void *module_param)
{
	unsigned int i;
	struct unit_module *module = (struct unit_module *) module_param;
	struct gk20a *g;

	g = module->fw->nvgpu.nvgpu_posix_probe();

	if (!g) {
		core_msg_color(module->fw, C_RED,
				"  nvgpu_posix_probe failed: Module %s\n",
				module->name);
		goto thread_exit;
	}

	core_vbs(module->fw, 1, "Execing module: %s\n", module->name);

	thread_local_module = module;

	/*
	 * Execute each test within the module. No reinit is done between tests.
	 * Thats up to the module itself to handle. Any setup/teardown between
	 * unit tests must be handled within the module.
	 */
	for (i = 0; i < module->nr_tests; i++) {
		struct unit_module_test *t = module->tests + i;
		int test_status;
		thread_local_test = t;

		core_msg(module->fw, "Running %s.%s\n", module->name,
			t->name);
		test_status = t->fn(module, g, t->args);

		if (test_status != UNIT_SUCCESS)
			core_msg_color(module->fw, C_RED,
				       "  Unit error! Test %s.%s FAILED!\n",
				       module->name, t->name);

		core_add_test_record(module->fw, module, t,
				     test_status == UNIT_SUCCESS);
	}

	module->fw->nvgpu.nvgpu_posix_cleanup(g);

	core_vbs(module->fw, 1, "Module completed: %s\n", module->name);
thread_exit:
	sem_post(&unit_thread_semaphore);
	return NULL;
}

/*
 * According to POSIX, "Signals which are generated by some action attributable
 * to a particular thread, such as a hardware fault, shall be generated for the
 * thread that caused the signal to be generated."
 * This custom signal handler will be run from within the thread that caused the
 * exception. Thanks to the context being saved in local thread storage, it is
 * then trivial to report which test case failed, and then terminate the thread.
 */
static void thread_error_handler(int sig, siginfo_t *siginfo, void *context)
{
	core_msg_color(thread_local_module->fw, C_RED,
			"  Signal %d in Test: %s.%s!\n", sig,
			thread_local_module->name, thread_local_test->name);
	core_add_test_record(thread_local_module->fw, thread_local_module,
			thread_local_test, false);
	sem_post(&unit_thread_semaphore);
	pthread_exit(NULL);
}

/*
 * Install a custom signal handler for several signals to be used when running
 * in multithreaded environment.
 */
static int install_thread_error_handler(void)
{
	struct sigaction action;
	int err;

	memset(&action, 0, sizeof(action));
	action.sa_sigaction = &thread_error_handler;
	action.sa_flags = SA_SIGINFO;

	/* SIGSEGV: Invalid memory reference */
	err = sigaction(SIGSEGV, &action, NULL);
	if (err < 0) {
		return err;
	}
	/* SIGILL: Illegal Instruction */
	err = sigaction(SIGILL, &action, NULL);
	if (err < 0) {
		return err;
	}
	/* SIGFPE: Floating-point exception */
	err = sigaction(SIGFPE, &action, NULL);
	if (err < 0) {
		return err;
	}
	/* SIGBUS: Bus error */
	err = sigaction(SIGBUS, &action, NULL);
	if (err < 0) {
		return err;
	}
	/* SIGSYS: Bad system call */
	err = sigaction(SIGSYS, &action, NULL);
	if (err < 0) {
		return err;
	}
	return 0;
}

/*
 * Execute all modules loaded by the unit test framework.
 */
int core_exec(struct unit_fw *fw)
{
	struct unit_module **modules;
	int err = 0;

	core_vbs(fw, 1, "Using %d threads\n", fw->args->thread_count);
	sem_init(&unit_thread_semaphore, 0, fw->args->thread_count);

	/*
	 * If running single threaded, keep the default SIGSEGV handler to make
	 * interactive debugging easier, otherwise install the custom one.
	 */
	if (fw->args->thread_count > 1) {
		err = install_thread_error_handler();
		if (err != 0) {
			core_msg_color(fw, C_RED,
			"  Failed to install signal handler!\n");
			return err;
		}
	}

	for (modules = fw->modules; *modules != NULL; modules++) {
		if (fw->args->thread_count == 1) {
			core_exec_module(*modules);
		} else {
			sem_wait(&unit_thread_semaphore);
			pthread_create(&((*modules)->thread), NULL,
				core_exec_module, (void *) *modules);
		}
	}

	if (fw->args->thread_count > 1) {
		for (modules = fw->modules; *modules != NULL; modules++) {
			pthread_join((*modules)->thread, NULL);
		}
	}

	return 0;
}