// SPDX-License-Identifier: LGPL-2.1

/*
 * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
 */

 /**
 *  @file    libkshark.c
 *  @brief   API for processing of tracing data.
 */

#ifndef _GNU_SOURCE
/** Use GNU C Library. */
#define _GNU_SOURCE
#endif // _GNU_SOURCE

// C
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>

// KernelShark
#include "libkshark.h"
#include "libkshark-tepdata.h"

static struct kshark_context *kshark_context_handler = NULL;

static bool kshark_default_context(struct kshark_context **context)
{
	struct kshark_context *kshark_ctx;

	kshark_ctx = calloc(1, sizeof(*kshark_ctx));
	if (!kshark_ctx)
		return false;

	kshark_ctx->stream = calloc(KS_DEFAULT_NUM_STREAMS,
				    sizeof(*kshark_ctx->stream));
	kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS;
	kshark_ctx->stream_info.next_free_stream_id = 0;
	kshark_ctx->stream_info.max_stream_id = -1;

	/* Will free kshark_context_handler. */
	kshark_free(NULL);

	/* Will do nothing if *context is NULL. */
	kshark_free(*context);

	*context = kshark_context_handler = kshark_ctx;

	return true;
}

/**
 * @brief Initialize a kshark session. This function must be called before
 *	  calling any other kshark function. If the session has been
 *	  initialized, this function can be used to obtain the session's
 *	  context.
 *
 * @param kshark_ctx: Optional input/output location for context pointer.
 *		      If it points to a context, that context will become
 *		      the new session. If it points to NULL, it will obtain
 *		      the current (or new) session. The result is only
 *		      valid on return of true.
 *
 * @returns True on success, or false on failure.
 */
bool kshark_instance(struct kshark_context **kshark_ctx)
{
	if (*kshark_ctx != NULL) {
		/* Will free kshark_context_handler */
		kshark_free(NULL);

		/* Use the context provided by the user. */
		kshark_context_handler = *kshark_ctx;
	} else {
		if (kshark_context_handler) {
			/*
			 * No context is provided by the user, but the context
			 * handler is already set. Use the context handler.
			 */
			*kshark_ctx = kshark_context_handler;
		} else {
			/* No kshark_context exists. Create a default one. */
			if (!kshark_default_context(kshark_ctx))
				return false;
		}
	}

	return true;
}

/**
 * @brief Open and prepare for reading a trace data file specified by "file".
 *
 * @param kshark_ctx: Input location for context pointer.
 * @param file: The file to load.
 *
 * @returns The Id number of the data stream associated with this file on success.
 * 	    Otherwise a negative errno code.
 */
int kshark_open(struct kshark_context *kshark_ctx, const char *file)
{
	int sd, rt;

	sd = kshark_add_stream(kshark_ctx);
	if (sd < 0)
		return sd;

	rt = kshark_stream_open(kshark_ctx->stream[sd], file);
	if (rt < 0) {
		kshark_remove_stream(kshark_ctx, sd);
		return rt;
	}

	return sd;
}

static void kshark_stream_free(struct kshark_data_stream *stream)
{
	if (!stream)
		return;

	kshark_hash_id_free(stream->idle_cpus);

	kshark_hash_id_free(stream->show_task_filter);
	kshark_hash_id_free(stream->hide_task_filter);

	kshark_hash_id_free(stream->show_event_filter);
	kshark_hash_id_free(stream->hide_event_filter);

	kshark_hash_id_free(stream->show_cpu_filter);
	kshark_hash_id_free(stream->hide_cpu_filter);

	kshark_hash_id_free(stream->tasks);

	free(stream->calib_array);
	free(stream->file);
	free(stream->name);
	free(stream->interface);
	free(stream);
}

static struct kshark_data_stream *kshark_stream_alloc()
{
	struct kshark_data_stream *stream;

	stream = calloc(1, sizeof(*stream));
	if (!stream)
		goto fail;

	stream->idle_cpus = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS);

	stream->show_task_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS);
	stream->hide_task_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS);

	stream->show_event_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS);
	stream->hide_event_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS);

	stream->show_cpu_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS);
	stream->hide_cpu_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS);

	stream->tasks = kshark_hash_id_alloc(KS_TASK_HASH_NBITS);

	if (!stream->show_task_filter ||
	    !stream->hide_task_filter ||
	    !stream->show_event_filter ||
	    !stream->hide_event_filter ||
	    !stream->tasks) {
		    goto fail;
	}

	stream->filter_is_applied = false;
	kshark_set_data_format(stream->data_format, KS_INVALID_DATA);
	stream->name = strdup(KS_UNNAMED);

	return stream;

 fail:
	kshark_stream_free(stream);

	return NULL;
}
/**
 * The maximum number of Data streams that can be added simultaneously.
 * The limit is determined by the 16 bit integer used to store the stream Id
 * inside struct kshark_entry.
 */
#define KS_MAX_STREAM_ID	INT16_MAX

/**
 * Bit mask (0 - 15) used when converting indexes to pointers and vise-versa.
 */
#define INDEX_MASK		UINT16_MAX

/**
 * Bit mask (16 - 31/63) used when converting indexes to pointers and
 * vise-versa.
 */
#define INVALID_STREAM_MASK	(~((unsigned long) INDEX_MASK))

static int index_from_ptr(void *ptr)
{
	unsigned long index = (unsigned long) ptr;

	return (int) (index & INDEX_MASK);
}

static void *index_to_ptr(unsigned int index)
{
	unsigned long p;

	p = INVALID_STREAM_MASK | index;

	return (void *) p;
}

static bool kshark_is_valid_stream(void *ptr)
{
	unsigned long p = (unsigned long) ptr;
	bool v = !((p & ~INDEX_MASK) == INVALID_STREAM_MASK);

	return p && v;
}

/**
 * @brief Add new Data stream.
 *
 * @param kshark_ctx: Input location for context pointer.
 *
 * @returns Zero on success or a negative errno code on failure.
 */
int kshark_add_stream(struct kshark_context *kshark_ctx)
{
	struct kshark_data_stream *stream;
	int new_stream;

	if(kshark_ctx->stream_info.next_free_stream_id > KS_MAX_STREAM_ID)
		return -ENODEV;

	if (kshark_ctx->stream_info.next_free_stream_id ==
	    kshark_ctx->stream_info.array_size) {
		if (!KS_DOUBLE_SIZE(kshark_ctx->stream,
				    kshark_ctx->stream_info.array_size))
			return -ENOMEM;
	}

	stream = kshark_stream_alloc();
	if (!stream)
		return -ENOMEM;

	if (pthread_mutex_init(&stream->input_mutex, NULL) != 0) {
		kshark_stream_free(stream);
		return -EAGAIN;
	}

	if (kshark_ctx->stream_info.next_free_stream_id >
	    kshark_ctx->stream_info.max_stream_id) {
		new_stream = ++kshark_ctx->stream_info.max_stream_id;

		kshark_ctx->stream_info.next_free_stream_id = new_stream + 1;

		kshark_ctx->stream[new_stream] = stream;
		stream->stream_id = new_stream;
	} else {
		new_stream = kshark_ctx->stream_info.next_free_stream_id;

		kshark_ctx->stream_info.next_free_stream_id =
			index_from_ptr(kshark_ctx->stream[new_stream]);

		kshark_ctx->stream[new_stream] = stream;
		stream->stream_id = new_stream;
	}

	kshark_ctx->n_streams++;

	return stream->stream_id;
}

/**
 * @brief Use an existing Trace data stream to open and prepare for reading
 *	  a trace data file specified by "file".
 *
 * @param stream: Input location for a Trace data stream pointer.
 * @param file: The file to load.
 *
 * @returns Zero on success or a negative error code in the case of an errno.
 */
int kshark_stream_open(struct kshark_data_stream *stream, const char *file)
{
	struct kshark_context *kshark_ctx = NULL;
	struct kshark_dri_list *input;

	if (!stream || !kshark_instance(&kshark_ctx))
		return -EFAULT;

	stream->file = strdup(file);
	if (!stream->file)
		return -ENOMEM;

	if (kshark_tep_check_data(file)) {
		kshark_set_data_format(stream->data_format,
				       TEP_DATA_FORMAT_IDENTIFIER);

		return kshark_tep_init_input(stream);
	}

	for (input = kshark_ctx->inputs; input; input = input->next)
		if (input->interface->check_data(file)) {
			strcpy(stream->data_format,
			       input->interface->data_format);

			return input->interface->init(stream);
		}

	return -ENODATA;
}

/**
 * @brief Remove Data stream.
 *
 * @param kshark_ctx: Input location for context pointer.
 * @param sd: Data stream identifier.
 *
 * @returns Zero on success or a negative errno code on failure.
 */
int kshark_remove_stream(struct kshark_context *kshark_ctx, int sd)
{
	struct kshark_data_stream *stream;

	if (sd < 0 ||
	    sd > kshark_ctx->stream_info.max_stream_id ||
	    !kshark_is_valid_stream(kshark_ctx->stream[sd]))
		return -EFAULT;

	stream = kshark_ctx->stream[sd];

	pthread_mutex_destroy(&stream->input_mutex);

	kshark_stream_free(stream);
	kshark_ctx->stream[sd] =
		index_to_ptr(kshark_ctx->stream_info.next_free_stream_id);
	kshark_ctx->stream_info.next_free_stream_id = sd;
	kshark_ctx->n_streams--;

	return 0;
}

/**
 * @brief Get the Data stream object having given Id.
 *
 * @param kshark_ctx: Input location for context pointer.
 * @param sd: Data stream identifier.
 *
 * @returns Pointer to a Data stream object if the sream exists. Otherwise
 *	    NULL.
 */
struct kshark_data_stream *
kshark_get_data_stream(struct kshark_context *kshark_ctx, int sd)
{
	if (sd >= 0 && sd <= kshark_ctx->stream_info.max_stream_id)
		if (kshark_ctx->stream[sd] &&
		    kshark_is_valid_stream(kshark_ctx->stream[sd]) &&
		    kshark_ctx->stream[sd]->interface)
			return kshark_ctx->stream[sd];

	return NULL;
}

static struct kshark_data_stream *
get_stream_object(struct kshark_context *kshark_ctx, int sd)
{
	if (sd >= 0 && sd <= kshark_ctx->stream_info.max_stream_id)
		if (kshark_ctx->stream[sd] &&
		    kshark_is_valid_stream(kshark_ctx->stream[sd]))
			return kshark_ctx->stream[sd];

	return NULL;
}

/**
 * @brief Get the Data stream object corresponding to a given entry
 *
 * @param entry: Input location for the KernelShark entry.
 *
 * @returns Pointer to a Data stream object on success. Otherwise NULL.
 */
struct kshark_data_stream *
kshark_get_stream_from_entry(const struct kshark_entry *entry)
{
	struct kshark_context *kshark_ctx = NULL;

	if (!kshark_instance(&kshark_ctx))
		return NULL;

	return kshark_get_data_stream(kshark_ctx, entry->stream_id);
}

/**
 * @brief Get an array containing the Ids of all opened Trace data streams.
 * 	  The User is responsible for freeing the array.
 *
 * @param kshark_ctx: Input location for context pointer.
 */
int *kshark_all_streams(struct kshark_context *kshark_ctx)
{
	int *ids, i, count = 0;

	ids = calloc(kshark_ctx->n_streams, (sizeof(*ids)));
	if (!ids)
		return NULL;

	for (i = 0; i <= kshark_ctx->stream_info.max_stream_id; ++i)
		if (kshark_ctx->stream[i] &&
		    kshark_is_valid_stream(kshark_ctx->stream[i]))
			ids[count++] = i;

	return ids;
}

static int kshark_stream_close(struct kshark_data_stream *stream)
{
	struct kshark_context *kshark_ctx = NULL;
	struct kshark_dri_list *input;

	if (!stream || !kshark_instance(&kshark_ctx))
		return -EFAULT;

	/*
	 * All filters are file specific. Make sure that all Process Ids and
	 * Event Ids from this file are not going to be used with another file.
	 */
	kshark_hash_id_clear(stream->show_task_filter);
	kshark_hash_id_clear(stream->hide_task_filter);
	kshark_hash_id_clear(stream->show_event_filter);
	kshark_hash_id_clear(stream->hide_event_filter);
	kshark_hash_id_clear(stream->show_cpu_filter);
	kshark_hash_id_clear(stream->hide_cpu_filter);

	kshark_hash_id_clear(stream->idle_cpus);

	if (kshark_is_tep(stream))
		return kshark_tep_close_interface(stream);

	for (input = kshark_ctx->inputs; input; input = input->next)
		if (strcmp(stream->data_format, input->interface->data_format) == 0)
			return input->interface->close(stream);

	return -ENODATA;
}

/**
 * @brief Close the trace data file and free the trace data handle.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param sd: Data stream identifier.
 *
 * @returns Zero on success or a negative errno code on failure.
 */
int kshark_close(struct kshark_context *kshark_ctx, int sd)
{
	struct kshark_data_stream *stream;
	int ret;

	stream = get_stream_object(kshark_ctx, sd);
	if (!stream)
		return -EFAULT;

	/*
	 * All data collections are file specific. Make sure that collections
	 * from this file are not going to be used with another file.
	 */
	kshark_unregister_stream_collections(&kshark_ctx->collections, sd);

	/* Close all active plugins for this stream. */
	if (stream->plugins) {
		kshark_handle_all_dpis(stream, KSHARK_PLUGIN_CLOSE);
		kshark_free_event_handler_list(stream->event_handlers);
		kshark_free_dpi_list(stream->plugins);
	}

	ret = kshark_stream_close(stream);
	kshark_remove_stream(kshark_ctx, stream->stream_id);

	return ret;
}

/**
 * @brief Close all currently open trace data file and free the trace data handle.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 */
void kshark_close_all(struct kshark_context *kshark_ctx)
{
	size_t mem_reset_size;
	int i;

	if (kshark_ctx->stream_info.max_stream_id < 0)
		return;

	for (i = 0; i <= kshark_ctx->stream_info.max_stream_id; ++i)
		kshark_close(kshark_ctx, i);

	/* Reset the array of data stream descriptors. */
	mem_reset_size = (kshark_ctx->stream_info.max_stream_id + 1 ) *
			 sizeof(*kshark_ctx->stream);
	memset(kshark_ctx->stream, 0, mem_reset_size);

	kshark_ctx->stream_info.next_free_stream_id = 0;
	kshark_ctx->stream_info.max_stream_id = -1;
}

/**
 * @brief Deinitialize kshark session. Should be called after closing all
 *	  open trace data files and before your application terminates.
 *
 * @param kshark_ctx: Optional input location for session context pointer.
 *		      If it points to a context of a session, that session
 *		      will be deinitialize. If it points to NULL, it will
 *		      deinitialize the current session.
 */
void kshark_free(struct kshark_context *kshark_ctx)
{
	if (kshark_ctx == NULL) {
		if (kshark_context_handler == NULL)
			return;

		kshark_ctx = kshark_context_handler;
		/* kshark_ctx_handler will be set to NULL below. */
	}

	kshark_close_all(kshark_ctx);

	free(kshark_ctx->stream);

	if (kshark_ctx->plugins)
		kshark_free_plugin_list(kshark_ctx->plugins);

	kshark_free_dri_list(kshark_ctx->inputs);

	if (kshark_ctx == kshark_context_handler)
		kshark_context_handler = NULL;

	free(kshark_ctx);
}

/**
 * @brief Get the name of the command/task from its Process Id.
 *
 * @param sd: Data stream identifier.
 * @param pid: Process Id of the command/task.
 */
char *kshark_comm_from_pid(int sd, int pid)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_context *kshark_ctx = NULL;
	struct kshark_data_stream *stream;
	struct kshark_entry e;

	if (!kshark_instance(&kshark_ctx))
		return NULL;

	stream = kshark_get_data_stream(kshark_ctx, sd);
	if (!stream)
		return NULL;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_task) {
		e.visible = KS_PLUGIN_UNTOUCHED_MASK;
		e.pid = pid;

		return interface->get_task(stream, &e);
	}

	return NULL;
}

/**
 * @brief Get the name of the event from its Id.
 *
 * @param sd: Data stream identifier.
 * @param event_id: The unique Id of the event type.
 */
char *kshark_event_from_id(int sd, int event_id)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_context *kshark_ctx = NULL;
	struct kshark_data_stream *stream;
	struct kshark_entry e;

	if (!kshark_instance(&kshark_ctx))
		return NULL;

	stream = kshark_get_data_stream(kshark_ctx, sd);
	if (!stream)
		return NULL;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_event_name) {
		e.visible = KS_PLUGIN_UNTOUCHED_MASK;
		e.event_id = event_id;

		return interface->get_event_name(stream, &e);
	}

	return NULL;
}

/**
 * @brief Get the original process Id of the entry. Using this function make
 *	  sense only in cases when the original value can be overwritten by
 *	  plugins. If you know that no plugins are loaded use "entry->pid"
 *	  directly.
 *
 * @param entry: Input location for an entry.
 */
int kshark_get_pid(const struct kshark_entry *entry)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return -EFAULT;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_pid)
		return interface->get_pid(stream, entry);

	return -EFAULT;
}

/**
 * @brief Get the original event Id of the entry. Using this function make
 *	  sense only in cases when the original value can be overwritten by
 *	  plugins. If you know that no plugins are loaded use "entry->event_id"
 *	  directly.
 *
 * @param entry: Input location for an entry.
 */
int kshark_get_event_id(const struct kshark_entry *entry)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return -EFAULT;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_event_id)
		return interface->get_event_id(stream, entry);

	return -EFAULT;
}

/**
 * @brief Get an array of all event Ids for a given data stream.
 *
 * @param stream: Input location for a Trace data stream pointer.
 *
 * @returns An array of event Ids. The user is responsible for freeing the
 *	    outputted array.
 */
int *kshark_get_all_event_ids(struct kshark_data_stream *stream)
{
	struct kshark_generic_stream_interface *interface;

	if (!stream)
		return NULL;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_all_event_ids)
		return interface->get_all_event_ids(stream);

	return NULL;
}

/**
 * @brief Find the event Ids corresponding to a given event name.
 *
 * @param stream: Input location for a Trace data stream pointer.
 * @param event_name: The name of the event.
 *
 * @returns Event Ids number.
 */
int kshark_find_event_id(struct kshark_data_stream *stream,
			 const char *event_name)
{
	struct kshark_generic_stream_interface *interface;

	if (!stream)
		return -EFAULT;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->find_event_id)
		return interface->find_event_id(stream, event_name);

	return -EFAULT;
}

/**
 * @brief Find the event name corresponding to a given entry.
 *
 * @param entry: Input location for an entry.
 *
 * @returns The mane of the event on success, or NULL in case of failure.
 *	    The use is responsible for freeing the output string.
 */
char *kshark_get_event_name(const struct kshark_entry *entry)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return NULL;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_event_name)
		return interface->get_event_name(stream, entry);

	return NULL;
}

/**
 * @brief Find the task name corresponding to a given entry.
 *
 * @param entry: Input location for an entry.
 *
 * @returns The mane of the task on success, or NULL in case of failure.
 *	    The use is responsible for freeing the output string.
 */
char *kshark_get_task(const struct kshark_entry *entry)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return NULL;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_task)
		return interface->get_task(stream, entry);

	return NULL;
}

/**
 * @brief Get the basic information (text) about the entry.
 *
 * @param entry: Input location for an entry.
 *
 * @returns A the info text. The user is responsible for freeing the
 *	    outputted string.
 */
char *kshark_get_info(const struct kshark_entry *entry)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return NULL;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_info)
		return interface->get_info(stream, entry);

	return NULL;
}

/**
 * @brief Get the auxiliary information about the entry. In the case of
 * 	  TEP (Ftrace) data, this function provides the latency info.
 *
 * @param entry: Input location for an entry.
 *
 * @returns A the auxiliary text info. The user is responsible for freeing the
 *	    outputted string.
 */
char *kshark_get_aux_info(const struct kshark_entry *entry)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return NULL;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->aux_info)
		return interface->aux_info(stream, entry);

	return NULL;
}

/**
 * @brief Get an array of all data field names associated with a given entry.
 *
 * @param entry: Input location for an entry.
 * @param fields: Output location of the array of field names. The user is
 *		  responsible for freeing the elements of the outputted array.
 *
 * @returns Total number of event fields on success, or a negative errno in
 *	    the case of a failure.
 */
int kshark_get_all_event_field_names(const struct kshark_entry *entry,
				     char ***fields)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return -EFAULT;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_all_event_field_names)
		return interface->get_all_event_field_names(stream,
							    entry, fields);

	return -EFAULT;
}

/**
 * @brief Get the value type of an event field corresponding to a given entry.
 *
 * @param entry: Input location for an entry.
 * @param field: The name of the data field.
 *
 * @returns The type of the data field on success, or KS_INVALID_FIELD in
 *	    the case of a failure.
 */
kshark_event_field_format
kshark_get_event_field_type(const struct kshark_entry *entry,
			    const char *field)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return KS_INVALID_FIELD;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->get_event_field_type)
		return interface->get_event_field_type(stream, entry, field);

	return KS_INVALID_FIELD;
}

/**
 * @brief Get the value of an event field in a given trace record.
 *
 * @param stream: Input location for a Trace data stream pointer.
 * @param rec: Input location for a record.
 * @param field: The name of the data field.
 * @param val: Output location for the value of the field.
 *
 * @returns Zero on success or a negative errno in the case of a failure.
 */
int kshark_read_record_field_int(struct kshark_data_stream *stream, void *rec,
				 const char *field, int64_t *val)
{
	struct kshark_generic_stream_interface *interface;

	if (!stream)
		return -EFAULT;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->read_record_field_int64)
		return interface->read_record_field_int64(stream, rec,
							  field, val);

	return -EFAULT;
}

/**
 * @brief Get the value of an event field corresponding to a given entry.
 *	  The value is retrieved via the offset in the file of the original
 *	  record.
 *
 * @param entry: Input location for an entry.
 * @param field: The name of the data field.
 * @param val: Output location for the value of the field.
 *
 * @returns Zero on success or a negative errno in the case of a failure.
 */
int kshark_read_event_field_int(const struct kshark_entry *entry,
				const char* field, int64_t *val)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return -EFAULT;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->read_event_field_int64)
		return interface->read_event_field_int64(stream, entry, field, val);

	return -EFAULT;
}

/**
 * @brief Get a summary of the entry.
 *
 * @param entry: Input location for an entry.
 *
 * @returns A summary text info. The user is responsible for freeing the
 *	    outputted string.
 */
char *kshark_dump_entry(const struct kshark_entry *entry)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_stream_from_entry(entry);

	if (!stream)
		return NULL;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->dump_entry)
		return interface->dump_entry(stream, entry);

	return NULL;
}

/** @brief Print the entry. */
void kshark_print_entry(const struct kshark_entry *entry)
{
	char *entry_str = kshark_dump_entry(entry);

	if (!entry_str)
		puts("(nil)");

	puts(entry_str);
	free(entry_str);
}

/**
 * @brief Load the content of the trace data file asociated with a given
 *	  Data stream into an array of kshark_entries.
 *	  If one or more filters are set, the "visible" fields of each entry
 *	  is updated according to the criteria provided by the filters. The
 *	  field "filter_mask" of the session's context is used to control the
 *	  level of visibility/invisibility of the filtered entries.
 *
 * @param kshark_ctx: Input location for context pointer.
 * @param sd: Data stream identifier.
 * @param data_rows: Output location for the trace data. The user is
 *		     responsible for freeing the elements of the outputted
 *		     array.
 *
 * @returns The size of the outputted data in the case of success, or a
 *	    negative error code on failure.
 */
ssize_t kshark_load_entries(struct kshark_context *kshark_ctx, int sd,
			    struct kshark_entry ***data_rows)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_data_stream(kshark_ctx, sd);

	if (!stream)
		return -EFAULT;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->load_entries)
		return interface->load_entries(stream, kshark_ctx, data_rows);

	return -EFAULT;
}

/**
 * @brief Load the content of the trace data file asociated with a given
 *	  Data stream into a data matrix. The user is responsible
 *	  for freeing the outputted data.
 *
 * @param kshark_ctx: Input location for context pointer.
 * @param sd: Data stream identifier.
 * @param cpu_array: Output location for the CPU column (array) of the matrix.
 * @param event_array: Output location for the Event Id column (array) of the
 *		       matrix.
 * @param pid_array: Output location for the PID column (array) of the matrix.
 * @param offset_array: Output location for the offset column (array) of the
 *			matrix.
 * @param ts_array: Output location for the time stamp column (array) of the
 *		    matrix.
 */
ssize_t kshark_load_matrix(struct kshark_context *kshark_ctx, int sd,
			   int16_t **event_array,
			   int16_t **cpu_array,
			   int32_t **pid_array,
			   int64_t **offset_array,
			   int64_t **ts_array)
{
	struct kshark_generic_stream_interface *interface;
	struct kshark_data_stream *stream =
		kshark_get_data_stream(kshark_ctx, sd);

	if (!stream)
		return -EFAULT;

	interface = stream->interface;
	if (interface->type == KS_GENERIC_DATA_INTERFACE &&
	    interface->load_matrix)
		return interface->load_matrix(stream, kshark_ctx, event_array,
								  cpu_array,
								  pid_array,
								  offset_array,
								  ts_array);

	return -EFAULT;
}

/**
 * @brief Get an array containing the Process Ids of all tasks presented in
 *	  the loaded trace data file.
 *
 * @param kshark_ctx: Input location for context pointer.
 * @param sd: Data stream identifier.
 * @param pids: Output location for the Pids of the tasks. The user is
 *		responsible for freeing the elements of the outputted array.
 *
 * @returns The size of the outputted array of Pids in the case of success,
 *	    or a negative error code on failure.
 */
ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int sd,
			     int **pids)
{
	struct kshark_data_stream *stream;

	stream = kshark_get_data_stream(kshark_ctx, sd);
	if (!stream)
		return -EBADF;

	*pids = kshark_hash_ids(stream->tasks);
	return stream->tasks->count;
}

static bool filter_find(struct kshark_hash_id *filter, int pid,
			bool test)
{
	return !filter || !filter->count ||
	       kshark_hash_id_find(filter, pid) == test;
}

static bool kshark_show_task(struct kshark_data_stream *stream, int pid)
{
	return filter_find(stream->show_task_filter, pid, true) &&
	       filter_find(stream->hide_task_filter, pid, false);
}

static bool kshark_show_event(struct kshark_data_stream *stream, int pid)
{
	return filter_find(stream->show_event_filter, pid, true) &&
	       filter_find(stream->hide_event_filter, pid, false);
}

static bool kshark_show_cpu(struct kshark_data_stream *stream, int cpu)
{
	return filter_find(stream->show_cpu_filter, cpu, true) &&
	       filter_find(stream->hide_cpu_filter, cpu, false);
}

static struct kshark_hash_id *get_filter(struct kshark_context *kshark_ctx,
					 int sd,
					 enum kshark_filter_type filter_id)
{
	struct kshark_data_stream *stream;

	stream = kshark_get_data_stream(kshark_ctx, sd);
	if (!stream)
		return NULL;

	return kshark_get_filter(stream, filter_id);
}

/**
 * @brief Get an Id Filter.
 *
 * @param stream: Input location for a Trace data stream pointer.
 * @param filter_id: Identifier of the filter.
 */
struct kshark_hash_id *
kshark_get_filter(struct kshark_data_stream *stream,
		  enum kshark_filter_type filter_id)
{
	switch (filter_id) {
	case KS_SHOW_CPU_FILTER:
		return stream->show_cpu_filter;
	case KS_HIDE_CPU_FILTER:
		return stream->hide_cpu_filter;
	case KS_SHOW_EVENT_FILTER:
		return stream->show_event_filter;
	case KS_HIDE_EVENT_FILTER:
		return stream->hide_event_filter;
	case KS_SHOW_TASK_FILTER:
		return stream->show_task_filter;
	case KS_HIDE_TASK_FILTER:
		return stream->hide_task_filter;
	default:
		return NULL;
	}
}

/**
 * @brief Add an Id value to the filter specified by "filter_id".
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param sd: Data stream identifier.
 * @param filter_id: Identifier of the filter.
 * @param id: Id value to be added to the filter.
 */
void kshark_filter_add_id(struct kshark_context *kshark_ctx, int sd,
			  int filter_id, int id)
{
	struct kshark_hash_id *filter;

	filter = get_filter(kshark_ctx, sd, filter_id);
	if (filter)
		kshark_hash_id_add(filter, id);
}

/**
 * @brief Get an array containing all Ids associated with a given Id Filter.
 *
 * @param kshark_ctx: Input location for context pointer.
 * @param sd: Data stream identifier.
 * @param filter_id: Identifier of the filter.
 * @param n: Output location for the size of the returned array.
 *
 * @return The user is responsible for freeing the array.
 */
int *kshark_get_filter_ids(struct kshark_context *kshark_ctx, int sd,
			   int filter_id, int *n)
{
	struct kshark_hash_id *filter;

	filter = get_filter(kshark_ctx, sd, filter_id);
	if (filter) {
		if (n)
			*n = filter->count;

		return kshark_hash_ids(filter);
	}

	if (n)
		*n = 0;

	return NULL;
}

/**
 * @brief Clear (reset) the filter specified by "filter_id".
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param sd: Data stream identifier.
 * @param filter_id: Identifier of the filter.
 */
void kshark_filter_clear(struct kshark_context *kshark_ctx, int sd,
			 int filter_id)
{
	struct kshark_hash_id *filter;

	filter = get_filter(kshark_ctx, sd, filter_id);
	if (filter)
		kshark_hash_id_clear(filter);
}

/**
 * @brief Check if a given Id filter is set.
 *
 * @param filter: Input location for the Id filster.
 *
 * @returns True if the Id filter is set, otherwise False.
 */
bool kshark_this_filter_is_set(struct kshark_hash_id *filter)
{
	return filter && filter->count;
}

/**
 * @brief Check if an Id filter is set.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param sd: Data stream identifier.
 *
 * @returns True if at least one Id filter of the stream is set, otherwise
 *	    False.
 */
bool kshark_filter_is_set(struct kshark_context *kshark_ctx, int sd)
{
	struct kshark_data_stream *stream;

	stream = get_stream_object(kshark_ctx, sd);
	if (!stream)
		return false;

	return kshark_this_filter_is_set(stream->show_task_filter) ||
	       kshark_this_filter_is_set(stream->hide_task_filter)  ||
	       kshark_this_filter_is_set(stream->show_cpu_filter)   ||
	       kshark_this_filter_is_set(stream->hide_cpu_filter)   ||
	       kshark_this_filter_is_set(stream->show_event_filter) ||
	       kshark_this_filter_is_set(stream->hide_event_filter);
}

/**
 * @brief Apply filters to a given entry.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param stream: Input location for a Trace data stream pointer.
 * @param entry: Input location for entry.
 */
void kshark_apply_filters(struct kshark_context *kshark_ctx,
			  struct kshark_data_stream *stream,
			  struct kshark_entry *entry)
{
	/* Apply event filtering. */
	if (!kshark_show_event(stream, entry->event_id))
		unset_event_filter_flag(kshark_ctx, entry);

	/* Apply CPU filtering. */
	if (!kshark_show_cpu(stream, entry->cpu))
		entry->visible &= ~kshark_ctx->filter_mask;

	/* Apply task filtering. */
	if (!kshark_show_task(stream, entry->pid))
		entry->visible &= ~kshark_ctx->filter_mask;
}

static void set_all_visible(uint16_t *v) {
	/*  Keep the original value of the PLUGIN_UNTOUCHED bit flag. */
	*v |= 0xFF & ~KS_PLUGIN_UNTOUCHED_MASK;
}

static void filter_entries(struct kshark_context *kshark_ctx, int sd,
			   struct kshark_entry **data, size_t n_entries)
{
	struct kshark_data_stream *stream = NULL;
	size_t i;

	/* Sanity checks before starting. */
	if (sd >= 0) {
		/* We will filter particular Data stream. */
		stream = kshark_get_data_stream(kshark_ctx, sd);
		if (!stream)
			return;

		if (kshark_is_tep(stream) &&
		    kshark_tep_filter_is_set(stream)) {
			/* The advanced filter is set. */
			fprintf(stderr,
				"Failed to filter (sd = %i)!\n", sd);
			fprintf(stderr,
				"Reset the Advanced filter or reload the data.\n");

			return;
		}

		if (!kshark_filter_is_set(kshark_ctx, sd) &&
		    !stream->filter_is_applied) {
			/* Nothing to be done. */
			return;
		}
	}

	/* Apply only the Id filters. */
	for (i = 0; i < n_entries; ++i) {
		if (sd >= 0) {
			/*
			 * We only filter particular stream. Chack is the entry
			 * belongs to this stream.
			 */
			if (data[i]->stream_id != sd)
				continue;
		} else {
			/* We filter all streams. */
			stream = kshark_ctx->stream[data[i]->stream_id];
		}

		/* Start with and entry which is visible everywhere. */
		set_all_visible(&data[i]->visible);

		/* Apply Id filtering. */
		kshark_apply_filters(kshark_ctx, stream, data[i]);

		stream->filter_is_applied =
			kshark_filter_is_set(kshark_ctx, sd)? true : false;
	}
}

/**
 * @brief This function loops over the array of entries specified by "data"
 *	  and "n_entries" and sets the "visible" fields of each entry from a
 *	  given Data stream according to the criteria provided by the filters
 *	  of the session's context. The field "filter_mask" of the session's
 *	  context is used to control the level of visibility/invisibility of
 *	  the entries which are filtered-out.
 *	  WARNING: Do not use this function if the advanced filter is set.
 *	  Applying the advanced filter requires access to prevent_record,
 *	  hence the data has to be reloaded using kshark_load_entries().
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param sd: Data stream identifier.
 * @param data: Input location for the trace data to be filtered.
 * @param n_entries: The size of the inputted data.
 */
void kshark_filter_stream_entries(struct kshark_context *kshark_ctx,
				  int sd,
				  struct kshark_entry **data,
				  size_t n_entries)
{
	if (sd >= 0)
		filter_entries(kshark_ctx, sd, data, n_entries);
}

/**
 * @brief This function loops over the array of entries specified by "data"
 *	  and "n_entries" and sets the "visible" fields of each entry from
 *	  all Data stream according to the criteria provided by the filters
 *	  of the session's context. The field "filter_mask" of the session's
 *	  context is used to control the level of visibility/invisibility of
 *	  the entries which are filtered-out.
 *	  WARNING: Do not use this function if the advanced filter is set.
 *	  Applying the advanced filter requires access to prevent_record,
 *	  hence the data has to be reloaded using kshark_load_data_entries().
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param data: Input location for the trace data to be filtered.
 * @param n_entries: The size of the inputted data.
 */
void kshark_filter_all_entries(struct kshark_context *kshark_ctx,
			       struct kshark_entry **data, size_t n_entries)
{
	filter_entries(kshark_ctx, -1, data, n_entries);
}

/**
 * @brief This function loops over the array of entries specified by "data"
 *	  and "n_entries" and resets the "visible" fields of each entry to
 *	  the default value of "0xFF" (visible everywhere).
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param data: Input location for the trace data to be unfiltered.
 * @param n_entries: The size of the inputted data.
 */
void kshark_clear_all_filters(struct kshark_context *kshark_ctx,
			      struct kshark_entry **data,
			      size_t n_entries)
{
	struct kshark_data_stream *stream;
	int *stream_ids, i;

	for (i = 0; i < n_entries; ++i)
		set_all_visible(&data[i]->visible);

	stream_ids = kshark_all_streams(kshark_ctx);
	for (i = 0; i < kshark_ctx->n_streams; i++) {
		stream = kshark_get_data_stream(kshark_ctx, stream_ids[i]);
		stream->filter_is_applied = false;
	}

	free(stream_ids);
}

/**
 * @brief Process all registered event-specific plugin actions.
 *
 * @param stream: Input location for a Trace data stream pointer.
 * @param record: Input location for the trace record.
 * @param entry: Output location for entry.
 */
void kshark_plugin_actions(struct kshark_data_stream *stream,
			   void *record, struct kshark_entry *entry)
{
	if (stream->event_handlers) {
		/* Execute all plugin-provided actions for this event (if any). */
		struct kshark_event_proc_handler *evt_handler = stream->event_handlers;

		while ((evt_handler = kshark_find_event_handler(evt_handler,
								entry->event_id))) {
			evt_handler->event_func(stream, record, entry);
			evt_handler = evt_handler->next;
			entry->visible &= ~KS_PLUGIN_UNTOUCHED_MASK;
		}
	}
}

/**
 * @brief Time calibration of the timestamp of the entry.
 *
 * @param stream: Input location for a Trace data stream pointer.
 * @param entry: Output location for entry.
 */
void kshark_calib_entry(struct kshark_data_stream *stream,
			struct kshark_entry *entry)
{
	if (stream->calib && stream->calib_array) {
		/* Calibrate the timestamp of the entry. */
		stream->calib(&entry->ts, stream->calib_array);
	}
}

/**
 * @brief Post-process the content of the entry. This includes time calibration
 *	  and all registered event-specific plugin actions.
 *
 * @param stream: Input location for a Trace data stream pointer.
 * @param record: Input location for the trace record.
 * @param entry: Output location for entry.
 */
void kshark_postprocess_entry(struct kshark_data_stream *stream,
			      void *record, struct kshark_entry *entry)
{
	kshark_calib_entry(stream, entry);

	kshark_plugin_actions(stream, record, entry);
}

static inline void free_ptr(void *ptr)
{
	if (ptr)
		free(*(void **)ptr);
}

/**
 * @brief Allocate data arrays (matrix columns) to be used to load the tracing
 *	  data into a data matrix form.
 *
 * @param n_rows: Number matrix rows to be allocated. Must be equal to the
 *	 	  number of trace records.
 * @param cpu_array: Output location for the CPU Id column.
 * @param pid_array: Output location for the PID column.
 * @param event_array: Output location for the Event Id column.
 * @param offset_array: Output location for the record offset column.
 * @param ts_array: Output location for the timestamp column.
 *
 * @returns True on success. Else false.
 */
bool kshark_data_matrix_alloc(size_t n_rows, int16_t **event_array,
					     int16_t **cpu_array,
					     int32_t **pid_array,
					     int64_t **offset_array,
					     int64_t **ts_array)
{
	if (offset_array) {
		*offset_array = calloc(n_rows, sizeof(**offset_array));
		if (!*offset_array)
			return false;
	}

	if (cpu_array) {
		*cpu_array = calloc(n_rows, sizeof(**cpu_array));
		if (!*cpu_array)
			goto free_offset;
	}

	if (ts_array) {
		*ts_array = calloc(n_rows, sizeof(**ts_array));
		if (!*ts_array)
			goto free_cpu;
	}

	if (pid_array) {
		*pid_array = calloc(n_rows, sizeof(**pid_array));
		if (!*pid_array)
			goto free_ts;
	}

	if (event_array) {
		*event_array = calloc(n_rows, sizeof(**event_array));
		if (!*event_array)
			goto free_pid;
	}

	return true;

 free_pid:
	free_ptr(pid_array);
 free_ts:
	free_ptr(ts_array);
 free_cpu:
	free_ptr(cpu_array);
 free_offset:
	free_ptr(offset_array);

	fprintf(stderr, "Failed to allocate memory during data loading.\n");
	return false;
}

/**
 * @brief Convert the timestamp of the trace record (nanosecond precision) into
 *	  seconds and microseconds.
 *
 * @param time: Input location for the timestamp.
 * @param sec: Output location for the value of the seconds.
 * @param usec: Output location for the value of the microseconds.
 */
void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec)
{
	uint64_t s;

	*sec = s = time / 1000000000ULL;
	*usec = (time - s * 1000000000ULL) / 1000;
}

/**
 * @brief Binary search inside a time-sorted array of kshark_entries.
 *
 * @param time: The value of time to search for.
 * @param data: Input location for the trace data.
 * @param l: Array index specifying the lower edge of the range to search in.
 * @param h: Array index specifying the upper edge of the range to search in.
 *
 * @returns On success, the first kshark_entry inside the range, having a
 *	    timestamp equal or bigger than "time".
 *	    If all entries inside the range have timestamps greater than "time"
 *	    the function returns BSEARCH_ALL_GREATER (negative value).
 *	    If all entries inside the range have timestamps smaller than "time"
 *	    the function returns BSEARCH_ALL_SMALLER (negative value).
 */
ssize_t kshark_find_entry_by_time(int64_t time,
				  struct kshark_entry **data,
				  size_t l, size_t h)
{
	size_t mid;

	if (data[l]->ts > time)
		return BSEARCH_ALL_GREATER;

	if (data[h]->ts < time)
		return BSEARCH_ALL_SMALLER;

	/*
	 * After executing the BSEARCH macro, "l" will be the index of the last
	 * entry having timestamp < time and "h" will be the index of the first
	 * entry having timestamp >= time.
	 */
	BSEARCH(h, l, data[mid]->ts < time);
	return h;
}

/**
 * @brief Simple Pid matching function to be user for data requests.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param e: kshark_entry to be checked.
 * @param sd: Data stream identifier.
 * @param pid: Matching condition value.
 *
 * @returns True if the Pid of the entry matches the value of "pid".
 *	    Else false.
 */
bool kshark_match_pid(struct kshark_context *kshark_ctx,
		      struct kshark_entry *e, int sd, int *pid)
{
	if (e->stream_id == sd && e->pid == *pid)
		return true;

	return false;
}

/**
 * @brief Simple Cpu matching function to be user for data requests.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param e: kshark_entry to be checked.
 * @param sd: Data stream identifier.
 * @param cpu: Matching condition value.
 *
 * @returns True if the Cpu of the entry matches the value of "cpu".
 *	    Else false.
 */
bool kshark_match_cpu(struct kshark_context *kshark_ctx,
		      struct kshark_entry *e, int sd, int *cpu)
{
	if (e->stream_id == sd && e->cpu == *cpu)
		return true;

	return false;
}

/**
 * @brief Simple event Id matching function to be user for data requests.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param e: kshark_entry to be checked.
 * @param sd: Data stream identifier.
 * @param event_id: Matching condition value.
 *
 * @returns True if the event Id of the entry matches the value of "event_id".
 *	    Else false.
 */
bool kshark_match_event_id(struct kshark_context *kshark_ctx,
			   struct kshark_entry *e, int sd, int *event_id)
{
	return e->stream_id == sd && e->event_id == *event_id;
}

/**
 * @brief Simple Event Id and PID matching function to be user for data requests.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param e: kshark_entry to be checked.
 * @param sd: Data stream identifier.
 * @param values: An array of matching condition value.
 *	  values[0] is the matches PID and values[1] is the matches event Id.
 *
 * @returns True if the event Id of the entry matches the values.
 *	    Else false.
 */
bool kshark_match_event_and_pid(struct kshark_context *kshark_ctx,
				struct kshark_entry *e,
				int sd, int *values)
{
	return e->stream_id == sd &&
	       e->event_id == values[0] &&
	       e->pid == values[1];
}

/**
 * @brief Simple Event Id and CPU matching function to be user for data requests.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param e: kshark_entry to be checked.
 * @param sd: Data stream identifier.
 * @param values: An array of matching condition value.
 *	  values[0] is the matches PID and values[1] is the matches event Id.
 *
 * @returns True if the event Id of the entry matches the values.
 *	    Else false.
 */
bool kshark_match_event_and_cpu(struct kshark_context *kshark_ctx,
				struct kshark_entry *e,
				int sd, int *values)
{
	return e->stream_id == sd &&
	       e->event_id == values[0] &&
	       e->cpu == values[1];
}

/**
 * @brief Create Data request. The request defines the properties of the
 *	  requested kshark_entry.
 *
 * @param first: Array index specifying the position inside the array from
 *		 where the search starts.
 * @param n: Number of array elements to search in.
 * @param cond: Matching condition function.
 * @param sd: Data stream identifier.
 * @param values: Matching condition values, used by the Matching condition
 *		  function.
 * @param vis_only: If true, a visible entry is requested.
 * @param vis_mask: If "vis_only" is true, use this mask to specify the level
 *		    of visibility of the requested entry.
 *
 * @returns Pointer to kshark_entry_request on success, or NULL on failure.
 *	    The user is responsible for freeing the returned
 *	    kshark_entry_request.
 */
struct kshark_entry_request *
kshark_entry_request_alloc(size_t first, size_t n,
			   matching_condition_func cond, int sd, int *values,
			   bool vis_only, int vis_mask)
{
	struct kshark_entry_request *req = malloc(sizeof(*req));

	if (!req) {
		fprintf(stderr,
			"Failed to allocate memory for entry request.\n");
		return NULL;
	}

	req->next = NULL;
	req->first = first;
	req->n = n;
	req->cond = cond;
	req->sd = sd;
	req->values = values;
	req->vis_only = vis_only;
	req->vis_mask = vis_mask;

	return req;
}

/**
 * @brief Free all Data requests in a given list.
 * @param req: Intput location for the Data request list.
 */
void kshark_free_entry_request(struct kshark_entry_request *req)
{
	struct kshark_entry_request *last;

	while (req) {
		last = req;
		req = req->next;
		free(last);
	}
}

/** Dummy entry, used to indicate the existence of filtered entries. */
const struct kshark_entry dummy_entry = {
	.next		= NULL,
	.visible	= 0x00,
	.cpu		= KS_FILTERED_BIN,
	.pid		= KS_FILTERED_BIN,
	.event_id	= -1,
	.offset		= 0,
	.ts		= 0
};

static const struct kshark_entry *
get_entry(const struct kshark_entry_request *req,
          struct kshark_entry **data,
          ssize_t *index, ssize_t start, ssize_t end, int inc)
{
	struct kshark_context *kshark_ctx = NULL;
	const struct kshark_entry *e = NULL;
	ssize_t i;

	if (index)
		*index = KS_EMPTY_BIN;

	if (!kshark_instance(&kshark_ctx))
		return e;

	/*
	 * We will do a sanity check in order to protect against infinite
	 * loops.
	 */
	assert((inc > 0 && start < end) || (inc < 0 && start > end));
	for (i = start; i != end; i += inc) {
		if (req->cond(kshark_ctx, data[i], req->sd, req->values)) {
			/*
			 * Data satisfying the condition has been found.
			 */
			if (req->vis_only &&
			    !(data[i]->visible & req->vis_mask)) {
				/* This data entry has been filtered. */
				e = &dummy_entry;
			} else {
				e = data[i];
				break;
			}
		}
	}

	if (index) {
		if (e)
			*index = (e->cpu != KS_FILTERED_BIN)? i : KS_FILTERED_BIN;
		else
			*index = KS_EMPTY_BIN;
	}

	return e;
}

/**
 * @brief Search for an entry satisfying the requirements of a given Data
 *	  request. Start from the position provided by the request and go
 *	  searching in the direction of the increasing timestamps (front).
 *
 * @param req: Input location for Data request.
 * @param data: Input location for the trace data.
 * @param index: Optional output location for the index of the returned
 *		 entry inside the array.
 *
 * @returns Pointer to the first entry satisfying the matching conditionon
 *	    success, or NULL on failure.
 *	    In the special case when some entries, satisfying the Matching
 *	    condition function have been found, but all these entries have
 *	    been discarded because of the visibility criteria (filtered
 *	    entries), the function returns a pointer to a special
 *	    "Dummy entry".
 */
const struct kshark_entry *
kshark_get_entry_front(const struct kshark_entry_request *req,
                       struct kshark_entry **data,
                       ssize_t *index)
{
	ssize_t end = req->first + req->n;

	return get_entry(req, data, index, req->first, end, +1);
}

/**
 * @brief Search for an entry satisfying the requirements of a given Data
 *	  request. Start from the position provided by the request and go
 *	  searching in the direction of the decreasing timestamps (back).
 *
 * @param req: Input location for Data request.
 * @param data: Input location for the trace data.
 * @param index: Optional output location for the index of the returned
 *		 entry inside the array.
 *
 * @returns Pointer to the first entry satisfying the matching conditionon
 *	    success, or NULL on failure.
 *	    In the special case when some entries, satisfying the Matching
 *	    condition function have been found, but all these entries have
 *	    been discarded because of the visibility criteria (filtered
 *	    entries), the function returns a pointer to a special
 *	    "Dummy entry".
 */
const struct kshark_entry *
kshark_get_entry_back(const struct kshark_entry_request *req,
                      struct kshark_entry **data,
                      ssize_t *index)
{
	ssize_t end = req->first - req->n;

	if (end < 0)
		end = -1;

	return get_entry(req, data, index, req->first, end, -1);
}

static int compare_time(const void* a, const void* b)
{
	const struct kshark_entry *entry_a, *entry_b;

	entry_a = *(const struct kshark_entry **) a;
	entry_b = *(const struct kshark_entry **) b;

	if (entry_a->ts > entry_b->ts)
		return 1;

	if (entry_a->ts < entry_b->ts)
		return -1;

	return 0;
}

static void kshark_data_qsort(struct kshark_entry **entries, size_t size)
{
	qsort(entries, size, sizeof(struct kshark_entry *), compare_time);
}

/**
 * Add constant offset to the timestamp of the entry. To be used by the sream
 * object as a System clock calibration callback function.
 */
void kshark_offset_calib(int64_t *ts, int64_t *argv)
{
	*ts += argv[0];
}

/**
 * @brief Apply constant offset to the timestamps of all entries from a given
 *	  Data stream.
 *
 * @param kshark_ctx: Input location for the session context pointer.
 * @param entries: Input location for the trace data.
 * @param size: The size of the trace data.
 * @param sd: Data stream identifier.
 * @param offset: The constant offset to be added (in nanosecond).
 */
void kshark_set_clock_offset(struct kshark_context *kshark_ctx,
			     struct kshark_entry **entries, size_t size,
			     int sd, int64_t offset)
{
	struct kshark_data_stream *stream;
	int64_t correction;

	stream = kshark_get_data_stream(kshark_ctx, sd);
	if (!stream)
		return;

	if (!stream->calib_array) {
		stream->calib = kshark_offset_calib;
		stream->calib_array = calloc(1, sizeof(*stream->calib_array));
		stream->calib_array_size = 1;
	}

	correction = offset - stream->calib_array[0];
	stream->calib_array[0] = offset;

	for (size_t i = 0; i < size; ++i)
		if (entries[i]->stream_id == sd)
			entries[i]->ts += correction;

	kshark_data_qsort(entries, size);
}

static int first_in_time_entry(struct kshark_entry_data_set *buffer, int n_buffers, size_t *count)
{
	int64_t t_min = INT64_MAX;
	int i, min = -1;

	for (i = 0; i < n_buffers; ++i) {
		if (count[i] == buffer[i].n_rows)
			continue;

		if (t_min > buffer[i].data[count[i]]->ts) {
			t_min = buffer[i].data[count[i]]->ts;
			min = i;
		}
	}

	return min;
}

/**
 * @brief Merge trace data streams.
 *
 * @param buffers: Input location for the data-sets to be merged.
 * @param n_buffers: The number of the data-sets to be merged.
 *
 * @returns Merged and sorted in time trace data entries. The user is
 *	    responsible for freeing the elements of the outputted array.
 */
struct kshark_entry **
kshark_merge_data_entries(struct kshark_entry_data_set *buffers, int n_buffers)
{
	struct kshark_entry **merged_data;
	size_t i, tot = 0, count[n_buffers];
	int i_first;

	if (n_buffers < 2) {
		fputs("kshark_merge_data_entries needs multipl data sets.\n",
		      stderr);
		return NULL;
	}

	for (i = 0; i < n_buffers; ++i) {
		count[i] = 0;
		if (buffers[i].n_rows > 0)
			tot += buffers[i].n_rows;
	}

	merged_data = calloc(tot, sizeof(*merged_data));
	if (!merged_data) {
		fputs("Failed to allocate memory for mergeing data entries.\n",
		      stderr);
		return NULL;
	}

	for (i = 0; i < tot; ++i) {
		i_first = first_in_time_entry(buffers, n_buffers, count);
		assert(i_first >= 0);
		merged_data[i] = buffers[i_first].data[count[i_first]];
		++count[i_first];
	}

	return merged_data;
}

static ssize_t load_all_entries(struct kshark_context *kshark_ctx,
				struct kshark_entry **loaded_rows,
				ssize_t n_loaded,
				int sd_first_new, int n_streams,
				struct kshark_entry ***data_rows)
{
	int i, j = 0, n_data_sets;
	ssize_t data_size = 0;

	if (n_streams <= 0 || sd_first_new < 0)
		return data_size;

	n_data_sets = n_streams - sd_first_new;
	if (loaded_rows && n_loaded > 0)
		++n_data_sets;

	struct kshark_entry_data_set buffers[n_data_sets];
	memset(buffers, 0, sizeof(buffers));

	if (loaded_rows && n_loaded > 0) {
		/* Add the data that is already loaded. */
		data_size = buffers[n_data_sets - 1].n_rows = n_loaded;
		buffers[n_data_sets - 1].data = loaded_rows;
	}

	/* Add the data of the new streams. */
	for (i = sd_first_new; i < n_streams; ++i) {
		buffers[j].data = NULL;
		buffers[j].n_rows = kshark_load_entries(kshark_ctx, i,
							&buffers[j].data);

		if (buffers[j].n_rows < 0) {
			/* Loading failed. */
			data_size = buffers[j].n_rows;
			goto error;
		}

		data_size += buffers[j++].n_rows;
	}

	if (n_data_sets == 1) {
		*data_rows = buffers[0].data;
	} else {
		/* Merge all streams. */
		*data_rows = kshark_merge_data_entries(buffers, n_data_sets);
	}

 error:
	for (i = 1; i < n_data_sets; ++i)
		free(buffers[i].data);

	return data_size;
}

/**
 * @brief Load the content of the all opened data file into an array of
 *	  kshark_entries.
 *	  If one or more filters are set, the "visible" fields of each entry
 *	  is updated according to the criteria provided by the filters. The
 *	  field "filter_mask" of the session's context is used to control the
 *	  level of visibility/invisibility of the filtered entries.
 *
 * @param kshark_ctx: Input location for context pointer.
 * @param data_rows: Output location for the trace data. The user is
 *		     responsible for freeing the elements of the outputted
 *		     array.
 *
 * @returns The size of the outputted data in the case of success, or a
 *	    negative error code on failure.
 */
ssize_t kshark_load_all_entries(struct kshark_context *kshark_ctx,
				struct kshark_entry ***data_rows)
{
	return load_all_entries(kshark_ctx,
				NULL, 0,
				0,
				kshark_ctx->n_streams,
				data_rows);
}

/**
 * @brief Append the content of the all opened data file into an array of
 *	  kshark_entries.
 *	  If one or more filters are set, the "visible" fields of each entry
 *	  is updated according to the criteria provided by the filters. The
 *	  field "filter_mask" of the session's context is used to control the
 *	  level of visibility/invisibility of the filtered entries.
 *
 * @param kshark_ctx: Input location for context pointer.
 * @param prior_data: Input location for the already loaded trace data.
 * @param n_prior_rows: The size of the already loaded trace data.
 * @param sd_first_new: Data stream identifier of the first data stream to be
 *			appended.
 * @param merged_data: Output location for the trace data. The user is
 *		       responsible for freeing the elements of the outputted
 *		       array.
 * @returns The size of the outputted data in the case of success, or a
 *	    negative error code on failure.
 */
ssize_t kshark_append_all_entries(struct kshark_context *kshark_ctx,
				  struct kshark_entry **prior_data,
				  ssize_t n_prior_rows,
				  int sd_first_new,
				  struct kshark_entry ***merged_data)
{
	return load_all_entries(kshark_ctx,
				prior_data,
				n_prior_rows,
			        sd_first_new,
				kshark_ctx->n_streams,
				merged_data);
}

static int first_in_time_row(struct kshark_matrix_data_set *buffers, int n_buffers, size_t *count)
{
	int64_t t_min = INT64_MAX;
	int i, min = -1;

	for (i = 0; i < n_buffers; ++i) {
		if (count[i] == buffers[i].n_rows)
			continue;

		if (t_min > buffers[i].ts_array[count[i]]) {
			t_min = buffers[i].ts_array[count[i]];
			min = i;
		}
	}

	return min;
}

/**
 * @brief Merge trace data streams.
 *
 * @param buffers: Input location for the data-sets to be merged.
 * @param n_buffers: The number of the data-sets to be merged.
 *
 * @returns Merged and sorted in time trace data matrix. The user is
 *	    responsible for freeing the columns (arrays) of the outputted
 *	    matrix.
 */
struct kshark_matrix_data_set
kshark_merge_data_matrices(struct kshark_matrix_data_set *buffers, int n_buffers)
{
	struct kshark_matrix_data_set merged_data;
	size_t i, tot = 0, count[n_buffers];
	int i_first;
	bool status;

	merged_data.n_rows = -1;
	if (n_buffers < 2) {
		fputs("kshark_merge_data_matrices needs multipl data sets.\n",
		      stderr);
		goto end;
	}

	for (i = 0; i < n_buffers; ++i) {
		count[i] = 0;
		if (buffers[i].n_rows > 0)
			tot += buffers[i].n_rows;
	}

	status = kshark_data_matrix_alloc(tot, &merged_data.event_array,
					       &merged_data.cpu_array,
					       &merged_data.pid_array,
					       &merged_data.offset_array,
					       &merged_data.ts_array);
	if (!status) {
		fputs("Failed to allocate memory for mergeing data matrices.\n",
		      stderr);
		goto end;
	}

	merged_data.n_rows = tot;

	for (i = 0; i < tot; ++i) {
		i_first = first_in_time_row(buffers, n_buffers, count);
		assert(i_first >= 0);

		merged_data.cpu_array[i] = buffers[i_first].cpu_array[count[i_first]];
		merged_data.pid_array[i] = buffers[i_first].pid_array[count[i_first]];
		merged_data.event_array[i] = buffers[i_first].event_array[count[i_first]];
		merged_data.offset_array[i] = buffers[i_first].offset_array[count[i_first]];
		merged_data.ts_array[i] = buffers[i_first].ts_array[count[i_first]];

		++count[i_first];
	}

 end:
	return merged_data;
}

/** @brief Allocate memory for kshark_data_container. */
struct kshark_data_container *kshark_init_data_container()
{
	struct kshark_data_container *container;

	container = calloc(1, sizeof(*container));
	if (!container)
		goto fail;

	container->data = calloc(KS_CONTAINER_DEFAULT_SIZE,
				  sizeof(*container->data));

	if (!container->data)
		goto fail;

	container->capacity = KS_CONTAINER_DEFAULT_SIZE;
	container->sorted = false;

	return container;

 fail:
	fprintf(stderr, "Failed to allocate memory for data container.\n");
	kshark_free_data_container(container);
	return NULL;
}

/**
 * @brief Free the memory allocated for a kshark_data_container
 * @param container: Input location for the kshark_data_container object.
 */
void kshark_free_data_container(struct kshark_data_container *container)
{
	if (!container)
		return;

	for (ssize_t i = 0; i < container->size; ++i)
		free(container->data[i]);

	free(container->data);
	free(container);
}

/**
 * @brief Append data field value to a kshark_data_container
 * @param container: Input location for the kshark_data_container object.
 * @param entry: The entry that needs addition data field value.
 * @param field: The value of data field to be added.
 *
 * @returns The size of the container after the addition.
 */
ssize_t kshark_data_container_append(struct kshark_data_container *container,
				     struct kshark_entry *entry, int64_t field)
{
	struct kshark_data_field_int64 *data_field;

	if (container->capacity == container->size) {
		if (!KS_DOUBLE_SIZE(container->data,
				    container->capacity))
			return -ENOMEM;
	}

	data_field = malloc(sizeof(*data_field));
	data_field->entry = entry;
	data_field->field = field;
	container->data[container->size++] = data_field;

	return container->size;
}

static int compare_time_dc(const void* a, const void* b)
{
	const struct kshark_data_field_int64 *field_a, *field_b;

	field_a = *(const struct kshark_data_field_int64 **) a;
	field_b = *(const struct kshark_data_field_int64 **) b;

	if (field_a->entry->ts > field_b->entry->ts)
		return 1;

	if (field_a->entry->ts < field_b->entry->ts)
		return -1;

	return 0;
}

/**
 * @brief Sort in time the records in kshark_data_container. The container is
 *	  resized in order to free the unused memory capacity.
 *
 * @param container: Input location for the kshark_data_container object.
 */
void kshark_data_container_sort(struct kshark_data_container *container)
{
	struct kshark_data_field_int64	**data_tmp;

	qsort(container->data, container->size,
	      sizeof(struct kshark_data_field_int64 *),
	      compare_time_dc);

	container->sorted = true;

	data_tmp = realloc(container->data,
			   container->size * sizeof(*container->data));

	if (!data_tmp)
		return;

	container->data = data_tmp;
	container->capacity = container->size;
}

/**
 * @brief Binary search inside a time-sorted array of kshark_data_field_int64.
 *
 * @param time: The value of time to search for.
 * @param data: Input location for the data.
 * @param l: Array index specifying the lower edge of the range to search in.
 * @param h: Array index specifying the upper edge of the range to search in.
 *
 * @returns On success, the index of the first kshark_data_field_int64 inside
 *	    the range, having a timestamp equal or bigger than "time".
 *	    If all fields inside the range have timestamps greater than "time"
 *	    the function returns BSEARCH_ALL_GREATER (negative value).
 *	    If all fields inside the range have timestamps smaller than "time"
 *	    the function returns BSEARCH_ALL_SMALLER (negative value).
 */
ssize_t kshark_find_entry_field_by_time(int64_t time,
					struct kshark_data_field_int64 **data,
					size_t l, size_t h)
{
	size_t mid;

	if (data[l]->entry->ts > time)
		return BSEARCH_ALL_GREATER;

	if (data[h]->entry->ts < time)
		return BSEARCH_ALL_SMALLER;

	/*
	 * After executing the BSEARCH macro, "l" will be the index of the last
	 * entry having timestamp < time and "h" will be the index of the first
	 * entry having timestamp >= time.
	 */
	BSEARCH(h, l, data[mid]->entry->ts < time);
	return h;
}
