
#include <stdio.h>  
#include <stdlib.h>  
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>  
#include <signal.h>       /* for signal */  
#include <unwind.h>
#include <stdint.h>
#include <execinfo.h>     /* for backtrace() */  
#include <syslog.h>
#include "pgSysSigTrace.h"


#define BACKTRACE_SIZE   32  

static struct sigaction act_old_segv;

static char s_szLabel[256] = {0};
static FILE* s_pFile = 0;

static void LogOpen()
{
	time_t stTime;
	time(&stTime);

	char szTime[64] = {0};
	struct tm* ti = localtime((const time_t*)&stTime);
	sprintf(szTime, "%04u%02u%02u%02u%02u%02u", (ti->tm_year + 1900),
		(ti->tm_mon + 1), ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec);

	char szPath[256] = {0};
	sprintf(szPath, "pgsyssigtrace_%s_%s.txt", s_szLabel, szTime);
	s_pFile = fopen(szPath, "w+");
	if (s_pFile == 0) {
		sprintf(szPath, "/var/pgsyssigtrace_%s_%s.txt", s_szLabel, szTime);
		s_pFile = fopen(szPath, "w+");
	}

	sprintf(szPath, "pgsyssigtrace_%s", s_szLabel);
	openlog(szPath, LOG_PID | LOG_CONS, LOG_USER);
}

static void LogPrintf(const char* lpszFmt, ...)
{
	char szBuf[1024] = {0};

	va_list args;
	va_start(args, lpszFmt);
	int iSize = vsnprintf(szBuf, sizeof(szBuf), lpszFmt, args);
	if (iSize > 0 && ((unsigned int)iSize + 2) < sizeof(szBuf)) {
		szBuf[iSize + 0] = '\n';
		szBuf[iSize + 1] = '\0';
		syslog(LOG_EMERG, "%s", szBuf);
		if (s_pFile != 0) {
			fprintf(s_pFile, "%s", szBuf);
		}
	}
	va_end(args);
}

static void LogClose()
{
	closelog();
	if (s_pFile != 0) {
		fclose(s_pFile);
		s_pFile = 0;
	}
}

  
static void sig_trace_dump(int signo)  
{
	LogPrintf("sig_trace_dump: Dump stack trace start\n");

	void *buffer[BACKTRACE_SIZE];
	memset(buffer, 0, sizeof(buffer));
	int nptrs = backtrace(buffer, BACKTRACE_SIZE);
	LogPrintf("sig_trace_dump: backtrace() returned %d addresses\n", nptrs);

	char **strings = backtrace_symbols(buffer, nptrs);
	if (strings != NULL) {
		for (int j = 0; j < nptrs; j++) {
			LogPrintf("sig_trace_dump: [%02d] %s\n", j, strings[j]);
		}
		free(strings);
	}
	else {
		LogPrintf("sig_trace_dump: get backtrace_symbols failed\n");
	}

	LogPrintf("sig_trace_dump: Dump stack trace end\n"); 
}  

static void sig_exit_handler(int sig, struct siginfo* info, void* buf)
{
	LogOpen();
	LogPrintf("sig_exit_handler: sig=%d\n", sig);

	if (sig == SIGSEGV
		|| sig == SIGABRT)
	{
		sig_trace_dump(sig);
	}

	if (sig == SIGSEGV) {
		sigaction(sig, &act_old_segv, 0);
	}

	LogClose();
}

void pgSysSigTraceInit(const char* lpszLabel)
{
	strcpy(s_szLabel, lpszLabel);

	struct sigaction act;
	act.sa_sigaction = sig_exit_handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;

	sigaction(SIGKILL, &act, 0);
	sigaction(SIGINT, &act, 0);
	sigaction(SIGQUIT, &act, 0);
	sigaction(SIGILL, &act, 0);
	sigaction(SIGBUS, &act, 0);
	sigaction(SIGABRT, &act, 0);
	sigaction(SIGSEGV, &act, &act_old_segv);
}
