#include <stdio.h>
#include <uthash.h>
#include <sys/gmon_out.h>
#include <stdlib.h>

#define GMON_SEC "seconds        s"
uint32_t mineip = 0xffffffff;
uint32_t maxeip = 0;

/* a hash structure to hold the arc */
struct arec {
	uint32_t eip;
	uint32_t from;
	uint32_t count;
	UT_hash_handle hh;
};

struct arec *arc = NULL;

void note_arc(uint32_t eip, uint32_t from)
{
	struct arec *s;

	HASH_FIND_INT(arc, &eip, s);
	if (s == NULL) {
		s = malloc(sizeof(struct arec));
		s->eip = eip;
		s->from = from;
		s->count = 1;
		if (eip > maxeip)
			maxeip = eip;
		if (eip < mineip)
			maxeip = eip;

		HASH_ADD_INT(arc, eip, s);
	} else {
		s->count++;
	}
}

int main(int argc, char* argv[])
{
	FILE *f, *fo;
	struct arec *s;
	uint32_t eip, from, tmp;
	uint8_t tag;
	uint16_t hit;

	if (argc != 2) {
		fprintf(stderr, "Please specify the coreboot trace log as parameter\n");
		return 1;
	}

	f = fopen(argv[1], "r");
	if (f == NULL) {
		perror("Unable to open the input file");
		return 1;
	}

	fo = fopen("gmon.out", "w+");
	if (fo == NULL) {
		perror("Unable to open the output file");
		fclose(f);
		return 1;
	}

	while (!feof(f)) {
		if (fscanf(f, "~%x(%x)%*[^\n]\n", &eip, &from) == 2) {
			note_arc(eip, from);
		} else if (fscanf(f, "%*c~%x(%x)%*[^\n]\n", &eip, &from) == 2) {
			note_arc(eip, from);
		} else {
			/* just drop a line */
			tmp = fscanf(f, "%*[^\n]\n");
		}
	}

	/* write gprof header */
	fwrite(GMON_MAGIC, 1, sizeof(GMON_MAGIC) - 1, fo);
	tmp = GMON_VERSION;
	fwrite(&tmp, 1, sizeof(tmp), fo);
	tmp = 0;
	fwrite(&tmp, 1, sizeof(tmp), fo);
	fwrite(&tmp, 1, sizeof(tmp), fo);
	fwrite(&tmp, 1, sizeof(tmp), fo);
	/* write fake histogram */
	tag = GMON_TAG_TIME_HIST;
	fwrite(&tag, 1, sizeof(tag), fo);
	fwrite(&mineip, 1, sizeof(mineip), fo);
	fwrite(&maxeip, 1, sizeof(maxeip), fo);
	/* size of histogram */
	tmp = 1;
	fwrite(&tmp, 1, sizeof(tmp), fo);
	/* prof rate */
	tmp = 1000;
	fwrite(&tmp, 1, sizeof(tmp), fo);
	fwrite(GMON_SEC, 1, sizeof(GMON_SEC) - 1, fo);
	hit = 1;
	fwrite(&hit, 1, sizeof(hit), fo);

	/* write call graph data */
	tag = GMON_TAG_CG_ARC;
	for (s = arc; s != NULL; s = s->hh.next) {
		fwrite(&tag, 1, sizeof(tag), fo);
		fwrite(&s->from, 1, sizeof(s->from), fo);
		fwrite(&s->eip, 1, sizeof(s->eip), fo);
		fwrite(&s->count, 1, sizeof(s->count), fo);
	}

	fclose(fo);
	fclose(f);

	return 0;
}
