/* $Id$
 *
 * Helper to obtain the current stack trace. Should not be used in the actual
 * compiler (only there for debugging).
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include "frontend/misc/StackTrace.hpp"
#include <iostream>
#include <cassert>
extern "C" {

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <dlfcn.h>
#include <stdio.h>

static void *
__attribute__((always_inline))
getStackElement(
	void *frame_pointer, 
	const char **entry
)
{
	void **fp = reinterpret_cast<void **>(frame_pointer);
	void *return_address;
	void *old_fp;
	int ret;
	Dl_info info;

	old_fp = *fp;
	fp++;
	return_address = *fp;

	ret = dladdr(return_address, &info);
	if (ret == 0) {
		return NULL;
	}

	*entry = info.dli_sname;

	return old_fp;
}



#if defined(__i386__)
static void *
__attribute__((always_inline))
getFramePointer(void)
{
	void *fp;
	asm (
		"mov %%ebp, %0;"
		: "=r"(fp)	/* output */
		: 		/* no input */
		: 		/* nothing clobbered */
	);

	return fp;
}
/* __i386__ */
#elif defined(__x86_64__)
static void *
__attribute__((always_inline))
getFramePointer(void)
{
	void *fp;
	asm (
		"mov %%rbp, %0;"
		: "=r"(fp)	/* output */
		: 		/* no input */
		: 		/* nothing clobbered */
	);

	return fp;
}
#else /* ! __x86_64__ */
#error Not ported yet.
#endif


const char *
demangle(const char *sym)
{
	static char cmd[4096];
	unsigned int ret;
	FILE *fp;
	char *s;

	ret = snprintf(cmd, sizeof(cmd), "/usr/bin/c++filt \"%s\"", sym);
	assert(ret < sizeof(cmd));

	fp = popen(cmd, "r");
	if (fp == NULL) {
		return sym;
	}

	s = fgets(cmd, sizeof(cmd), fp);
	if (s == NULL) {
		pclose(fp);
		return sym;
	}

	pclose(fp);
	return cmd;
}

}; /* extern "C" */

namespace ast {

std::map<std::string, std::string> StackTrace::mangleMap = 
	std::map<std::string, std::string>();

StackTrace *
StackTrace::getStackTrace(void)
{
	void *fp;
	const char *symbol;
	StackTrace *strace;
	std::string demangled_sym;

	strace = new StackTrace();

	fp = getFramePointer();

	while (fp != NULL) {
		fp = getStackElement(fp, &symbol);

		if (fp != NULL) {
			demangled_sym = resolveSym(std::string(symbol));
			strace->stackTrace.push_back(demangled_sym);
		}

	}
	return strace;
}

void
StackTrace::put(std::ostream &stream) const
{
	stream << "Stack trace:" << std::endl;
	for (std::list<std::string>::const_iterator i = 
		this->stackTrace.begin(); i != this->stackTrace.end(); i++) {

		stream << "\t" << *i;
	}
}

std::string
StackTrace::resolveSym(std::string sym)
{
	std::map<std::string, std::string>::const_iterator i = 
		StackTrace::mangleMap.find(sym);

	if (i != StackTrace::mangleMap.end()) {
		return i->second;
	}

	// resolve + insert into mangle map.
	const char *res;
	res = demangle(sym.c_str());
	if (res != NULL) {
		mangleMap[sym] = std::string(res);
		return std::string(res);
	}

	// resolution not possible
	mangleMap[sym] = sym;
	return sym;
}

std::ostream& 
operator <<(std::ostream &stream, const StackTrace &st)
{
	st.put(stream);
	return stream;
}

}; /* namespace ast */
