// stacktrace.h (c) 2008, Timo Bingmann from http://idlebox.net/
// published under the WTFPL v2.0

#include "simpleStacktrace.h"

#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <cxxabi.h>
#include <unistd.h>
#include <link.h>
#include <cstring>

#include <sstream>
#include <alib/string>
#include <alib/map>

namespace ext {

int callback(struct dl_phdr_info *info, size_t, void * data) {
	ext::map<std::string, long>& dlToBaseAddress = *(reinterpret_cast<ext::map<std::string, long>*>(data));

	dlToBaseAddress.insert(std::make_pair(info->dlpi_name, info->dlpi_addr));

	return 0;
}

#if defined DEBUG && defined BACKTRACE
	void simpleStacktrace ( std::ostream & out, unsigned int max_frames ) {
		char linkname[512]; /* /proc/exe */
		char buf[512];
		pid_t pid;
		int ret;

		/* Get our PID and build the name of the link in /proc */
		pid = getpid();
		snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid);

		/* Now read the symbolic link */
		ret = readlink(linkname, buf, 512);
		if ( ret < 0 ) {
			strcpy(buf, "[UNKNOWN]");
		} else {
			buf[ret] = 0;
		}

		out << "stack trace for process " << buf << " (PID:" << pid << "):"<< std::endl;

		// storage array for stack trace address data
		void** addrlist = (void**) malloc( (max_frames + 1 ) * sizeof(void*) );

		// retrieve current stack addresses
		int addrlen = backtrace(addrlist, max_frames);

		if (addrlen == 0) {
			out << "  <empty, possibly corrupt>" << std::endl;
			free(addrlist);
			return;
		}

		// resolve addresses into strings containing "filename(function+address)",
		// this array must be free()-ed
		char** symbollist = backtrace_symbols(addrlist, addrlen);

		// allocate string which will be filled with the demangled function name
		size_t funcnamesize = 256;
		char* funcname = (char*)malloc(funcnamesize);

		ext::map<std::string, long> dlToBaseAddress;

		dl_iterate_phdr(callback, &dlToBaseAddress);

		// iterate over the returned symbol lines. skip the first, it is the
		// address of this function.
		for (int i = 1; i < addrlen; ++i) {
			char *begin_name = 0, *begin_offset = 0, *end_offset = 0, *addr_offset = 0;

			// find parentheses and +address offset surrounding the mangled name:
			// module(function+0x15c) [0x8048a6d]
			for (char *p = symbollist[i]; *p; ++p) {
				if (*p == '(')
					begin_name = p;
				else if (*p == '+')
					begin_offset = p;
				else if (*p == ')' && begin_offset) {
					end_offset = p;
					addr_offset = p + 3;
					break;
				}
			}

			if (begin_name && begin_offset && end_offset && begin_name < begin_offset) {
				*begin_name++ = '\0';
				*begin_offset++ = '\0';
				*end_offset = '\0';
				addr_offset[strlen(addr_offset) - 1] = '\0';

				unsigned long long x;
				std::stringstream ss1;
				ss1 << std::hex << addr_offset;
				ss1 >> x;

				unsigned long long y = x - dlToBaseAddress[symbollist[i]];

				std::stringstream ss2;
				ss2 << std::hex << y;
				std::string addr_offset_by_dl;
				ss2 >> addr_offset_by_dl;

	//			out << addrToLine(symbollist[i], nullptr, nullptr, addr_offset_by_dl.c_str()) << " [" << addr_offset << "]" << endl;

				// mangled name is now in [begin_name, begin_offset) and caller
				// offset in [begin_offset, end_offset). now apply
				// __cxa_demangle():

				int status;
				char* demangled = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status);
				if (status == 0) {
					funcname = demangled; // use possibly realloc()-ed string
					out << "  " << symbollist[i] << " : " << funcname << "+" << begin_offset << " " << begin_name << " [0x" << addr_offset_by_dl.c_str() << "; @ " << addr_offset << "]" << std::endl;
				} else {
					// demangling failed. Output function name as a C function with
					// no arguments.
					out << "  " << symbollist[i] << " : " << begin_name << "()+" << begin_offset << " " << " [0x" << addr_offset_by_dl.c_str() << "; @ " << addr_offset << "]" << std::endl;
				}
			} else {
				// couldn't parse the line? print the whole line.
				out << "  " << symbollist[i] << std::endl;
			}
		}
		free(funcname);
		free(symbollist);
		free(addrlist);
	}
#else
	void simpleStacktrace ( std::ostream &, unsigned int ) {
	}
#endif

} /* namespace ext */

