diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index ed3c60e..ec22f63 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -1,7 +1,8 @@
 message("\n src ...")
 
 message(STATUS "libkshark")
-add_library(kshark SHARED libkshark.c)
+add_library(kshark SHARED libkshark.c
+                          libkshark-model.c)
 
 target_link_libraries(kshark ${CMAKE_DL_LIBS}
                              ${TRACEEVENT_LIBRARY}
diff --git a/kernel-shark-qt/src/libkshark-model.c b/kernel-shark-qt/src/libkshark-model.c
new file mode 100644
index 0000000..5f3583f
--- /dev/null
+++ b/kernel-shark-qt/src/libkshark-model.c
@@ -0,0 +1,1131 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+ /**
+ *  @file    libkshark.c
+ *  @brief   Visualization model for FTRACE (trace-cmd) data.
+ */
+
+// C
+#include <stdlib.h>
+
+// KernelShark
+#include "libkshark-model.h"
+
+/**
+ * @brief Initialize the Visualization model.
+ * @param histo: Input location for the model descriptor.
+ */
+void ksmodel_init(struct kshark_trace_histo *histo)
+{
+	/*
+	 * Initialize an empty histo. The histo will have no bins and will
+	 * contain no data.
+	 */
+	histo->bin_size = 0;
+	histo->min = 0;
+	histo->max = 0;
+	histo->n_bins = 0;
+
+	histo->bin_count = NULL;
+	histo->map = NULL;
+}
+
+/**
+ * @brief Clear (reset) the Visualization model.
+ * @param histo: Input location for the model descriptor.
+ */
+void ksmodel_clear(struct kshark_trace_histo *histo)
+{
+	/* Reset the histo. It will have no bins and will contain no data. */
+	histo->bin_size = 0;
+	histo->min = 0;
+	histo->max = 0;
+	histo->n_bins = 0;
+
+	free(histo->map);
+	histo->map = NULL;
+
+	free(histo->bin_count);
+	histo->bin_count = NULL;
+}
+
+static void ksmodel_reset_bins(struct kshark_trace_histo *histo,
+			       size_t first, size_t last)
+{
+	size_t i;
+
+	/* Reset the content of the bins. */
+	for (i = first; i <= last; ++i) {
+		histo->map[i] = KS_EMPTY_BIN;
+		histo->bin_count[i] = 0;
+	}
+}
+
+static void ksmodel_histo_alloc(struct kshark_trace_histo *histo, size_t n)
+{
+	free(histo->bin_count);
+	free(histo->map);
+
+	/* Create bins. Two overflow bins are added. */
+	histo->map = calloc(n + 2, sizeof(*histo->map));
+	histo->bin_count = calloc(n + 2, sizeof(*histo->bin_count));
+
+	if (!histo->map || !histo->bin_count) {
+		ksmodel_clear(histo);
+		fprintf(stderr, "Failed to allocate memory for a histo.\n");
+		return;
+	}
+
+	histo->n_bins = n;
+}
+
+static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo,
+					size_t n, uint64_t min, uint64_t max,
+					bool force_in_range)
+{
+	uint64_t corrected_range, delta_range, range = max - min;
+	struct kshark_entry *last;
+
+	/* The size of the bin must be >= 1, hence the range must be >= n. */
+	if (n == 0 || range < n)
+		return;
+
+	/*
+	 * If the number of bins changes, allocate memory for the descriptor
+	 * of the model.
+	 */
+	if (n != histo->n_bins) {
+		ksmodel_histo_alloc(histo, n);
+	}
+
+	/* Reset the content of all bins (including overflow bins) to zero. */
+	ksmodel_reset_bins(histo, 0, histo->n_bins + 1);
+
+	if (range % n == 0) {
+		/*
+		 * The range is multiple of the number of bin and needs no
+		 * adjustment. This is very unlikely to happen but still ...
+		 */
+		histo->min = min;
+		histo->max = max;
+		histo->bin_size = range / n;
+	} else {
+		/*
+		 * The range needs adjustment. The new range will be slightly
+		 * bigger, compared to the requested one.
+		 */
+		histo->bin_size = range / n + 1;
+		corrected_range = histo->bin_size * n;
+		delta_range = corrected_range - range;
+		histo->min = min - delta_range / 2;
+		histo->max = histo->min + corrected_range;
+
+		if (!force_in_range)
+			return;
+
+		/*
+		 * Make sure that the new range doesn't go outside of the time
+		 * interval of the dataset.
+		 */
+		last = histo->data[histo->data_size - 1];
+		if (histo->min < histo->data[0]->ts) {
+			histo->min = histo->data[0]->ts;
+			histo->max = histo->min + corrected_range;
+		} else if (histo->max > last->ts) {
+			histo->max = last->ts;
+			histo->min = histo->max - corrected_range;
+		}
+	}
+}
+
+/**
+ * @brief Prepare the bining of the Visualization model.
+ * @param histo: Input location for the model descriptor.
+ * @param n: Number of bins.
+ * @param min: Lower edge of the time-window to be visualized.
+ * @param max: Upper edge of the time-window to be visualized.
+ */
+void ksmodel_set_bining(struct kshark_trace_histo *histo,
+			size_t n, uint64_t min, uint64_t max)
+{
+	ksmodel_set_in_range_bining(histo, n, min, max, false);
+}
+
+static size_t ksmodel_set_lower_edge(struct kshark_trace_histo *histo)
+{
+	/*
+	 * Find the index of the first entry inside
+	 * the range (timestamp > min).
+	 */
+	size_t row = kshark_find_entry_by_time(histo->min,
+					       histo->data,
+					       0,
+					       histo->data_size - 1);
+
+	if (row != 0) {
+		/*
+		 * The first entry inside the range is not the first entry
+		 * of the dataset. This means that the Lower Overflow bin
+		 * contains data.
+		 */
+
+		/* Lower Overflow bin starts at "0". */
+		histo->map[histo->n_bins + 1] = 0;
+
+		/*
+		 * The number of entries inside the Lower Overflow bin is
+		 * equal to the index of the first entry inside the range.
+		 */
+		histo->bin_count[histo->n_bins + 1] = row;
+	}  else {
+		/*
+		 * Lower Overflow bin is empty. The number of entries is
+		 * already set to "0".
+		 */
+		histo->map[histo->n_bins + 1] = KS_EMPTY_BIN;
+	}
+
+	/*
+	 * Now check if the first entry inside the range falls into the
+	 * first bin.
+	 */
+	if (histo->data[row]->ts  < histo->min + histo->bin_size) {
+		/*
+		 * It is inside the fisrs bin. Set the beginning
+		 * of the fisrs bin.
+		 */
+		histo->map[0] = row;
+	} else {
+		/* The fisrs bin is empty. */
+		histo->map[0] = KS_EMPTY_BIN;
+	}
+
+	return row;
+}
+
+static size_t ksmodel_set_upper_edge(struct kshark_trace_histo *histo)
+{
+	/*
+	 * Find the index of the first entry outside
+	 * the range (timestamp > max).
+	 */
+	size_t row = kshark_find_entry_by_time(histo->max,
+					       histo->data,
+					       0,
+					       histo->data_size - 1);
+
+	if (row < histo->data_size - 1 ||
+	    (row == histo->data_size - 1 &&
+	     histo->data[histo->data_size - 1]->ts > histo->max)) {
+		/*
+		 * The Upper Overflow bin contains data. Set its beginning
+		 * and the number of entries.
+		 */
+		histo->map[histo->n_bins] = row;
+		histo->bin_count[histo->n_bins] = histo->data_size - row;
+	}  else {
+		/*
+		 * Upper Overflow bin is empty. The number of entries is
+		 * already set to "0".
+		 */
+		histo->map[histo->n_bins] = KS_EMPTY_BIN;
+	}
+
+	return row;
+}
+
+static void ksmodel_set_next_bin_edge(struct kshark_trace_histo *histo,
+				      size_t bin)
+{
+	size_t time, row, next_bin = bin + 1;
+
+	/* Calculate the beginning of the next bin. */
+	time = histo->min + next_bin * histo->bin_size;
+
+	/*
+	 * Find the index of the first entry inside
+	 * the next bin (timestamp > time).
+	 */
+	row = kshark_find_entry_by_time(time,histo->data, 0,
+					histo->data_size - 1);
+
+	/*
+	 * The timestamp of the very last entry of the dataset can be exactly
+	 * equal to the value of the upper edge of the range. This is very
+	 * likely to happen when we use ksmodel_set_in_range_bining(). In this
+	 * case we have to increase the size of the very last bin in order to
+	 * make sure that the last entry of the dataset will fall into it.
+	 */
+	if (next_bin == histo->n_bins - 1)
+		++time;
+
+	if (histo->data[row]->ts  >= time + histo->bin_size) {
+		/* The bin is empty. */
+		histo->map[next_bin] = KS_EMPTY_BIN;
+		return;
+	}
+
+	/* Set the index of the first entry. */
+	histo->map[next_bin] = row;
+}
+
+static void ksmodel_set_bin_counts(struct kshark_trace_histo *histo)
+{
+	size_t i = 0, prev_not_empty;
+
+	/*
+	 * Find the first bin which contain data. Start by checking the
+	 * Lower Overflow bin.
+	 */
+	if (histo->map[histo->n_bins + 1] != KS_EMPTY_BIN) {
+		prev_not_empty = histo->n_bins + 1;
+	} else {
+		while (histo->map[i] < 0) {
+			++i;
+		}
+
+		prev_not_empty = i++;
+	}
+
+	/*
+	 * Starting from the first not empty bin, loop over all bins and
+	 * calculate the number of entries.
+	 */
+	while (i < histo->n_bins) {
+		if (histo->map[i] != KS_EMPTY_BIN) {
+			histo->bin_count[prev_not_empty] =
+				histo->map[i] - histo->map[prev_not_empty];
+	
+			prev_not_empty = i;
+		}
+
+		++i;
+	}
+
+	/* Check if the Upper Overflow bin contains data. */
+	if (histo->map[histo->n_bins] == KS_EMPTY_BIN) {
+		/*
+		 * The Upper Overflow bin is empty. Use the size of the
+		 * dataset to calculate the content of the previouse not
+		 * empty bin.
+		 */
+		histo->bin_count[prev_not_empty] = histo->data_size -
+						   histo->map[prev_not_empty];
+	} else {
+		/*
+		 * Use the index of the first entry inside the Upper Overflow
+		 * bin to calculate the content of the previouse not empty
+		 * bin.
+		 */
+		histo->bin_count[prev_not_empty] = histo->map[histo->n_bins] -
+						   histo->map[prev_not_empty];
+	}
+}
+
+/**
+ * @brief Provide the Visualization model with data. Calculate the current
+ *	  state of the model.
+ * @param histo: Input location for the model descriptor.
+ * @param data: Input location for the trace data.
+ * @param n: Number of bins.
+ */
+void ksmodel_fill(struct kshark_trace_histo *histo,
+		  struct kshark_entry **data, size_t n)
+{
+	size_t bin;
+
+	histo->data_size = n;
+	histo->data = data;
+
+	if (histo->n_bins == 0 ||
+	    histo->bin_size == 0 ||
+	    histo->data_size == 0) {
+		/*
+		 * Something is wrong with this histo.
+		 * Most likely the binning is not set.
+		 */
+		ksmodel_clear(histo);
+		fprintf(stderr,
+			"Unable to fill the model with data.\n");
+		fprintf(stderr,
+			"Try to set the bining of the model first.\n");
+
+		return;
+	}
+
+	/* Set the Lower Overflow bin */
+	ksmodel_set_lower_edge(histo);
+
+	/*
+	 * Loop over the dataset and set the beginning of all individual bins.
+	 */
+	bin = 0;
+	while (bin < histo->n_bins) {
+		ksmodel_set_next_bin_edge(histo, bin);
+		++bin;
+	}
+
+	/* Set the Upper Overflow bin. */
+	ksmodel_set_upper_edge(histo);
+
+	/* Calculate the number of entries in each bin. */
+	ksmodel_set_bin_counts(histo);
+}
+
+/**
+ * @brief Get the total number of entries in a given bin.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @returns The number of entries in this bin.
+ */
+size_t ksmodel_bin_count(struct kshark_trace_histo *histo, int bin)
+{
+	if (bin >= 0 && bin < (int) histo->n_bins)
+		return histo->bin_count[bin];
+
+	if (bin == UPPER_OVERFLOW_BIN)
+		return histo->bin_count[histo->n_bins];
+
+	if (bin == LOWER_OVERFLOW_BIN)
+		return histo->bin_count[histo->n_bins + 1];
+
+	return 0;
+}
+
+/**
+ * @brief Shift the time-window of the model forward. Recalculate the current
+ *	  state of the model.
+ * @param histo: Input location for the model descriptor.
+ * @param n: Number of bins to shift.
+ */
+void ksmodel_shift_forward(struct kshark_trace_histo *histo, size_t n)
+{
+	size_t bin;
+	
+	if (!histo->data_size)
+		return;
+
+	if (ksmodel_bin_count(histo, UPPER_OVERFLOW_BIN) == 0) {
+		/*
+		 * The Upper Overflow bin is empty. This means that we are at
+		 * the upper edge of the dataset already. Do nothing in this
+		 * case.
+		 */
+		return;
+	}
+
+	if (n >= histo->n_bins) {
+		/*
+		 * No overlap between the new and the old ranges. Recalculate
+		 * all bins from scratch. First calculate the new range.
+		 */
+		histo->min += n * histo->bin_size;
+		histo->max += n * histo->bin_size;
+
+		ksmodel_set_bining(histo, histo->n_bins, histo->min,
+							 histo->max);
+
+		ksmodel_fill(histo, histo->data, histo->data_size);
+		return;
+	}
+
+	/* Set the new Lower Overflow bin */
+	ksmodel_set_lower_edge(histo);
+
+	/*
+	 * Copy the content of all overlaping bins starting from bin "0"
+	 * of the new histo.
+	 */
+	bin = 0;
+	while (bin <  histo->n_bins - n) {
+		histo->map[bin] = histo->map[bin + n];
+		histo->bin_count[bin] = histo->bin_count[bin + n];
+		++bin;
+	}
+
+	histo->map[bin] = histo->map[bin + n];
+	histo->bin_count[bin] = 0;
+
+	/* Calculate only the content of the new (non-overlapping) bins. */
+	ksmodel_reset_bins(histo, bin + 1, histo->n_bins);
+	while (bin < histo->n_bins) {
+		ksmodel_set_next_bin_edge(histo, bin);
+		++bin;
+	}
+
+	/*
+	 * Set the new Upper Overflow bin and calculate the number of entries
+	 * in each bin.
+	 */
+	ksmodel_set_upper_edge(histo);
+	ksmodel_set_bin_counts(histo);
+}
+
+/**
+ * @brief Shift the time-window of the model backward. Recalculate the current
+ *	  state of the model.
+ * @param histo: Input location for the model descriptor.
+ * @param n: Number of bins to shift.
+ */
+void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n)
+{
+	size_t bin;
+
+	if (!histo->data_size)
+		return;
+
+	if (ksmodel_bin_count(histo, LOWER_OVERFLOW_BIN) == 0) {
+		/*
+		 * The Lower Overflow bin is empty. This means that we are at
+		 * the Lower edge of the dataset already. Do nothing in this
+		 * case.
+		 */
+		return;
+	}
+
+	if (n >= histo->n_bins) {
+		/*
+		 * No overlap between the new and the old range. Recalculate
+		 * all bins from scratch. First calculate the new range.
+		 */
+		histo->min -= n * histo->bin_size;
+		histo->max -= n * histo->bin_size;
+
+		ksmodel_set_bining(histo, histo->n_bins, histo->min,
+							 histo->max);
+
+		ksmodel_fill(histo, histo->data, histo->data_size);
+		return;
+	}
+
+	/*
+	 * Copy the content of all overlaping bins starting from the last bin
+	 * of the new histo.
+	 */
+	bin = histo->n_bins - 1;
+	while (1) {
+		if (bin > histo->n_bins - n && histo->map[bin] >= 0) {
+			histo->map[histo->n_bins] = histo->map[bin];
+			histo->bin_count[histo->n_bins] +=
+				histo->bin_count[bin];
+		}
+
+		histo->map[bin] = histo->map[bin-n];
+		histo->bin_count[bin] = histo->bin_count[bin-n];
+
+		if (bin == n)
+			break;
+
+		--bin;
+	}
+
+	/* Reset all new bins, including overflow bins. */
+	ksmodel_reset_bins(histo, 0, bin);
+	ksmodel_reset_bins(histo, histo->n_bins, histo->n_bins + 1);
+
+	/* Set the new Lower Overflow bin */
+	ksmodel_set_lower_edge(histo);
+
+	/* Calculate only the content of the new (non-overlapping) bins. */
+	bin = 0;
+	while (bin < n) {
+		ksmodel_set_next_bin_edge(histo, bin);
+		++bin;
+	}
+
+	/*
+	 * Set the new Upper Overflow bin and calculate the number of entries
+	 * in each bin.
+	 */
+	ksmodel_set_upper_edge(histo);
+	ksmodel_set_bin_counts(histo);
+}
+
+/**
+ * @brief Move the time-window of the model to a given location. Recalculate
+ *	  the current state of the model.
+ * @param histo: Input location for the model descriptor.
+ * @param ts: position in time to be visualized.
+ */
+void ksmodel_jump_to(struct kshark_trace_histo *histo, size_t ts)
+{
+	size_t min, max, range_min;
+
+	if (ts > histo->min && ts < histo->max) {
+		/*
+		 * The new position is already inside the range.
+		 * Do nothing in this case.
+		 */
+		return;
+	}
+
+	/*
+	 * Calculate the new range without changing the size and the number
+	 * of bins.
+	 */
+	min = ts - histo->n_bins * histo->bin_size / 2;
+
+	/* Make sure that the range does not go outside of the dataset. */
+	if (min < histo->data[0]->ts)
+		min = histo->data[0]->ts;
+
+	range_min = histo->data[histo->data_size - 1]->ts -
+		   histo->n_bins * histo->bin_size;
+
+	if (min > range_min)
+		min = range_min;
+
+	max = min + histo->n_bins * histo->bin_size;
+
+	/* Use the new range to recalculate all bins from scratch. */
+	ksmodel_set_bining(histo, histo->n_bins, min, max);
+	ksmodel_fill(histo, histo->data, histo->data_size);
+}
+
+/**
+ * @brief Extend the time-window of the model. Recalculate the current state
+ *	  of the model.
+ * @param histo: Input location for the model descriptor.
+ * @param r: Scale factor of the zoom-out.
+ * @param mark: Focus point of the zoom-out.
+ */
+void ksmodel_zoom_out(struct kshark_trace_histo *histo,
+		      double r, int mark)
+{
+	size_t range, min, max, delta_min;
+	double delta_tot;
+
+	if (!histo->data_size) 
+		return;
+
+	/*
+	 * If the marker is not set, assume that the focal point of the zoom
+	 * is the center of the range.
+	 */
+	if (mark < 0)
+		mark = histo->n_bins / 2;
+
+	/*
+	 * Calculate the new range of the histo. Use the bin of the marker
+	 * as a focal point for the zoomout. With this the maker will stay
+	 * inside the same bin in the new histo.
+	 */
+	range = histo->max - histo->min;
+	delta_tot = range * r;
+	delta_min = delta_tot * mark / histo->n_bins;
+
+	min = histo->min - delta_min;
+	max = histo->max + (size_t) delta_tot - delta_min;
+
+	/* Make sure the new range doesn't go outside of the dataset. */
+	if (min < histo->data[0]->ts)
+		min = histo->data[0]->ts;
+
+	if (max > histo->data[histo->data_size - 1]->ts)
+		max = histo->data[histo->data_size - 1]->ts;
+
+	/*
+	 * Use the new range to recalculate all bins from scratch. Enforce
+	 * "In Range" adjustment of the range of the model, in order to avoid
+	 * slowly drifting outside of the data-set in the case when the very
+	 * first or the very last entry is used as a focal point.
+	 */
+	ksmodel_set_in_range_bining(histo, histo->n_bins, min, max, true);
+	ksmodel_fill(histo, histo->data, histo->data_size);
+}
+
+/**
+ * @brief Shrink the time-window of the model. Recalculate the current state
+ *	  of the model.
+ * @param histo: Input location for the model descriptor.
+ * @param r: Scale factor of the zoom-in.
+ * @param mark: Focus point of the zoom-in.
+ */
+void ksmodel_zoom_in(struct kshark_trace_histo *histo,
+		     double r, int mark)
+{
+	size_t range, min, max, delta_min;
+	double delta_tot;
+
+	if (!histo->data_size)
+		return;
+
+	/*
+	 * If the marker is not set, assume that the focal point of the zoom
+	 * is the center of the range.
+	 */
+	if (mark < 0)
+		mark = histo->n_bins / 2;
+
+	range = histo->max - histo->min;
+
+	/* Avoid overzooming. */
+	if (range < histo->n_bins * 4)
+		return;
+
+	/*
+	 * Calculate the new range of the histo. Use the bin of the marker
+	 * as a focal point for the zoomin. With this the maker will stay
+	 * inside the same bin in the new histo.
+	 */
+	delta_tot =  range * r;
+	if (mark == (int)histo->n_bins - 1)
+		delta_min = delta_tot;
+	else if (mark == 0)
+		delta_min = 0;
+	else
+		delta_min = delta_tot * mark / histo->n_bins;
+
+	min = histo->min + delta_min;
+	max = histo->max - (size_t) delta_tot + delta_min;
+
+	/*
+	 * Use the new range to recalculate all bins from scratch. Enforce
+	 * "In Range" adjustment of the range of the model, in order to avoid
+	 * slowly drifting outside of the data-set in the case when the very
+	 * first or the very last entry is used as a focal point.
+	 */
+	ksmodel_set_in_range_bining(histo, histo->n_bins, min, max, true);
+	ksmodel_fill(histo, histo->data, histo->data_size);
+}
+
+/**
+ * @brief Get the index of the first entry in a given bin.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @returns Index of the first entry in this bin. If the bin is empty the
+ *	    function returns negative error identifier (KS_EMPTY_BIN).
+ */
+ssize_t ksmodel_first_index_at_bin(struct kshark_trace_histo *histo, int bin)
+{
+	if (bin >= 0 && bin < (int) histo->n_bins)
+		return histo->map[bin];
+
+	if (bin == UPPER_OVERFLOW_BIN)
+		return histo->map[histo->n_bins];
+
+	if (bin == LOWER_OVERFLOW_BIN)
+		return histo->map[histo->n_bins + 1];
+
+	return KS_EMPTY_BIN;
+}
+
+/**
+ * @brief Get the index of the last entry in a given bin.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @returns Index of the last entry in this bin. If the bin is empty the
+ *	    function returns negative error identifier (KS_EMPTY_BIN).
+ */
+ssize_t ksmodel_last_index_at_bin(struct kshark_trace_histo *histo, int bin)
+{
+	ssize_t index = ksmodel_first_index_at_bin(histo, bin);
+	size_t count = ksmodel_bin_count(histo, bin);
+
+	if (index >= 0 && count)
+		index += count - 1;
+
+	return index;
+}
+
+static bool ksmodel_is_visible(struct kshark_entry *e)
+{
+	if ((e->visible & KS_GRAPH_VIEW_FILTER_MASK) &&
+	    (e->visible & KS_EVENT_VIEW_FILTER_MASK))
+		return true;
+
+	return false;
+}
+
+static struct kshark_entry_request *
+ksmodel_entry_front_request_alloc(struct kshark_trace_histo *histo,
+				  int bin, bool vis_only,
+				  matching_condition_func func, int val)
+{
+	struct kshark_entry_request *req;
+	size_t first, n;
+
+	/* Get the number of entries in this bin. */
+	n = ksmodel_bin_count(histo, bin);
+	if (!n)
+		return NULL;
+
+	first = ksmodel_first_index_at_bin(histo, bin);
+
+	req = kshark_entry_request_alloc(first, n,
+					 func, val,
+					 vis_only, KS_GRAPH_VIEW_FILTER_MASK);
+
+	return req;
+}
+
+static struct kshark_entry_request *
+ksmodel_entry_back_request_alloc(struct kshark_trace_histo *histo,
+				 int bin, bool vis_only,
+				 matching_condition_func func, int val)
+{
+	struct kshark_entry_request *req;
+	size_t first, n;
+
+	/* Get the number of entries in this bin. */
+	n = ksmodel_bin_count(histo, bin);
+	if (!n)
+		return NULL;
+
+	first = ksmodel_last_index_at_bin(histo, bin);
+
+	req = kshark_entry_request_alloc(first, n,
+					 func, val,
+					 vis_only, KS_GRAPH_VIEW_FILTER_MASK);
+
+	return req;
+}
+
+/**
+ * @brief Get the index of the first entry from a given Cpu in a given bin.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @param cpu: Cpu Id.
+ * @returns Index of the first entry from a given Cpu in this bin.
+ */
+ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo,
+				   int bin, int cpu)
+{
+	size_t i, n, first, not_found = KS_EMPTY_BIN;
+
+	n = ksmodel_bin_count(histo, bin);
+	if (!n)
+		return not_found;
+
+	first = ksmodel_first_index_at_bin(histo, bin);
+
+	for (i = first; i < first + n; ++i) {
+		if (histo->data[i]->cpu == cpu) {
+			if (ksmodel_is_visible(histo->data[i]))
+				return i;
+			else
+				not_found = KS_FILTERED_BIN;
+		}
+	}
+
+	return not_found;
+}
+
+/**
+ * @brief Get the index of the first entry from a given Task in a given bin.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @param pid: Process Id of a task.
+ * @returns Index of the first entry from a given Task in this bin.
+ */
+ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo,
+				   int bin, int pid)
+{
+	size_t i, n, first, not_found = KS_EMPTY_BIN;
+
+	n = ksmodel_bin_count(histo, bin);
+	if (!n)
+		return not_found;
+
+	first = ksmodel_first_index_at_bin(histo, bin);
+	
+	for (i = first; i < first + n; ++i) {
+		if (histo->data[i]->pid == pid) {
+			if (ksmodel_is_visible(histo->data[i]))
+				return i;
+			else
+				not_found = KS_FILTERED_BIN;
+		}
+	}
+
+	return not_found;
+}
+
+/**
+ * @brief In a given bin, start from the front end of the bin and go towards
+ *	  the back end, searching for an entry satisfying the Matching
+ *	  condition defined by a Matching condition function.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @param vis_only: If true, a visible entry is requested.
+ * @param func: Matching condition finction.
+ * @param val: Matching condition value, used by the Matching condition
+ *	       finction.
+ * @param index: Optional output location for the index of the requested
+ *		 entry inside the array.
+ * @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL.
+ */
+const struct kshark_entry *
+ksmodel_get_entry_front(struct kshark_trace_histo *histo,
+			int bin, bool vis_only,
+			matching_condition_func func, int val,
+			ssize_t *index)
+{
+	struct kshark_entry_request *req;
+	const struct kshark_entry *entry;
+
+	/* Set the position at the beginning of the bin and go forward. */
+	req = ksmodel_entry_front_request_alloc(histo, bin, vis_only,
+							    func, val);
+	if (!req)
+		return NULL;
+
+	entry = kshark_get_entry_front(req, histo->data, index);
+	free(req);
+
+	return entry;
+}
+
+/**
+ * @brief In a given bin, start from the back end of the bin and go towards
+ *	  the front end, searching for an entry satisfying the Matching
+ *	  condition defined by a Matching condition function.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @param vis_only: If true, a visible entry is requested.
+ * @param func: Matching condition finction.
+ * @param val: Matching condition value, used by the Matching condition
+ *	       finction.
+ * @param index: Optional output location for the index of the requested
+ *		 entry inside the array.
+ * @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL.
+ */
+const struct kshark_entry *
+ksmodel_get_entry_back(struct kshark_trace_histo *histo,
+		       int bin, bool vis_only,
+		       matching_condition_func func, int val,
+		       ssize_t *index)
+{
+	struct kshark_entry_request *req;
+	const struct kshark_entry *entry;
+
+	/* Set the position at the end of the bin and go backwards. */
+	req = ksmodel_entry_back_request_alloc(histo, bin, vis_only,
+							   func, val);
+	if (!req)
+		return NULL;
+
+	entry = kshark_get_entry_back(req, histo->data, index);
+	free(req);
+
+	return entry;
+}
+
+static int ksmodel_get_entry_pid(const struct kshark_entry *entry)
+{
+	if (!entry) {
+		/* No data has been found. */
+		return KS_EMPTY_BIN;
+	}
+
+	if (!entry->visible) {
+		/* Some data has been found, but it is filtered-out. */
+		return KS_FILTERED_BIN;
+	}
+
+	return entry->pid;
+}
+
+/**
+ * @brief In a given bin, start from the front end of the bin and go towards
+ *	  the back end, searching for an entry from a given Cpu. Return
+ *	  the Process Id of the task of the entry found.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @param cpu: Cpu Id.
+ * @param vis_only: If true, a visible entry is requested.
+ * @param index: Optional output location for the index of the requested
+ *		 entry inside the array.
+ * @returns Process Id of the task if an entry has been found. Else a negative
+ *	    Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN).
+ */
+int ksmodel_get_pid_front(struct kshark_trace_histo *histo,
+			  int bin, int cpu, bool vis_only,
+			  ssize_t *index)
+{
+	struct kshark_entry_request *req;
+	const struct kshark_entry *entry;
+
+	/* Set the position at the beginning of the bin and go forward. */
+	req = ksmodel_entry_front_request_alloc(histo,
+						bin, vis_only,
+						kshark_check_cpu, cpu);
+	if (!req)
+		return KS_EMPTY_BIN;
+
+	entry = kshark_get_entry_front(req, histo->data, index);
+	free(req);
+
+	return ksmodel_get_entry_pid(entry);
+}
+
+/**
+ * @brief In a given bin, start from the back end of the bin and go towards
+ *	  the front end, searching for an entry from a given Cpu. Return
+ *	  the Process Id of the task of the entry found.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @param cpu: Cpu Id.
+ * @param vis_only: If true, a visible entry is requested.
+ * @param index: Optional output location for the index of the requested
+ *		 entry inside the array.
+ * @returns Process Id of the task if an entry has been found. Else a negative
+ *	    Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN).
+ */
+int ksmodel_get_pid_back(struct kshark_trace_histo *histo,
+			 int bin, int cpu, bool vis_only,
+			 ssize_t *index)
+{
+	struct kshark_entry_request *req;
+	const struct kshark_entry *entry;
+
+	/* Set the position at the end of the bin and go backwards. */
+	req = ksmodel_entry_back_request_alloc(histo,
+					       bin, vis_only,
+					       kshark_check_cpu, cpu);
+	if (!req)
+		return KS_EMPTY_BIN;
+
+	entry = kshark_get_entry_back(req, histo->data, index);
+	free(req);
+
+	return ksmodel_get_entry_pid(entry);
+}
+
+static int ksmodel_get_entry_cpu(const struct kshark_entry *entry)
+{
+	if (!entry) {
+		/* No data has been found. */
+		return KS_EMPTY_BIN;
+	}
+
+	if (!entry->visible) {
+		/* Some data has been found, but it is filtered-out. */
+		return KS_FILTERED_BIN;
+	}
+
+	return entry->cpu;
+}
+
+/**
+ * @brief Get the Cpu Id of the first entry from a given Task in a given bin.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @param pid: Process Id of a task.
+ * @param vis_only: If true, a visible entry is requested.
+ * @param index: Optional output location for the index of the requested
+ *		 entry inside the array.
+ * @returns Cpu Id of the first entry from a given Task in this bin.
+ */
+int ksmodel_get_cpu(struct kshark_trace_histo *histo,
+		    int bin, int pid, bool vis_only,
+		    ssize_t *index)
+{
+	struct kshark_entry_request *req;
+	const struct kshark_entry *entry;
+	size_t n;
+
+	/* Get the number of entries in this bin. */
+	n = ksmodel_bin_count(histo, bin);
+	if (!n)
+		return KS_EMPTY_BIN;
+
+	/* Create an entry request but keep the starting position unset. */
+	req = kshark_entry_request_alloc(0, n,
+					 kshark_check_pid, pid,
+					 vis_only, KS_GRAPH_VIEW_FILTER_MASK);
+
+	if (bin == UPPER_OVERFLOW_BIN) {
+		/*
+		 * Set the position at the end of the Lower Overflow bin and
+		 * go backwards.
+		 */
+		req->first = ksmodel_bin_count(histo, bin) - 1;
+		entry = kshark_get_entry_back(req, histo->data, index);
+	} else {
+		/*
+		 * Set the position at the beginning of the bin and go
+		 * forward.
+		 */
+		req->first = ksmodel_first_index_at_bin(histo, bin);
+		entry = kshark_get_entry_front(req, histo->data, index);
+	}
+
+	free(req);
+
+	return ksmodel_get_entry_cpu(entry);
+}
+
+/**
+ * @brief Check if a visible entry from a given Cpu exist in this bin.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @param cpu: Cpu Id.
+ * @param index: Optional output location for the index of the requested
+ *		 entry inside the array.
+ * @returns True, if a visible entry from the Cpu exists in this bin.
+ * 	    Else false.
+ */
+bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo,
+				     int bin, int cpu, ssize_t *index)
+{
+	struct kshark_entry_request *req;
+	const struct kshark_entry *entry;
+
+	/* Set the position at the beginning of the bin and go forward. */
+	req = ksmodel_entry_front_request_alloc(histo,
+						bin, true,
+						kshark_check_cpu, cpu);
+	if (!req)
+		return false;
+
+	req->vis_mask = KS_EVENT_VIEW_FILTER_MASK;
+	entry = kshark_get_entry_front(req, histo->data, index);
+	free(req);
+
+	if (!entry || !entry->visible) {
+		/* No visible entry has been found. */
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * @brief Check if a visible entry from a given Task exist in this bin.
+ * @param histo: Input location for the model descriptor.
+ * @param bin: Bin id.
+ * @param pid: Process Id of the task.
+ * @param index: Optional output location for the index of the requested
+ *		 entry inside the array.
+ * @returns True, if a visible entry from the task exists in this bin.
+ * 	    Else false.
+ */
+bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo,
+				      int bin, int pid, ssize_t *index)
+{
+	struct kshark_entry_request *req;
+	const struct kshark_entry *entry;
+
+	/* Set the position at the beginning of the bin and go forward. */
+	req = ksmodel_entry_front_request_alloc(histo,
+						bin, true,
+						kshark_check_pid, pid);
+	if (!req)
+		return false;
+
+	req->vis_mask = KS_EVENT_VIEW_FILTER_MASK;
+	entry = kshark_get_entry_front(req, histo->data, index);
+	free(req);
+
+	if (!entry || !entry->visible) {
+		/* No visible entry has been found. */
+		return false;
+	}
+
+	return true;
+}
diff --git a/kernel-shark-qt/src/libkshark-model.h b/kernel-shark-qt/src/libkshark-model.h
new file mode 100644
index 0000000..9b2c54c
--- /dev/null
+++ b/kernel-shark-qt/src/libkshark-model.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+ /**
+ *  @file    libkshark-model.h
+ *  @brief   Visualization model for FTRACE (trace-cmd) data.
+ */
+
+#ifndef _LIB_KSHARK_MODEL_H
+#define _LIB_KSHARK_MODEL_H
+
+// KernelShark
+#include "libkshark.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+/** Overflow Bin identifiers. */
+enum OverflowBin {
+	/** Identifier of the Upper Overflow Bin. */
+	UPPER_OVERFLOW_BIN = -1,
+
+	/** Identifier of the Lower Overflow Bin. */
+	LOWER_OVERFLOW_BIN = -2,
+};
+
+/** Structure describing the current state of the visualization model. */
+struct kshark_trace_histo {
+	/** Trace data. */
+	struct kshark_entry	**data;
+
+	/** The size of the data. */
+	size_t			data_size;
+
+	/** The index of the first entry in each bin. */
+	ssize_t			*map;
+
+	/** Number of entries in each bin. */
+	size_t			*bin_count;
+
+	/** Lower edge of the time-window to be visualized. */
+	uint64_t		min;
+
+	/** Upper edge of the time-window to be visualized. */
+	uint64_t		max;
+
+	/** The size of the bins. */
+	uint64_t		bin_size;
+
+	/** Number of bins. */
+	size_t			n_bins;
+};
+
+void ksmodel_init(struct kshark_trace_histo *histo);
+
+void ksmodel_clear(struct kshark_trace_histo *histo);
+
+void ksmodel_set_bining(struct kshark_trace_histo *histo,
+			size_t n, uint64_t min, uint64_t max);
+
+void ksmodel_fill(struct kshark_trace_histo *histo,
+		  struct kshark_entry **data, size_t n);
+
+size_t ksmodel_bin_count(struct kshark_trace_histo *histo, int bin);
+
+void ksmodel_shift_forward(struct kshark_trace_histo *histo, size_t n);
+
+void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n);
+
+void ksmodel_jump_to(struct kshark_trace_histo *histo, size_t ts);
+
+void ksmodel_zoom_out(struct kshark_trace_histo *histo,
+		      double r, int mark);
+
+void ksmodel_zoom_in(struct kshark_trace_histo *histo,
+		     double r, int mark);
+
+ssize_t ksmodel_first_index_at_bin(struct kshark_trace_histo *histo, int bin);
+
+ssize_t ksmodel_last_index_at_bin(struct kshark_trace_histo *histo, int bin);
+
+ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo,
+				   int bin, int cpu);
+
+ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo,
+				   int bin, int pid);
+
+const struct kshark_entry *
+ksmodel_get_entry_front(struct kshark_trace_histo *histo,
+			int bin, bool vis_only,
+			matching_condition_func func, int val,
+			ssize_t *index);
+
+const struct kshark_entry *
+ksmodel_get_entry_back(struct kshark_trace_histo *histo,
+		       int bin, bool vis_only,
+		       matching_condition_func func, int val,
+		       ssize_t *index);
+
+int ksmodel_get_pid_front(struct kshark_trace_histo *histo,
+			  int bin, int cpu, bool vis_only,
+			  ssize_t *index);
+
+int ksmodel_get_pid_back(struct kshark_trace_histo *histo,
+			 int bin, int cpu, bool vis_only,
+			 ssize_t *index);
+
+int ksmodel_get_cpu(struct kshark_trace_histo *histo,
+		    int bin, int pid, bool vis_only,
+		    ssize_t *index);
+
+bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo,
+				     int bin, int cpu, ssize_t *index);
+
+bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo,
+				      int bin, int pid, ssize_t *index);
+
+static inline double ksmodel_bin_time(struct kshark_trace_histo *histo,
+				      int bin)
+{
+	return (histo->min + bin*histo->bin_size) * 1e-9;
+}
+
+static inline uint64_t ksmodel_bin_ts(struct kshark_trace_histo *histo,
+				      int bin)
+{
+	return (histo->min + bin*histo->bin_size);
+}
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif
