162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * tools/testing/selftests/kvm/lib/assert.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018, Google LLC.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define _GNU_SOURCE /* for getline(3) and strchrnul(3)*/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "test_util.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <execinfo.h>
1362306a36Sopenharmony_ci#include <sys/syscall.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "kselftest.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Dumps the current stack trace to stderr. */
1862306a36Sopenharmony_cistatic void __attribute__((noinline)) test_dump_stack(void);
1962306a36Sopenharmony_cistatic void test_dump_stack(void)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	/*
2262306a36Sopenharmony_ci	 * Build and run this command:
2362306a36Sopenharmony_ci	 *
2462306a36Sopenharmony_ci	 *	addr2line -s -e /proc/$PPID/exe -fpai {backtrace addresses} | \
2562306a36Sopenharmony_ci	 *		cat -n 1>&2
2662306a36Sopenharmony_ci	 *
2762306a36Sopenharmony_ci	 * Note that the spacing is different and there's no newline.
2862306a36Sopenharmony_ci	 */
2962306a36Sopenharmony_ci	size_t i;
3062306a36Sopenharmony_ci	size_t n = 20;
3162306a36Sopenharmony_ci	void *stack[n];
3262306a36Sopenharmony_ci	const char *addr2line = "addr2line -s -e /proc/$PPID/exe -fpai";
3362306a36Sopenharmony_ci	const char *pipeline = "|cat -n 1>&2";
3462306a36Sopenharmony_ci	char cmd[strlen(addr2line) + strlen(pipeline) +
3562306a36Sopenharmony_ci		 /* N bytes per addr * 2 digits per byte + 1 space per addr: */
3662306a36Sopenharmony_ci		 n * (((sizeof(void *)) * 2) + 1) +
3762306a36Sopenharmony_ci		 /* Null terminator: */
3862306a36Sopenharmony_ci		 1];
3962306a36Sopenharmony_ci	char *c = cmd;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	n = backtrace(stack, n);
4262306a36Sopenharmony_ci	/*
4362306a36Sopenharmony_ci	 * Skip the first 2 frames, which should be test_dump_stack() and
4462306a36Sopenharmony_ci	 * test_assert(); both of which are declared noinline.  Bail if the
4562306a36Sopenharmony_ci	 * resulting stack trace would be empty. Otherwise, addr2line will block
4662306a36Sopenharmony_ci	 * waiting for addresses to be passed in via stdin.
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	if (n <= 2) {
4962306a36Sopenharmony_ci		fputs("  (stack trace empty)\n", stderr);
5062306a36Sopenharmony_ci		return;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	c += sprintf(c, "%s", addr2line);
5462306a36Sopenharmony_ci	for (i = 2; i < n; i++)
5562306a36Sopenharmony_ci		c += sprintf(c, " %lx", ((unsigned long) stack[i]) - 1);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	c += sprintf(c, "%s", pipeline);
5862306a36Sopenharmony_ci#pragma GCC diagnostic push
5962306a36Sopenharmony_ci#pragma GCC diagnostic ignored "-Wunused-result"
6062306a36Sopenharmony_ci	system(cmd);
6162306a36Sopenharmony_ci#pragma GCC diagnostic pop
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic pid_t _gettid(void)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return syscall(SYS_gettid);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_civoid __attribute__((noinline))
7062306a36Sopenharmony_citest_assert(bool exp, const char *exp_str,
7162306a36Sopenharmony_ci	const char *file, unsigned int line, const char *fmt, ...)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	va_list ap;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (!(exp)) {
7662306a36Sopenharmony_ci		va_start(ap, fmt);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		fprintf(stderr, "==== Test Assertion Failure ====\n"
7962306a36Sopenharmony_ci			"  %s:%u: %s\n"
8062306a36Sopenharmony_ci			"  pid=%d tid=%d errno=%d - %s\n",
8162306a36Sopenharmony_ci			file, line, exp_str, getpid(), _gettid(),
8262306a36Sopenharmony_ci			errno, strerror(errno));
8362306a36Sopenharmony_ci		test_dump_stack();
8462306a36Sopenharmony_ci		if (fmt) {
8562306a36Sopenharmony_ci			fputs("  ", stderr);
8662306a36Sopenharmony_ci			vfprintf(stderr, fmt, ap);
8762306a36Sopenharmony_ci			fputs("\n", stderr);
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci		va_end(ap);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		if (errno == EACCES) {
9262306a36Sopenharmony_ci			print_skip("Access denied - Exiting");
9362306a36Sopenharmony_ci			exit(KSFT_SKIP);
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci		exit(254);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return;
9962306a36Sopenharmony_ci}
100