diff mbox series

[v4,3/9] kernel-shark-qt: Add API for loading trace.dat files

Message ID 20180702140424.23221-3-y.karadz@gmail.com (mailing list archive)
State Accepted, archived
Headers show
Series [v4,1/9] kernel-shark-qt: Add Cmake build system for the Qt based KernelShark | expand

Commit Message

Yordan Karadzhov July 2, 2018, 2:04 p.m. UTC
This patch introduces the first component of the C API used
by the new Qt-based version of KernelShark. The patch includes
only the part of the API responsible for loading data files
generated by trace-cmd. The following patch will introduce
an example, demonstrating the usage of this part of the API.

This version of the patch contains a number of improvements
suggested by Steven Rostedt. Thanks Steven!

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 kernel-shark-qt/src/CMakeLists.txt |   9 +
 kernel-shark-qt/src/libkshark.c    | 602 +++++++++++++++++++++++++++++
 kernel-shark-qt/src/libkshark.h    | 114 ++++++
 3 files changed, 725 insertions(+)
 create mode 100644 kernel-shark-qt/src/libkshark.c
 create mode 100644 kernel-shark-qt/src/libkshark.h

Comments

Steven Rostedt July 2, 2018, 7:03 p.m. UTC | #1
On Mon,  2 Jul 2018 17:04:18 +0300
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:


> +/**
> + * @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 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 **pids)
> +{
> +	size_t i, pid_count = 0, pid_size = KS_TASK_HASH_SIZE;
> +	struct kshark_task_list *list;
> +	int *temp_pids;
> +
> +	*pids = calloc(pid_size, sizeof(int));
> +	if(!*pids)
> +		goto fail;
> +
> +	for (i = 0; i < KS_TASK_HASH_SIZE; ++i) {
> +		list = kshark_ctx->tasks[i];
> +		while (list) {
> +			(*pids)[pid_count] = list->pid;
> +			list = list->next;
> +			if (++pid_count >= pid_size) {
> +				pid_size *= 2;
> +				temp_pids = realloc(*pids, pid_size * sizeof(int));
> +				if (!temp_pids) {
> +					goto fail;
> +				}
> +				*pids = temp_pids;
> +			}
> +		}
> +	}
> +
> +	temp_pids = realloc(*pids, pid_count * sizeof(int));
> +	if (!temp_pids)
> +		goto fail;

Almost, but we need:

	*pids = temp_pids;

Otherwise, if the unlikely case of *pids gets reallocated (it shouldn't
because we are shrinking *pids), it gets assigned properly. Yes, I know
I'm being paranoid here, but it's better safe than sorry.

No need to send another patch, I'll add this myself.

Thanks!

-- Steve

> +
> +	return pid_count;
> +
> +fail:
> +	fprintf(stderr, "Failed to allocate memory for Task Pids.\n");
> +	free(*pids);
> +	*pids = NULL;
> +	return -ENOMEM;
> +}
> +
Steven Rostedt July 9, 2018, 3:57 p.m. UTC | #2
On Mon,  2 Jul 2018 17:04:18 +0300
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> diff --git a/kernel-shark-qt/src/libkshark.h b/kernel-shark-qt/src/libkshark.h
> new file mode 100644
> index 0000000..b59c94c
> --- /dev/null
> +++ b/kernel-shark-qt/src/libkshark.h
> @@ -0,0 +1,114 @@
> +/* SPDX-License-Identifier: LGPL-2.1 */
> +
> +/*
> + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
> + */
> +
> + /**
> + *  @file    libkshark.h
> + *  @brief   API for processing of FTRACE (trace-cmd) data.
> + */
> +
> +#ifndef _LIB_KSHARK_H
> +#define _LIB_KSHARK_H
> +
> +// C
> +#include <stdint.h>
> +#include <pthread.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +// trace-cmd
> +#include "trace-cmd.h"
> +#include "event-parse.h"
> +#include "trace-filter-hash.h"
> +

Making updates, I notice some issues here.

> +/**
> + * Kernel Shark entry contains all information from one trace record needed
> + * in order to  visualize the time-series of trace records. The part of the
> + * data which is not directly required for the visualization (latency, record
> + * info etc.) is available on-demand via the offset into the trace file.
> + */
> +struct kshark_entry {
> +	/**
> +	 * A bit mask controlling the visibility of the entry. A value of OxFF
> +	 * would mean that the entry is visible everywhere.
> +	 */
> +	uint8_t		visible;
> +
> +	/** The CPU core of the record. */
> +	uint8_t		cpu;

One byte is too small. We already have boxes that I trace with over
1000 CPUs.

> +
> +	/** The PID of the task the record was generated. */
> +	int16_t		pid;

16bits is too small for pid. There's work to have it grow to 32 bits.

I know it's going to take up more memory, but both of these will need
to grow to at 32bits. Perhaps we can combine the cpu and visible into
one 32 bits, where we reserve the MSBs for flags for visible. But to do
that, one needs to be concerned about updates to them.

-- Steve


> +
> +	/** Unique Id ot the trace event type. */
> +	int		event_id;
> +
> +	/** The offset into the trace file, used to find the record. */
> +	uint64_t	offset;
> +
> +	/**
> +	 * The time of the record in nano seconds. The value is taken from
> +	 * the timestamps within the trace data file, which are architecture
> +	 * dependent. The time usually is the timestamp from when the system
> +	 * started.
> +	 */
> +	uint64_t	ts;
> +
> +	/** Pointer to the next (in time) kshark_entry on the same CPU core. */
> +	struct kshark_entry *next;
> +};
> +
diff mbox series

Patch

diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index 8c66424..ed3c60e 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -1,4 +1,13 @@ 
 message("\n src ...")
 
+message(STATUS "libkshark")
+add_library(kshark SHARED libkshark.c)
+
+target_link_libraries(kshark ${CMAKE_DL_LIBS}
+                             ${TRACEEVENT_LIBRARY}
+                             ${TRACECMD_LIBRARY})
+
+set_target_properties(kshark  PROPERTIES SUFFIX	".so.${KS_VERSION_STRING}")
+
 configure_file( ${KS_DIR}/build/deff.h.cmake
                 ${KS_DIR}/src/KsDeff.h)
diff --git a/kernel-shark-qt/src/libkshark.c b/kernel-shark-qt/src/libkshark.c
new file mode 100644
index 0000000..a5349ed
--- /dev/null
+++ b/kernel-shark-qt/src/libkshark.c
@@ -0,0 +1,602 @@ 
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+ /**
+ *  @file    libkshark.c
+ *  @brief   API for processing of FTRACE (trace-cmd) data.
+ */
+
+/** Use GNU C Library. */
+#define _GNU_SOURCE 1
+
+// C
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+
+// KernelShark
+#include "libkshark.h"
+
+static __thread struct trace_seq seq;
+
+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;
+
+	/* 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;
+}
+
+static bool init_thread_seq(void)
+{
+	if (!seq.buffer)
+		trace_seq_init(&seq);
+
+	return seq.buffer != NULL;
+}
+
+/**
+ * @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;
+		}
+	}
+
+	if (!init_thread_seq())
+		return false;
+
+	return true;
+}
+
+static void kshark_free_task_list(struct kshark_context *kshark_ctx)
+{
+	struct kshark_task_list *task;
+	int i;
+
+	if (!kshark_ctx)
+		return;
+
+	for (i = 0; i < KS_TASK_HASH_SIZE; ++i) {
+		while (kshark_ctx->tasks[i]) {
+			task = kshark_ctx->tasks[i];
+			kshark_ctx->tasks[i] = task->next;
+			free(task);
+		}
+	}
+}
+
+/**
+ * @brief Open and prepare for reading a trace data file specified by "file".
+ *	  If the specified file does not exist, or contains no trace data,
+ *	  the function returns false.
+ * @param kshark_ctx: Input location for context pointer.
+ * @param file: The file to load.
+ * @returns True on success, or false on failure.
+ */
+bool kshark_open(struct kshark_context *kshark_ctx, const char *file)
+{
+	struct tracecmd_input *handle;
+
+	kshark_free_task_list(kshark_ctx);
+
+	handle = tracecmd_open(file);
+	if (!handle)
+		return false;
+
+	if (pthread_mutex_init(&kshark_ctx->input_mutex, NULL) != 0) {
+		tracecmd_close(handle);
+		return false;
+	}
+
+	kshark_ctx->handle = handle;
+	kshark_ctx->pevent = tracecmd_get_pevent(handle);
+
+	/*
+	 * Turn off function trace indent and turn on show parent
+	 * if possible.
+	 */
+	trace_util_add_option("ftrace:parent", "1");
+	trace_util_add_option("ftrace:indent", "0");
+
+	return true;
+}
+
+/**
+ * @brief Close the trace data file and free the trace data handle.
+ * @param kshark_ctx: Input location for the session context pointer.
+ */
+void kshark_close(struct kshark_context *kshark_ctx)
+{
+	if (!kshark_ctx || !kshark_ctx->handle)
+		return;
+
+	tracecmd_close(kshark_ctx->handle);
+	kshark_ctx->handle = NULL;
+	kshark_ctx->pevent = NULL;
+
+	pthread_mutex_destroy(&kshark_ctx->input_mutex);
+}
+
+/**
+ * @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 sessuin, that sessuin
+ *		      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_free_task_list(kshark_ctx);
+
+	if (seq.buffer)
+		trace_seq_destroy(&seq);
+
+	if (kshark_ctx == kshark_context_handler)
+		kshark_context_handler = NULL;
+
+	free(kshark_ctx);
+}
+
+static inline uint8_t knuth_hash8(uint32_t val)
+{
+	/*
+	 * Hashing functions, based on Donald E. Knuth's Multiplicative
+	 * hashing. See The Art of Computer Programming (TAOCP).
+	 * Multiplication by the Prime number, closest to the golden
+	 * ratio of 2^8.
+	 */
+	return UINT8_C(val) * UINT8_C(157);
+}
+
+static struct kshark_task_list *
+kshark_find_task(struct kshark_context *kshark_ctx, uint8_t key, int pid)
+{
+	struct kshark_task_list *list;
+
+	for (list = kshark_ctx->tasks[key]; list; list = list->next) {
+		if (list->pid == pid)
+			return list;
+	}
+
+	return NULL;
+}
+
+static struct kshark_task_list *
+kshark_add_task(struct kshark_context *kshark_ctx, int pid)
+{
+	struct kshark_task_list *list;
+	uint8_t key;
+
+	key = knuth_hash8(pid);
+	list = kshark_find_task(kshark_ctx, key, pid);
+	if (list)
+		return list;
+
+	list = malloc(sizeof(*list));
+	if (!list)
+		return NULL;
+
+	list->pid = pid;
+	list->next = kshark_ctx->tasks[key];
+	kshark_ctx->tasks[key] = list;
+
+	return list;
+}
+
+/**
+ * @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 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 **pids)
+{
+	size_t i, pid_count = 0, pid_size = KS_TASK_HASH_SIZE;
+	struct kshark_task_list *list;
+	int *temp_pids;
+
+	*pids = calloc(pid_size, sizeof(int));
+	if(!*pids)
+		goto fail;
+
+	for (i = 0; i < KS_TASK_HASH_SIZE; ++i) {
+		list = kshark_ctx->tasks[i];
+		while (list) {
+			(*pids)[pid_count] = list->pid;
+			list = list->next;
+			if (++pid_count >= pid_size) {
+				pid_size *= 2;
+				temp_pids = realloc(*pids, pid_size * sizeof(int));
+				if (!temp_pids) {
+					goto fail;
+				}
+				*pids = temp_pids;
+			}
+		}
+	}
+
+	temp_pids = realloc(*pids, pid_count * sizeof(int));
+	if (!temp_pids)
+		goto fail;
+
+	return pid_count;
+
+fail:
+	fprintf(stderr, "Failed to allocate memory for Task Pids.\n");
+	free(*pids);
+	*pids = NULL;
+	return -ENOMEM;
+}
+
+static void kshark_set_entry_values(struct kshark_context *kshark_ctx,
+				    struct pevent_record *record,
+				    struct kshark_entry *entry)
+{
+	/* Offset of the record */
+	entry->offset = record->offset;
+
+	/* CPU Id of the record */
+	entry->cpu = record->cpu;
+
+	/* Time stamp of the record */
+	entry->ts = record->ts;
+
+	/* Event Id of the record */
+	entry->event_id = pevent_data_type(kshark_ctx->pevent, record);
+
+	/*
+	 * Is visible mask. This default value means that the entry
+	 * is visible everywhere.
+	 */
+	entry->visible = 0xFF;
+
+	/* Process Id of the record */
+	entry->pid = pevent_data_pid(kshark_ctx->pevent, record);
+}
+
+/**
+ * @brief Load the content of the trace data file into an array of
+ *	  kshark_entries. This function provides fast loading, however the
+ *	  "latency" and the "info" fields can be accessed only via the offset
+ *	  into the file. This makes the access to these two fields much
+ *	  slower.
+ * @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_data_entries(struct kshark_context *kshark_ctx,
+				struct kshark_entry ***data_rows)
+{
+	struct kshark_entry **cpu_list, **rows;
+	struct kshark_entry *entry, **next;
+	struct kshark_task_list *task;
+	struct pevent_record *rec;
+	int cpu, n_cpus, next_cpu;
+	size_t count, total = 0;
+	uint64_t ts;
+
+	if (*data_rows)
+		free(*data_rows);
+
+	n_cpus = tracecmd_cpus(kshark_ctx->handle);
+	cpu_list = calloc(n_cpus, sizeof(struct kshark_entry *));
+
+	for (cpu = 0; cpu < n_cpus; ++cpu) {
+		count = 0;
+		cpu_list[cpu] = NULL;
+		next = &cpu_list[cpu];
+
+		rec = tracecmd_read_cpu_first(kshark_ctx->handle, cpu);
+		while (rec) {
+			*next = entry = malloc(sizeof(struct kshark_entry));
+			if (!entry)
+				goto fail;
+
+			kshark_set_entry_values(kshark_ctx, rec, entry);
+			task = kshark_add_task(kshark_ctx, entry->pid);
+			if (!task)
+				goto fail;
+
+			entry->next = NULL;
+			next = &entry->next;
+			free_record(rec);
+
+			++count;
+			rec = tracecmd_read_data(kshark_ctx->handle, cpu);
+		}
+
+		total += count;
+	}
+
+	rows = calloc(total, sizeof(struct kshark_entry *));
+	if (!rows)
+		goto fail;
+
+	count = 0;
+	while (count < total) {
+		ts = 0;
+		next_cpu = -1;
+		for (cpu = 0; cpu < n_cpus; ++cpu) {
+			if (!cpu_list[cpu])
+				continue;
+
+			if (!ts || cpu_list[cpu]->ts < ts) {
+				ts = cpu_list[cpu]->ts;
+				next_cpu = cpu;
+			}
+		}
+
+		if (next_cpu >= 0) {
+			rows[count] = cpu_list[next_cpu];
+			cpu_list[next_cpu] = cpu_list[next_cpu]->next;
+		}
+		++count;
+	}
+
+	free(cpu_list);
+	*data_rows = rows;
+	return total;
+
+fail:
+	fprintf(stderr, "Failed to allocate memory during data loading.\n");
+	return -ENOMEM;
+}
+
+/**
+ * @brief Load the content of the trace data file into an array of
+ *	  pevent_records. Use this function only if you need fast access
+ *	  to all fields of the record.
+ * @param kshark_ctx: Input location for the session context pointer.
+ * @param data_rows: Output location for the trace data. Use free_record()
+ *	 	     to free 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_data_records(struct kshark_context *kshark_ctx,
+				struct pevent_record ***data_rows)
+{
+	struct temp {
+		struct pevent_record	*rec;
+		struct temp		*next;
+	} **cpu_list, **temp_next, *temp_rec;
+
+	struct kshark_task_list *task;
+	struct pevent_record **rows;
+	struct pevent_record *data;
+	int cpu, n_cpus, next_cpu;
+	size_t count, total = 0;
+	uint64_t ts;
+	int pid;
+
+	n_cpus = tracecmd_cpus(kshark_ctx->handle);
+	cpu_list = calloc(n_cpus, sizeof(struct temp *));
+
+	for (cpu = 0; cpu < n_cpus; ++cpu) {
+		count = 0;
+		cpu_list[cpu] = NULL;
+		temp_next = &cpu_list[cpu];
+
+		data = tracecmd_read_cpu_first(kshark_ctx->handle, cpu);
+		while (data) {
+			*temp_next = temp_rec = malloc(sizeof(*temp_rec));
+			if (!temp_rec)
+				goto fail;
+
+			pid = pevent_data_pid(kshark_ctx->pevent, data);
+			task = kshark_add_task(kshark_ctx, pid);
+			if (!task)
+				goto fail;
+
+			temp_rec->rec = data;
+			temp_rec->next = NULL;
+			temp_next = &(temp_rec->next);
+
+			++count;
+			data = tracecmd_read_data(kshark_ctx->handle, cpu);
+		}
+
+		total += count;
+	}
+
+	rows = calloc(total, sizeof(struct pevent_record *));
+	if (!rows)
+		goto fail;
+
+	count = 0;
+	while (count < total) {
+		ts = 0;
+		next_cpu = -1;
+		for (cpu = 0; cpu < n_cpus; ++cpu) {
+			if (!cpu_list[cpu])
+				continue;
+
+			if (!ts || cpu_list[cpu]->rec->ts < ts) {
+				ts = cpu_list[cpu]->rec->ts;
+				next_cpu = cpu;
+			}
+		}
+
+		if (next_cpu >= 0) {
+			rows[count] = cpu_list[next_cpu]->rec;
+			temp_rec = cpu_list[next_cpu];
+			cpu_list[next_cpu] = cpu_list[next_cpu]->next;
+			free (temp_rec);
+		}
+
+		++count;
+	}
+
+	free(cpu_list);
+	*data_rows = rows;
+	return total;
+
+fail:
+	fprintf(stderr, "Failed to allocate memory during data loading.\n");
+	return -ENOMEM;
+}
+
+static struct pevent_record *kshark_read_at(struct kshark_context *kshark_ctx,
+					    uint64_t offset)
+{
+	/*
+	 * It turns that tracecmd_read_at() is not thread-safe.
+	 * TODO: Understand why and see if this can be fixed.
+	 * For the time being use a mutex to protect the access.
+	 */
+	pthread_mutex_lock(&kshark_ctx->input_mutex);
+
+	struct pevent_record *data = tracecmd_read_at(kshark_ctx->handle,
+						      offset, NULL);
+
+	pthread_mutex_unlock(&kshark_ctx->input_mutex);
+
+	return data;
+}
+
+static const char *kshark_get_latency(struct pevent *pe,
+				      struct pevent_record *record)
+{
+	if (!record)
+		return NULL;
+
+	trace_seq_reset(&seq);
+	pevent_data_lat_fmt(pe, &seq, record);
+	return seq.buffer;
+}
+
+static const char *kshark_get_info(struct pevent *pe,
+				   struct pevent_record *record,
+				   struct event_format *event)
+{
+	char *pos;
+
+	if (!record || !event)
+		return NULL;
+
+	trace_seq_reset(&seq);
+	pevent_event_info(&seq, event, record);
+
+	/*
+	 * The event info string contains a trailing newline.
+	 * Remove this newline.
+	 */
+	if ((pos = strchr(seq.buffer, '\n')) != NULL)
+		*pos = '\0';
+
+	return seq.buffer;
+}
+
+/**
+ * @brief Dump into a string the content of one entry. The function allocates
+ *	  a null terminated string and returns a pointer to this string. The
+ *	  user has to free the returned string.
+ * @param entry: A Kernel Shark entry to be printed.
+ * @returns The returned string contains a semicolon-separated list of data
+ *	    fields.
+ */
+char* kshark_dump_entry(struct kshark_entry *entry)
+{
+	const char *event_name, *task, *lat, *info;
+	struct kshark_context *kshark_ctx;
+	struct pevent_record *data;
+	struct event_format *event;
+	char *temp_str, *entry_str;
+	int event_id, size = 0;
+
+	kshark_ctx = NULL;
+	if (!kshark_instance(&kshark_ctx) || !init_thread_seq())
+		return NULL;
+
+	data = kshark_read_at(kshark_ctx, entry->offset);
+
+	event_id = pevent_data_type(kshark_ctx->pevent, data);
+	event = pevent_data_event_from_type(kshark_ctx->pevent, event_id);
+
+	event_name = event? event->name : "[UNKNOWN EVENT]";
+	task = pevent_data_comm_from_pid(kshark_ctx->pevent, entry->pid);
+	lat = kshark_get_latency(kshark_ctx->pevent, data);
+
+	size = asprintf(&temp_str, "%li %s-%i; CPU %i; %s;",
+			entry->ts,
+			task,
+			entry->pid,
+			entry->cpu,
+			lat);
+
+	info = kshark_get_info(kshark_ctx->pevent, data, event);
+	if (size > 0) {
+		size = asprintf(&entry_str, "%s %s; %s; 0x%x",
+				temp_str,
+				event_name,
+				info,
+				entry->visible);
+
+		free(temp_str);
+	}
+
+	free_record(data);
+
+	if (size > 0)
+		return entry_str;
+
+	return NULL;
+}
diff --git a/kernel-shark-qt/src/libkshark.h b/kernel-shark-qt/src/libkshark.h
new file mode 100644
index 0000000..b59c94c
--- /dev/null
+++ b/kernel-shark-qt/src/libkshark.h
@@ -0,0 +1,114 @@ 
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+ /**
+ *  @file    libkshark.h
+ *  @brief   API for processing of FTRACE (trace-cmd) data.
+ */
+
+#ifndef _LIB_KSHARK_H
+#define _LIB_KSHARK_H
+
+// C
+#include <stdint.h>
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// trace-cmd
+#include "trace-cmd.h"
+#include "event-parse.h"
+#include "trace-filter-hash.h"
+
+/**
+ * Kernel Shark entry contains all information from one trace record needed
+ * in order to  visualize the time-series of trace records. The part of the
+ * data which is not directly required for the visualization (latency, record
+ * info etc.) is available on-demand via the offset into the trace file.
+ */
+struct kshark_entry {
+	/**
+	 * A bit mask controlling the visibility of the entry. A value of OxFF
+	 * would mean that the entry is visible everywhere.
+	 */
+	uint8_t		visible;
+
+	/** The CPU core of the record. */
+	uint8_t		cpu;
+
+	/** The PID of the task the record was generated. */
+	int16_t		pid;
+
+	/** Unique Id ot the trace event type. */
+	int		event_id;
+
+	/** The offset into the trace file, used to find the record. */
+	uint64_t	offset;
+
+	/**
+	 * The time of the record in nano seconds. The value is taken from
+	 * the timestamps within the trace data file, which are architecture
+	 * dependent. The time usually is the timestamp from when the system
+	 * started.
+	 */
+	uint64_t	ts;
+
+	/** Pointer to the next (in time) kshark_entry on the same CPU core. */
+	struct kshark_entry *next;
+};
+
+/** Size of the task's hash table. */
+#define KS_TASK_HASH_SIZE 256
+
+/** Linked list of tasks. */
+struct kshark_task_list {
+	/** Pointer to the next task's PID. */
+	struct kshark_task_list	*next;
+
+	/** PID of a task. */
+	int			 pid;
+};
+
+/** Structure representing a kshark session. */
+struct kshark_context {
+	/** Input handle for the trace data file. */
+	struct tracecmd_input	*handle;
+
+	/** Page event used to parse the page. */
+	struct pevent		*pevent;
+
+	/** Hash table of task PIDs. */
+	struct kshark_task_list	*tasks[KS_TASK_HASH_SIZE];
+
+	/** A mutex, used to protect the access to the input file. */
+	pthread_mutex_t		input_mutex;
+};
+
+bool kshark_instance(struct kshark_context **kshark_ctx);
+
+bool kshark_open(struct kshark_context *kshark_ctx, const char *file);
+
+ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx,
+				 struct kshark_entry ***data_rows);
+
+ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx,
+				 struct pevent_record ***data_rows);
+
+ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int **pids);
+
+void kshark_close(struct kshark_context *kshark_ctx);
+
+void kshark_free(struct kshark_context *kshark_ctx);
+
+char* kshark_dump_entry(struct kshark_entry *entry);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIB_KSHARK_H