/*
 *  debug.c
 *
 *  Copyright (C) 2004 - 2018 Vladislav Bolkhovitin <vst@vlnb.net>
 *  Copyright (C) 2004 - 2005 Leonid Stoljar
 *  Copyright (C) 2007 - 2018 Western Digital Corporation
 *
 *  Contains helper functions for execution tracing and error reporting.
 *  Intended to be included in main .c file.
 *
 *  This program 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, version 2
 *  of the License.
 *
 *  This program 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.
 */

#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>

#include "debug.h"

pid_t gettid(void)
{
	return syscall(__NR_gettid);
}

#if defined(DEBUG) || defined(TRACING)

#define TRACE_BUF_SIZE    512

static char trace_buf[TRACE_BUF_SIZE];
static pthread_spinlock_t trace_buf_lock;

int debug_print_prefix(unsigned long trace_flag, const char *prefix,
	const char *func, int line)
{
	int i = 0;

	pthread_spin_lock(&trace_buf_lock);

	if (trace_flag & TRACE_TIME) {
		struct tm t;
		time_t tt;

		time(&tt);
		localtime_r(&tt, &t);
		i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%d:%d:%d ",
			t.tm_hour, t.tm_min, t.tm_sec);
	}
	if (trace_flag & TRACE_PID)
		i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "[%d]: ",
			      gettid());
	if (prefix != NULL)
		i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", prefix);
	if (trace_flag & TRACE_FUNCTION)
		i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func);
	if (trace_flag & TRACE_LINE)
		i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line);

	if (i > 0)
		PRINTN(LOG_INFO, "%s", trace_buf);

	pthread_spin_unlock(&trace_buf_lock);

	return i;
}

void debug_print_buffer(const void *data, int len)
{
	int z, z1, i;
	const unsigned char *buf = (const unsigned char *) data;
	int f = 0;

	if (buf == NULL)
		return;

	pthread_spin_lock(&trace_buf_lock);

	PRINT(LOG_INFO, " (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F");
	for (z = 0, z1 = 0, i = 0; z < len; z++) {
		if (z % 16 == 0) {
			if (z != 0) {
				i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
					      " ");
				for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1);
				     z1++) {
					if ((buf[z1] >= 0x20) &&
					    (buf[z1] < 0x80))
						trace_buf[i++] = buf[z1];
					else
						trace_buf[i++] = '.';
				}
				trace_buf[i] = '\0';
				PRINT(LOG_INFO, "%s", trace_buf);
				i = 0;
				f = 1;
			}
			i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
				      "%4x: ", z);
		}
		i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ",
			      buf[z]);
	}
	i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "  ");
	for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) {
		if ((buf[z1] > 0x20) && (buf[z1] < 0x80))
			trace_buf[i++] = buf[z1];
		else
			trace_buf[i++] = '.';
	}
	trace_buf[i] = '\0';
	if (f)
		PRINT(LOG_INFO, "%s", trace_buf);
	else
		PRINT(LOG_INFO, "%s", trace_buf);

	pthread_spin_unlock(&trace_buf_lock);
}

int debug_init(void)
{
	int res;

	res = pthread_spin_init(&trace_buf_lock, PTHREAD_PROCESS_PRIVATE);
	if (res != 0) {
		res = errno;
		PRINT_ERROR("pthread_spin_init() failed: %s", strerror(res));
	}

	return res;
}

void debug_done(void)
{
	pthread_spin_destroy(&trace_buf_lock);
}

#endif /* DEBUG || TRACING */
