/**
 * @file daemon/opd_mangling.c
 * Mangling and opening of sample files
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Read the file COPYING
 *
 * @author John Levon
 * @author Philippe Elie
 */

#include <sys/types.h>
 
#include "opd_mangling.h"
#include "opd_kernel.h"
#include "opd_cookie.h"
#include "opd_sfile.h"
#include "opd_anon.h"
#include "opd_printf.h"
#include "opd_events.h"
#include "oprofiled.h"

#include "op_file.h"
#include "op_sample_file.h"
#include "op_config.h"
#include "op_mangle.h"
#include "op_events.h"
#include "op_libiberty.h"

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>


static char const * get_dep_name(struct sfile const * sf)
{
	if (sf->anon)
		return find_cookie(sf->app_cookie);

	/* avoid to call find_cookie(), caller can recover using image_name */
	if (sf->cookie == sf->app_cookie)
		return NULL;

	if (!separate_kernel && !(separate_lib && !sf->kernel))
		return NULL;

	/* this will fail if e.g. kernel thread */
	if (sf->app_cookie == 0)
		return NULL;

	return find_cookie(sf->app_cookie);
}


static char * mangle_anon(struct anon_mapping const * anon)
{
	char * name = xmalloc(PATH_MAX);

	snprintf(name, 1024, "%u.0x%llx.0x%llx", (unsigned int)anon->tgid,
	       anon->start, anon->end);

	return name;
}


static char *
mangle_filename(struct sfile * last, struct sfile const * sf, int counter, int cg)
{
	char * mangled;
	struct mangle_values values;
	struct opd_event * event = find_counter_event(counter);

	values.flags = 0;

	if (sf->kernel) {
		values.image_name = sf->kernel->name;
		values.flags |= MANGLE_KERNEL;
	} else if (sf->anon) {
		values.flags |= MANGLE_ANON;
		values.image_name = mangle_anon(sf->anon);
		values.anon_name = sf->anon->name;
	} else {
		values.image_name = find_cookie(sf->cookie);
	}

	values.dep_name = get_dep_name(sf);
	if (!values.dep_name)
		values.dep_name = values.image_name;
 
	/* FIXME: log */
	if (!values.image_name || !values.dep_name)
		return NULL;

	if (separate_thread) {
		values.flags |= MANGLE_TGID | MANGLE_TID;
		values.tid = sf->tid;
		values.tgid = sf->tgid;
	}
 
	if (separate_cpu) {
		values.flags |= MANGLE_CPU;
		values.cpu = sf->cpu;
	}

	if (cg) {
		values.flags |= MANGLE_CALLGRAPH;
		if (last->kernel) {
			values.cg_image_name = last->kernel->name;
		} else if (last->anon) {
			values.flags |= MANGLE_CG_ANON;
			values.cg_image_name = mangle_anon(last->anon);
			values.anon_name = last->anon->name;
		} else {
			values.cg_image_name = find_cookie(last->cookie);
		}

		/* FIXME: log */
		if (!values.cg_image_name) {
			if (values.flags & MANGLE_ANON)
				free((char *)values.image_name);
			return NULL;
		}
	}

	values.event_name = event->name;
	values.count = event->count;
	values.unit_mask = event->um;

	mangled = op_mangle_filename(&values);

	if (values.flags & MANGLE_ANON)
		free((char *)values.image_name);
	if (values.flags & MANGLE_CG_ANON)
		free((char *)values.cg_image_name);
	return mangled;
}


int opd_open_sample_file(odb_t *file, struct sfile *last,
                         struct sfile * sf, int counter, int cg)
{
	char * mangled;
	char const * binary;
	int spu_profile = 0;
	vma_t last_start = 0;
	int err;

	mangled = mangle_filename(last, sf, counter, cg);

	if (!mangled)
		return EINVAL;

	verbprintf(vsfile, "Opening \"%s\"\n", mangled);

	create_path(mangled);

	/* locking sf will lock associated cg files too */
	sfile_get(sf);
	if (sf != last)
		sfile_get(last);

retry:
	err = odb_open(file, mangled, ODB_RDWR, sizeof(struct opd_header));

	/* This can naturally happen when racing against opcontrol --reset. */
	if (err) {
		if (err == EMFILE) {
			if (sfile_lru_clear()) {
				printf("LRU cleared but odb_open() fails for %s.\n", mangled);
				abort();
			}
			goto retry;
		}

		fprintf(stderr, "oprofiled: open of %s failed: %s\n",
		        mangled, strerror(err));
		goto out;
	}

	if (!sf->kernel)
		binary = find_cookie(sf->cookie);
	else
		binary = sf->kernel->name;

	if (last && last->anon)
		last_start = last->anon->start;

	if (sf->embedded_offset != UNUSED_EMBEDDED_OFFSET)
		spu_profile = 1;

	fill_header(odb_get_data(file), counter,
		    sf->anon ? sf->anon->start : 0, last_start,
		    !!sf->kernel, last ? !!last->kernel : 0,
		    spu_profile, sf->embedded_offset,
		    binary ? op_get_mtime(binary) : 0);

out:
	sfile_put(sf);
	if (sf != last)
		sfile_put(last);
	free(mangled);
	return err;
}