All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/20] Start KernelShark v2 transformation
@ 2020-11-18 14:49 Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 01/20] kernel-shark: Use only signed types in kshark_entry Yordan Karadzhov (VMware)
                   ` (19 more replies)
  0 siblings, 20 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

In this patch-set we are starting the introduction of the changes
in the C API of libkshark. The goal of this transformation is to
turn KernelShark into a general purpose toolkit for analysis of
tracing data that is no longer strongly coupled to the Ftrace/trace-cmd
data format. At the end of the transformation KernelShark will be able
to merge and simultaneously visualize data from different tracers,
recorded in different data formats. It will also significantly extend
the possibilities for user customization.

This  patch-set transforms the way KernelShark processes the raw tracing
data. Later, in a consecutive patch-sets we will introduce the changes
in the visualization instruments and in the KernelShark GUI itself.

Changes is v4:
 * The integer data format identifier is replaced with a string value 
   that gets registered by the readout plugin.

 * Plus several small modifications addressing the   issues pointed out
   by Steven


Changes is v3.
  This version contains number of small modifications addressing the
  issues pointed out by Steven in his review. However, it also contains
  the following major changes:

   * The session context (kshark_context) holds a pointer to unspecified
     data processing interface (void *interface). This will allow to
     easily introduce new versions of this interface in the future, while
     keeping the interface that we have now for backward compatibility.
     The necessary type checking when using the interface made the helper
     functions wrapping the corresponding methods of the interface
     relatively complicated, so the implementations have been moved from
     the header to the source file.

   * A more sophisticated management of the Data stream descriptors that
     can handle the case when Data streams are constantly added and
     removed has been implemented. The design for this change was
     suggested by Steven in his review.


Changes is v2:
 * Tried to address review comments for "kernel-shark: Introduce libkshark-hash"

 * The huge patch "kernel-shark: Integrate the stream definitions with
the C API" in the previous version is now split into 8 smaller patches.
The last of these 8 new patches (kernel-shark: Complete the stream
integration) is still a bit entangled. I can try to further break this
patch in future versions of the patch-set, if this is needed.


Yordan Karadzhov (VMware) (20):
  kernel-shark: Use only signed types in kshark_entry
  kernel-shark: Add stream_id to kshark_entry
  kernel-shark: Introduce libkshark-hash
  kernel-shark: Introduce Data streams
  kernel-shark: Rename static methods in libkshark
  kernel-shark: Add basic methods for Data streams
  kernel-shark: Housekeeping before implementing stream interface
  kernel-shark: Add stream interface for trace-cmd data
  kernel-shark: Start introducing KernelShark 2.0
  kernel-shark: Start using data streams
  kernel-shark: Remove dead code
  kernel-shark: Redesign the plugin interface
  kernel-shark: Complete the stream integration
  kernel-shark: Provide merging of multiple data streams
  kernel-shark: Integrate the stream definitions with data model
  kernel-shark: Use only signed types for model defs
  kernel-shark: Add ksmodel_get_bin()
  kernel-shark: Protect ksmodel_set_in_range_bining()
  kernel-shark: Add methods for time calibration
  kernel-shark: Integrate streams with libkshark-configio

 CMakeLists.txt             |   10 +-
 build/deff.h.cmake         |   14 +-
 examples/CMakeLists.txt    |   34 +-
 examples/configio.c        |   20 +-
 examples/datafilter.c      |   67 +-
 examples/datahisto.c       |   38 +-
 examples/dataload.c        |   28 +-
 examples/multibufferload.c |   60 +
 src/CMakeLists.txt         |    4 +-
 src/libkshark-collection.c |  121 +-
 src/libkshark-configio.c   | 1208 ++++++++++++++---
 src/libkshark-hash.c       |  239 ++++
 src/libkshark-model.c      |  154 ++-
 src/libkshark-model.h      |   55 +-
 src/libkshark-plugin.c     |  658 +++++++--
 src/libkshark-plugin.h     |  294 +++-
 src/libkshark-tepdata.c    | 1917 ++++++++++++++++++++++++++
 src/libkshark-tepdata.h    |  113 ++
 src/libkshark.c            | 2616 ++++++++++++++++++++----------------
 src/libkshark.h            |  704 ++++++++--
 20 files changed, 6531 insertions(+), 1823 deletions(-)
 create mode 100644 examples/multibufferload.c
 create mode 100644 src/libkshark-hash.c
 create mode 100644 src/libkshark-tepdata.c
 create mode 100644 src/libkshark-tepdata.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v4 01/20] kernel-shark: Use only signed types in kshark_entry
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 02/20] kernel-shark: Add stream_id to kshark_entry Yordan Karadzhov (VMware)
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Using uint64_t for the value of the offset was just wrong. According
to the POSIX standard off_t is a signed integer type with unspecified
size. Here we stick to a 64 bit integer, because this size guaranties
optimal packing of the kshark_entry structure.

Using unsigned values for the timestamps is also a source of problems
and has been a reason for the introduction of multiple bugs in the past.
In principal the value of the timestamps cannot be negative. However,
this value must have the same type as the values used to define the
state of the visualization model, like the range of the model or the
size of the bin. The model state definitions should not take negative
values as well, however their values are recalculated automatically when
the user browses the data and those calculations may result in negative
values in some corner cases. Because of this it is better to use a
signed integer type and treat the negative values as an indicator
of an error rather than have the negative result of the calculations
casted into unsigned type which results into unpredictable behavior of
the model.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libkshark.h b/src/libkshark.h
index 0d6c50d1..9eecc2d0 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -61,7 +61,7 @@ struct kshark_entry {
 	int32_t		event_id;
 
 	/** The offset into the trace file, used to find the record. */
-	uint64_t	offset;
+	int64_t		offset;
 
 	/**
 	 * The time of the record in nano seconds. The value is taken from
@@ -69,7 +69,7 @@ struct kshark_entry {
 	 * dependent. The time usually is the timestamp from when the system
 	 * started.
 	 */
-	uint64_t	ts;
+	int64_t		ts;
 };
 
 /** Size of the task's hash table. */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 02/20] kernel-shark: Add stream_id to kshark_entry
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 01/20] kernel-shark: Use only signed types in kshark_entry Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 03/20] kernel-shark: Introduce libkshark-hash Yordan Karadzhov (VMware)
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

kshark_entry contains all information from one trace record, needed
in order to visualize the time-series of trace records. Here we
reorganize the data fields of kshark_entry in order to make room for
the unique identifier of the Data stream this entry belongs to.

The Id is coded in 16 bits that are taken from the "event_id" field.
So far "event_id" uses 32 bits but we found that it is coded with only
16 bits inside the kernel, so we still have 16 unused (spare) bits in
it.

Because the stream Id is coded with int8_t, we need to set an upper
limit (127) for the total number of Data streams loaded.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark.h | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/libkshark.h b/src/libkshark.h
index 9eecc2d0..7d8b53b0 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1 */
 
 /*
- * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
  */
 
  /**
@@ -51,15 +51,18 @@ struct kshark_entry {
 	 */
 	uint16_t	visible;
 
+	/** Data stream identifier. */
+	int16_t		stream_id;
+
+	/** Unique Id of the trace event type. */
+	int16_t		event_id;
+
 	/** The CPU core of the record. */
 	int16_t		cpu;
 
 	/** The PID of the task the record was generated. */
 	int32_t		pid;
 
-	/** Unique Id ot the trace event type. */
-	int32_t		event_id;
-
 	/** The offset into the trace file, used to find the record. */
 	int64_t		offset;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 03/20] kernel-shark: Introduce libkshark-hash
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 01/20] kernel-shark: Use only signed types in kshark_entry Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 02/20] kernel-shark: Add stream_id to kshark_entry Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 04/20] kernel-shark: Introduce Data streams Yordan Karadzhov (VMware)
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

So far KernelShark have been using an implementation of a hash table
from trace-cmd/include/trace-cmd/trace-filter-hash.h. However it turns
that KernelShark is the only user of trace-filter-hash, which means
that it make more sense to make this implementation of the hash table
part of KernelShark. In this patch we adapt the original trace-cmd
implementation and change the naming convention used.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt   |   1 +
 src/libkshark-hash.c | 239 +++++++++++++++++++++++++++++++++++++++++++
 src/libkshark.h      |  46 +++++++++
 3 files changed, 286 insertions(+)
 create mode 100644 src/libkshark-hash.c

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 457c1007..0b84851c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,6 +2,7 @@ message("\n src ...")
 
 message(STATUS "libkshark")
 add_library(kshark SHARED libkshark.c
+                          libkshark-hash.c
                           libkshark-model.c
                           libkshark-plugin.c
                           libkshark-configio.c
diff --git a/src/libkshark-hash.c b/src/libkshark-hash.c
new file mode 100644
index 00000000..89c021ba
--- /dev/null
+++ b/src/libkshark-hash.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
+ * Copyright (C) 2018 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ */
+
+/**
+ *  @file    libkshark-hash.c
+ *  @brief   Hash table of integer Id numbers.
+ */
+
+// C
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+// KernelShark
+#include "libkshark.h"
+
+/**
+ * @brief: quick_hash - A quick (non secured) hash alogirthm
+ * @param val: The value to perform the hash on
+ * @param bits: The size in bits you need to return
+ *
+ * This is a quick hashing function adapted from Donald E. Knuth's 32
+ * bit multiplicative hash. See The Art of Computer Programming (TAOCP).
+ * Multiplication by the Prime number, closest to the golden ratio of
+ * 2^32.
+ *
+ * "bits" is used to max the result for use cases that require
+ * a power of 2 return value that is less than 32 bits. Any value
+ * of "bits" greater than 31 (or zero), will simply return the full hash
+ * on "val".
+ */
+static inline uint32_t quick_hash(uint32_t val, unsigned int bits)
+{
+	val *= UINT32_C(2654435761);
+
+	if (!bits || bits > 31)
+		return val;
+
+	return val & ((1 << bits) - 1);
+}
+
+static size_t hash_size(struct kshark_hash_id *hash)
+{
+	return (1 << hash->n_bits);
+}
+
+/**
+ * Create new hash table of Ids.
+ */
+struct kshark_hash_id *kshark_hash_id_alloc(size_t n_bits)
+{
+	struct kshark_hash_id *hash;
+	size_t size;
+
+	hash = calloc(1, sizeof(*hash));
+	if (!hash)
+		goto fail;
+
+	hash->n_bits = n_bits;
+	hash->count = 0;
+
+	size = hash_size(hash);
+	hash->hash = calloc(size, sizeof(*hash->hash));
+	if (!hash->hash)
+		goto fail;
+
+	return hash;
+
+ fail:
+	fprintf(stderr, "Failed to allocate memory for hash table.\n");
+	kshark_hash_id_free(hash);
+	return NULL;
+}
+
+/** Free the hash table of Ids. */
+void kshark_hash_id_free(struct kshark_hash_id *hash)
+{
+	if (!hash)
+		return;
+
+	if (hash->hash) {
+		kshark_hash_id_clear(hash);
+		free(hash->hash);
+	}
+
+	free(hash);
+}
+
+/**
+ * @brief Check if an Id with a given value exists in this hash table.
+ */
+bool kshark_hash_id_find(struct kshark_hash_id *hash, int id)
+{
+	uint32_t key = quick_hash(id, hash->n_bits);
+	struct kshark_hash_id_item *item;
+
+	for (item = hash->hash[key]; item; item = item->next)
+		if (item->id == id)
+			break;
+
+	return !!(unsigned long) item;
+}
+
+/**
+ * @brief Add Id to the hash table.
+ *
+ * @param hash: The hash table to add to.
+ * @param id: The Id number to be added.
+ *
+ * @returns Zero if the Id already exists in the table, one if the Id has been
+ *	    added and negative errno code on failure.
+ */
+int kshark_hash_id_add(struct kshark_hash_id *hash, int id)
+{
+	uint32_t key = quick_hash(id, hash->n_bits);
+	struct kshark_hash_id_item *item;
+
+	if (kshark_hash_id_find(hash, id))
+		return 0;
+
+	item = calloc(1, sizeof(*item));
+	if (!item) {
+		fprintf(stderr,
+			"Failed to allocate memory for hash table item.\n");
+		return -ENOMEM;
+	}
+
+	item->id = id;
+	item->next = hash->hash[key];
+	hash->hash[key] = item;
+	hash->count++;
+
+	return 1;
+}
+
+/**
+ * @brief Remove Id from the hash table.
+ */
+void kshark_hash_id_remove(struct kshark_hash_id *hash, int id)
+{
+	struct kshark_hash_id_item *item, **next;
+	int key = quick_hash(id, hash->n_bits);
+
+	next = &hash->hash[key];
+	while (*next) {
+		if ((*next)->id == id)
+			break;
+		next = &(*next)->next;
+	}
+
+	if (!*next)
+		return;
+
+	assert(hash->count);
+
+	hash->count--;
+	item = *next;
+	*next = item->next;
+
+	free(item);
+}
+
+/** Remove (free) all Ids (items) from this hash table. */
+void kshark_hash_id_clear(struct kshark_hash_id *hash)
+{
+	struct kshark_hash_id_item *item, *next;
+	size_t size;
+	int i;
+
+	if (!hash || ! hash->hash)
+		return;
+
+	size = hash_size(hash);
+	for (i = 0; i < size; i++) {
+		next = hash->hash[i];
+		if (!next)
+			continue;
+
+		hash->hash[i] = NULL;
+		while (next) {
+			item = next;
+			next = item->next;
+			free(item);
+		}
+	}
+
+	hash->count = 0;
+}
+
+static int compare_ids(const void* a, const void* b)
+{
+	int arg1 = *(const int*)a;
+	int arg2 = *(const int*)b;
+
+	if (arg1 < arg2)
+		return -1;
+
+	if (arg1 > arg2)
+		return 1;
+
+	return 0;
+}
+
+/**
+ * @brief Get a sorted array containing all Ids of this hash table.
+ */
+int *kshark_hash_ids(struct kshark_hash_id *hash)
+{
+	struct kshark_hash_id_item *item;
+	size_t size = hash_size(hash);
+	int count = 0, i;
+	int *ids;
+
+	if (!hash->count)
+		return NULL;
+
+	ids = calloc(hash->count, sizeof(*ids));
+	if (!ids) {
+		fprintf(stderr,
+			"Failed to allocate memory for Id array.\n");
+		return NULL;
+	}
+
+	for (i = 0; i < size; i++) {
+		item = hash->hash[i];
+		while (item) {
+			ids[count++] = item->id;
+			item = item->next;
+		}
+	}
+
+	qsort(ids, hash->count, sizeof(*ids), compare_ids);
+
+	return ids;
+}
diff --git a/src/libkshark.h b/src/libkshark.h
index 7d8b53b0..cc20077f 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -75,6 +75,52 @@ struct kshark_entry {
 	int64_t		ts;
 };
 
+/** Size of the hash table of PIDs in terms of bits being used by the key. */
+#define KS_TASK_HASH_NBITS	16
+
+/** Size of the hash table of Ids in terms of bits being used by the key. */
+#define KS_FILTER_HASH_NBITS	8
+
+/** A bucket for the hash table of integer Id numbers (kshark_hash_id). */
+struct kshark_hash_id_item {
+	/** Pointer to the Id in this bucket. */
+	struct kshark_hash_id_item	*next;
+
+	/** The Id value. */
+	int				id;
+};
+
+/**
+ * Hash table of integer Id numbers. To be used for fast filter of trace
+ * entries.
+ */
+struct kshark_hash_id {
+	/** Array of buckets. */
+	struct kshark_hash_id_item	**hash;
+
+	/** The number of Ids in the table. */
+	size_t	count;
+
+	/**
+	 * The number of bits used by the hashing function.
+	 * Note that the number of buckets in the table if given by
+	 * 1 << n_bits.
+	 */
+	size_t	n_bits;
+};
+
+bool kshark_hash_id_find(struct kshark_hash_id *hash, int id);
+
+int kshark_hash_id_add(struct kshark_hash_id *hash, int id);
+
+void kshark_hash_id_clear(struct kshark_hash_id *hash);
+
+struct kshark_hash_id *kshark_hash_id_alloc(size_t n_bits);
+
+void kshark_hash_id_free(struct kshark_hash_id *hash);
+
+int *kshark_hash_ids(struct kshark_hash_id *hash);
+
 /** Size of the task's hash table. */
 #define KS_TASK_HASH_SHIFT 16
 #define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 04/20] kernel-shark: Introduce Data streams
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (2 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 03/20] kernel-shark: Introduce libkshark-hash Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 05/20] kernel-shark: Rename static methods in libkshark Yordan Karadzhov (VMware)
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

With the help of Data stream, KernelShark will be able to load and
merge multiple trace files (streams). Each stream can have different
plugins or filters, registered for it, which means that the raw trace
data of the streams can have different formats, and will allow for a
great degree of customization of the provided data visualization. In
this patch we only provide the basic definitions. The actual integration
of the Data streams into the C API of KernelShark will happen in the
following patches.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark.h | 199 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 199 insertions(+)

diff --git a/src/libkshark.h b/src/libkshark.h
index cc20077f..9f7b6b1f 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -121,6 +121,205 @@ void kshark_hash_id_free(struct kshark_hash_id *hash);
 
 int *kshark_hash_ids(struct kshark_hash_id *hash);
 
+struct kshark_data_stream;
+
+/** A function type to be used by the method interface of the data stream. */
+typedef char *(*stream_get_str_func) (struct kshark_data_stream *,
+				      const struct kshark_entry *);
+
+/** A function type to be used by the method interface of the data stream. */
+typedef const int (*stream_get_int_func) (struct kshark_data_stream *,
+					  const struct kshark_entry *);
+
+/** A function type to be used by the method interface of the data stream. */
+typedef int (*stream_find_id_func) (struct kshark_data_stream *,
+				    const char *);
+
+/** A function type to be used by the method interface of the data stream. */
+typedef int *(*stream_get_ids_func) (struct kshark_data_stream *);
+
+/** A function type to be used by the method interface of the data stream. */
+typedef int (*stream_get_names_func) (struct kshark_data_stream *,
+				      const struct kshark_entry *,
+				      char ***);
+
+/** Event field format identifier. */
+typedef enum kshark_event_field_format {
+	/** A field of unknown type. */
+	KS_INVALID_FIELD,
+
+	/** Integer number */
+	KS_INTEGER_FIELD,
+
+	/** Floating-point number */
+	KS_FLOAT_FIELD
+} kshark_event_field_format;
+
+/** A function type to be used by the method interface of the data stream. */
+typedef kshark_event_field_format
+(*stream_event_field_type) (struct kshark_data_stream *,
+			    const struct kshark_entry *,
+			    const char *);
+
+/** A function type to be used by the method interface of the data stream. */
+typedef const int (*stream_read_event_field) (struct kshark_data_stream *,
+					      const struct kshark_entry *,
+					      const char *,
+					      int64_t *);
+
+/** A function type to be used by the method interface of the data stream. */
+typedef const int (*stream_read_record_field) (struct kshark_data_stream *,
+					       void *,
+					       const char *,
+					       int64_t *);
+
+struct kshark_context;
+
+/** A function type to be used by the method interface of the data stream. */
+typedef ssize_t (*load_entries_func) (struct kshark_data_stream *,
+				      struct kshark_context *,
+				      struct kshark_entry ***);
+
+/** A function type to be used by the method interface of the data stream. */
+typedef ssize_t (*load_matrix_func) (struct kshark_data_stream *,
+				     struct kshark_context *,
+				     int16_t **event_array,
+				     int16_t **cpu_array,
+				     int32_t **pid_array,
+				     int64_t **offset_array,
+				     int64_t **ts_array);
+
+/** Data interface identifier. */
+typedef enum kshark_data_interface_id {
+	/** An interface with unknown type. */
+	KS_INVALID_INTERFACE,
+
+	/** Generic interface suitable for Ftrace data. */
+	KS_GENERIC_DATA_INTERFACE,
+} kshark_data_interface_id;
+
+/**
+ * Structure representing the interface of methods used to operate over
+ * the data from a given stream.
+ */
+struct kshark_generic_stream_interface {
+	/** Interface version identifier. */
+	kshark_data_interface_id	type; /* MUST BE FIRST ENTRY. */
+
+	/** Method used to retrieve the Process Id of the entry. */
+	stream_get_int_func	get_pid;
+
+	/** Method used to retrieve the Event Id of the entry. */
+	stream_get_int_func	get_event_id;
+
+	/** Method used to retrieve the Event name of the entry. */
+	stream_get_str_func	get_event_name;
+
+	/** Method used to retrieve the Task name of the entry. */
+	stream_get_str_func	get_task;
+
+	/** Method used to retrieve the Info string of the entry. */
+	stream_get_str_func	get_info;
+
+	/**
+	 * Method used to retrieve an unspecified auxiliary info of the trace
+	 * record.
+	 */
+	stream_get_str_func	aux_info;
+
+	/** Method used to retrieve Id of the Event from its name. */
+	stream_find_id_func	find_event_id;
+
+	/** Method used to retrieve the array of Ids of all Events. */
+	stream_get_ids_func	get_all_event_ids;
+
+	/** Method used to dump the entry's content to string. */
+	stream_get_str_func	dump_entry;
+
+	/**
+	 * Method used to retrieve the array of all field names of a given
+	 * event.
+	 */
+	stream_get_names_func	get_all_event_field_names;
+
+	/** Method used to access the type of an event's data field. */
+	stream_event_field_type		get_event_field_type;
+
+	/** Method used to access the value of an event's data field. */
+	stream_read_event_field		read_event_field_int64;
+
+	/** Method used to access the value of an event's data field. */
+	stream_read_record_field	read_record_field_int64;
+
+	/** Method used to load the data in the form of entries. */
+	load_entries_func	load_entries;
+
+	/** Method used to load the data in matrix form. */
+	load_matrix_func	load_matrix;
+
+	/** Generic data handle. */
+	void			*handle;
+};
+
+/** The limit in size of the data format identifier string. */
+#define KS_DATA_FORMAT_SIZE	15
+
+/** Structure representing a stream of trace data. */
+struct kshark_data_stream {
+	/** Data stream identifier. */
+	uint16_t		stream_id;
+
+	/** The number of CPUs presented in this data stream. */
+	int			n_cpus;
+
+	/**
+	 * The number of distinct event types presented in this data stream.
+	 */
+	int			n_events;
+
+	/** The Process Id of the Idle task. */
+	int			idle_pid;
+
+	/** Trace data file pathname. */
+	char			*file;
+
+	/** Stream name. */
+	char			*name;
+
+	/** Hash table of task PIDs. */
+	struct kshark_hash_id	*tasks;
+
+	/** A mutex, used to protect the access to the input file. */
+	pthread_mutex_t		input_mutex;
+
+	/** Hash of tasks to filter on. */
+	struct kshark_hash_id	*show_task_filter;
+
+	/** Hash of tasks to not display. */
+	struct kshark_hash_id	*hide_task_filter;
+
+	/** Hash of events to filter on. */
+	struct kshark_hash_id	*show_event_filter;
+
+	/** Hash of events to not display. */
+	struct kshark_hash_id	*hide_event_filter;
+
+	/** Hash of CPUs to filter on. */
+	struct kshark_hash_id	*show_cpu_filter;
+
+	/** Hash of CPUs to not display. */
+	struct kshark_hash_id	*hide_cpu_filter;
+
+	/** The type of the data. */
+	char			data_format[KS_DATA_FORMAT_SIZE];
+
+	/**
+	 * The interface of methods used to operate over the data from a given
+	 * stream.
+	 */
+	void				*interface;
+};
+
 /** Size of the task's hash table. */
 #define KS_TASK_HASH_SHIFT 16
 #define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 05/20] kernel-shark: Rename static methods in libkshark
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (3 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 04/20] kernel-shark: Introduce Data streams Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams Yordan Karadzhov (VMware)
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

"kshark" prefix is needed only for the public methods of the API.
We are changing the names of those static methods because we need to
use those names when integrating the data streams into the API.i

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/libkshark.c b/src/libkshark.c
index 52aacd30..f3323ae3 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -1097,8 +1097,8 @@ size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx,
 	return -ENOMEM;
 }
 
-static const char *kshark_get_latency(struct tep_handle *pe,
-				      struct tep_record *record)
+static const char *get_latency(struct tep_handle *pe,
+			       struct tep_record *record)
 {
 	if (!record)
 		return NULL;
@@ -1108,7 +1108,7 @@ static const char *kshark_get_latency(struct tep_handle *pe,
 	return seq.buffer;
 }
 
-static const char *kshark_get_info(struct tep_handle *pe,
+static const char *get_info(struct tep_handle *pe,
 				   struct tep_record *record,
 				   struct tep_event *event)
 {
@@ -1233,7 +1233,7 @@ const char *kshark_get_latency_easy(struct kshark_entry *entry)
 	pthread_mutex_lock(&kshark_ctx->input_mutex);
 
 	data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL);
-	lat = kshark_get_latency(kshark_ctx->pevent, data);
+	lat = get_latency(kshark_ctx->pevent, data);
 	free_record(data);
 
 	pthread_mutex_unlock(&kshark_ctx->input_mutex);
@@ -1376,7 +1376,7 @@ const char *kshark_get_info_easy(struct kshark_entry *entry)
 	event_id = tep_data_type(kshark_ctx->pevent, data);
 	event = tep_find_event(kshark_ctx->pevent, event_id);
 	if (event)
-		info = kshark_get_info(kshark_ctx->pevent, data, event);
+		info = get_info(kshark_ctx->pevent, data, event);
 
 	free_record(data);
 
@@ -1471,7 +1471,7 @@ char* kshark_dump_entry(const struct kshark_entry *entry)
 		event = tep_find_event(kshark_ctx->pevent, entry->event_id);
 
 		event_name = event? event->name : "[UNKNOWN EVENT]";
-		lat = kshark_get_latency(kshark_ctx->pevent, data);
+		lat = get_latency(kshark_ctx->pevent, data);
 
 		size = asprintf(&temp_str, "%" PRIu64 "; %s-%i; CPU %i; %s;",
 				entry->ts,
@@ -1480,7 +1480,7 @@ char* kshark_dump_entry(const struct kshark_entry *entry)
 				entry->cpu,
 				lat);
 
-		info = kshark_get_info(kshark_ctx->pevent, data, event);
+		info = get_info(kshark_ctx->pevent, data, event);
 
 		if (size > 0) {
 			size = asprintf(&entry_str, "%s %s; %s; 0x%x",
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (4 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 05/20] kernel-shark: Rename static methods in libkshark Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 22:13   ` Steven Rostedt
  2020-11-18 22:17   ` Steven Rostedt
  2020-11-18 14:49 ` [PATCH v4 07/20] kernel-shark: Housekeeping before implementing stream interface Yordan Karadzhov (VMware)
                   ` (13 subsequent siblings)
  19 siblings, 2 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Here we introduce the basic mechanisms for using data streams.
For the moment these are just stand alone definitions and the
integration with the API is yet to be introduced in the following
patches.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark.c | 715 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/libkshark.h | 104 +++++++
 2 files changed, 819 insertions(+)

diff --git a/src/libkshark.c b/src/libkshark.c
index f3323ae3..39081993 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -166,6 +166,257 @@ bool kshark_open(struct kshark_context *kshark_ctx, const char *file)
 	return true;
 }
 
+static void kshark_stream_free(struct kshark_data_stream *stream)
+{
+	if (!stream)
+		return;
+
+	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->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->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;
+	}
+
+	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
+ * struct inside 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-63) used when converting indexes to pointers and vise-versa.
+ */
+#define INVALID_STREAM_MASK	(~((uint64_t) 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) {
+		size_t new_size = 2 * kshark_ctx->stream_info.array_size;
+		struct kshark_data_stream **streams_tmp;
+
+		streams_tmp = realloc(kshark_ctx->stream,
+				      new_size * sizeof(*kshark_ctx->stream));
+		if (!streams_tmp)
+			return -ENOMEM;
+
+		kshark_ctx->stream = streams_tmp;
+		kshark_ctx->stream_info.array_size = new_size;
+	}
+
+	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 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;
+}
+
+/**
+ * @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;
+}
 /**
  * @brief Close the trace data file and free the trace data handle.
  *
@@ -252,6 +503,470 @@ void kshark_free(struct kshark_context *kshark_ctx)
 	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 Print the entry. */
+void kshark_print_entry(const struct kshark_entry *entry)
+{
+	char *entry_str = kshark_dump_entry(entry);
+
+	if (!entry_str)
+		puts("null");
+
+	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;
+}
+
 static struct kshark_task_list *
 kshark_find_task(struct kshark_context *kshark_ctx, uint32_t key, int pid)
 {
diff --git a/src/libkshark.h b/src/libkshark.h
index 9f7b6b1f..77fa4c54 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -121,6 +121,19 @@ void kshark_hash_id_free(struct kshark_hash_id *hash);
 
 int *kshark_hash_ids(struct kshark_hash_id *hash);
 
+/* Quiet warnings over documenting simple structures */
+//! @cond Doxygen_Suppress
+
+static const char top_name[] = { 0x1b, 0x00 }; // Non printable character
+
+//! @endcond
+
+/**
+ * Non printable character used for the name in the case when the name has to
+ * be ignored.
+ */
+#define KS_UNNAMED	(char *) &top_name
+
 struct kshark_data_stream;
 
 /** A function type to be used by the method interface of the data stream. */
@@ -264,6 +277,9 @@ struct kshark_generic_stream_interface {
 /** The limit in size of the data format identifier string. */
 #define KS_DATA_FORMAT_SIZE	15
 
+/** Data format identifier string indicating invalid data. */
+#define KS_INVALID_DATA		"invalid data"
+
 /** Structure representing a stream of trace data. */
 struct kshark_data_stream {
 	/** Data stream identifier. */
@@ -320,6 +336,12 @@ struct kshark_data_stream {
 	void				*interface;
 };
 
+static inline char *kshark_set_data_format(char *dest_format,
+					   const char *src_format)
+{
+	return strncpy(dest_format, src_format, KS_DATA_FORMAT_SIZE - 1);
+}
+
 /** Size of the task's hash table. */
 #define KS_TASK_HASH_SHIFT 16
 #define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT)
@@ -333,8 +355,32 @@ struct kshark_task_list {
 	int			 pid;
 };
 
+/**
+ * Structure representing the parameters of the stream descriptor array owned
+ * by the kshark session.
+ */
+struct kshark_stream_array_descriptor {
+	/** The identifier of the Data stream added. */
+	int		max_stream_id;
+
+	/** The the next free Data stream identifier (index). */
+	int		next_free_stream_id;
+
+	/** The capacity of the array of stream objects (pointers). */
+	int		array_size;
+};
+
 /** Structure representing a kshark session. */
 struct kshark_context {
+	/** Array of data stream descriptors. */
+	struct kshark_data_stream	**stream;
+
+	/** The number of data streams. */
+	int				n_streams;
+
+	/** Parameters of the stream descriptor array. */
+	struct kshark_stream_array_descriptor	stream_info;
+
 	/** Input handle for the trace data file. */
 	struct tracecmd_input	*handle;
 
@@ -392,6 +438,18 @@ bool kshark_instance(struct kshark_context **kshark_ctx);
 
 bool kshark_open(struct kshark_context *kshark_ctx, const char *file);
 
+int kshark_add_stream(struct kshark_context *kshark_ctx);
+
+int kshark_remove_stream(struct kshark_context *kshark_ctx, int sd);
+
+struct kshark_data_stream *
+kshark_get_data_stream(struct kshark_context *kshark_ctx, int sd);
+
+struct kshark_data_stream *
+kshark_get_stream_from_entry(const struct kshark_entry *entry);
+
+int *kshark_all_streams(struct kshark_context *kshark_ctx);
+
 ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx,
 				 struct kshark_entry ***data_rows);
 
@@ -411,6 +469,10 @@ void kshark_close(struct kshark_context *kshark_ctx);
 
 void kshark_free(struct kshark_context *kshark_ctx);
 
+char *kshark_comm_from_pid(int sd, int pid);
+
+char *kshark_event_from_id(int sd, int event_id);
+
 int kshark_get_pid_easy(struct kshark_entry *entry);
 
 const char *kshark_get_task_easy(struct kshark_entry *entry);
@@ -427,6 +489,48 @@ void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec);
 
 char* kshark_dump_entry(const struct kshark_entry *entry);
 
+void kshark_print_entry(const struct kshark_entry *entry);
+
+int kshark_get_pid(const struct kshark_entry *entry);
+
+int kshark_get_event_id(const struct kshark_entry *entry);
+
+int *kshark_get_all_event_ids(struct kshark_data_stream *stream);
+
+int kshark_find_event_id(struct kshark_data_stream *stream,
+			 const char *event_name);
+
+char *kshark_get_event_name(const struct kshark_entry *entry);
+
+char *kshark_get_task(const struct kshark_entry *entry);
+
+char *kshark_get_info(const struct kshark_entry *entry);
+
+char *kshark_get_aux_info(const struct kshark_entry *entry);
+
+kshark_event_field_format
+kshark_get_event_field_type(const struct kshark_entry *entry,
+			    const char *field);
+
+int kshark_get_all_event_field_names(const struct kshark_entry *entry,
+				     char ***field);
+
+int kshark_read_record_field_int(struct kshark_data_stream *stream, void *rec,
+				 const char *field, int64_t *val);
+
+int kshark_read_event_field_int(const struct kshark_entry *entry,
+				const char* field, int64_t *val);
+
+ssize_t kshark_load_entries(struct kshark_context *kshark_ctx, int sd,
+			    struct kshark_entry ***data_rows);
+
+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);
+
 /**
  * Custom entry info function type. To be user for dumping info for custom
  * KernelShark entryes.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 07/20] kernel-shark: Housekeeping before implementing stream interface
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (5 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 08/20] kernel-shark: Add stream interface for trace-cmd data Yordan Karadzhov (VMware)
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

kshark_load data_matrix() is a method that is used only in trace-cruncher
prototype. The version of this function that is currently part of the API
is anyway not compatible with the latest version of trace-cruncher because
of the mismatch of the argument types, so we can directly remove the
existing implementation. An equivalent functionality will be provided as
part of the implantation of the Data stream interface in the following
patch. Here we just make kshark_data_matrix_alloc() and
unset_event_filter_flag() public, because the methods will be used in the
stream interface implementation.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark.c | 121 ++++++++----------------------------------------
 src/libkshark.h |  28 ++++++++---
 2 files changed, 40 insertions(+), 109 deletions(-)

diff --git a/src/libkshark.c b/src/libkshark.c
index 39081993..422fb7d4 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -1189,21 +1189,6 @@ bool kshark_filter_is_set(struct kshark_context *kshark_ctx)
 -              kshark_this_filter_is_set(kshark_ctx->hide_event_filter);
 }
 
-static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx,
-					   struct kshark_entry *e)
-{
-	/*
-	 * All entries, filtered-out by the event filters, will be treated
-	 * differently, when visualized. Because of this, ignore the value
-	 * of the GRAPH_VIEW flag provided by the user via
-	 * kshark_ctx->filter_mask. The value of the EVENT_VIEW flag in
-	 * kshark_ctx->filter_mask will be used instead.
-	 */
-	int event_mask = kshark_ctx->filter_mask & ~KS_GRAPH_VIEW_FILTER_MASK;
-
-	e->visible &= ~event_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;
@@ -1679,11 +1664,25 @@ static inline void free_ptr(void *ptr)
 		free(*(void **)ptr);
 }
 
-static bool data_matrix_alloc(size_t n_rows, uint64_t **offset_array,
-					     uint16_t **cpu_array,
-					     uint64_t **ts_array,
-					     uint16_t **pid_array,
-					     int **event_array)
+/**
+ * @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));
@@ -1730,88 +1729,6 @@ static bool data_matrix_alloc(size_t n_rows, uint64_t **offset_array,
 	return false;
 }
 
-/**
- * @brief Load the content of the trace data file into a table / matrix made
- *	  of columns / arrays of data. The user is responsible for freeing the
- *	  elements of the outputted array
- *
- * @param kshark_ctx: Input location for the session context pointer.
- * @param offset_array: Output location for the array of record offsets.
- * @param cpu_array: Output location for the array of CPU Ids.
- * @param ts_array: Output location for the array of timestamps.
- * @param pid_array: Output location for the array of Process Ids.
- * @param event_array: Output location for the array of Event Ids.
- *
- * @returns The size of the outputted arrays in the case of success, or a
- *	    negative error code on failure.
- */
-size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx,
-			       uint64_t **offset_array,
-			       uint16_t **cpu_array,
-			       uint64_t **ts_array,
-			       uint16_t **pid_array,
-			       int **event_array)
-{
-	enum rec_type type = REC_ENTRY;
-	struct rec_list **rec_list;
-	ssize_t count, total = 0;
-	bool status;
-	int n_cpus;
-
-	total = get_records(kshark_ctx, &rec_list, type);
-	if (total < 0)
-		goto fail;
-
-	n_cpus = tracecmd_cpus(kshark_ctx->handle);
-
-	status = data_matrix_alloc(total, offset_array,
-					  cpu_array,
-					  ts_array,
-					  pid_array,
-					  event_array);
-	if (!status)
-		goto fail_free;
-
-	for (count = 0; count < total; count++) {
-		int next_cpu;
-
-		next_cpu = pick_next_cpu(rec_list, n_cpus, type);
-		if (next_cpu >= 0) {
-			struct rec_list *rec = rec_list[next_cpu];
-			struct kshark_entry *e = &rec->entry;
-
-			if (offset_array)
-				(*offset_array)[count] = e->offset;
-
-			if (cpu_array)
-				(*cpu_array)[count] = e->cpu;
-
-			if (ts_array)
-				(*ts_array)[count] = e->ts;
-
-			if (pid_array)
-				(*pid_array)[count] = e->pid;
-
-			if (event_array)
-				(*event_array)[count] = e->event_id;
-
-			rec_list[next_cpu] = rec_list[next_cpu]->next;
-			free(rec);
-		}
-	}
-
-	/* There should be no entries left in rec_list. */
-	free_rec_list(rec_list, n_cpus, type);
-	return total;
-
- fail_free:
-	free_rec_list(rec_list, n_cpus, type);
-
- fail:
-	fprintf(stderr, "Failed to allocate memory during data loading.\n");
-	return -ENOMEM;
-}
-
 static const char *get_latency(struct tep_handle *pe,
 			       struct tep_record *record)
 {
diff --git a/src/libkshark.h b/src/libkshark.h
index 77fa4c54..36d60001 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -456,13 +456,6 @@ ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx,
 ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx,
 				 struct tep_record ***data_rows);
 
-size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx,
-			       uint64_t **offset_array,
-			       uint16_t **cpu_array,
-			       uint64_t **ts_array,
-			       uint16_t **pid_array,
-			       int **event_array);
-
 ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int **pids);
 
 void kshark_close(struct kshark_context *kshark_ctx);
@@ -617,6 +610,21 @@ bool kshark_this_filter_is_set(struct tracecmd_filter_id *filter);
 
 bool kshark_filter_is_set(struct kshark_context *kshark_ctx);
 
+static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx,
+					   struct kshark_entry *e)
+{
+	/*
+	 * All entries, filtered-out by the event filters, will be treated
+	 * differently, when visualized. Because of this, ignore the value
+	 * of the GRAPH_VIEW flag provided by the user via
+	 * stream->filter_mask. The value of the EVENT_VIEW flag in
+	 * stream->filter_mask will be used instead.
+	 */
+	int event_mask = kshark_ctx->filter_mask & ~KS_GRAPH_VIEW_FILTER_MASK;
+
+	e->visible &= ~event_mask;
+}
+
 void kshark_filter_entries(struct kshark_context *kshark_ctx,
 			   struct kshark_entry **data,
 			   size_t n_entries);
@@ -995,6 +1003,12 @@ struct kshark_config_doc *kshark_open_config_file(const char *file_name,
 
 struct kshark_config_doc *kshark_json_to_conf(struct json_object *jobj);
 
+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);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 08/20] kernel-shark: Add stream interface for trace-cmd data
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (6 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 07/20] kernel-shark: Housekeeping before implementing stream interface Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-19  1:11   ` Steven Rostedt
  2020-11-18 14:49 ` [PATCH v4 09/20] kernel-shark: Start introducing KernelShark 2.0 Yordan Karadzhov (VMware)
                   ` (11 subsequent siblings)
  19 siblings, 1 reply; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Here we provide an implementation of the Data stream interface that will
process the trace-cmd (Ftrace) data. This can be considered the most
essential change in the transformation of the C API towards version 2.
However, for the moment we only have stand alone definitions that are not
made functional yet. The actual integration with the API will be
introduced in the following patches.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt      |    1 +
 src/libkshark-tepdata.c | 1516 +++++++++++++++++++++++++++++++++++++++
 src/libkshark-tepdata.h |   62 ++
 3 files changed, 1579 insertions(+)
 create mode 100644 src/libkshark-tepdata.c
 create mode 100644 src/libkshark-tepdata.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0b84851c..4d4f37b1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,7 @@ add_library(kshark SHARED libkshark.c
                           libkshark-hash.c
                           libkshark-model.c
                           libkshark-plugin.c
+                          libkshark-tepdata.c
                           libkshark-configio.c
                           libkshark-collection.c)
 
diff --git a/src/libkshark-tepdata.c b/src/libkshark-tepdata.c
new file mode 100644
index 00000000..e697296d
--- /dev/null
+++ b/src/libkshark-tepdata.c
@@ -0,0 +1,1516 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2019 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    libkshark-tepdata.c
+ *  @brief   Interface for processing of FTRACE (trace-cmd) data.
+ */
+
+
+// C
+#ifndef _GNU_SOURCE
+/** Use GNU C Library. */
+#define _GNU_SOURCE
+#endif // _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// trace-cmd
+#include "trace-cmd/trace-cmd.h"
+#include "tracefs/tracefs.h"
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plugin.h"
+#include "libkshark-tepdata.h"
+
+static __thread struct trace_seq seq;
+
+static bool init_thread_seq(void)
+{
+	if (!seq.buffer)
+		trace_seq_init(&seq);
+
+	return seq.buffer != NULL;
+}
+
+/** Structure for handling all unique attributes of the FTRACE data. */
+struct tepdata_handle {
+	/** Page event used to parse the page. */
+	struct tep_handle	*tep; /* MUST BE FIRST ENTRY */
+
+	/** Input handle for the trace data file. */
+	struct tracecmd_input	*input;
+
+	/**
+	 * Filter allowing sophisticated filtering based on the content of
+	 * the event.
+	 */
+	struct tep_event_filter	*advanced_event_filter;
+
+	/** The unique Id of the sched_switch_event event. */
+	int sched_switch_event_id;
+
+	/** Pointer to the sched_switch_next_field format descriptor. */
+	struct tep_format_field	*sched_switch_next_field;
+
+	/** Pointer to the sched_switch_comm_field format descriptor. */
+	struct tep_format_field	*sched_switch_comm_field;
+};
+
+static inline int get_tepdate_handle(struct kshark_data_stream *stream,
+				     struct tepdata_handle **handle)
+{
+	struct kshark_generic_stream_interface *interface;
+
+	interface = stream->interface;
+	if (!interface)
+		return -EFAULT;
+
+	*handle = interface->handle;
+
+	return 0;
+}
+
+/** Get the Page event object used to parse the page. */
+struct tep_handle *kshark_get_tep(struct kshark_data_stream *stream)
+{
+	struct tepdata_handle *tep_handle;
+	int ret;
+
+	ret = get_tepdate_handle(stream, &tep_handle);
+	if (ret < 0)
+		return NULL;
+
+	return tep_handle->tep;
+}
+
+/** Get the input handle for the trace data file */
+struct tracecmd_input *kshark_get_tep_input(struct kshark_data_stream *stream)
+{
+	struct tepdata_handle *tep_handle;
+	int ret;
+
+	ret = get_tepdate_handle(stream, &tep_handle);
+	if (ret < 0)
+		return NULL;
+
+	return tep_handle->input;
+}
+
+static inline struct tep_event_filter *
+get_adv_filter(struct kshark_data_stream *stream)
+{
+	struct tepdata_handle *tep_handle;
+	int ret;
+
+	ret = get_tepdate_handle(stream, &tep_handle);
+	if (ret < 0)
+		return NULL;
+
+	return tep_handle->advanced_event_filter;
+}
+
+static int get_sched_switch_id(struct kshark_data_stream *stream)
+{
+	struct tepdata_handle *tep_handle;
+	int ret;
+
+	ret = get_tepdate_handle(stream, &tep_handle);
+	if (ret < 0)
+		return ret;
+
+	return tep_handle->sched_switch_event_id;
+}
+
+static struct tep_format_field *get_sched_next(struct kshark_data_stream *stream)
+{
+	struct tepdata_handle *tep_handle;
+	int ret;
+
+	ret = get_tepdate_handle(stream, &tep_handle);
+	if (ret < 0)
+		return NULL;
+
+	return tep_handle->sched_switch_next_field;
+}
+
+static struct tep_format_field *get_sched_comm(struct kshark_data_stream *stream)
+{
+	struct tepdata_handle *tep_handle;
+	int ret;
+
+	ret = get_tepdate_handle(stream, &tep_handle);
+	if (ret < 0)
+		return NULL;
+
+	return tep_handle->sched_switch_comm_field;
+}
+
+static void set_entry_values(struct kshark_data_stream *stream,
+			     struct tep_record *record,
+			     struct kshark_entry *entry)
+{
+	struct tep_handle *tep = kshark_get_tep(stream);
+
+	if (!tep)
+		return;
+
+	/* 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 = tep_data_type(tep, record);
+
+	/*
+	 * Is visible mask. This default value means that the entry
+	 * is visible everywhere.
+	 */
+	entry->visible = 0xFF;
+
+	/* Process Id of the record */
+	entry->pid = tep_data_pid(tep, record);
+}
+
+/** Prior time offset of the "missed_events" entry. */
+#define ME_ENTRY_TIME_SHIFT	10
+
+static void missed_events_action(struct kshark_data_stream *stream,
+				 struct tep_record *record,
+				 struct kshark_entry *entry)
+{
+	/*
+	 * Use the offset field of the entry to store the number of missed
+	 * events.
+	 */
+	entry->offset = record->missed_events;
+
+	entry->cpu = record->cpu;
+
+	/*
+	 * Position the "missed_events" entry a bit before (in time)
+	 * the original record.
+	 */
+	entry->ts = record->ts - ME_ENTRY_TIME_SHIFT;
+
+	/* All custom entries must have negative event Identifiers. */
+	entry->event_id = KS_EVENT_OVERFLOW;
+
+	entry->visible = 0xFF;
+
+	entry->pid = tep_data_pid(kshark_get_tep(stream), record);
+}
+
+/**
+ * rec_list is used to pass the data to the load functions.
+ * The rec_list will contain the list of entries from the source,
+ * and will be a link list of per CPU entries.
+ */
+struct rec_list {
+	union {
+		/* Used by kshark_load_data_records */
+		struct {
+			/** next pointer, matches entry->next */
+			struct rec_list		*next;
+			/** pointer to the raw record data */
+			struct tep_record	*rec;
+		};
+		/** entry - Used for kshark_load_data_entries() */
+		struct kshark_entry		entry;
+	};
+};
+
+static int get_next_pid(struct kshark_data_stream *stream,
+			struct tep_record *record)
+{
+	unsigned long long val;
+	int ret;
+
+	ret = tep_read_number_field(get_sched_next(stream),
+				    record->data, &val);
+
+	return ret ? : val;
+}
+
+static void register_command(struct kshark_data_stream *stream,
+			     struct tep_record *record,
+			     int pid)
+{
+	struct tep_format_field *comm_field = get_sched_comm(stream);
+	const char *comm = record->data + comm_field->offset;
+	/*
+	 * TODO: The retrieve of the name of the command above needs to be
+	 * implemented as a wrapper function in libtracevent.
+	 */
+
+	if (!tep_is_pid_registered(kshark_get_tep(stream), pid))
+			tep_register_comm(kshark_get_tep(stream), comm, pid);
+}
+
+/**
+ * rec_type defines what type of rec_list is being used.
+ */
+enum rec_type {
+	REC_RECORD,
+	REC_ENTRY,
+};
+
+static void free_rec_list(struct rec_list **rec_list, int n_cpus,
+			  enum rec_type type)
+{
+	struct rec_list *temp_rec;
+	int cpu;
+
+	for (cpu = 0; cpu < n_cpus; ++cpu) {
+		while (rec_list[cpu]) {
+			temp_rec = rec_list[cpu];
+			rec_list[cpu] = temp_rec->next;
+			if (type == REC_RECORD)
+				free_record(temp_rec->rec);
+			free(temp_rec);
+		}
+	}
+	free(rec_list);
+}
+
+static ssize_t get_records(struct kshark_context *kshark_ctx,
+			   struct kshark_data_stream *stream,
+			   struct rec_list ***rec_list,
+			   enum rec_type type)
+{
+	struct tep_event_filter *adv_filter = NULL;
+	struct tracecmd_input *input;
+	struct rec_list **temp_next;
+	struct rec_list **cpu_list;
+	struct rec_list *temp_rec;
+	struct tep_record *rec;
+	ssize_t count, total = 0;
+	int pid, next_pid, cpu;
+
+	input = kshark_get_tep_input(stream);
+	if (!input)
+		return -EFAULT;
+
+	cpu_list = calloc(stream->n_cpus, sizeof(*cpu_list));
+	if (!cpu_list)
+		return -ENOMEM;
+
+	if (type == REC_ENTRY)
+		adv_filter = get_adv_filter(stream);
+
+	for (cpu = 0; cpu < stream->n_cpus; ++cpu) {
+		count = 0;
+		cpu_list[cpu] = NULL;
+		temp_next = &cpu_list[cpu];
+
+		rec = tracecmd_read_cpu_first(kshark_get_tep_input(stream), cpu);
+		while (rec) {
+			*temp_next = temp_rec = calloc(1, sizeof(*temp_rec));
+			if (!temp_rec)
+				goto fail;
+
+			temp_rec->next = NULL;
+
+			switch (type) {
+			case REC_RECORD:
+				temp_rec->rec = rec;
+				pid = tep_data_pid(kshark_get_tep(stream), rec);
+				break;
+			case REC_ENTRY: {
+				struct kshark_entry *entry;
+
+				if (rec->missed_events) {
+					/*
+					 * Insert a custom "missed_events" entry just
+					 * befor this record.
+					 */
+					entry = &temp_rec->entry;
+					missed_events_action(stream, rec, entry);
+
+					entry->stream_id = stream->stream_id;
+
+					temp_next = &temp_rec->next;
+					++count;
+
+					/* Now allocate a new rec_list node and comtinue. */
+					*temp_next = temp_rec = calloc(1, sizeof(*temp_rec));
+					if (!temp_rec)
+						goto fail;
+				}
+
+				entry = &temp_rec->entry;
+				set_entry_values(stream, rec, entry);
+
+				if (entry->event_id == get_sched_switch_id(stream)) {
+					next_pid = get_next_pid(stream, rec);
+					if (next_pid >= 0)
+						register_command(stream, rec, next_pid);
+				}
+
+				entry->stream_id = stream->stream_id;
+
+				pid = entry->pid;
+
+				/* Apply advanced event filtering. */
+				if (adv_filter && adv_filter->filters &&
+				    tep_filter_match(adv_filter, rec) != FILTER_MATCH)
+					unset_event_filter_flag(kshark_ctx, entry);
+
+				free_record(rec);
+				break;
+			} /* REC_ENTRY */
+			}
+
+			kshark_hash_id_add(stream->tasks, pid);
+
+			temp_next = &temp_rec->next;
+
+			++count;
+			rec = tracecmd_read_data(kshark_get_tep_input(stream), cpu);
+		}
+
+		total += count;
+	}
+
+	*rec_list = cpu_list;
+	return total;
+
+ fail:
+	free_rec_list(cpu_list, stream->n_cpus, type);
+	return -ENOMEM;
+}
+
+static int pick_next_cpu(struct rec_list **rec_list, int n_cpus,
+			 enum rec_type type)
+{
+	uint64_t ts = 0;
+	uint64_t rec_ts;
+	int next_cpu = -1;
+	int cpu;
+
+	for (cpu = 0; cpu < n_cpus; ++cpu) {
+		if (!rec_list[cpu])
+			continue;
+
+		switch (type) {
+		case REC_RECORD:
+			rec_ts = rec_list[cpu]->rec->ts;
+			break;
+		case REC_ENTRY:
+			rec_ts = rec_list[cpu]->entry.ts;
+			break;
+		default:
+			return -1;
+		}
+		if (!ts || rec_ts < ts) {
+			ts = rec_ts;
+			next_cpu = cpu;
+		}
+	}
+
+	return next_cpu;
+}
+
+/**
+ * @brief Load the content of the trace data file asociated with a given
+ *	  Data stream into an array of kshark_entries. This function
+ *	  provides an abstraction of the entries from the raw data
+ *	  that is read, 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.
+ *	  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 stream: Input location for the FTRACE data stream pointer.
+ * @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 tepdata_load_entries(struct kshark_data_stream *stream,
+				struct kshark_context *kshark_ctx,
+				struct kshark_entry ***data_rows)
+{
+	enum rec_type type = REC_ENTRY;
+	struct kshark_entry **rows;
+	struct rec_list **rec_list;
+	ssize_t count, total = 0;
+
+	total = get_records(kshark_ctx, stream, &rec_list, type);
+	if (total < 0)
+		goto fail;
+
+	rows = calloc(total, sizeof(struct kshark_entry *));
+	if (!rows)
+		goto fail_free;
+
+	for (count = 0; count < total; count++) {
+		int next_cpu;
+
+		next_cpu = pick_next_cpu(rec_list, stream->n_cpus, type);
+
+		if (next_cpu >= 0) {
+			rows[count] = &rec_list[next_cpu]->entry;
+			rec_list[next_cpu] = rec_list[next_cpu]->next;
+		}
+	}
+
+	/* There should be no entries left in rec_list. */
+	free_rec_list(rec_list, stream->n_cpus, type);
+	*data_rows = rows;
+
+	return total;
+
+ fail_free:
+	free_rec_list(rec_list, stream->n_cpus, type);
+
+ fail:
+	fprintf(stderr, "Failed to allocate memory during data loading.\n");
+	return -ENOMEM;
+}
+
+static ssize_t tepdata_load_matrix(struct kshark_data_stream *stream,
+				   struct kshark_context *kshark_ctx,
+				   int16_t **event_array,
+				   int16_t **cpu_array,
+				   int32_t **pid_array,
+				   int64_t **offset_array,
+				   int64_t **ts_array)
+{
+	enum rec_type type = REC_ENTRY;
+	struct rec_list **rec_list;
+	ssize_t count, total = 0;
+	bool status;
+
+	total = get_records(kshark_ctx, stream, &rec_list, type);
+	if (total < 0)
+		goto fail;
+
+	status = kshark_data_matrix_alloc(total, event_array,
+						 cpu_array,
+						 pid_array,
+						 offset_array,
+						 ts_array);
+	if (!status)
+		goto fail_free;
+
+	for (count = 0; count < total; count++) {
+		int next_cpu;
+
+		next_cpu = pick_next_cpu(rec_list, stream->n_cpus, type);
+		if (next_cpu >= 0) {
+			struct rec_list *rec = rec_list[next_cpu];
+			struct kshark_entry *e = &rec->entry;
+
+			if (offset_array)
+				(*offset_array)[count] = e->offset;
+
+			if (cpu_array)
+				(*cpu_array)[count] = e->cpu;
+
+			if (ts_array)
+				(*ts_array)[count] = e->ts;
+
+			if (pid_array)
+				(*pid_array)[count] = e->pid;
+
+			if (event_array)
+				(*event_array)[count] = e->event_id;
+
+			rec_list[next_cpu] = rec_list[next_cpu]->next;
+			free(rec);
+		}
+	}
+
+	/* There should be no entries left in rec_list. */
+	free_rec_list(rec_list, stream->n_cpus, type);
+	return total;
+
+ fail_free:
+	free_rec_list(rec_list, stream->n_cpus, type);
+
+ 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
+ *	  tep_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 sd: Data stream identifier.
+ * @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_tep_records(struct kshark_context *kshark_ctx, int sd,
+				struct tep_record ***data_rows)
+{
+	struct kshark_data_stream *stream;
+	enum rec_type type = REC_RECORD;
+	struct rec_list **rec_list;
+	struct rec_list *temp_rec;
+	struct tep_record **rows;
+	struct tep_record *rec;
+	ssize_t count, total = 0;
+
+	if (*data_rows)
+		free(*data_rows);
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return -EBADF;
+
+	total = get_records(kshark_ctx, stream, &rec_list, type);
+	if (total < 0)
+		goto fail;
+
+	rows = calloc(total, sizeof(struct tep_record *));
+	if (!rows)
+		goto fail_free;
+
+	for (count = 0; count < total; count++) {
+		int next_cpu;
+
+		next_cpu = pick_next_cpu(rec_list, stream->n_cpus, type);
+
+		if (next_cpu >= 0) {
+			rec = rec_list[next_cpu]->rec;
+			rows[count] = rec;
+
+			temp_rec = rec_list[next_cpu];
+			rec_list[next_cpu] = rec_list[next_cpu]->next;
+			free(temp_rec);
+			/* The record is still referenced in rows */
+		}
+	}
+
+	/* There should be no records left in rec_list. */
+	free_rec_list(rec_list, stream->n_cpus, type);
+	*data_rows = rows;
+	return total;
+
+ fail_free:
+	free_rec_list(rec_list, stream->n_cpus, type);
+
+ fail:
+	fprintf(stderr, "Failed to allocate memory during data loading.\n");
+	return -ENOMEM;
+}
+
+static const int tepdata_get_event_id(struct kshark_data_stream *stream,
+				      const struct kshark_entry *entry)
+{
+	int event_id = KS_EMPTY_BIN;
+	struct tep_record *record;
+
+	if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) {
+		event_id = entry->event_id;
+	} else {
+		/*
+		 * The entry has been touched by a plugin callback function.
+		 * Because of this we do not trust the value of
+		 * "entry->event_id".
+		 *
+		 * Currently the data reading operations are not thread-safe.
+		 * Use a mutex to protect the access.
+		 */
+		pthread_mutex_lock(&stream->input_mutex);
+
+		record = tracecmd_read_at(kshark_get_tep_input(stream),
+					  entry->offset, NULL);
+
+		if (record)
+			event_id = tep_data_type(kshark_get_tep(stream), record);
+
+		free_record(record);
+
+		pthread_mutex_unlock(&stream->input_mutex);
+	}
+
+	return (event_id == -1)? -EFAULT : event_id;
+}
+
+static char* missed_events_dump(struct kshark_data_stream *stream,
+				      const struct kshark_entry *entry,
+				      bool get_info)
+{
+	char *buffer;
+	int size = 0;
+
+	if (get_info)
+		size = asprintf(&buffer, "missed_events=%i",
+				(int) entry->offset);
+	else
+		size = asprintf(&buffer, "missed_events");
+
+	if (size > 0)
+		return buffer;
+
+	return NULL;
+}
+
+static char *tepdata_get_event_name(struct kshark_data_stream *stream,
+				    const struct kshark_entry *entry)
+{
+	struct kshark_generic_stream_interface *interface;
+	struct tep_event *event;
+	char *buffer;
+
+	interface = stream->interface;
+	if (!interface)
+		return NULL;
+
+	int event_id = interface->get_event_id(stream, entry);
+	if (event_id == -EFAULT)
+		return NULL;
+
+	if (event_id < 0) {
+		switch (event_id) {
+		case KS_EVENT_OVERFLOW:
+			return missed_events_dump(stream, entry, false);
+		default:
+			return NULL;
+		}
+	}
+
+	/*
+	 * Currently the data reading operations are not thread-safe.
+	 * Use a mutex to protect the access.
+	 */
+	pthread_mutex_lock(&stream->input_mutex);
+
+	event = tep_find_event(kshark_get_tep(stream), event_id);
+
+	pthread_mutex_unlock(&stream->input_mutex);
+
+	if (!event ||
+            asprintf(&buffer, "%s/%s", event->system, event->name) <= 0)
+		return NULL;
+
+	return buffer;
+}
+
+static const int tepdata_get_pid(struct kshark_data_stream *stream,
+				 const struct kshark_entry *entry)
+{
+	struct tep_record *record;
+	int pid = KS_EMPTY_BIN;
+
+	if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) {
+		pid = entry->pid;
+	} else {
+		/*
+		 * The entry has been touched by a plugin callback function.
+		 * Because of this we do not trust the value of "entry->pid".
+		 *
+		 * Currently the data reading operations are not thread-safe.
+		 * Use a mutex to protect the access.
+		 */
+		pthread_mutex_lock(&stream->input_mutex);
+
+		record = tracecmd_read_at(kshark_get_tep_input(stream),
+					  entry->offset, NULL);
+
+		if (record)
+			pid = tep_data_pid(kshark_get_tep(stream), record);
+
+		free_record(record);
+
+		pthread_mutex_unlock(&stream->input_mutex);
+	}
+
+	return pid;
+}
+
+static char *tepdata_get_task(struct kshark_data_stream *stream,
+			      const struct kshark_entry *entry)
+{
+	struct kshark_generic_stream_interface *interface = stream->interface;
+	const char *task;
+	char *buffer;
+	int pid;
+
+	if (!interface)
+		return NULL;
+
+	pid = interface->get_pid(stream, entry);
+	task = tep_data_comm_from_pid(kshark_get_tep(stream), pid);
+	if (asprintf(&buffer, "%s", task)  <= 0)
+		return NULL;
+
+	return buffer;
+}
+
+static char *tepdata_get_latency(struct kshark_data_stream *stream,
+				 const struct kshark_entry *entry)
+{
+	struct tep_record *record;
+	char *buffer;
+
+	/* Check if this is a "Missed event" (event_id < 0). */
+	if (!init_thread_seq() || entry->event_id < 0)
+		return NULL;
+
+	/*
+	 * Currently the data reading operations are not thread-safe.
+	 * Use a mutex to protect the access.
+	 */
+	pthread_mutex_lock(&stream->input_mutex);
+
+	record = tracecmd_read_at(kshark_get_tep_input(stream), entry->offset, NULL);
+
+	if (!record)
+		return NULL;
+
+	trace_seq_reset(&seq);
+	tep_print_event(kshark_get_tep(stream), &seq, record,
+			"%s", TEP_PRINT_LATENCY);
+
+	free_record(record);
+
+	pthread_mutex_unlock(&stream->input_mutex);
+
+	if (asprintf(&buffer, "%s", seq.buffer)  <= 0)
+		return NULL;
+
+	return buffer;
+}
+
+static char *get_info_str(struct kshark_data_stream *stream,
+			  struct tep_record *record,
+			  struct tep_event *event)
+{
+	char *pos, *buffer;
+
+	if (!init_thread_seq() || !record || !event)
+		return NULL;
+
+	trace_seq_reset(&seq);
+	tep_print_event(kshark_get_tep(stream), &seq, record,
+			"%s", TEP_PRINT_INFO);
+
+	/*
+	 * The event info string contains a trailing newline.
+	 * Remove this newline.
+	 */
+	if ((pos = strchr(seq.buffer, '\n')) != NULL)
+		*pos = '\0';
+
+	if (asprintf(&buffer, "%s", seq.buffer)  <= 0)
+		return NULL;
+
+	return buffer;
+}
+
+static char *tepdata_get_info(struct kshark_data_stream *stream,
+			      const struct kshark_entry *entry)
+{
+	struct tep_record *record;
+	struct tep_event *event;
+	char *info = NULL;
+	int event_id;
+
+	if (entry->event_id < 0) {
+		switch (entry->event_id) {
+		case KS_EVENT_OVERFLOW:
+			return missed_events_dump(stream, entry, true);
+		default:
+			return NULL;
+		}
+	}
+
+	/*
+	 * Currently the data reading operations are not thread-safe.
+	 * Use a mutex to protect the access.
+	 */
+	pthread_mutex_lock(&stream->input_mutex);
+
+	record = tracecmd_read_at(kshark_get_tep_input(stream), entry->offset, NULL);
+	if (!record) {
+		pthread_mutex_unlock(&stream->input_mutex);
+		return NULL;
+	}
+
+	event_id = tep_data_type(kshark_get_tep(stream), record);
+	event = tep_find_event(kshark_get_tep(stream), event_id);
+
+	if (event)
+		info = get_info_str(stream, record, event);
+
+	free_record(record);
+
+	pthread_mutex_unlock(&stream->input_mutex);
+
+	return info;
+}
+
+static int *tepdata_get_event_ids(struct kshark_data_stream *stream)
+{
+	struct tep_event **events;
+	int i, *evt_ids;
+
+	events = tep_list_events(kshark_get_tep(stream), TEP_EVENT_SORT_SYSTEM);
+	if (!events)
+		return NULL;
+
+	evt_ids = calloc(stream->n_events, sizeof(*evt_ids));
+	if (!evt_ids)
+		return NULL;
+
+	for (i = 0; i < stream->n_events ; ++i)
+		evt_ids[i] = events[i]->id;
+
+	return evt_ids;
+}
+
+static int tepdata_get_field_names(struct kshark_data_stream *stream,
+				   const struct kshark_entry *entry,
+				   char ***fields_str)
+{
+	struct tep_format_field *field, **fields;
+	struct tep_event *event;
+	int i= 0, nr_fields;
+	char **buffer;
+
+	*fields_str = NULL;
+	event = tep_find_event(kshark_get_tep(stream), entry->event_id);
+	if (!event)
+		return 0;
+
+	nr_fields = event->format.nr_fields + event->format.nr_common;
+	buffer = calloc(nr_fields, sizeof(**fields_str));
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Add all common fields. */
+	fields = tep_event_common_fields(event);
+	if (!fields)
+		goto fail;
+
+	for (field = *fields; field; field = field->next)
+		if (asprintf(&buffer[i++], "%s", field->name) <= 0)
+			goto fail;
+
+	free(fields);
+
+	/* Add all unique fields. */
+	fields = tep_event_fields(event);
+	if (!fields)
+		goto fail;
+
+	for (field = *fields; field; field = field->next)
+		if (asprintf(&buffer[i++], "%s", field->name) <= 0)
+			goto fail;
+
+	free(fields);
+
+	*fields_str = buffer;
+	return nr_fields;
+
+ fail:
+	for (i = 0; i < nr_fields; ++i)
+		free(buffer[i]);
+
+	return -EFAULT;
+}
+
+/**
+ * Custom entry info function type. To be user for dumping info for custom
+ * KernelShark entryes.
+ */
+typedef char *(tepdata_custom_info_func)(struct kshark_data_stream *,
+					const struct kshark_entry *,
+					bool);
+
+static char* tepdata_dump_custom_entry(struct kshark_data_stream *stream,
+				       const struct kshark_entry *entry,
+				       tepdata_custom_info_func info_func)
+{
+	char *entry_str;
+	int size = 0;
+
+	size = asprintf(&entry_str, "%" PRIu64 "; %s-%i; CPU %i; ; %s; %s; 0x%x",
+			entry->ts,
+			tep_data_comm_from_pid(kshark_get_tep(stream), entry->pid),
+			entry->pid,
+			entry->cpu,
+			info_func(stream, entry, false),
+			info_func(stream, entry, true),
+			entry->visible);
+
+	if (size > 0)
+		return entry_str;
+
+	return NULL;
+}
+
+/**
+ * @brief Dump into a string the content of one entry. The function allocates
+ *	  a null terminated string and returns a pointer to this string.
+ *
+ * @param stream: Input location for the FTRACE data stream pointer.
+ * @param entry: A Kernel Shark entry to be printed.
+ *
+ * @returns The returned string contains a semicolon-separated list of data
+ *	    fields. The user has to free the returned string.
+ */
+static char *tepdata_dump_entry(struct kshark_data_stream *stream,
+				const struct kshark_entry *entry)
+{
+	char *entry_str, *task, *latency, *event, *info;
+	struct kshark_generic_stream_interface *interface;
+	struct kshark_context *kshark_ctx = NULL;
+	int n = 0;
+
+	if (!kshark_instance(&kshark_ctx) || !init_thread_seq())
+		return NULL;
+
+	interface = stream->interface;
+	if (!interface)
+		return NULL;
+
+	if (entry->event_id >= 0) {
+		if (kshark_get_tep(stream)) {
+			task = interface->get_task(stream, entry);
+			latency = interface->aux_info(stream, entry);
+			event = interface->get_event_name(stream, entry);
+			info = interface->get_info(stream, entry);
+			n = asprintf(&entry_str,
+				     "%i; %" PRIu64 "; %s-%i; CPU %i; %s; %s; %s; 0x%x",
+				     entry->stream_id,
+				     entry->ts,
+				     task,
+				     interface->get_pid(stream, entry),
+				     entry->cpu,
+				     latency,
+				     event,
+				     info,
+				     entry->visible);
+
+			free(task);
+			free(latency);
+			free(event);
+			free(info);
+		} else {
+			n = asprintf(&entry_str,
+				     "%i; %li; [UNKNOWN TASK]-%i; CPU %i; ; [UNKNOWN EVENT]; [NO INFO]; 0x%x",
+				     entry->stream_id,
+				     entry->ts,
+				     interface->get_pid(stream, entry),
+				     entry->cpu,
+				     entry->visible);
+		}
+
+		if (n < 1)
+			return NULL;
+	} else {
+		switch (entry->event_id) {
+		case KS_EVENT_OVERFLOW:
+			entry_str = tepdata_dump_custom_entry(stream, entry,
+							     missed_events_dump);
+		default:
+			return NULL;
+		}
+	}
+
+	return entry_str;
+}
+
+static const int tepdata_find_event_id(struct kshark_data_stream *stream,
+				       const char *event_name)
+{
+	struct tep_event *event;
+	char *buffer, *system, *name;
+
+	if (asprintf(&buffer, "%s", event_name) < 1)
+		return -1;
+
+	system = strtok(buffer, "/");
+	name = strtok(NULL, "");
+	if (!system || !name)
+		return -1;
+
+	event = tep_find_event_by_name(kshark_get_tep(stream), system, name);
+
+	free(buffer);
+
+	if (!event)
+		return -1;
+
+	return event->id;
+}
+
+static struct tep_format_field *
+get_evt_field(struct kshark_data_stream *stream,
+	      int event_id, const char *field_name)
+{
+	struct tep_event *event = tep_find_event(kshark_get_tep(stream),
+						 event_id);
+	if (!event)
+		return NULL;
+
+	return tep_find_any_field(event, field_name);
+}
+
+/**
+ * @brief  Get the type of a trace record field. For the moment only integer
+ *	   fields are supported.
+ *
+ * @param stream: Input location for the FTRACE data stream pointer.
+ * @param entry: Input location for the Kernel Shark entry asociated with thes
+ *		 record.
+ * @param field: The name of the field.
+ *
+ * @returns KS_INTEGER_FIELD in case the field has an integer type. Otherwise
+ *	    KS_INVALID_FIELD.
+ */
+kshark_event_field_format
+tepdata_get_field_type(struct kshark_data_stream *stream,
+		       const struct kshark_entry *entry,
+		       const char *field)
+{
+	struct tep_format_field *evt_field;
+	int mask = ~(TEP_FIELD_IS_SIGNED |
+		     TEP_FIELD_IS_LONG |
+		     TEP_FIELD_IS_FLAG);
+
+	evt_field = get_evt_field(stream, entry->event_id, field);
+	if (!evt_field)
+		return KS_INVALID_FIELD;
+
+	if (mask & evt_field->flags)
+		return KS_INVALID_FIELD;
+
+	return KS_INTEGER_FIELD;
+}
+
+/**
+ * @brief  Get the value of a trace record field.
+ *
+ * @param stream: Input location for the FTRACE data stream pointer.
+ * @param rec: Input location for the trace record.
+ * @param field: The name of the field.
+ * @param val: Output location for the field value.
+ *
+ * @returns Returns 0 on success, otherwise a negative error code..
+ */
+int tepdata_read_record_field(struct kshark_data_stream *stream,
+			      void *rec,
+			      const char *field, int64_t *val)
+{
+	struct tep_format_field *evt_field;
+	struct tep_record *record = rec;
+	int event_id, ret;
+
+	if (!record)
+		return -EFAULT;
+
+	event_id = tep_data_type(kshark_get_tep(stream), record);
+	evt_field = get_evt_field(stream, event_id, field);
+	if (!evt_field)
+		return -EINVAL;
+
+	ret = tep_read_number_field(evt_field, record->data,
+				    (unsigned long long *) val);
+
+	return ret;
+}
+
+/**
+ * @brief  Get the value of a trace record field.
+ *
+ * @param stream: Input location for the FTRACE data stream pointer.
+ * @param entry: Input location for the Kernel Shark entry asociated with thes
+ *		 record.
+ * @param field: The name of the field.
+ * @param val: Output location for the field value.
+ *
+ * @returns Returns 0 on success, otherwise a negative error code.
+ */
+int tepdata_read_event_field(struct kshark_data_stream *stream,
+			     const struct kshark_entry *entry,
+			     const char *field, int64_t *val)
+{
+	struct tep_format_field *evt_field;
+	struct tep_record *record;
+	int ret;
+
+	evt_field = get_evt_field(stream, entry->event_id, field);
+	if (!evt_field)
+		return -EINVAL;
+
+	record = tracecmd_read_at(kshark_get_tep_input(stream),
+				  entry->offset, NULL);
+	if (!record)
+		return -EFAULT;
+
+	ret = tep_read_number_field(evt_field, record->data,
+				    (unsigned long long *) val);
+	free_record(record);
+
+	return ret;
+}
+
+/** Initialize all methods used by a stream of FTRACE data. */
+static void kshark_tep_init_methods(struct kshark_generic_stream_interface *interface)
+{
+	if (!interface)
+		return;
+
+	interface->get_pid = tepdata_get_pid;
+	interface->get_task = tepdata_get_task;
+	interface->get_event_id = tepdata_get_event_id;
+	interface->get_event_name = tepdata_get_event_name;
+	interface->aux_info= tepdata_get_latency;
+	interface->get_info = tepdata_get_info;
+	interface->find_event_id = tepdata_find_event_id;
+	interface->get_all_event_ids = tepdata_get_event_ids;
+	interface->dump_entry = tepdata_dump_entry;
+	interface->get_all_event_field_names = tepdata_get_field_names;
+	interface->get_event_field_type = tepdata_get_field_type;
+	interface->read_record_field_int64 = tepdata_read_record_field;
+	interface->read_event_field_int64 = tepdata_read_event_field;
+	interface->load_entries = tepdata_load_entries;
+	interface->load_matrix = tepdata_load_matrix;
+}
+
+/** Find a host stream from the same tracing session, that has guest information */
+static struct tracecmd_input *
+kshark_tep_find_merge_peer(struct kshark_context *kshark_ctx,
+			   struct tracecmd_input *handle)
+{
+	struct tracecmd_input *peer_handle = NULL;
+	struct kshark_data_stream *peer_stream;
+	unsigned long long trace_id;
+	int *stream_ids = NULL;
+	int ret;
+	int i;
+
+	trace_id = tracecmd_get_traceid(handle);
+	if (!trace_id)
+		goto out;
+
+	stream_ids = kshark_all_streams(kshark_ctx);
+	if (!stream_ids)
+		goto out;
+
+	for (i = 0; i < kshark_ctx->n_streams - 1; i++) {
+		peer_stream = kshark_get_data_stream(kshark_ctx, stream_ids[i]);
+		if (!peer_stream || !kshark_is_tep(peer_stream))
+			continue;
+
+		peer_handle = kshark_get_tep_input(peer_stream);
+		if (!peer_handle)
+			continue;
+
+		ret = tracecmd_get_guest_cpumap(peer_handle, trace_id,
+						NULL, NULL, NULL);
+		if (!ret)
+			break;
+	}
+
+	if (i == kshark_ctx->n_streams)
+		peer_handle = NULL;
+
+out:
+	free(stream_ids);
+	return peer_handle;
+}
+
+/** The Process Id of the Idle tasks is zero. */
+#define LINUX_IDLE_TASK_PID	0
+
+static int kshark_tep_stream_init(struct kshark_data_stream *stream,
+				  struct tracecmd_input *input)
+{
+	struct kshark_generic_stream_interface *interface;
+	struct tepdata_handle *tep_handle;
+	struct tep_event *event;
+
+	stream->interface = interface = calloc(1, sizeof(*interface));
+	if (!interface)
+		return -ENOMEM;
+
+	interface->type = KS_GENERIC_DATA_INTERFACE;
+
+	tep_handle = calloc(1, sizeof(*tep_handle));
+	if (!tep_handle)
+		goto fail;
+
+	tep_handle->input = input;
+	tep_handle->tep = tracecmd_get_pevent(tep_handle->input);
+	if (!tep_handle->tep)
+		goto fail;
+
+	tep_handle->sched_switch_event_id = -EINVAL;
+	event = tep_find_event_by_name(tep_handle->tep,
+				       "sched", "sched_switch");
+	if (event) {
+		tep_handle->sched_switch_event_id = event->id;
+
+		tep_handle->sched_switch_next_field =
+			tep_find_any_field(event, "next_pid");
+
+		tep_handle->sched_switch_comm_field =
+			tep_find_field(event, "next_comm");
+	}
+
+	stream->n_cpus = tep_get_cpus(tep_handle->tep);
+	stream->n_events = tep_get_events_count(tep_handle->tep);
+	stream->idle_pid = LINUX_IDLE_TASK_PID;
+
+	tep_handle->advanced_event_filter =
+		tep_filter_alloc(tep_handle->tep);
+
+	kshark_tep_init_methods(interface);
+
+	interface->handle = tep_handle;
+
+	return 0;
+
+ fail:
+	free(tep_handle);
+	free(interface);
+	stream->interface = NULL;
+	return -EFAULT;
+}
+
+static inline char *set_tep_format(struct kshark_data_stream *stream)
+{
+	return kshark_set_data_format(stream->data_format,
+				      TEP_DATA_FORMAT_IDENTIFIER);
+}
+
+/** Check is the file contains TEP tracing data. */
+bool kshark_tep_check_data(const char *file_name)
+{
+	/*
+	 * TODO: This is very naive. Implement more appropriate check. Ideally
+	 * it should be part of the trace-cmd library.
+	 */
+	char *ext = strrchr(file_name, '.');
+	if (ext && strcmp(ext, ".dat") == 0) {
+		return true;
+	}
+
+	return false;
+}
+
+/** Initialize the FTRACE data input (from file). */
+int kshark_tep_init_input(struct kshark_data_stream *stream,
+			  const char *file)
+{
+	struct kshark_context *kshark_ctx = NULL;
+	struct tracecmd_input *merge_peer;
+	struct tracecmd_input *input;
+
+	if (!kshark_instance(&kshark_ctx) || !init_thread_seq())
+		return -EEXIST;
+
+	/*
+	 * Turn off function trace indent and turn on show parent
+	 * if possible.
+	 */
+	tep_plugin_add_option("ftrace:parent", "1");
+	tep_plugin_add_option("ftrace:indent", "0");
+
+	input = tracecmd_open_head(file);
+	if (!input)
+		return -EEXIST;
+
+	/* Find a merge peer from the same tracing session. */
+	merge_peer = kshark_tep_find_merge_peer(kshark_ctx, input);
+	if (merge_peer)
+		tracecmd_pair_peer(input, merge_peer);
+
+	/* Read the tracing data from the file. */
+	if (tracecmd_init_data(input) < 0)
+		goto fail;
+
+	/* Initialize the stream asociated with the main buffer. */
+	if (kshark_tep_stream_init(stream, input) < 0)
+		goto fail;
+
+	stream->name = strdup(KS_UNNAMED);
+
+	return 0;
+
+ fail:
+	tracecmd_close(input);
+	return -EFAULT;
+}
+
+/** Initialize using the locally available tracing events. */
+int kshark_tep_init_local(struct kshark_data_stream *stream)
+{
+	struct kshark_generic_stream_interface *interface;
+	struct tepdata_handle *tep_handle;
+
+	stream->interface = interface = calloc(1, sizeof(*interface));
+	if (!interface)
+		return -ENOMEM;
+
+	interface->type = KS_GENERIC_DATA_INTERFACE;
+
+	tep_handle = calloc(1, sizeof(*tep_handle));
+	if (!tep_handle)
+		goto fail;
+
+	tep_handle->tep = tracefs_local_events(tracefs_get_tracing_dir());
+	if (!tep_handle->tep)
+		goto fail;
+
+	stream->n_events = tep_get_events_count(tep_handle->tep);
+	stream->n_cpus =  tep_get_cpus(tep_handle->tep);
+	set_tep_format(stream);
+	if (asprintf(&stream->file, "Local system") <= 0)
+		goto fail;
+
+	interface->handle = tep_handle;
+	kshark_tep_init_methods(interface);
+
+	return 0;
+
+ fail:
+	free(tep_handle);
+	free(interface);
+	stream->interface = NULL;
+	return -EFAULT;
+}
+
+/** Method used to close a stream of FTRACE data. */
+int kshark_tep_close_interface(struct kshark_data_stream *stream)
+{
+	struct kshark_generic_stream_interface *interface = stream->interface;
+	struct tepdata_handle *tep_handle;
+
+	if (!interface)
+		return -EFAULT;
+
+	tep_handle = interface->handle;
+	if (!tep_handle)
+		return -EFAULT;
+
+	if (seq.buffer)
+		trace_seq_destroy(&seq);
+
+	if (tep_handle->advanced_event_filter) {
+		tep_filter_reset(tep_handle->advanced_event_filter);
+		tep_filter_free(tep_handle->advanced_event_filter);
+		tep_handle->advanced_event_filter = NULL;
+	}
+
+	if (tep_handle->input)
+		tracecmd_close(tep_handle->input);
+
+	free(tep_handle);
+	interface->handle = NULL;
+
+	return 0;
+}
+
+/** Check if the filter any filter is set. */
+bool kshark_tep_filter_is_set(struct kshark_data_stream *stream)
+{
+	struct tep_event_filter *adv_filter = get_adv_filter(stream);
+
+	if (adv_filter && adv_filter->filters)
+		return true;
+
+	return false;
+}
+
+/**
+ * @brief Add a filter based on the content of the event.
+ *
+ * @param stream: Input location for the FTRACE data stream pointer.
+ * @param filter_str: The definition of the filter.
+ *
+ * @returns 0 if the filter was successfully added or a negative error code.
+ */
+int kshark_tep_add_filter_str(struct kshark_data_stream *stream,
+			       const char *filter_str)
+{
+	struct tep_event_filter *adv_filter = get_adv_filter(stream);
+	int ret = tep_filter_add_filter_str(adv_filter, filter_str);
+
+	if (ret < 0) {
+		char error_str[200];
+		int error_status =
+			tep_strerror(kshark_get_tep(stream), ret, error_str,
+				     sizeof(error_str));
+
+		if (error_status == 0)
+			fprintf(stderr, "filter failed due to: %s\n",
+					error_str);
+	}
+
+	return ret;
+}
+
+/**
+ * @brief Get a string showing the filter definition.
+ *
+ * @param stream: Input location for the FTRACE data stream pointer.
+ * @param event_id: The unique Id of the event type of the filter.
+ *
+ * @returns A string that displays the filter contents. This string must be
+ *	    freed with free(str). NULL is returned if no filter is found or
+ *	    allocation failed.
+ */
+char *kshark_tep_filter_make_string(struct kshark_data_stream *stream,
+				    int event_id)
+{
+	struct tep_event_filter *adv_filter = get_adv_filter(stream);
+
+	return tep_filter_make_string(adv_filter, event_id);
+}
+
+/**
+ * @brief Remove a filter based on the content of the event.
+ *
+ * @param stream: Input location for the FTRACE data stream pointer.
+ * @param event_id: The unique Id of the event type of the filter.
+ *
+ * @return 1: if an event was removed or 0 if the event was not found.
+ */
+int kshark_tep_filter_remove_event(struct kshark_data_stream *stream,
+				   int event_id)
+{
+	struct tep_event_filter *adv_filter = get_adv_filter(stream);
+
+	return tep_filter_remove_event(adv_filter, event_id);
+}
+
+/** Reset all filters based on the content of the event. */
+void kshark_tep_filter_reset(struct kshark_data_stream *stream)
+{
+	return tep_filter_reset(get_adv_filter(stream));
+}
+
+/** Get an array of available tracer plugins. */
+char **kshark_tracecmd_local_plugins()
+{
+	return tracefs_tracers(tracefs_get_tracing_dir());
+}
diff --git a/src/libkshark-tepdata.h b/src/libkshark-tepdata.h
new file mode 100644
index 00000000..b7d6c163
--- /dev/null
+++ b/src/libkshark-tepdata.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2019 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    libkshark-tepdata.h
+ *  @brief   Interface for processing of FTRACE (trace-cmd) data.
+ */
+
+#ifndef _KSHARK_TEPDATA_H
+#define _KSHARK_TEPDATA_H
+
+// KernelShark
+#include "libkshark.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Unique identifier of the TEP tracing data format. */
+#define TEP_DATA_FORMAT_IDENTIFIER	"tep data"
+
+/** Check if this Data stream corresponds to a TEP tracing data. */
+static inline bool kshark_is_tep(struct kshark_data_stream *stream)
+{
+	return strcmp(stream->data_format, TEP_DATA_FORMAT_IDENTIFIER) == 0;
+}
+
+bool kshark_tep_check_data(const char *file_name);
+
+int kshark_tep_init_input(struct kshark_data_stream *stream,
+			  const char *file);
+
+int kshark_tep_init_local(struct kshark_data_stream *stream);
+
+int kshark_tep_close_interface(struct kshark_data_stream *stream);
+
+bool kshark_tep_filter_is_set(struct kshark_data_stream *stream);
+
+int kshark_tep_add_filter_str(struct kshark_data_stream *stream,
+			      const char *filter_str);
+
+char *kshark_tep_filter_make_string(struct kshark_data_stream *stream,
+				    int event_id);
+
+int kshark_tep_filter_remove_event(struct kshark_data_stream *stream,
+				   int event_id);
+
+void kshark_tep_filter_reset(struct kshark_data_stream *stream);
+
+char **kshark_tracecmd_local_plugins();
+
+ssize_t kshark_load_tep_records(struct kshark_context *kshark_ctx, int sd,
+				struct tep_record ***data_rows);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _KSHARK_TEPDATA_H
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 09/20] kernel-shark: Start introducing KernelShark 2.0
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (7 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 08/20] kernel-shark: Add stream interface for trace-cmd data Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 10/20] kernel-shark: Start using data streams Yordan Karadzhov (VMware)
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

This is the very first patch from a series of patches that will
introduce KernelShark 2.0. The new version will take advantage
of the definition of Data streams and will imply significant
changes in the low level C API, used by the KernelShark GUI. As
a consequence of the API modifications, significant changes are
needed in every source file of the project. Because, we want to
avoid having a huge and ugly single commit that will be extremely
hard to review, we will introduce a regression. We will first
disable the compilation of all sub-components of KernelShark,
except the API itself. This is done by bypassing the search of
the necessary third party components (OpenGL and Qt). Later we
will introduce the changes of the API in several sub-steps and
will re-enable one by one the modified components of the GUI.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 CMakeLists.txt          | 10 ++++----
 build/deff.h.cmake      | 14 ++---------
 examples/CMakeLists.txt | 54 ++++++++++++++++++++---------------------
 src/CMakeLists.txt      |  6 ++---
 4 files changed, 37 insertions(+), 47 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c95249e9..ccca95c1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,12 +26,12 @@ include(${KS_DIR}/build/FindJSONC.cmake)
 
 find_package(Doxygen)
 
-set(OpenGL_GL_PREFERENCE LEGACY)
-find_package(OpenGL)
-find_package(GLUT)
+# set(OpenGL_GL_PREFERENCE LEGACY)
+# find_package(OpenGL)
+# find_package(GLUT)
 
-find_package(Qt5Widgets 5.7.1)
-find_package(Qt5Network)
+# find_package(Qt5Widgets 5.7.1)
+# find_package(Qt5Network)
 if (Qt5Widgets_FOUND)
 
     message(STATUS "Found Qt5Widgets:  (version ${Qt5Widgets_VERSION})")
diff --git a/build/deff.h.cmake b/build/deff.h.cmake
index 79726ff6..e398c0ca 100644
--- a/build/deff.h.cmake
+++ b/build/deff.h.cmake
@@ -23,17 +23,7 @@
 /** "pkexec" executable. */
 #cmakedefine DO_AS_ROOT "@DO_AS_ROOT@"
 
-#ifdef __cplusplus
-
-	#include <QString>
-
-	/**
-	 * String containing semicolon-separated list of plugin names.
-	 * The plugins to be loaded when KernelShark starts are tagged
-	 * with "default".
-	 */
-	const QString plugins = "@PLUGINS@";
-
-#endif /* __cplusplus */
+/** Semicolon-separated list of plugin names. */
+#define KS_BUILTIN_PLUGINS "@PLUGINS@"
 
 #endif // _KS_CONFIG_H
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 032f3057..f6ed8978 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -4,30 +4,30 @@ message(STATUS "dataload")
 add_executable(dload          dataload.c)
 target_link_libraries(dload   kshark)
 
-message(STATUS "datafilter")
-add_executable(dfilter          datafilter.c)
-target_link_libraries(dfilter   kshark)
-
-message(STATUS "datahisto")
-add_executable(dhisto          datahisto.c)
-target_link_libraries(dhisto   kshark)
-
-message(STATUS "confogio")
-add_executable(confio          configio.c)
-target_link_libraries(confio   kshark)
-
-message(STATUS "dataplot")
-add_executable(dplot          dataplot.cpp)
-target_link_libraries(dplot   kshark-plot)
-
-if (Qt5Widgets_FOUND)
-
-    message(STATUS "widgetdemo")
-    add_executable(widgetdemo          widgetdemo.cpp)
-    target_link_libraries(widgetdemo   kshark-gui)
-
-    message(STATUS "cmd_split")
-    add_executable(cmd_split           cmd_split.cpp)
-    target_link_libraries(cmd_split    kshark-gui)
-
-endif (Qt5Widgets_FOUND)
+# message(STATUS "datafilter")
+# add_executable(dfilter          datafilter.c)
+# target_link_libraries(dfilter   kshark)
+#
+# message(STATUS "datahisto")
+# add_executable(dhisto          datahisto.c)
+# target_link_libraries(dhisto   kshark)
+#
+# message(STATUS "confogio")
+# add_executable(confio          configio.c)
+# target_link_libraries(confio   kshark)
+#
+# message(STATUS "dataplot")
+# add_executable(dplot          dataplot.cpp)
+# target_link_libraries(dplot   kshark-plot)
+#
+# if (Qt5Widgets_FOUND)
+#
+#     message(STATUS "widgetdemo")
+#     add_executable(widgetdemo          widgetdemo.cpp)
+#     target_link_libraries(widgetdemo   kshark-gui)
+#
+#     message(STATUS "cmd_split")
+#     add_executable(cmd_split           cmd_split.cpp)
+#     target_link_libraries(cmd_split    kshark-gui)
+#
+# endif (Qt5Widgets_FOUND)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4d4f37b1..2bdab1a5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,10 +3,10 @@ message("\n src ...")
 message(STATUS "libkshark")
 add_library(kshark SHARED libkshark.c
                           libkshark-hash.c
-                          libkshark-model.c
+#                           libkshark-model.c
                           libkshark-plugin.c
                           libkshark-tepdata.c
-                          libkshark-configio.c
+#                           libkshark-configio.c
                           libkshark-collection.c)
 
 target_link_libraries(kshark ${TRACECMD_LIBRARY}
@@ -101,7 +101,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 
 endif (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 
-add_subdirectory(plugins)
+# add_subdirectory(plugins)
 
 find_program(DO_AS_ROOT pkexec)
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 10/20] kernel-shark: Start using data streams
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (8 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 09/20] kernel-shark: Start introducing KernelShark 2.0 Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-19  1:29   ` Steven Rostedt
  2020-11-18 14:49 ` [PATCH v4 11/20] kernel-shark: Remove dead code Yordan Karadzhov (VMware)
                   ` (9 subsequent siblings)
  19 siblings, 1 reply; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Here we switch to using the trace data readout, provided by the Data
stream interface. The actual implementation of the new readout was
done in the previous commits.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 examples/dataload.c |  28 ++--
 src/libkshark.c     | 342 ++++++++++++++++++--------------------------
 src/libkshark.h     |  14 +-
 3 files changed, 164 insertions(+), 220 deletions(-)

diff --git a/examples/dataload.c b/examples/dataload.c
index 15c5de05..e259b3ad 100644
--- a/examples/dataload.c
+++ b/examples/dataload.c
@@ -19,8 +19,7 @@ int main(int argc, char **argv)
 	struct kshark_entry **data = NULL;
 	ssize_t r, n_rows, n_tasks;
 	char *entry_str;
-	bool status;
-	int *pids;
+	int sd, *pids;
 
 	/* Create a new kshark session. */
 	kshark_ctx = NULL;
@@ -29,30 +28,28 @@ int main(int argc, char **argv)
 
 	/* Open a trace data file produced by trace-cmd. */
 	if (argc > 1)
-		status = kshark_open(kshark_ctx, argv[1]);
+		sd = kshark_open(kshark_ctx, argv[1]);
 	else
-		status = kshark_open(kshark_ctx, default_file);
+		sd = kshark_open(kshark_ctx, default_file);
 
-	if (!status) {
+	if (sd < 0) {
 		kshark_free(kshark_ctx);
 		return 1;
 	}
 
 	/* Load the content of the file into an array of entries. */
-	n_rows = kshark_load_data_entries(kshark_ctx, &data);
+	n_rows = kshark_load_entries(kshark_ctx, sd, &data);
 	if (n_rows < 1) {
 		kshark_free(kshark_ctx);
 		return 1;
 	}
 
 	/* Print to the screen the list of all tasks. */
-	n_tasks = kshark_get_task_pids(kshark_ctx, &pids);
+	n_tasks = kshark_get_task_pids(kshark_ctx, sd, &pids);
 	for (r = 0; r < n_tasks; ++r) {
-		const char *task_str =
-			tep_data_comm_from_pid(kshark_ctx->pevent,
-					       pids[r]);
-
+		char *task_str = kshark_comm_from_pid(sd, pids[r]);
 		printf("task: %s-%i\n", task_str, pids[r]);
+		free(task_str);
 	}
 
 	free(pids);
@@ -69,11 +66,8 @@ int main(int argc, char **argv)
 	puts("\n...\n");
 
 	/* Print the last 10 entries. */
-	for (r = n_rows - 10; r < n_rows; ++r) {
-		entry_str = kshark_dump_entry(data[r]);
-		puts(entry_str);
-		free(entry_str);
-	}
+	for (r = n_rows - 10; r < n_rows; ++r)
+		kshark_print_entry(data[r]);
 
 	/* Free the memory. */
 	for (r = 0; r < n_rows; ++r)
@@ -82,7 +76,7 @@ int main(int argc, char **argv)
 	free(data);
 
 	/* Close the file. */
-	kshark_close(kshark_ctx);
+	kshark_close(kshark_ctx, sd);
 
 	/* Close the session. */
 	kshark_free(kshark_ctx);
diff --git a/src/libkshark.c b/src/libkshark.c
index 422fb7d4..af1545d7 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -19,6 +19,7 @@
 
 // KernelShark
 #include "libkshark.h"
+#include "libkshark-tepdata.h"
 
 static __thread struct trace_seq seq;
 
@@ -32,6 +33,9 @@ static bool kshark_default_context(struct kshark_context **context)
 	if (!kshark_ctx)
 		return false;
 
+	kshark_ctx->stream = calloc(KS_DEFAULT_NUM_STREAMS,
+				    sizeof(*kshark_ctx->stream));
+
 	kshark_ctx->event_handlers = NULL;
 	kshark_ctx->collections = NULL;
 	kshark_ctx->plugins = NULL;
@@ -47,6 +51,9 @@ static bool kshark_default_context(struct kshark_context **context)
 
 	kshark_ctx->filter_mask = 0x0;
 
+	kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS;
+	kshark_ctx->stream_info.max_stream_id = -1;
+
 	/* Will free kshark_context_handler. */
 	kshark_free(NULL);
 
@@ -108,62 +115,30 @@ bool kshark_instance(struct kshark_context **kshark_ctx)
 	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.
+ * @returns The Id number of the data stream associated with this file on success.
+ * 	    Otherwise a negative errno code.
  */
-bool kshark_open(struct kshark_context *kshark_ctx, const char *file)
+int kshark_open(struct kshark_context *kshark_ctx, const char *file)
 {
-	struct tracecmd_input *handle;
+	int sd, rt;
 
-	kshark_free_task_list(kshark_ctx);
+	sd = kshark_add_stream(kshark_ctx);
+	if (sd < 0)
+		return sd;
 
-	handle = tracecmd_open(file);
-	if (!handle)
-		return false;
-
-	if (pthread_mutex_init(&kshark_ctx->input_mutex, NULL) != 0) {
-		tracecmd_close(handle);
-		return false;
+	rt = kshark_stream_open(kshark_ctx->stream[sd], file);
+	if (rt < 0) {
+		kshark_remove_stream(kshark_ctx, sd);
+		return rt;
 	}
 
-	kshark_ctx->handle = handle;
-	kshark_ctx->pevent = tracecmd_get_pevent(handle);
-
-	kshark_ctx->advanced_event_filter =
-		tep_filter_alloc(kshark_ctx->pevent);
-
-	/*
-	 * Turn off function trace indent and turn on show parent
-	 * if possible.
-	 */
-	tep_plugin_add_option("ftrace:parent", "1");
-	tep_plugin_add_option("ftrace:indent", "0");
-
-	return true;
+	return sd;
 }
 
 static void kshark_stream_free(struct kshark_data_stream *stream)
@@ -327,6 +302,34 @@ int kshark_add_stream(struct kshark_context *kshark_ctx)
 	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;
+
+	if (!stream || !kshark_instance(&kshark_ctx))
+		return -EFAULT;
+
+	stream->file = strdup(file);
+
+	if (kshark_tep_check_data(file)) {
+		kshark_set_data_format(stream->data_format,
+				       TEP_DATA_FORMAT_IDENTIFIER);
+
+		return kshark_tep_init_input(stream, file);
+	}
+
+	return -ENODATA;
+}
+
 /**
  * @brief Remove Data stream.
  *
@@ -417,45 +420,74 @@ int *kshark_all_streams(struct kshark_context *kshark_ctx)
 
 	return ids;
 }
+
+static int kshark_stream_close(struct kshark_data_stream *stream)
+{
+	struct kshark_context *kshark_ctx = NULL;
+
+	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);
+
+	if (kshark_is_tep(stream))
+		return kshark_tep_close_interface(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.
  */
-void kshark_close(struct kshark_context *kshark_ctx)
+int kshark_close(struct kshark_context *kshark_ctx, int sd)
 {
-	if (!kshark_ctx || !kshark_ctx->handle)
-		return;
+	struct kshark_data_stream *stream;
+	int ret;
 
-	/*
-	 * All filters are file specific. Make sure that the Pids and Event Ids
-	 * from this file are not going to be used with another file.
-	 */
-	tracecmd_filter_id_clear(kshark_ctx->show_task_filter);
-	tracecmd_filter_id_clear(kshark_ctx->hide_task_filter);
-	tracecmd_filter_id_clear(kshark_ctx->show_event_filter);
-	tracecmd_filter_id_clear(kshark_ctx->hide_event_filter);
-	tracecmd_filter_id_clear(kshark_ctx->show_cpu_filter);
-	tracecmd_filter_id_clear(kshark_ctx->hide_cpu_filter);
-
-	if (kshark_ctx->advanced_event_filter) {
-		tep_filter_reset(kshark_ctx->advanced_event_filter);
-		tep_filter_free(kshark_ctx->advanced_event_filter);
-		kshark_ctx->advanced_event_filter = NULL;
-	}
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return -EFAULT;
+
+	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)
+{
+	int i, *stream_ids, n_streams;
+
+	stream_ids = kshark_all_streams(kshark_ctx);
 
 	/*
-	 * All data collections are file specific. Make sure that collections
-	 * from this file are not going to be used with another file.
+	 * Get a copy of shark_ctx->n_streams befor you start closing. Be aware
+	 * that kshark_close() will decrement shark_ctx->n_streams.
 	 */
-	kshark_free_collection_list(kshark_ctx->collections);
-	kshark_ctx->collections = NULL;
+	n_streams = kshark_ctx->n_streams;
+	for (i = 0; i < n_streams; ++i)
+		kshark_close(kshark_ctx, stream_ids[i]);
 
-	tracecmd_close(kshark_ctx->handle);
-	kshark_ctx->handle = NULL;
-	kshark_ctx->pevent = NULL;
-
-	pthread_mutex_destroy(&kshark_ctx->input_mutex);
+	free(stream_ids);
 }
 
 /**
@@ -463,7 +495,7 @@ void kshark_close(struct kshark_context *kshark_ctx)
  *	  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
+ *		      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.
  */
@@ -477,25 +509,12 @@ void kshark_free(struct kshark_context *kshark_ctx)
 		/* kshark_ctx_handler will be set to NULL below. */
 	}
 
-	tracecmd_filter_id_hash_free(kshark_ctx->show_task_filter);
-	tracecmd_filter_id_hash_free(kshark_ctx->hide_task_filter);
-
-	tracecmd_filter_id_hash_free(kshark_ctx->show_event_filter);
-	tracecmd_filter_id_hash_free(kshark_ctx->hide_event_filter);
+	kshark_close_all(kshark_ctx);
 
-	tracecmd_filter_id_hash_free(kshark_ctx->show_cpu_filter);
-	tracecmd_filter_id_hash_free(kshark_ctx->hide_cpu_filter);
+	free(kshark_ctx->stream);
 
-	if (kshark_ctx->plugins) {
-		kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_CLOSE);
+	if (kshark_ctx->plugins)
 		kshark_free_plugin_list(kshark_ctx->plugins);
-		kshark_free_event_handler_list(kshark_ctx->event_handlers);
-	}
-
-	kshark_free_task_list(kshark_ctx);
-
-	if (seq.buffer)
-		trace_seq_destroy(&seq);
 
 	if (kshark_ctx == kshark_context_handler)
 		kshark_context_handler = NULL;
@@ -878,6 +897,31 @@ int kshark_read_event_field_int(const struct kshark_entry *entry,
 	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)
 {
@@ -1008,57 +1052,24 @@ kshark_add_task(struct kshark_context *kshark_ctx, int pid)
  *	  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 **pids)
+ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int sd,
+			     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;
-			}
-		}
-	}
-
-	if (pid_count) {
-		temp_pids = realloc(*pids, pid_count * sizeof(int));
-		if (!temp_pids)
-			goto fail;
-
-		/* Paranoid: In the unlikely case of shrinking *pids, realloc moves it */
-		*pids = temp_pids;
-	} else {
-		free(*pids);
-		*pids = NULL;
-	}
+	struct kshark_data_stream *stream;
 
-	return pid_count;
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return -EBADF;
 
-fail:
-	fprintf(stderr, "Failed to allocate memory for Task Pids.\n");
-	free(*pids);
-	*pids = NULL;
-	return -ENOMEM;
+	*pids = kshark_hash_ids(stream->tasks);
+	return stream->tasks->count;
 }
 
 static bool filter_find(struct tracecmd_filter_id *filter, int pid,
@@ -2070,75 +2081,6 @@ char* kshark_dump_custom_entry(struct kshark_context *kshark_ctx,
 	return NULL;
 }
 
-/**
- * @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(const struct kshark_entry *entry)
-{
-	const char *event_name, *task, *lat, *info;
-	struct kshark_context *kshark_ctx;
-	char *temp_str, *entry_str;
-	int size = 0;
-
-	kshark_ctx = NULL;
-	if (!kshark_instance(&kshark_ctx) || !init_thread_seq())
-		return NULL;
-
-	task = tep_data_comm_from_pid(kshark_ctx->pevent, entry->pid);
-
-	if (entry->event_id >= 0) {
-		struct tep_event *event;
-		struct tep_record *data;
-
-		data = tracecmd_read_at(kshark_ctx->handle, entry->offset,
-					NULL);
-
-		event = tep_find_event(kshark_ctx->pevent, entry->event_id);
-
-		event_name = event? event->name : "[UNKNOWN EVENT]";
-		lat = get_latency(kshark_ctx->pevent, data);
-
-		size = asprintf(&temp_str, "%" PRIu64 "; %s-%i; CPU %i; %s;",
-				entry->ts,
-				task,
-				entry->pid,
-				entry->cpu,
-				lat);
-
-		info = 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 < 1)
-			entry_str = NULL;
-	} else {
-		switch (entry->event_id) {
-		case KS_EVENT_OVERFLOW:
-			entry_str = kshark_dump_custom_entry(kshark_ctx, entry,
-							     missed_events_dump);
-		default:
-			entry_str = NULL;
-		}
-	}
-
-	return entry_str;
-}
 
 /**
  * @brief Binary search inside a time-sorted array of kshark_entries.
diff --git a/src/libkshark.h b/src/libkshark.h
index 36d60001..523bdb6a 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -342,6 +342,9 @@ static inline char *kshark_set_data_format(char *dest_format,
 	return strncpy(dest_format, src_format, KS_DATA_FORMAT_SIZE - 1);
 }
 
+/** Hard-coded default number of data streams available at initialization. */
+#define KS_DEFAULT_NUM_STREAMS	256
+
 /** Size of the task's hash table. */
 #define KS_TASK_HASH_SHIFT 16
 #define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT)
@@ -436,7 +439,9 @@ struct kshark_context {
 
 bool kshark_instance(struct kshark_context **kshark_ctx);
 
-bool kshark_open(struct kshark_context *kshark_ctx, const char *file);
+int kshark_open(struct kshark_context *kshark_ctx, const char *file);
+
+int kshark_stream_open(struct kshark_data_stream *stream, const char *file);
 
 int kshark_add_stream(struct kshark_context *kshark_ctx);
 
@@ -456,9 +461,12 @@ ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx,
 ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx,
 				 struct tep_record ***data_rows);
 
-ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int **pids);
+ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int sd,
+			     int **pids);
+
+int kshark_close(struct kshark_context *kshark_ctx, int sd);
 
-void kshark_close(struct kshark_context *kshark_ctx);
+void kshark_close_all(struct kshark_context *kshark_ctx);
 
 void kshark_free(struct kshark_context *kshark_ctx);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 11/20] kernel-shark: Remove dead code
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (9 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 10/20] kernel-shark: Start using data streams Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 12/20] kernel-shark: Redesign the plugin interface Yordan Karadzhov (VMware)
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Clean up the old readout code that is no longer used after switching
to the implementation provided by the Data stream interface.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark.c | 826 +-----------------------------------------------
 src/libkshark.h |  70 +---
 2 files changed, 8 insertions(+), 888 deletions(-)

diff --git a/src/libkshark.c b/src/libkshark.c
index af1545d7..63dd233e 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -6,7 +6,7 @@
 
  /**
  *  @file    libkshark.c
- *  @brief   API for processing of FTRACE (trace-cmd) data.
+ *  @brief   API for processing of tracing data.
  */
 
 /** Use GNU C Library. */
@@ -21,8 +21,6 @@
 #include "libkshark.h"
 #include "libkshark-tepdata.h"
 
-static __thread struct trace_seq seq;
-
 static struct kshark_context *kshark_context_handler = NULL;
 
 static bool kshark_default_context(struct kshark_context **context)
@@ -35,6 +33,8 @@ static bool kshark_default_context(struct kshark_context **context)
 
 	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.max_stream_id = -1;
 
 	kshark_ctx->event_handlers = NULL;
 	kshark_ctx->collections = NULL;
@@ -65,14 +65,6 @@ static bool kshark_default_context(struct kshark_context **context)
 	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
@@ -109,9 +101,6 @@ bool kshark_instance(struct kshark_context **kshark_ctx)
 		}
 	}
 
-	if (!init_thread_seq())
-		return false;
-
 	return true;
 }
 
@@ -475,19 +464,10 @@ int kshark_close(struct kshark_context *kshark_ctx, int sd)
  */
 void kshark_close_all(struct kshark_context *kshark_ctx)
 {
-	int i, *stream_ids, n_streams;
-
-	stream_ids = kshark_all_streams(kshark_ctx);
-
-	/*
-	 * Get a copy of shark_ctx->n_streams befor you start closing. Be aware
-	 * that kshark_close() will decrement shark_ctx->n_streams.
-	 */
-	n_streams = kshark_ctx->n_streams;
-	for (i = 0; i < n_streams; ++i)
-		kshark_close(kshark_ctx, stream_ids[i]);
+	int i;
 
-	free(stream_ids);
+	for (i = 0; i <= kshark_ctx->stream_info.max_stream_id; ++i)
+		kshark_close(kshark_ctx, i);
 }
 
 /**
@@ -1011,42 +991,6 @@ ssize_t kshark_load_matrix(struct kshark_context *kshark_ctx, int sd,
 	return -EFAULT;
 }
 
-static struct kshark_task_list *
-kshark_find_task(struct kshark_context *kshark_ctx, uint32_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;
-	uint32_t key;
-
-	key = tracecmd_quick_hash(pid, KS_TASK_HASH_SHIFT);
-
-	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.
@@ -1214,7 +1158,7 @@ static void set_all_visible(uint16_t *v) {
  *	  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().
+ *	  hence the data has to be reloaded using kshark_load_entries().
  *
  * @param kshark_ctx: Input location for the session context pointer.
  * @param data: Input location for the trace data to be filtered.
@@ -1275,400 +1219,6 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx,
 		set_all_visible(&data[i]->visible);
 }
 
-static void kshark_set_entry_values(struct kshark_context *kshark_ctx,
-				    struct tep_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 = tep_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 = tep_data_pid(kshark_ctx->pevent, record);
-}
-
-/** Prior time offset of the "missed_events" entry. */
-#define ME_ENTRY_TIME_SHIFT	10
-
-static void missed_events_action(struct kshark_context *kshark_ctx,
-				 struct tep_record *record,
-				 struct kshark_entry *entry)
-{
-	/*
-	 * Use the offset field of the entry to store the number of missed
-	 * events.
-	 */
-	entry->offset = record->missed_events;
-
-	entry->cpu = record->cpu;
-
-	/*
-	 * Position the "missed_events" entry a bit before (in time)
-	 * the original record.
-	 */
-	entry->ts = record->ts - ME_ENTRY_TIME_SHIFT;
-
-	/* All custom entries must have negative event Identifiers. */
-	entry->event_id = KS_EVENT_OVERFLOW;
-
-	entry->visible = 0xFF;
-
-	entry->pid = tep_data_pid(kshark_ctx->pevent, record);
-}
-
-static const char* missed_events_dump(struct kshark_context *kshark_ctx,
-				      const struct kshark_entry *entry,
-				      bool get_info)
-{
-	int size = 0;
-	static char *buffer;
-
-	if (get_info)
-		size = asprintf(&buffer, "missed_events=%i", (int) entry->offset);
-	else
-		size = asprintf(&buffer, "missed_events");
-	if (size > 0)
-		return buffer;
-
-	return NULL;
-}
-
-/**
- * rec_list is used to pass the data to the load functions.
- * The rec_list will contain the list of entries from the source,
- * and will be a link list of per CPU entries.
- */
-struct rec_list {
-	union {
-		/* Used by kshark_load_data_records */
-		struct {
-			/** next pointer, matches entry->next */
-			struct rec_list		*next;
-			/** pointer to the raw record data */
-			struct tep_record	*rec;
-		};
-		/** entry - Used for kshark_load_data_entries() */
-		struct kshark_entry		entry;
-	};
-};
-
-/**
- * rec_type defines what type of rec_list is being used.
- */
-enum rec_type {
-	REC_RECORD,
-	REC_ENTRY,
-};
-
-static void free_rec_list(struct rec_list **rec_list, int n_cpus,
-			  enum rec_type type)
-{
-	struct rec_list *temp_rec;
-	int cpu;
-
-	for (cpu = 0; cpu < n_cpus; ++cpu) {
-		while (rec_list[cpu]) {
-			temp_rec = rec_list[cpu];
-			rec_list[cpu] = temp_rec->next;
-			if (type == REC_RECORD)
-				free_record(temp_rec->rec);
-			free(temp_rec);
-		}
-	}
-	free(rec_list);
-}
-
-static ssize_t get_records(struct kshark_context *kshark_ctx,
-			   struct rec_list ***rec_list, enum rec_type type)
-{
-	struct kshark_event_handler *evt_handler;
-	struct tep_event_filter *adv_filter;
-	struct kshark_task_list *task;
-	struct tep_record *rec;
-	struct rec_list **temp_next;
-	struct rec_list **cpu_list;
-	struct rec_list *temp_rec;
-	size_t count, total = 0;
-	int n_cpus;
-	int pid;
-	int cpu;
-
-	n_cpus = tracecmd_cpus(kshark_ctx->handle);
-	cpu_list = calloc(n_cpus, sizeof(*cpu_list));
-	if (!cpu_list)
-		return -ENOMEM;
-
-	/* Just to shorten the name */
-	if (type == REC_ENTRY)
-		adv_filter = kshark_ctx->advanced_event_filter;
-
-	for (cpu = 0; cpu < n_cpus; ++cpu) {
-		count = 0;
-		cpu_list[cpu] = NULL;
-		temp_next = &cpu_list[cpu];
-
-		rec = tracecmd_read_cpu_first(kshark_ctx->handle, cpu);
-		while (rec) {
-			*temp_next = temp_rec = calloc(1, sizeof(*temp_rec));
-			if (!temp_rec)
-				goto fail;
-
-			temp_rec->next = NULL;
-
-			switch (type) {
-			case REC_RECORD:
-				temp_rec->rec = rec;
-				pid = tep_data_pid(kshark_ctx->pevent, rec);
-				break;
-			case REC_ENTRY: {
-				struct kshark_entry *entry;
-				int ret;
-
-				if (rec->missed_events) {
-					/*
-					 * Insert a custom "missed_events" entry just
-					 * befor this record.
-					 */
-					entry = &temp_rec->entry;
-					missed_events_action(kshark_ctx, rec, entry);
-
-					temp_next = &temp_rec->next;
-					++count;
-
-					/* Now allocate a new rec_list node and comtinue. */
-					*temp_next = temp_rec = calloc(1, sizeof(*temp_rec));
-				}
-
-				entry = &temp_rec->entry;
-				kshark_set_entry_values(kshark_ctx, rec, entry);
-
-				/* Execute all plugin-provided actions (if any). */
-				evt_handler = kshark_ctx->event_handlers;
-				while ((evt_handler = kshark_find_event_handler(evt_handler,
-										entry->event_id))) {
-					evt_handler->event_func(kshark_ctx, rec, entry);
-					evt_handler = evt_handler->next;
-					entry->visible &= ~KS_PLUGIN_UNTOUCHED_MASK;
-				}
-
-				pid = entry->pid;
-				/* Apply event filtering. */
-				ret = FILTER_MATCH;
-				if (adv_filter->filters)
-					ret = tep_filter_match(adv_filter, rec);
-
-				if (!kshark_show_event(kshark_ctx, entry->event_id) ||
-				    ret != FILTER_MATCH) {
-					unset_event_filter_flag(kshark_ctx, entry);
-				}
-
-				/* Apply CPU filtering. */
-				if (!kshark_show_cpu(kshark_ctx, entry->pid)) {
-					entry->visible &= ~kshark_ctx->filter_mask;
-				}
-
-				/* Apply task filtering. */
-				if (!kshark_show_task(kshark_ctx, entry->pid)) {
-					entry->visible &= ~kshark_ctx->filter_mask;
-				}
-				free_record(rec);
-				break;
-			} /* REC_ENTRY */
-			}
-
-			task = kshark_add_task(kshark_ctx, pid);
-			if (!task) {
-				free_record(rec);
-				goto fail;
-			}
-
-			temp_next = &temp_rec->next;
-
-			++count;
-			rec = tracecmd_read_data(kshark_ctx->handle, cpu);
-		}
-
-		total += count;
-	}
-
-	*rec_list = cpu_list;
-	return total;
-
- fail:
-	free_rec_list(cpu_list, n_cpus, type);
-	return -ENOMEM;
-}
-
-static int pick_next_cpu(struct rec_list **rec_list, int n_cpus,
-			 enum rec_type type)
-{
-	uint64_t ts = 0;
-	uint64_t rec_ts;
-	int next_cpu = -1;
-	int cpu;
-
-	for (cpu = 0; cpu < n_cpus; ++cpu) {
-		if (!rec_list[cpu])
-			continue;
-
-		switch (type) {
-		case REC_RECORD:
-			rec_ts = rec_list[cpu]->rec->ts;
-			break;
-		case REC_ENTRY:
-			rec_ts = rec_list[cpu]->entry.ts;
-			break;
-		}
-		if (!ts || rec_ts < ts) {
-			ts = rec_ts;
-			next_cpu = cpu;
-		}
-	}
-
-	return next_cpu;
-}
-
-/**
- * @brief Load the content of the trace data file into an array of
- *	  kshark_entries. This function provides an abstraction of the
- *	  entries from the raw data that is read, 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.
- *	  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_data_entries(struct kshark_context *kshark_ctx,
-				 struct kshark_entry ***data_rows)
-{
-	struct kshark_entry **rows;
-	struct rec_list **rec_list;
-	enum rec_type type = REC_ENTRY;
-	ssize_t count, total = 0;
-	int n_cpus;
-
-	if (*data_rows)
-		free(*data_rows);
-
-	total = get_records(kshark_ctx, &rec_list, type);
-	if (total < 0)
-		goto fail;
-
-	n_cpus = tracecmd_cpus(kshark_ctx->handle);
-
-	rows = calloc(total, sizeof(struct kshark_entry *));
-	if (!rows)
-		goto fail_free;
-
-	for (count = 0; count < total; count++) {
-		int next_cpu;
-
-		next_cpu = pick_next_cpu(rec_list, n_cpus, type);
-
-		if (next_cpu >= 0) {
-			rows[count] = &rec_list[next_cpu]->entry;
-			rec_list[next_cpu] = rec_list[next_cpu]->next;
-		}
-	}
-
-	free_rec_list(rec_list, n_cpus, type);
-	*data_rows = rows;
-	return total;
-
- fail_free:
-	free_rec_list(rec_list, n_cpus, type);
-
- 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
- *	  tep_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 tep_record ***data_rows)
-{
-	struct tep_record **rows;
-	struct tep_record *rec;
-	struct rec_list **rec_list;
-	struct rec_list *temp_rec;
-	enum rec_type type = REC_RECORD;
-	ssize_t count, total = 0;
-	int n_cpus;
-
-	total = get_records(kshark_ctx, &rec_list, type);
-	if (total < 0)
-		goto fail;
-
-	n_cpus = tracecmd_cpus(kshark_ctx->handle);
-
-	rows = calloc(total, sizeof(struct tep_record *));
-	if (!rows)
-		goto fail_free;
-
-	for (count = 0; count < total; count++) {
-		int next_cpu;
-
-		next_cpu = pick_next_cpu(rec_list, n_cpus, type);
-
-		if (next_cpu >= 0) {
-			rec = rec_list[next_cpu]->rec;
-			rows[count] = rec;
-
-			temp_rec = rec_list[next_cpu];
-			rec_list[next_cpu] = rec_list[next_cpu]->next;
-			free(temp_rec);
-			/* The record is still referenced in rows */
-		}
-	}
-
-	/* There should be no records left in rec_list */
-	free_rec_list(rec_list, n_cpus, type);
-	*data_rows = rows;
-	return total;
-
- fail_free:
-	free_rec_list(rec_list, n_cpus, type);
-
- fail:
-	fprintf(stderr, "Failed to allocate memory during data loading.\n");
-	return -ENOMEM;
-}
-
 static inline void free_ptr(void *ptr)
 {
 	if (ptr)
@@ -1740,294 +1290,6 @@ bool kshark_data_matrix_alloc(size_t n_rows, int16_t **event_array,
 	return false;
 }
 
-static const char *get_latency(struct tep_handle *pe,
-			       struct tep_record *record)
-{
-	if (!record)
-		return NULL;
-
-	trace_seq_reset(&seq);
-	tep_print_event(pe, &seq, record, "%s", TEP_PRINT_LATENCY);
-	return seq.buffer;
-}
-
-static const char *get_info(struct tep_handle *pe,
-				   struct tep_record *record,
-				   struct tep_event *event)
-{
-	char *pos;
-
-	if (!record || !event)
-		return NULL;
-
-	trace_seq_reset(&seq);
-	tep_print_event(pe, &seq, record, "%s", TEP_PRINT_INFO);
-
-	/*
-	 * The event info string contains a trailing newline.
-	 * Remove this newline.
-	 */
-	if ((pos = strchr(seq.buffer, '\n')) != NULL)
-		*pos = '\0';
-
-	return seq.buffer;
-}
-
-/**
- * @brief This function allows for an easy access to the original value of the
- *	  Process Id as recorded in the tep_record object. The record is read
- *	  from the file only in the case of an entry being touched by a plugin.
- *	  Be aware that using the kshark_get_X_easy functions can be
- *	  inefficient if you need an access to more than one of the data fields
- *	  of the record.
- *
- * @param entry: Input location for the KernelShark entry.
- *
- * @returns The original value of the Process Id as recorded in the
- *	    tep_record object on success, otherwise negative error code.
- */
-int kshark_get_pid_easy(struct kshark_entry *entry)
-{
-	struct kshark_context *kshark_ctx = NULL;
-	struct tep_record *data;
-	int pid;
-
-	if (!kshark_instance(&kshark_ctx))
-		return -ENODEV;
-
-	if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) {
-		pid = entry->pid;
-	} else {
-		/*
-		 * The entry has been touched by a plugin callback function.
-		 * Because of this we do not trust the value of "entry->pid".
-		 *
-		 * Currently the data reading operations are not thread-safe.
-		 * Use a mutex to protect the access.
-		 */
-		pthread_mutex_lock(&kshark_ctx->input_mutex);
-
-		data = tracecmd_read_at(kshark_ctx->handle, entry->offset,
-					NULL);
-		pid = tep_data_pid(kshark_ctx->pevent, data);
-		free_record(data);
-
-		pthread_mutex_unlock(&kshark_ctx->input_mutex);
-	}
-
-	return pid;
-}
-
-/**
- * @brief This function allows for an easy access to the original value of the
- *	  Task name as recorded in the tep_record object. The record is read
- *	  from the file only in the case of an entry being touched by a plugin.
- *	  Be aware that using the kshark_get_X_easy functions can be
- *	  inefficient if you need an access to more than one of the data fields
- *	  of the record.
- *
- * @param entry: Input location for the KernelShark entry.
- *
- * @returns The original name of the task, retrieved from the Process Id
- *	    recorded in the tep_record object on success, otherwise NULL.
- */
-const char *kshark_get_task_easy(struct kshark_entry *entry)
-{
-	struct kshark_context *kshark_ctx = NULL;
-	int pid = kshark_get_pid_easy(entry);
-
-	if (pid < 0)
-		return NULL;
-
-	kshark_instance(&kshark_ctx);
-	return tep_data_comm_from_pid(kshark_ctx->pevent, pid);
-}
-
-/**
- * @brief This function allows for an easy access to the latency information
- *	  recorded in the tep_record object. The record is read from the file
- *	  using the offset field of kshark_entry. Be aware that using the
- *	  kshark_get_X_easy functions can be inefficient if you need an access
- *	  to more than one of the data fields of the record.
- *
- * @param entry: Input location for the KernelShark entry.
- *
- * @returns On success the function returns a string showing the latency
- *	    information, coded into 5 fields:
- *	    interrupts disabled, need rescheduling, hard/soft interrupt,
- *	    preempt count and lock depth. On failure it returns NULL.
- */
-const char *kshark_get_latency_easy(struct kshark_entry *entry)
-{
-	struct kshark_context *kshark_ctx = NULL;
-	struct tep_record *data;
-	const char *lat;
-
-	if (!kshark_instance(&kshark_ctx))
-		return NULL;
-
-	if (entry->event_id < 0)
-		return NULL;
-
-	/*
-	 * Currently the data reading operations are not thread-safe.
-	 * Use a mutex to protect the access.
-	 */
-	pthread_mutex_lock(&kshark_ctx->input_mutex);
-
-	data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL);
-	lat = get_latency(kshark_ctx->pevent, data);
-	free_record(data);
-
-	pthread_mutex_unlock(&kshark_ctx->input_mutex);
-
-	return lat;
-}
-
-/**
- * @brief This function allows for an easy access to the original value of the
- *	  Event Id as recorded in the tep_record object. The record is read
- *	  from the file only in the case of an entry being touched by a plugin.
- *	  Be aware that using the kshark_get_X_easy functions can be
- *	  inefficient if you need an access to more than one of the data fields
- *	  of the record.
- *
- * @param entry: Input location for the KernelShark entry.
- *
- * @returns The original value of the Event Id as recorded in the
- *	    tep_record object on success, otherwise negative error code.
- */
-int kshark_get_event_id_easy(struct kshark_entry *entry)
-{
-	struct kshark_context *kshark_ctx = NULL;
-	struct tep_record *data;
-	int event_id;
-
-	if (!kshark_instance(&kshark_ctx))
-		return -ENODEV;
-
-	if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) {
-		event_id = entry->event_id;
-	} else {
-		/*
-		 * The entry has been touched by a plugin callback function.
-		 * Because of this we do not trust the value of
-		 * "entry->event_id".
-		 *
-		 * Currently the data reading operations are not thread-safe.
-		 * Use a mutex to protect the access.
-		 */
-		pthread_mutex_lock(&kshark_ctx->input_mutex);
-
-		data = tracecmd_read_at(kshark_ctx->handle, entry->offset,
-					NULL);
-		event_id = tep_data_type(kshark_ctx->pevent, data);
-		free_record(data);
-
-		pthread_mutex_unlock(&kshark_ctx->input_mutex);
-	}
-
-	return (event_id == -1)? -EFAULT : event_id;
-}
-
-/**
- * @brief This function allows for an easy access to the original name of the
- *	  trace event as recorded in the tep_record object. The record is read
- *	  from the file only in the case of an entry being touched by a plugin.
- *	  Be aware that using the kshark_get_X_easy functions can be
- *	  inefficient if you need an access to more than one of the data fields
- *	  of the record.
- *
- * @param entry: Input location for the KernelShark entry.
- *
- * @returns The mane of the trace event recorded in the tep_record object on
- *	    success, otherwise "[UNKNOWN EVENT]" or NULL.
- */
-const char *kshark_get_event_name_easy(struct kshark_entry *entry)
-{
-	struct kshark_context *kshark_ctx = NULL;
-	struct tep_event *event;
-
-	int event_id = kshark_get_event_id_easy(entry);
-	if (event_id == -EFAULT)
-		return NULL;
-
-	kshark_instance(&kshark_ctx);
-
-	if (event_id < 0) {
-		switch (event_id) {
-		case KS_EVENT_OVERFLOW:
-			return missed_events_dump(kshark_ctx, entry, false);
-		default:
-			return NULL;
-		}
-	}
-
-	/*
-	 * Currently the data reading operations are not thread-safe.
-	 * Use a mutex to protect the access.
-	 */
-	pthread_mutex_lock(&kshark_ctx->input_mutex);
-	event = tep_find_event(kshark_ctx->pevent, event_id);
-	pthread_mutex_unlock(&kshark_ctx->input_mutex);
-
-	if (event)
-		return event->name;
-
-	return "[UNKNOWN EVENT]";
-}
-
-/**
- * @brief This function allows for an easy access to the tep_record's info
- *	  streang. The record is read from the file using the offset field of
- *	  kshark_entry. Be aware that using the kshark_get_X_easy functions can
- *	  be inefficient if you need an access to more than one of the data
- *	  fields of the record.
- *
- * @param entry: Input location for the KernelShark entry.
- *
- * @returns A string showing the data output of the trace event on success,
- *	    otherwise NULL.
- */
-const char *kshark_get_info_easy(struct kshark_entry *entry)
-{
-	struct kshark_context *kshark_ctx = NULL;
-	struct tep_event *event;
-	struct tep_record *data;
-	const char *info = NULL;
-	int event_id;
-
-	if (!kshark_instance(&kshark_ctx))
-		return NULL;
-
-	if (entry->event_id < 0) {
-		switch (entry->event_id) {
-		case KS_EVENT_OVERFLOW:
-			return missed_events_dump(kshark_ctx, entry, true);
-		default:
-			return NULL;
-		}
-	}
-
-	/*
-	 * Currently the data reading operations are not thread-safe.
-	 * Use a mutex to protect the access.
-	 */
-	pthread_mutex_lock(&kshark_ctx->input_mutex);
-
-	data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL);
-	event_id = tep_data_type(kshark_ctx->pevent, data);
-	event = tep_find_event(kshark_ctx->pevent, event_id);
-	if (event)
-		info = get_info(kshark_ctx->pevent, data, event);
-
-	free_record(data);
-
-	pthread_mutex_unlock(&kshark_ctx->input_mutex);
-
-	return info;
-}
-
 /**
  * @brief Convert the timestamp of the trace record (nanosecond precision) into
  *	  seconds and microseconds.
@@ -2044,44 +1306,6 @@ void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec)
 	*usec = (time - s * 1000000000ULL) / 1000;
 }
 
-/**
- * @brief Dump into a string the content a custom entry. The function allocates
- *	  a null terminated string and returns a pointer to this string.
- *
- * @param kshark_ctx: Input location for the session context pointer.
- * @param entry: A Kernel Shark entry to be printed.
- * @param info_func:
- *
- * @returns The returned string contains a semicolon-separated list of data
- *	    fields. The user has to free the returned string.
- */
-char* kshark_dump_custom_entry(struct kshark_context *kshark_ctx,
-			       const struct kshark_entry *entry,
-			       kshark_custom_info_func info_func)
-{
-	const char *event_name, *task, *info;
-	char *entry_str;
-	int size = 0;
-
-	task = tep_data_comm_from_pid(kshark_ctx->pevent, entry->pid);
-	event_name = info_func(kshark_ctx, entry, false);
-	info = info_func(kshark_ctx, entry, true);
-
-	size = asprintf(&entry_str, "%" PRIu64 "; %s-%i; CPU %i; ; %s; %s",
-			entry->ts,
-			task,
-			entry->pid,
-			entry->cpu,
-			event_name,
-			info);
-
-	if (size > 0)
-		return entry_str;
-
-	return NULL;
-}
-
-
 /**
  * @brief Binary search inside a time-sorted array of kshark_entries.
  *
@@ -2118,42 +1342,6 @@ ssize_t kshark_find_entry_by_time(uint64_t time,
 	return h;
 }
 
-/**
- * @brief Binary search inside a time-sorted array of tep_records.
- *
- * @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 tep_record 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_record_by_time(uint64_t time,
-				   struct tep_record **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
-	 * record having timestamp < time and "h" will be the index of the
-	 * first record having timestamp >= time.
-	 */
-	BSEARCH(h, l, data[mid]->ts < time);
-	return h;
-}
-
 /**
  * @brief Simple Pid matching function to be user for data requests.
  *
diff --git a/src/libkshark.h b/src/libkshark.h
index 523bdb6a..835be967 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -6,7 +6,7 @@
 
  /**
  *  @file    libkshark.h
- *  @brief   API for processing of FTRACE (trace-cmd) data.
+ *  @brief   API for processing of tracing data.
  */
 
 #ifndef _LIB_KSHARK_H
@@ -27,8 +27,6 @@ extern "C" {
 // trace-cmd
 #include "trace-cmd/trace-cmd.h"
 #include "trace-cmd/trace-filter-hash.h"
-#include "traceevent/event-parse.h"
-#include "tracefs/tracefs.h"
 
 // KernelShark
 #include "libkshark-plugin.h"
@@ -345,19 +343,6 @@ static inline char *kshark_set_data_format(char *dest_format,
 /** Hard-coded default number of data streams available at initialization. */
 #define KS_DEFAULT_NUM_STREAMS	256
 
-/** Size of the task's hash table. */
-#define KS_TASK_HASH_SHIFT 16
-#define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT)
-
-/** 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 the parameters of the stream descriptor array owned
  * by the kshark session.
@@ -384,15 +369,6 @@ struct kshark_context {
 	/** Parameters of the stream descriptor array. */
 	struct kshark_stream_array_descriptor	stream_info;
 
-	/** Input handle for the trace data file. */
-	struct tracecmd_input	*handle;
-
-	/** Page event used to parse the page. */
-	struct tep_handle	*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;
 
@@ -455,12 +431,6 @@ kshark_get_stream_from_entry(const struct kshark_entry *entry);
 
 int *kshark_all_streams(struct kshark_context *kshark_ctx);
 
-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 tep_record ***data_rows);
-
 ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int sd,
 			     int **pids);
 
@@ -474,18 +444,6 @@ char *kshark_comm_from_pid(int sd, int pid);
 
 char *kshark_event_from_id(int sd, int event_id);
 
-int kshark_get_pid_easy(struct kshark_entry *entry);
-
-const char *kshark_get_task_easy(struct kshark_entry *entry);
-
-const char *kshark_get_latency_easy(struct kshark_entry *entry);
-
-int kshark_get_event_id_easy(struct kshark_entry *entry);
-
-const char *kshark_get_event_name_easy(struct kshark_entry *entry);
-
-const char *kshark_get_info_easy(struct kshark_entry *entry);
-
 void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec);
 
 char* kshark_dump_entry(const struct kshark_entry *entry);
@@ -532,18 +490,6 @@ ssize_t kshark_load_matrix(struct kshark_context *kshark_ctx, int sd,
 			   int64_t **offset_array,
 			   int64_t **ts_array);
 
-/**
- * Custom entry info function type. To be user for dumping info for custom
- * KernelShark entryes.
- */
-typedef const char *(kshark_custom_info_func)(struct kshark_context *,
-					      const struct kshark_entry *,
-					      bool);
-
-char* kshark_dump_custom_entry(struct kshark_context *kshark_ctx,
-			       const struct kshark_entry *entry,
-			       kshark_custom_info_func info_func);
-
 /** Bit masks used to control the visibility of the entry after filtering. */
 enum kshark_filter_masks {
 	/**
@@ -666,10 +612,6 @@ ssize_t kshark_find_entry_by_time(uint64_t time,
 				  struct kshark_entry **data_rows,
 				  size_t l, size_t h);
 
-ssize_t kshark_find_record_by_time(uint64_t time,
-				   struct tep_record **data_rows,
-				   size_t l, size_t h);
-
 bool kshark_match_pid(struct kshark_context *kshark_ctx,
 		      struct kshark_entry *e, int pid);
 
@@ -954,16 +896,6 @@ bool kshark_export_adv_filters(struct kshark_context *kshark_ctx,
 bool kshark_import_adv_filters(struct kshark_context *kshark_ctx,
 			       struct kshark_config_doc *conf);
 
-bool kshark_export_event_filter(struct tep_handle *pevent,
-				struct tracecmd_filter_id *filter,
-				const char *filter_name,
-				struct kshark_config_doc *conf);
-
-int kshark_import_event_filter(struct tep_handle *pevent,
-			       struct tracecmd_filter_id *filter,
-			       const char *filter_name,
-			       struct kshark_config_doc *conf);
-
 bool kshark_export_user_mask(struct kshark_context *kshark_ctx,
 			     struct kshark_config_doc **conf);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 12/20] kernel-shark: Redesign the plugin interface
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (10 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 11/20] kernel-shark: Remove dead code Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 13/20] kernel-shark: Complete the stream integration Yordan Karadzhov (VMware)
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

This patch can be treated as an almost complete rewrite of the way
the C API of KernelShark deals with plugins. First of all, the two
pluggable user actions (one executed during data loading and another
executed during plotting) have separate handlers. This way the user
can register only one of the two callback functions, if this is what
is needed.

The second substantial change is that instead of having a single
interface for loading the plugin, we now have 3 different interfaces.
The one that exists in version 1 of KernelShark is now renamed to
Data Processing Interface (dpi).

The first new interface for loading can be used to register user
provided implementation of the Data stream readout and is called Data
Readout Interface (dri). Via this  plugin loading interface the user
can open trace data having an arbitrary format. In order to make this
possible the user has to provide a plugin that contains an implementation
of the data readout methods defined by the kshark_data_stream_interface
and to register all those methods.

The second new plugin loading interface is called Control interface
and can be used to provide the plugin with an access to the GUI's Main
window object. Via this interface the plugin can became capable to
modify the GUI. Such a modification for example can be to add new
dialog to the menus or to change the state of the GUI (to select entry
1with the marker, to zoom, ...).

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark-plugin.c | 658 +++++++++++++++++++++++++++++++++++------
 src/libkshark-plugin.h | 294 ++++++++++++++----
 src/libkshark.c        |  48 ++-
 src/libkshark.h        |  28 +-
 4 files changed, 866 insertions(+), 162 deletions(-)

diff --git a/src/libkshark-plugin.c b/src/libkshark-plugin.c
index 4b21392a..74a5862f 100644
--- a/src/libkshark-plugin.c
+++ b/src/libkshark-plugin.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: LGPL-2.1
 
 /*
- * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
  */
 
  /**
@@ -13,8 +13,7 @@
 #ifndef _GNU_SOURCE
 /** Use GNU C Library. */
 #define _GNU_SOURCE
-
-#endif
+#endif // _GNU_SOURCE
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -25,25 +24,39 @@
 
 // KernelShark
 #include "libkshark-plugin.h"
+#include "libkshark-tepdata.h"
 #include "libkshark.h"
 
-static struct kshark_event_handler *
-gui_event_handler_alloc(int event_id,
-			kshark_plugin_event_handler_func evt_func,
-			kshark_plugin_draw_handler_func dw_func)
+static struct kshark_event_proc_handler *
+data_event_handler_alloc(int event_id,
+			 kshark_plugin_event_handler_func evt_func)
 {
-	struct kshark_event_handler *handler = malloc(sizeof(*handler));
+	struct kshark_event_proc_handler *handler = malloc(sizeof(*handler));
 
 	if (!handler) {
-		fprintf(stderr,
-			"failed to allocate memory for gui eventhandler");
+		fputs("failed to allocate memory for event handler\n", stderr);
 		return NULL;
 	}
 
 	handler->next = NULL;
 	handler->id = event_id;
 	handler->event_func = evt_func;
-	handler->draw_func = dw_func;
+
+	return handler;
+}
+
+static struct kshark_draw_handler *
+data_draw_handler_alloc(kshark_plugin_draw_handler_func draw_func)
+{
+	struct kshark_draw_handler *handler = malloc(sizeof(*handler));
+
+	if (!handler) {
+		fputs("failed to allocate memory for draw handler\n", stderr);
+		return NULL;
+	}
+
+	handler->next = NULL;
+	handler->draw_func = draw_func;
 
 	return handler;
 }
@@ -55,8 +68,8 @@ gui_event_handler_alloc(int event_id,
  * @param handlers: Input location for the Event handler list.
  * @param event_id: Event Id to search for.
  */
-struct kshark_event_handler *
-kshark_find_event_handler(struct kshark_event_handler *handlers, int event_id)
+struct kshark_event_proc_handler *
+kshark_find_event_handler(struct kshark_event_proc_handler *handlers, int event_id)
 {
 	for (; handlers; handlers = handlers->next)
 		if (handlers->id == event_id)
@@ -68,26 +81,25 @@ kshark_find_event_handler(struct kshark_event_handler *handlers, int event_id)
 /**
  * @brief Add new event handler to an existing list of handlers.
  *
- * @param handlers: Input location for the Event handler list.
+ * @param stream: Input location for a Trace data stream pointer.
  * @param event_id: Event Id.
  * @param evt_func: Input location for an Event action provided by the plugin.
- * @param dw_func: Input location for a Draw action provided by the plugin.
  *
  * @returns Zero on success, or a negative error code on failure.
  */
-int kshark_register_event_handler(struct kshark_event_handler **handlers,
+int kshark_register_event_handler(struct kshark_data_stream *stream,
 				  int event_id,
-				  kshark_plugin_event_handler_func evt_func,
-				  kshark_plugin_draw_handler_func dw_func)
+				  kshark_plugin_event_handler_func evt_func)
 {
-	struct kshark_event_handler *handler =
-		gui_event_handler_alloc(event_id, evt_func, dw_func);
+	struct kshark_event_proc_handler *handler =
+		data_event_handler_alloc(event_id, evt_func);
 
 	if(!handler)
 		return -ENOMEM;
 
-	handler->next = *handlers;
-	*handlers = handler;
+	handler->next = stream->event_handlers;
+	stream->event_handlers = handler;
+
 	return 0;
 }
 
@@ -95,30 +107,29 @@ int kshark_register_event_handler(struct kshark_event_handler **handlers,
  * @brief Search the list for a specific plugin handle. If such a plugin handle
  *	  exists, unregister (remove and free) this handle from the list.
  *
- * @param handlers: Input location for the Event handler list.
+ * @param stream: Input location for a Trace data stream pointer.
  * @param event_id: Event Id of the plugin handler to be unregistered.
- * @param evt_func: Event action function of the handler to be unregistered.
- * @param dw_func: Draw action function of the handler to be unregistered.
+ * @param evt_func: Event action function to be unregistered.
  */
-void kshark_unregister_event_handler(struct kshark_event_handler **handlers,
-				     int event_id,
-				     kshark_plugin_event_handler_func evt_func,
-				     kshark_plugin_draw_handler_func dw_func)
+int kshark_unregister_event_handler(struct kshark_data_stream *stream,
+				    int event_id,
+				    kshark_plugin_event_handler_func evt_func)
 {
-	struct kshark_event_handler **last;
+	struct kshark_event_proc_handler **last;
 
-	for (last = handlers; *last; last = &(*last)->next) {
+	for (last = &stream->event_handlers; *last; last = &(*last)->next) {
 		if ((*last)->id == event_id &&
-		    (*last)->event_func == evt_func &&
-		    (*last)->draw_func == dw_func) {
-			struct kshark_event_handler *this_handler;
+		    (*last)->event_func == evt_func) {
+			struct kshark_event_proc_handler *this_handler;
 			this_handler = *last;
 			*last = this_handler->next;
 			free(this_handler);
 
-			return;
+			return 0;
 		}
 	}
+
+	return -EFAULT;
 }
 
 /**
@@ -126,9 +137,9 @@ void kshark_unregister_event_handler(struct kshark_event_handler **handlers,
  *
  * @param handlers: Input location for the Event handler list.
  */
-void kshark_free_event_handler_list(struct kshark_event_handler *handlers)
+void kshark_free_event_handler_list(struct kshark_event_proc_handler *handlers)
 {
-	struct kshark_event_handler *last;
+	struct kshark_event_proc_handler *last;
 
 	while (handlers) {
 		last = handlers;
@@ -137,98 +148,255 @@ void kshark_free_event_handler_list(struct kshark_event_handler *handlers)
 	}
 }
 
+/**
+ * @brief Add new event handler to an existing list of handlers.
+ *
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param draw_func: Input location for a Draw action provided by the plugin.
+ *
+ * @returns Zero on success, or a negative error code on failure.
+ */
+int kshark_register_draw_handler(struct kshark_data_stream *stream,
+				 kshark_plugin_draw_handler_func draw_func)
+{
+	struct kshark_draw_handler *handler = data_draw_handler_alloc(draw_func);
+
+	if(!handler)
+		return -ENOMEM;
+
+	handler->next = stream->draw_handlers;
+	stream->draw_handlers = handler;
+
+	return 0;
+}
+
+/**
+ * @brief Search the list for a specific plugin handle. If such a plugin handle
+ *	  exists, unregister (remove and free) this handle from the list.
+ *
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param draw_func: Draw action function to be unregistered.
+ */
+void kshark_unregister_draw_handler(struct kshark_data_stream *stream,
+				    kshark_plugin_draw_handler_func draw_func)
+{
+	struct kshark_draw_handler **last;
+
+	for (last = &stream->draw_handlers; *last; last = &(*last)->next) {
+		if ((*last)->draw_func == draw_func) {
+			struct kshark_draw_handler *this_handler;
+			this_handler = *last;
+			*last = this_handler->next;
+			free(this_handler);
+
+			return;
+		}
+	}
+}
+
+/**
+ * @brief Free all DRaw handlers in a given list.
+ *
+ * @param handlers: Input location for the Draw handler list.
+ */
+void kshark_free_draw_handler_list(struct kshark_draw_handler *handlers)
+{
+	struct kshark_draw_handler *last;
+
+	while (handlers) {
+		last = handlers;
+		handlers = handlers->next;
+		free(last);
+	}
+}
+
+/** Close and free this plugin. */
+static void free_plugin(struct kshark_plugin_list *plugin)
+{
+	dlclose(plugin->handle);
+
+	if (plugin->process_interface){
+		free(plugin->process_interface->name);
+		free(plugin->process_interface);
+	}
+
+	if (plugin->readout_interface) {
+		free(plugin->readout_interface->name);
+		free(plugin->readout_interface);
+	}
+
+	free(plugin->name);
+	free(plugin->file);
+	free(plugin);
+}
+
 /**
  * @brief Allocate memory for a new plugin. Add this plugin to the list of
- *	  plugins used by the session.
+ *	  plugins.
  *
  * @param kshark_ctx: Input location for the session context pointer.
+ * @param name: The name of the plugin to register.
  * @param file: The plugin object file to load.
  *
- * @returns Zero on success, or a negative error code on failure.
+ * @returns The plugin object on success, or NULL on failure.
  */
-int kshark_register_plugin(struct kshark_context *kshark_ctx,
-			   const char *file)
+struct kshark_plugin_list *
+kshark_register_plugin(struct kshark_context *kshark_ctx,
+		       const char *name,
+		       const char *file)
 {
-	struct kshark_plugin_list *plugin = kshark_ctx->plugins;
+	kshark_plugin_load_func init_func, close_func;
+	kshark_check_data_func check_func;
+	kshark_format_func format_func;
+	struct kshark_plugin_list *plugin;
 	struct stat st;
 	int ret;
 
-	while (plugin) {
-		if (strcmp(plugin->file, file) == 0)
-			return -EEXIST;
+	printf("loading plugin \"%s\" from %s\n", name, file);
 
-		plugin = plugin->next;
+	plugin = kshark_find_plugin(kshark_ctx->plugins, file);
+	if(plugin) {
+		fputs("the plugin is already loaded.\n", stderr);
+		return NULL;
 	}
 
 	ret = stat(file, &st);
 	if (ret < 0) {
-		fprintf(stderr, "plugin %s not found\n", file);
-		return -ENODEV;
+		fprintf(stderr, "plugin %s not found.\n", file);
+		return NULL;
 	}
 
-	plugin = calloc(sizeof(struct kshark_plugin_list), 1);
+	plugin = calloc(1, sizeof(struct kshark_plugin_list));
 	if (!plugin) {
-		fprintf(stderr, "failed to allocate memory for plugin\n");
-		return -ENOMEM;
+		fputs("failed to allocate memory for plugin.\n", stderr);
+		return NULL;
 	}
 
-	if (asprintf(&plugin->file, "%s", file) <= 0) {
+	plugin->handle = dlopen(file, RTLD_NOW | RTLD_GLOBAL);
+	if (!plugin->handle) {
 		fprintf(stderr,
-			"failed to allocate memory for plugin file name");
-		return -ENOMEM;
+			"failed to open plugin file.\n%s\n",
+			dlerror());
+		goto fail;
 	}
 
-	plugin->handle = dlopen(plugin->file, RTLD_NOW | RTLD_GLOBAL);
-	if (!plugin->handle)
+	plugin->file = strdup(file);
+	plugin->name = strdup(name);
+	if (!plugin->file|| !plugin->name)
 		goto fail;
 
-	plugin->init = dlsym(plugin->handle,
-			     KSHARK_PLUGIN_INITIALIZER_NAME);
+	plugin->ctrl_interface =
+		dlsym(plugin->handle, KSHARK_MENU_PLUGIN_INITIALIZER_NAME);
+
+	init_func = dlsym(plugin->handle,
+			  KSHARK_PLOT_PLUGIN_INITIALIZER_NAME);
+
+	close_func = dlsym(plugin->handle,
+			   KSHARK_PLOT_PLUGIN_DEINITIALIZER_NAME);
+
+	if (init_func && close_func) {
+		plugin->process_interface =
+			calloc(1, sizeof(*plugin->process_interface));
+
+		if (!plugin->process_interface)
+			goto fail;
+
+		plugin->process_interface->name = strdup(plugin->name);
+		if (!plugin->process_interface->name)
+			goto fail;
+
+		plugin->process_interface->init = init_func;
+		plugin->process_interface->close = close_func;
+	} else if (init_func || close_func) {
+		fprintf(stderr,
+			"incomplete draw interface found (will be ignored).\n%s\n",
+			dlerror());
+	}
+
+	init_func = dlsym(plugin->handle,
+			  KSHARK_INPUT_INITIALIZER_NAME);
+
+	close_func = dlsym(plugin->handle,
+			   KSHARK_INPUT_DEINITIALIZER_NAME);
+
+	check_func = dlsym(plugin->handle,
+			   KSHARK_INPUT_CHECK_NAME);
 
-	plugin->close = dlsym(plugin->handle,
-			      KSHARK_PLUGIN_DEINITIALIZER_NAME);
+	format_func = dlsym(plugin->handle,
+			    KSHARK_INPUT_FORMAT_NAME);
 
-	if (!plugin->init || !plugin->close)
+	if (init_func && close_func && check_func && format_func) {
+		plugin->readout_interface =
+			calloc(1, sizeof(*plugin->readout_interface));
+
+		if (!plugin->readout_interface)
+			goto fail;
+
+		plugin->readout_interface->name = strdup(plugin->name);
+		if (!plugin->readout_interface->name)
+			goto fail;
+
+		plugin->readout_interface->init = init_func;
+		plugin->readout_interface->close = close_func;
+		plugin->readout_interface->check_data = check_func;
+
+		kshark_set_data_format(plugin->readout_interface->data_format,
+				       format_func());
+
+		kshark_register_input(kshark_ctx, plugin->readout_interface);
+	} else if (init_func || close_func || check_func) {
+		fprintf(stderr,
+			"incomplete input interface found (will be ignored).\n%s\n",
+			dlerror());
+	}
+
+	if (!plugin->process_interface &&
+	    !plugin->readout_interface &&
+	    !plugin->ctrl_interface) {
+		fputs("no interfaces found in this plugin.\n", stderr);
 		goto fail;
+	}
 
 	plugin->next = kshark_ctx->plugins;
 	kshark_ctx->plugins = plugin;
+	kshark_ctx->n_plugins++;
 
-	return 0;
+	return plugin;
 
  fail:
-	fprintf(stderr, "cannot load plugin '%s'\n%s\n",
-		plugin->file, dlerror());
+	fprintf(stderr, "cannot load plugin '%s'\n", file);
 
-	if (plugin->handle) {
+	if (plugin->handle)
 		dlclose(plugin->handle);
-		plugin->handle = NULL;
-	}
 
-	free(plugin);
+	free_plugin(plugin);
 
-	return EFAULT;
+	return NULL;
 }
 
 /**
  * @brief Unrgister a plugin.
  *
- * @param kshark_ctx: Input location for context pointer.
+ * @param kshark_ctx: Input location for the session context pointer.
+ * @param name: The name of the plugin to unregister.
  * @param file: The plugin object file to unregister.
  */
 void kshark_unregister_plugin(struct kshark_context *kshark_ctx,
+			      const char *name,
 			      const char *file)
 {
 	struct kshark_plugin_list **last;
 
 	for (last = &kshark_ctx->plugins; *last; last = &(*last)->next) {
-		if (strcmp((*last)->file, file) == 0) {
+		if (strcmp((*last)->process_interface->name, name) == 0 &&
+		    strcmp((*last)->file, file) == 0) {
 			struct kshark_plugin_list *this_plugin;
+
 			this_plugin = *last;
 			*last = this_plugin->next;
+			free_plugin(this_plugin);
 
-			dlclose(this_plugin->handle);
-			free(this_plugin);
+			kshark_ctx->n_plugins--;
 
 			return;
 		}
@@ -248,48 +416,346 @@ void kshark_free_plugin_list(struct kshark_plugin_list *plugins)
 		last = plugins;
 		plugins = plugins->next;
 
-		free(last->file);
-		dlclose(last->handle);
+		free_plugin(last);
+	}
+}
+
+/**
+ * @brief Register a data readout interface (input).
+ *
+ * @param kshark_ctx: Input location for the context pointer.
+ * @param plugin: Input location for the data readout interface (input).
+ */
+struct kshark_dri_list *
+kshark_register_input(struct kshark_context *kshark_ctx,
+		      struct kshark_dri *plugin)
+{
+	struct kshark_dri_list *input;
+	struct kshark_dri_list **last;
+	const char *name_err, *format_err;
+
+	if (strcmp(plugin->data_format, TEP_DATA_FORMAT_IDENTIFIER) == 0) {
+		name_err = "built in";
+		format_err = TEP_DATA_FORMAT_IDENTIFIER;
+			goto conflict;
+	}
+
+	for (last = &kshark_ctx->inputs; *last; last = &(*last)->next)
+		if (strcmp((*last)->interface->name, plugin->name) == 0 ||
+		    strcmp((*last)->interface->data_format, plugin->data_format) == 0 ) {
+			name_err = (*last)->interface->name;
+			format_err = (*last)->interface->data_format;
+			goto conflict;
+		}
+
+	input = calloc(1, sizeof(*input));
+	if (!input) {
+		fputs("failed to allocate memory for readout plugin.\n", stderr);
+		return NULL;
+	}
+
+	input->interface = plugin;
+	input->next = kshark_ctx->inputs;
+	kshark_ctx->inputs = input;
+
+	return input;
+
+ conflict:
+	fprintf(stderr,
+		"Failed to register readout plugin (name=\'%s\', data_format=\'%s\')\n",
+		plugin->name, plugin->data_format);
+
+	fprintf(stderr,
+		"Conflict with registered readout  (name=\'%s\', data_format=\'%s\')\n",
+		name_err, format_err);
+
+	return NULL;
+}
+
+/**
+ * @brief Unrgister a data readout interface (input).
+ *
+ * @param kshark_ctx: Input location for the context pointer.
+ * @param name: The data readout's name.
+ */
+void kshark_unregister_input(struct kshark_context *kshark_ctx,
+			     const char *name)
+{
+	struct kshark_dri_list **last;
+
+	for (last = &kshark_ctx->inputs; *last; last = &(*last)->next) {
+		if (strcmp((*last)->interface->name, name) == 0) {
+			struct kshark_dri_list *this_input;
+			this_input = *last;
+			*last = this_input->next;
+
+			free(this_input);
+
+			return;
+		}
+	}
+}
 
+/**
+ * @brief Free a list of plugin interfaces.
+ *
+ * @param plugins: Input location for the plugins list.
+ */
+void
+kshark_free_dpi_list(struct kshark_dpi_list *plugins)
+{
+	struct kshark_dpi_list *last;
+
+	while (plugins) {
+		last = plugins;
+		plugins = plugins->next;
 		free(last);
 	}
 }
 
 /**
- * @brief Use this function to initialize/update/deinitialize all registered
- *	  plugins.
+ * @brief Find a plugin by its library file.
  *
- * @param kshark_ctx: Input location for context pointer.
+ * @param plugins: A list of plugins to search in.
+ * @param lib: The plugin object file to load.
+ *
+ * @returns The plugin object on success, or NULL on failure.
+ */
+struct kshark_plugin_list *
+kshark_find_plugin(struct kshark_plugin_list *plugins, const char *lib)
+{
+	for (; plugins; plugins = plugins->next)
+		if (strcmp(plugins->file, lib) == 0)
+			return plugins;
+
+	return NULL;
+}
+
+/**
+ * @brief Find a plugin by its name.
+ *
+ * @param plugins: A list of plugins to search in.
+ * @param name: The plugin object file to load.
+ *
+ * @returns The plugin object on success, or NULL on failure.
+ */
+struct kshark_plugin_list *
+kshark_find_plugin_by_name(struct kshark_plugin_list *plugins,
+			   const char *name)
+{
+	for (; plugins; plugins = plugins->next)
+		if (strcmp(plugins->name, name) == 0)
+			return plugins;
+
+	return NULL;
+}
+
+/**
+ * @brief Register plugin to a given data stream without initializing it.
+ *	  In order to initialize this plugin use kshark_handle_dpi() or
+ *	  kshark_handle_all_dpis().
+ *
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param plugin: Input location for the data processing interface.
+ * @param active: If false, the plugin will be registered but disabled.
+ *		  Otherwise the plugin will be active.
+ *
+ * @returns The plugin object on success, or NULL on failure.
+ */
+struct kshark_dpi_list *
+kshark_register_plugin_to_stream(struct kshark_data_stream *stream,
+				 struct kshark_dpi *plugin,
+				 bool active)
+{
+	struct kshark_dpi_list *plugin_list = stream->plugins;
+
+	/* Check if the plugin is already registered to this stream. */
+	while (plugin_list) {
+		if (strcmp(plugin_list->interface->name, plugin->name) == 0 &&
+		    plugin_list->interface->init == plugin->init &&
+		    plugin_list->interface->close == plugin->close) {
+			/*
+			 * The plugin has been registered already. Check if it
+			 * is initialized and if this is the case, close the
+			 * existing initialization. This way we guarantee a
+			 * clean new initialization from.
+			 */
+			if (plugin_list->status & KSHARK_PLUGIN_LOADED)
+				kshark_handle_dpi(stream, plugin_list,
+						  KSHARK_PLUGIN_CLOSE);
+
+			plugin_list->status =
+				active ? KSHARK_PLUGIN_ENABLED : 0;
+
+			return plugin_list;
+		}
+
+		plugin_list = plugin_list->next;
+	}
+
+	plugin_list = calloc(1, sizeof(*plugin_list));
+	plugin_list->interface = plugin;
+
+	if (active)
+		plugin_list->status = KSHARK_PLUGIN_ENABLED;
+
+	plugin_list->next = stream->plugins;
+	stream->plugins = plugin_list;
+	stream->n_plugins++;
+
+	return plugin_list;
+}
+
+/**
+ * @brief Unregister plugin to a given data stream.
+ *
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param plugin: Input location for the data processing interface.
+ */
+void kshark_unregister_plugin_from_stream(struct kshark_data_stream *stream,
+				          struct kshark_dpi *plugin)
+{
+	struct kshark_dpi_list **last;
+
+	for (last = &stream->plugins; *last; last = &(*last)->next) {
+		if ((*last)->interface->init == plugin->init &&
+		    (*last)->interface->close == plugin->close &&
+		    strcmp((*last)->interface->name, plugin->name) == 0) {
+			struct kshark_dpi_list *this_plugin;
+
+			this_plugin = *last;
+			*last = this_plugin->next;
+			this_plugin->interface->close(stream);
+			free(this_plugin);
+
+			stream->n_plugins--;
+
+			return;
+		}
+	}
+}
+
+static void plugin_init(struct kshark_data_stream *stream,
+			struct kshark_dpi_list *plugin)
+{
+	int handler_count = plugin->interface->init(stream);
+
+	if (handler_count > 0) {
+		plugin->status &= ~KSHARK_PLUGIN_FAILED;
+		plugin->status |= KSHARK_PLUGIN_LOADED;
+	} else {
+		if (strcmp(stream->name, KS_UNNAMED) == 0) {
+			fprintf(stderr,
+			"plugin \"%s\" failed to initialize on stream %s\n",
+			plugin->interface->name,
+			stream->file);
+		} else {
+		fprintf(stderr,
+			"plugin \"%s\" failed to initialize on stream %s:%s\n",
+			plugin->interface->name,
+			stream->file,
+			stream->name);
+		}
+
+		plugin->status |= KSHARK_PLUGIN_FAILED;
+		plugin->status &= ~KSHARK_PLUGIN_LOADED;
+	}
+}
+
+static void plugin_close(struct kshark_data_stream *stream,
+			 struct kshark_dpi_list *plugin)
+{
+	plugin->interface->close(stream);
+	plugin->status &= ~KSHARK_PLUGIN_LOADED;
+}
+
+/**
+ * @brief Use this function to initialize/update/deinitialize a plugin for
+ *	  a given Data stream.
+ *
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param plugin: The plugin to be handled.
  * @param task_id: Action identifier specifying the action to be executed.
  *
  * @returns The number of successful added/removed plugin handlers on success,
  *	    or a negative error code on failure.
  */
-int kshark_handle_plugins(struct kshark_context *kshark_ctx,
-			  enum kshark_plugin_actions task_id)
+int kshark_handle_dpi(struct kshark_data_stream *stream,
+		      struct kshark_dpi_list *plugin,
+		      enum kshark_plugin_actions task_id)
 {
-	struct kshark_plugin_list *plugin;
 	int handler_count = 0;
 
-	for (plugin = kshark_ctx->plugins; plugin; plugin = plugin->next) {
-		switch (task_id) {
-		case KSHARK_PLUGIN_INIT:
-			handler_count += plugin->init(kshark_ctx);
-			break;
+	switch (task_id) {
+	case KSHARK_PLUGIN_INIT:
+		if (plugin->status & KSHARK_PLUGIN_ENABLED)
+			plugin_init(stream, plugin);
 
-		case KSHARK_PLUGIN_UPDATE:
-			plugin->close(kshark_ctx);
-			handler_count += plugin->init(kshark_ctx);
-			break;
+		break;
 
-		case KSHARK_PLUGIN_CLOSE:
-			handler_count += plugin->close(kshark_ctx);
-			break;
+	case KSHARK_PLUGIN_UPDATE:
+		if (plugin->status & KSHARK_PLUGIN_LOADED)
+			plugin_close(stream, plugin);
 
-		default:
-			return -EINVAL;
-		}
+		plugin->status &= ~KSHARK_PLUGIN_FAILED;
+
+		if (plugin->status & KSHARK_PLUGIN_ENABLED)
+			plugin_init(stream, plugin);
+
+		break;
+
+	case KSHARK_PLUGIN_CLOSE:
+		if (plugin->status & KSHARK_PLUGIN_LOADED)
+			plugin_close(stream, plugin);
+
+		plugin->status &= ~KSHARK_PLUGIN_FAILED;
+		break;
+
+	default:
+		return -EINVAL;
 	}
 
 	return handler_count;
 }
+
+/**
+ * @brief Use this function to initialize/update/deinitialize all registered
+ *	  data processing plugins for a given Data stream.
+ *
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param task_id: Action identifier specifying the action to be executed. Can
+ *		   be KSHARK_PLUGIN_INIT, KSHARK_PLUGIN_UPDATE or
+ *		   KSHARK_PLUGIN_CLOSE.
+ *
+ * @returns The number of successful added/removed plugin handlers on success,
+ *	    or a negative error code on failure.
+ */
+int kshark_handle_all_dpis(struct kshark_data_stream *stream,
+			   enum kshark_plugin_actions task_id)
+{
+	struct kshark_dpi_list *plugin;
+	int handler_count = 0;
+
+	for (plugin = stream->plugins; plugin; plugin = plugin->next)
+		handler_count +=
+			kshark_handle_dpi(stream, plugin, task_id);
+
+	return handler_count;
+}
+
+/**
+ * @brief Free all readout interfaces in a given list.
+ *
+ * @param inputs: Input location for the inputs list.
+ */
+void kshark_free_dri_list(struct kshark_dri_list *inputs)
+{
+	struct kshark_dri_list *last;
+
+	while (inputs) {
+		last = inputs;
+		inputs = inputs->next;
+
+		free(last);
+	}
+}
diff --git a/src/libkshark-plugin.h b/src/libkshark-plugin.h
index b3cf1c62..1a642ad9 100644
--- a/src/libkshark-plugin.h
+++ b/src/libkshark-plugin.h
@@ -4,10 +4,10 @@
  * Copyright (C) 2016 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  */
 
- /**
-  *  @file    libkshark-plugin.h
-  *  @brief   KernelShark plugins.
-  */
+/**
+ *  @file    libkshark-plugin.h
+ *  @brief   KernelShark plugins.
+ */
 
 #ifndef _KSHARK_PLUGIN_H
 #define _KSHARK_PLUGIN_H
@@ -16,35 +16,51 @@
 extern "C" {
 #endif // __cplusplus
 
-// trace-cmd
-#include "traceevent/event-parse.h"
+// C
+#include <stdint.h>
+#include <stdbool.h>
 
 /* Quiet warnings over documenting simple structures */
 //! @cond Doxygen_Suppress
 
-#define KSHARK_PLUGIN_INITIALIZER kshark_plugin_initializer
-
-#define KSHARK_PLUGIN_DEINITIALIZER kshark_plugin_deinitializer
-
 #define _MAKE_STR(x)	#x
+
 #define MAKE_STR(x)	_MAKE_STR(x)
 
-#define KSHARK_PLUGIN_INITIALIZER_NAME MAKE_STR(KSHARK_PLUGIN_INITIALIZER)
+#define KSHARK_PLOT_PLUGIN_INITIALIZER kshark_data_plugin_initializer
 
-#define KSHARK_PLUGIN_DEINITIALIZER_NAME MAKE_STR(KSHARK_PLUGIN_DEINITIALIZER)
+#define KSHARK_PLOT_PLUGIN_DEINITIALIZER kshark_data_plugin_deinitializer
 
-struct kshark_context;
+#define KSHARK_PLOT_PLUGIN_INITIALIZER_NAME MAKE_STR(KSHARK_PLOT_PLUGIN_INITIALIZER)
+
+#define KSHARK_PLOT_PLUGIN_DEINITIALIZER_NAME MAKE_STR(KSHARK_PLOT_PLUGIN_DEINITIALIZER)
+
+#define KSHARK_MENU_PLUGIN_INITIALIZER kshark_plugin_menu_initializer
+
+#define KSHARK_MENU_PLUGIN_INITIALIZER_NAME MAKE_STR(KSHARK_MENU_PLUGIN_INITIALIZER)
+
+#define KSHARK_INPUT_INITIALIZER kshark_input_initializer
+
+#define KSHARK_INPUT_DEINITIALIZER kshark_input_deinitializer
+
+#define KSHARK_INPUT_CHECK kshark_input_check
+
+#define KSHARK_INPUT_FORMAT kshark_input_format
+
+#define KSHARK_INPUT_INITIALIZER_NAME MAKE_STR(KSHARK_INPUT_INITIALIZER)
 
+#define KSHARK_INPUT_DEINITIALIZER_NAME MAKE_STR(KSHARK_INPUT_DEINITIALIZER)
+
+#define KSHARK_INPUT_CHECK_NAME MAKE_STR(KSHARK_INPUT_CHECK)
+
+#define KSHARK_INPUT_FORMAT_NAME MAKE_STR(KSHARK_INPUT_FORMAT)
+
+struct kshark_data_stream;
+struct kshark_context;
 struct kshark_entry;
 
 //! @endcond
 
-/**
- * A function type to be used when defining load/reload/unload plugin
- * functions.
- */
-typedef int (*kshark_plugin_load_func)(struct kshark_context *);
-
 struct kshark_trace_histo;
 
 /**
@@ -57,18 +73,17 @@ struct kshark_cpp_argv {
 };
 
 /** A function type to be used when defining plugin functions for drawing. */
-typedef void
-(*kshark_plugin_draw_handler_func)(struct kshark_cpp_argv *argv,
-				   int val, int draw_action);
+typedef void (*kshark_plugin_draw_handler_func)(struct kshark_cpp_argv *argv,
+						int sd,
+						int val,
+						int draw_action);
 
 /**
  * A function type to be used when defining plugin functions for data
  * manipulation.
  */
-typedef void
-(*kshark_plugin_event_handler_func)(struct kshark_context *kshark_ctx,
-				    struct tep_record *rec,
-				    struct kshark_entry *e);
+typedef void (*kshark_plugin_event_handler_func)(struct kshark_data_stream *stream,
+						 void *rec, struct kshark_entry *e);
 
 /** Plugin action identifier. */
 enum kshark_plugin_actions {
@@ -89,30 +104,39 @@ enum kshark_plugin_actions {
 	 * plugins.
 	 */
 	KSHARK_PLUGIN_CLOSE,
+};
 
+/** Plotting action identifier. */
+enum kshark_plotting_actions {
 	/**
 	 * Task draw action. This action identifier is used by the plugin draw
 	 * function.
 	 */
-	KSHARK_PLUGIN_TASK_DRAW,
+	KSHARK_TASK_DRAW	= 1 << 0,
 
 	/**
 	 * CPU draw action. This action identifier is used by the plugin draw
 	 * function.
 	 */
-	KSHARK_PLUGIN_CPU_DRAW,
+	KSHARK_CPU_DRAW		= 1 << 1,
+
+	/**
+	 * Draw action for the Host graph in Virtual Combos. This action
+	 * identifier is used by the plugin draw function.
+	 */
+	KSHARK_HOST_DRAW	= 1 << 2,
+
+	/**
+	 * Draw action for the Guest graph in Virtual Combos. This action
+	 * identifier is used by the plugin draw function.
+	 */
+	KSHARK_GUEST_DRAW	= 1 << 3,
 };
 
-/**
- * Plugin Event handler structure, defining the properties of the required
- * kshark_entry.
- */
-struct kshark_event_handler {
+/** Plugin's Trace event processing handler structure. */
+struct kshark_event_proc_handler {
 	/** Pointer to the next Plugin Event handler. */
-	struct kshark_event_handler		*next;
-
-	/** Unique Id ot the trace event type. */
-	int					id;
+	struct kshark_event_proc_handler		*next;
 
 	/**
 	 * Event action function. This action can be used to modify the content
@@ -120,6 +144,28 @@ struct kshark_event_handler {
 	 */
 	kshark_plugin_event_handler_func	event_func;
 
+	/** Unique Id ot the trace event type. */
+	int id;
+};
+
+struct kshark_event_proc_handler *
+kshark_find_event_handler(struct kshark_event_proc_handler *handlers, int event_id);
+
+int kshark_register_event_handler(struct kshark_data_stream *stream,
+				  int event_id,
+				  kshark_plugin_event_handler_func evt_func);
+
+int kshark_unregister_event_handler(struct kshark_data_stream *stream,
+				    int event_id,
+				    kshark_plugin_event_handler_func evt_func);
+
+void kshark_free_event_handler_list(struct kshark_event_proc_handler *handlers);
+
+/** Plugin's drawing handler structure. */
+struct kshark_draw_handler {
+	/** Pointer to the next Plugin Event handler. */
+	struct kshark_draw_handler		*next;
+
 	/**
 	 * Draw action function. This action can be used to draw additional
 	 * graphical elements (shapes) for all kshark_entries having Event Ids
@@ -128,31 +174,73 @@ struct kshark_event_handler {
 	kshark_plugin_draw_handler_func		draw_func;
 };
 
-struct kshark_event_handler *
-kshark_find_event_handler(struct kshark_event_handler *handlers, int event_id);
+int kshark_register_draw_handler(struct kshark_data_stream *stream,
+				 kshark_plugin_draw_handler_func draw_func);
 
-int kshark_register_event_handler(struct kshark_event_handler **handlers,
-				  int event_id,
-				  kshark_plugin_event_handler_func evt_func,
-				  kshark_plugin_draw_handler_func dw_func);
+void kshark_unregister_draw_handler(struct kshark_data_stream *stream,
+				    kshark_plugin_draw_handler_func draw_func);
 
-void kshark_unregister_event_handler(struct kshark_event_handler **handlers,
-				     int event_id,
-				     kshark_plugin_event_handler_func evt_func,
-				     kshark_plugin_draw_handler_func dw_func);
+void kshark_free_draw_handler_list(struct kshark_draw_handler *handlers);
 
-void kshark_free_event_handler_list(struct kshark_event_handler *handlers);
+/**
+ * A function type to be used when defining load/reload/unload plugin
+ * functions.
+ */
+typedef int (*kshark_plugin_load_func)(struct kshark_data_stream *);
 
-/** Linked list of plugins. */
-struct kshark_plugin_list {
-	/** Pointer to the next Plugin. */
-	struct kshark_plugin_list	*next;
+/**
+ * A function type to be used when defining data check function for the plugin.
+ */
+typedef bool (*kshark_check_data_func)(const char *file_name);
 
-	/** The plugin object file to load. */
-	char				*file;
+/**
+ * A function type to be used when defining data format function for the plugin.
+ */
+typedef const char *(*kshark_format_func) ();
+
+/**
+ * A function type to be used when defining plugin's configuration/control
+ * function.
+ */
+typedef void *(*kshark_plugin_ctrl_func)(void *);
+
+/** The limit in size of the data format identifier string. */
+#define KS_DATA_FORMAT_SIZE	15
 
-	/** Plugin Event handler. */
-	void				*handle;
+/** Plugable Data Readout Interface (dri). */
+struct kshark_dri {
+	/** A short name for this data input. */
+	char				*name;
+
+	/** Data format identifier. */
+	char				data_format[KS_DATA_FORMAT_SIZE];
+
+	/** Callback function for initialization of the data input. */
+	kshark_plugin_load_func		init;
+
+	/** Callback function for deinitialization of the data input. */
+	kshark_plugin_load_func		close;
+
+	/**
+	 * Callback function for checking if the data input is applicable for
+	 * a given data file.
+	 */
+	kshark_check_data_func		check_data;
+};
+
+/** Linked list of Data Readout Interfaces (dri). */
+struct kshark_dri_list {
+	/** Pointer to the next input interface. */
+	struct kshark_dri_list		*next;
+
+	/** Pointer to the interface of methods used by the input. */
+	struct kshark_dri		*interface;
+};
+
+/** Plugable Data Processing Interface (dpi). */
+struct kshark_dpi {
+	/** The plugin's short name. */
+	char				*name;
 
 	/** Callback function for initialization of the plugin. */
 	kshark_plugin_load_func		init;
@@ -161,16 +249,102 @@ struct kshark_plugin_list {
 	kshark_plugin_load_func		close;
 };
 
-int kshark_register_plugin(struct kshark_context *kshark_ctx,
-			   const char *file);
+/** Linked list of data processing interfaces (dpi). */
+struct kshark_dpi_list {
+	/** Pointer to the next plugin interface. */
+	struct kshark_dpi_list		*next;
+
+	/** Pointer to the interface of methods used by the plugin. */
+	struct kshark_dpi		*interface;
+
+	/**
+	 * The status of the interface.
+	 */
+	int				status;
+};
+
+struct kshark_dri_list *
+kshark_register_input(struct kshark_context *kshark_ctx,
+		      struct kshark_dri *plugin);
+
+void kshark_unregister_input(struct kshark_context *kshark_ctx,
+			     const char *file);
+
+void kshark_free_dri_list(struct kshark_dri_list *inputs);
+
+/** Linked list of plugins. */
+struct kshark_plugin_list {
+	/** Pointer to the next plugin. */
+	struct kshark_plugin_list	*next;
+
+	/** The plugin's short name. */
+	char	*name;
+
+	/** The plugin object file to load. */
+	char	*file;
+
+	/** Plugin's object file handler. */
+	void	*handle;
+
+	/**
+	 * Control interface of the plugin. Can be used to configure
+	 * the plugin.
+	 */
+	kshark_plugin_ctrl_func		ctrl_interface;
+
+	/** The interface of methods used by a data processing plugin. */
+	struct kshark_dpi		*process_interface;
+
+	/** The interface of methods used by a data readout plugin. */
+	struct kshark_dri		*readout_interface;
+};
+
+/** Plugin status identifiers. */
+enum kshark_plugin_status {
+	/** The plugin is enabled. */
+	KSHARK_PLUGIN_ENABLED	= 1 << 0,
+
+	/** The plugin is successfully loaded. */
+	KSHARK_PLUGIN_LOADED	= 1 << 1,
+
+	/** The plugin failed to initialization. */
+	KSHARK_PLUGIN_FAILED	= 1 << 2,
+};
+
+struct kshark_plugin_list *
+kshark_register_plugin(struct kshark_context *kshark_ctx,
+		       const char *name,
+		       const char *file);
 
 void kshark_unregister_plugin(struct kshark_context *kshark_ctx,
+			      const char *name,
 			      const char *file);
 
 void kshark_free_plugin_list(struct kshark_plugin_list *plugins);
 
-int kshark_handle_plugins(struct kshark_context *kshark_ctx,
-			  enum kshark_plugin_actions  task_id);
+void kshark_free_dpi_list(struct kshark_dpi_list *plugins);
+
+struct kshark_plugin_list *
+kshark_find_plugin(struct kshark_plugin_list *plugins, const char *file);
+
+struct kshark_plugin_list *
+kshark_find_plugin_by_name(struct kshark_plugin_list *plugins,
+			   const char *name);
+
+struct kshark_dpi_list *
+kshark_register_plugin_to_stream(struct kshark_data_stream *stream,
+				 struct kshark_dpi *plugin,
+				 bool active);
+
+void kshark_unregister_plugin_from_stream(struct kshark_data_stream *stream,
+					  struct kshark_dpi *plugin);
+
+int kshark_handle_dpi(struct kshark_data_stream *stream,
+		      struct kshark_dpi_list *plugin,
+		      enum kshark_plugin_actions task_id);
+
+int kshark_handle_all_dpis(struct kshark_data_stream *stream,
+			   enum kshark_plugin_actions  task_id);
 
 #ifdef __cplusplus
 }
diff --git a/src/libkshark.c b/src/libkshark.c
index 63dd233e..ef36d211 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -16,6 +16,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <assert.h>
+#include <string.h>
 
 // KernelShark
 #include "libkshark.h"
@@ -36,7 +37,6 @@ static bool kshark_default_context(struct kshark_context **context)
 	kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS;
 	kshark_ctx->stream_info.max_stream_id = -1;
 
-	kshark_ctx->event_handlers = NULL;
 	kshark_ctx->collections = NULL;
 	kshark_ctx->plugins = NULL;
 
@@ -303,6 +303,7 @@ int kshark_add_stream(struct kshark_context *kshark_ctx)
 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;
@@ -316,6 +317,14 @@ int kshark_stream_open(struct kshark_data_stream *stream, const char *file)
 		return kshark_tep_init_input(stream, file);
 	}
 
+	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;
 }
 
@@ -413,6 +422,7 @@ int *kshark_all_streams(struct kshark_context *kshark_ctx)
 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;
@@ -431,6 +441,10 @@ static int kshark_stream_close(struct kshark_data_stream *stream)
 	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;
 }
 
@@ -451,6 +465,13 @@ int kshark_close(struct kshark_context *kshark_ctx, int sd)
 	if (!stream)
 		return -EFAULT;
 
+	/* 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);
 
@@ -496,6 +517,8 @@ void kshark_free(struct kshark_context *kshark_ctx)
 	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;
 
@@ -1219,6 +1242,29 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx,
 		set_all_visible(&data[i]->visible);
 }
 
+/**
+ * @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;
+		}
+	}
+}
+
 static inline void free_ptr(void *ptr)
 {
 	if (ptr)
diff --git a/src/libkshark.h b/src/libkshark.h
index 835be967..95f501de 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -272,9 +272,6 @@ struct kshark_generic_stream_interface {
 	void			*handle;
 };
 
-/** The limit in size of the data format identifier string. */
-#define KS_DATA_FORMAT_SIZE	15
-
 /** Data format identifier string indicating invalid data. */
 #define KS_INVALID_DATA		"invalid data"
 
@@ -327,6 +324,18 @@ struct kshark_data_stream {
 	/** The type of the data. */
 	char			data_format[KS_DATA_FORMAT_SIZE];
 
+	/** List of Plugin interfaces. */
+	struct kshark_dpi_list	*plugins;
+
+	/** The number of plugins registered for this stream.*/
+	int			n_plugins;
+
+	/** List of Plugin's Event handlers. */
+	struct kshark_event_proc_handler	*event_handlers;
+
+	/** List of Plugin's Draw handlers. */
+	struct kshark_draw_handler		*draw_handlers;
+
 	/**
 	 * The interface of methods used to operate over the data from a given
 	 * stream.
@@ -406,11 +415,17 @@ struct kshark_context {
 	/** List of Data collections. */
 	struct kshark_entry_collection *collections;
 
+	/** List of data readout interfaces. */
+	struct kshark_dri_list		*inputs;
+
+	/** The number of readout interfaces. */
+	int				n_inputs;
+
 	/** List of Plugins. */
 	struct kshark_plugin_list	*plugins;
 
-	/** List of Plugin Event handlers. */
-	struct kshark_event_handler	*event_handlers;
+	/** The number of plugins. */
+	int				n_plugins;
 };
 
 bool kshark_instance(struct kshark_context **kshark_ctx);
@@ -587,6 +602,9 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx,
 			      struct kshark_entry **data,
 			      size_t n_entries);
 
+void kshark_plugin_actions(struct kshark_data_stream *stream,
+			   void *record, struct kshark_entry *entry);
+
 /** Search failed identifiers. */
 enum kshark_search_failed {
 	/** All entries have timestamps greater timestamps. */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 13/20] kernel-shark: Complete the stream integration
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (11 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 12/20] kernel-shark: Redesign the plugin interface Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams Yordan Karadzhov (VMware)
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The patch contains a number various relatively small modifications
needed in order to finalize the integration. Unfortunately those
changes are hard to disentangle so we will do everything in a single
patch.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 examples/CMakeLists.txt    |   8 +-
 examples/datafilter.c      |  67 +++---
 src/libkshark-collection.c | 121 ++++++++--
 src/libkshark-tepdata.c    | 390 +++++++++++++++++++++++++++++++
 src/libkshark-tepdata.h    |  51 +++++
 src/libkshark.c            | 454 +++++++++++++++++++++++++------------
 src/libkshark.h            | 133 ++++++-----
 7 files changed, 959 insertions(+), 265 deletions(-)

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index f6ed8978..8d40e42c 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -4,10 +4,10 @@ message(STATUS "dataload")
 add_executable(dload          dataload.c)
 target_link_libraries(dload   kshark)
 
-# message(STATUS "datafilter")
-# add_executable(dfilter          datafilter.c)
-# target_link_libraries(dfilter   kshark)
-#
+message(STATUS "datafilter")
+add_executable(dfilter          datafilter.c)
+target_link_libraries(dfilter   kshark)
+
 # message(STATUS "datahisto")
 # add_executable(dhisto          datahisto.c)
 # target_link_libraries(dhisto   kshark)
diff --git a/examples/datafilter.c b/examples/datafilter.c
index bebc1813..38afab81 100644
--- a/examples/datafilter.c
+++ b/examples/datafilter.c
@@ -7,22 +7,22 @@
 // C
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 // KernelShark
 #include "libkshark.h"
+#include "libkshark-tepdata.h"
 
 const char *default_file = "trace.dat";
 
 int main(int argc, char **argv)
 {
-	ssize_t i, n_rows, n_tasks, n_evts, count;
+	size_t i, sd, n_rows, n_tasks, n_evts, count;
 	struct kshark_context *kshark_ctx;
+	struct kshark_data_stream *stream;
 	struct kshark_entry **data = NULL;
-	struct tep_event_filter *adv_filter;
-	struct tep_event *event;
+	int *pids, *evt_ids;
 	char *entry_str;
-	bool status;
-	int *pids;
 
 	/* Create a new kshark session. */
 	kshark_ctx = NULL;
@@ -31,32 +31,30 @@ int main(int argc, char **argv)
 
 	/* Open a trace data file produced by trace-cmd. */
 	if (argc > 1)
-		status = kshark_open(kshark_ctx, argv[1]);
+		sd = kshark_open(kshark_ctx, argv[1]);
 	else
-		status = kshark_open(kshark_ctx, default_file);
+		sd = kshark_open(kshark_ctx, default_file);
 
-	if (!status) {
+	if (sd < 0) {
 		kshark_free(kshark_ctx);
 		return 1;
 	}
 
 	/* Load the content of the file into an array of entries. */
-	n_rows = kshark_load_data_entries(kshark_ctx, &data);
-	if (n_rows < 1) {
-		kshark_free(kshark_ctx);
-		return 1;
-	}
+	n_rows = kshark_load_entries(kshark_ctx, sd, &data);
 
 	/* Filter the trace data coming from trace-cmd. */
-	n_tasks = kshark_get_task_pids(kshark_ctx, &pids);
+	n_tasks = kshark_get_task_pids(kshark_ctx, sd, &pids);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
 	for (i = 0; i < n_tasks; ++i) {
-		const char *task_str =
-			tep_data_comm_from_pid(kshark_ctx->pevent,
-					       pids[i]);
+		char *task_str =
+			kshark_comm_from_pid(sd, pids[i]);
 
 		if (strcmp(task_str, "trace-cmd") == 0)
-			kshark_filter_add_id(kshark_ctx, KS_HIDE_TASK_FILTER,
-							 pids[i]);
+			kshark_filter_add_id(kshark_ctx, sd,
+					     KS_HIDE_TASK_FILTER,
+					     pids[i]);
+		free(task_str);
 	}
 
 	free(pids);
@@ -66,7 +64,8 @@ int main(int argc, char **argv)
 	 * filterd entris in text format.
 	 */
 	kshark_ctx->filter_mask = KS_TEXT_VIEW_FILTER_MASK;
-	kshark_filter_entries(kshark_ctx, data, n_rows);
+	kshark_ctx->filter_mask |= KS_EVENT_VIEW_FILTER_MASK;
+	kshark_filter_stream_entries(kshark_ctx, sd, data, n_rows);
 
 	/* Print to the screen the first 10 visible entries. */
 	count = 0;
@@ -87,15 +86,19 @@ int main(int argc, char **argv)
 	puts("\n\n");
 
 	/* Show only "sched" events. */
-	n_evts = tep_get_events_count(kshark_ctx->pevent);
+	n_evts = stream->n_events;
+	evt_ids = kshark_get_all_event_ids(kshark_ctx->stream[sd]);
 	for (i = 0; i < n_evts; ++i) {
-		event = tep_get_event(kshark_ctx->pevent, i);
-		if (strcmp(event->system, "sched") == 0)
-			kshark_filter_add_id(kshark_ctx, KS_SHOW_EVENT_FILTER,
-							 event->id);
+		char *event_str =
+			kshark_event_from_id(sd, evt_ids[i]);
+		if (strstr(event_str, "sched/"))
+			kshark_filter_add_id(kshark_ctx, sd,
+					     KS_SHOW_EVENT_FILTER,
+					     evt_ids[i]);
+		free(event_str);
 	}
 
-	kshark_filter_entries(kshark_ctx, data, n_rows);
+	kshark_filter_stream_entries(kshark_ctx, sd, data, n_rows);
 
 	/* Print to the screen the first 10 visible entries. */
 	count = 0;
@@ -116,19 +119,17 @@ int main(int argc, char **argv)
 	puts("\n\n");
 
 	/* Clear all filters. */
-	kshark_filter_clear(kshark_ctx, KS_HIDE_TASK_FILTER);
-	kshark_filter_clear(kshark_ctx, KS_SHOW_EVENT_FILTER);
+	kshark_filter_clear(kshark_ctx, sd, KS_HIDE_TASK_FILTER);
+	kshark_filter_clear(kshark_ctx, sd, KS_SHOW_EVENT_FILTER);
 
 	/* Use the Advanced filter to do event content based filtering. */
-	adv_filter = kshark_ctx->advanced_event_filter;
-	tep_filter_add_filter_str(adv_filter,
-				  "sched/sched_wakeup:target_cpu==1");
+	kshark_tep_add_filter_str(stream, "sched/sched_wakeup:target_cpu>1");
 
 	/* The Advanced filter requires reloading the data. */
 	for (i = 0; i < n_rows; ++i)
 		free(data[i]);
 
-	n_rows = kshark_load_data_entries(kshark_ctx, &data);
+	n_rows = kshark_load_entries(kshark_ctx, sd, &data);
 
 	count = 0;
 	for (i = 0; i < n_rows; ++i) {
@@ -149,7 +150,7 @@ int main(int argc, char **argv)
 	free(data);
 
 	/* Close the file. */
-	kshark_close(kshark_ctx);
+	kshark_close(kshark_ctx, sd);
 
 	/* Close the session. */
 	kshark_free(kshark_ctx);
diff --git a/src/libkshark-collection.c b/src/libkshark-collection.c
index 66cdbca8..915983b9 100644
--- a/src/libkshark-collection.c
+++ b/src/libkshark-collection.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: LGPL-2.1
 
 /*
- * Copyright (C) 2018 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ * Copyright (C) 2018 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
  */
 
  /**
@@ -11,9 +11,11 @@
 
 // C
 #include <stdbool.h>
+#include <string.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <errno.h>
+#include <stdio.h>
 
 // KernelShark
 #include "libkshark.h"
@@ -74,7 +76,9 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx,
 			     ssize_t first,
 			     size_t n_rows,
 			     matching_condition_func cond,
-			     int val,
+			     int sd,
+			     int *values,
+			     int n_val,
 			     size_t margin)
 {
 	struct kshark_entry_collection *col_ptr = NULL;
@@ -117,7 +121,7 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx,
 	}
 
 	for (i = first + margin; i < end; ++i) {
-		if (!cond(kshark_ctx, data[i], val)) {
+		if (!cond(kshark_ctx, data[i], sd, values)) {
 			/*
 			 * The entry is irrelevant for this collection.
 			 * Do nothing.
@@ -147,7 +151,7 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx,
 			}
 		} else if (good_data &&
 			   data[i]->next &&
-			   !cond(kshark_ctx, data[i]->next, val)) {
+			   !cond(kshark_ctx, data[i]->next, sd, values)) {
 			/*
 			 * Break the collection here. Add some margin data
 			 * after the data of interest.
@@ -168,7 +172,7 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx,
 			 */
 			if (i + margin >= j) {
 				for (;j < i + margin; ++j) {
-					if (cond(kshark_ctx, data[j], val)) {
+					if (cond(kshark_ctx, data[j], sd, values)) {
 						/*
 						 * Good data has been found.
 						 * Continue extending the
@@ -228,9 +232,15 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx,
 	}
 
 	col_ptr->cond = cond;
-	col_ptr->val = val;
+	col_ptr->n_val = n_val;
+	col_ptr->stream_id = sd;
+	col_ptr->values = malloc(n_val * sizeof(*col_ptr->values));
+	memcpy(col_ptr->values, values, n_val * sizeof(*col_ptr->values));
 
 	col_ptr->size = resume_count;
+	if (!col_ptr->size)
+		free(col_list);
+
 	for (i = 0; i < col_ptr->size; ++i) {
 		assert(col_list->type == COLLECTION_RESUME);
 		col_ptr->resume_points[i] = col_list->index;
@@ -316,8 +326,8 @@ map_collection_request_init(const struct kshark_entry_collection *col,
 	size_t req_end;
 
 	if (req->next || col->size == 0) {
-		fprintf(stderr, "Unexpected input in ");
-		fprintf(stderr, "map_collection_request_init()\n");
+		fprintf(stderr,
+			"Unexpected input in map_collection_request_init()\n");
 		goto do_nothing;
 	}
 
@@ -477,7 +487,8 @@ map_collection_back_request(const struct kshark_entry_collection *col,
 				kshark_entry_request_alloc(req_first,
 							   0,
 							   req->cond,
-							   req->val,
+							   req->sd,
+							   req->values,
 							   req->vis_only,
 							   req->vis_mask);
 
@@ -561,7 +572,8 @@ map_collection_front_request(const struct kshark_entry_collection *col,
 				kshark_entry_request_alloc(req_first,
 							   0,
 							   req->cond,
-							   req->val,
+							   req->sd,
+							   req->values,
 							   req->vis_only,
 							   req->vis_mask);
 
@@ -702,25 +714,41 @@ kshark_get_collection_entry_back(struct kshark_entry_request *req,
 	return entry;
 }
 
+static bool val_compare(int *val_a, int *val_b, size_t n_val)
+{
+	size_t i;
+
+	for (i = 0; i < n_val; ++i)
+		if (val_a[i] != val_b[i])
+			return false;
+
+	return true;
+}
+
 /**
  * @brief Search the list of Data collections and find the collection defined
  *	  with a given Matching condition function and value.
  *
  * @param col: Input location for the Data collection list.
  * @param cond: Matching condition function.
- * @param val: Matching condition value, used by the Matching condition
- *	       function.
+ * @param sd: Data stream identifier.
+ * @param values: Array of matching condition value, used by the Matching
+ *		  condition function.
+ * @param n_val: The size of the array of Matching values.
  *
  * @returns Pointer to a Data collections on success, or NULL on failure.
  */
 struct kshark_entry_collection *
 kshark_find_data_collection(struct kshark_entry_collection *col,
 			    matching_condition_func cond,
-			    int val)
+			    int sd, int *values, size_t n_val)
 {
 	while (col) {
-		if (col->cond == cond && col->val == val)
-			return col;
+		if (col->cond == cond &&
+		    col->stream_id == sd &&
+		    col->n_val == n_val &&
+		    val_compare(col->values, values, n_val))
+				return col;
 
 		col = col->next;
 	}
@@ -748,6 +776,7 @@ static void kshark_free_data_collection(struct kshark_entry_collection *col)
 {
 	free(col->resume_points);
 	free(col->break_points);
+	free(col->values);
 	free(col);
 }
 
@@ -761,7 +790,10 @@ static void kshark_free_data_collection(struct kshark_entry_collection *col)
  * @param n_rows: The size of the inputted data.
  * @param cond: Matching condition function for the collection to be
  *	        registered.
- * @param val: Matching condition value of for collection to be registered.
+ * @param sd: Data stream identifier.
+ * @param values: Array of matching condition value, used by the Matching
+ *		  condition function.
+ * @param n_val: The size of the array of Matching values.
  * @param margin: The size of the additional (margin) data which do not
  *		  satisfy the matching condition, but is added at the
  *		  beginning and at the end of each interval of the collection
@@ -776,7 +808,8 @@ kshark_register_data_collection(struct kshark_context *kshark_ctx,
 				struct kshark_entry **data,
 				size_t n_rows,
 				matching_condition_func cond,
-				int val,
+				int sd,
+				int *values, size_t n_val,
 				size_t margin)
 {
 	struct kshark_entry_collection *col;
@@ -784,7 +817,7 @@ kshark_register_data_collection(struct kshark_context *kshark_ctx,
 	col = kshark_add_collection_to_list(kshark_ctx,
 					    &kshark_ctx->collections,
 					    data, n_rows,
-					    cond, val,
+					    cond, sd, values, n_val,
 					    margin);
 
 	return col;
@@ -801,7 +834,10 @@ kshark_register_data_collection(struct kshark_context *kshark_ctx,
  * @param n_rows: The size of the inputted data.
  * @param cond: Matching condition function for the collection to be
  *	        registered.
- * @param val: Matching condition value of for collection to be registered.
+ * @param sd: Data stream identifier.
+ * @param values: Array of matching condition value, used by the Matching
+ *		  condition function.
+ * @param n_val: The size of the array of Matching values.
  * @param margin: The size of the additional (margin) data which do not
  *		  satisfy the matching condition, but is added at the
  *		  beginning and at the end of each interval of the collection
@@ -817,14 +853,18 @@ kshark_add_collection_to_list(struct kshark_context *kshark_ctx,
 			      struct kshark_entry **data,
 			      size_t n_rows,
 			      matching_condition_func cond,
-			      int val,
+			      int sd, int *values, size_t n_val,
 			      size_t margin)
 {
 	struct kshark_entry_collection *col;
 
+	if (!data || n_rows == 0)
+		return NULL;
+
 	col = kshark_data_collection_alloc(kshark_ctx, data,
 					   0, n_rows,
-					   cond, val,
+					   cond, sd,
+					   values, n_val,
 					   margin);
 
 	if (col) {
@@ -844,18 +884,23 @@ kshark_add_collection_to_list(struct kshark_context *kshark_ctx,
  * @param col: Input location for the Data collection list.
  * @param cond: Matching condition function of the collection to be
  *	        unregistered.
- *
- * @param val: Matching condition value of the collection to be unregistered.
+ * @param sd: Data stream identifier.
+ * @param values: Array of matching condition value, used by the Matching
+ *		  condition function.
+ * @param n_val: The size of the array of Matching values.
  */
 void kshark_unregister_data_collection(struct kshark_entry_collection **col,
 				       matching_condition_func cond,
-				       int val)
+				       int sd, int *values, size_t n_val)
 {
 	struct kshark_entry_collection **last = col;
 	struct kshark_entry_collection *list;
 
 	for (list = *col; list; list = list->next) {
-		if (list->cond == cond && list->val == val) {
+		if (list->cond == cond &&
+		    list->stream_id == sd &&
+		    list->n_val == n_val &&
+		    val_compare(list->values, values, n_val)) {
 			*last = list->next;
 			kshark_free_data_collection(list);
 			return;
@@ -865,6 +910,32 @@ void kshark_unregister_data_collection(struct kshark_entry_collection **col,
 	}
 }
 
+/**
+ * @brief Unregister all Data collections associated with a given Data stream.
+ *
+ * @param col: Input location for the Data collection list.
+ * @param sd: Data stream identifier.
+ */
+void kshark_unregister_stream_collections(struct kshark_entry_collection **col,
+					  int sd)
+{
+	struct kshark_entry_collection **last = col;
+	struct kshark_entry_collection *list;
+
+	list = *col;
+	while (list) {
+		if (list->stream_id == sd) {
+			*last = list->next;
+			kshark_free_data_collection(list);
+			list = *last;
+			continue;
+		}
+
+		last = &list->next;
+		list = list->next;
+	}
+}
+
 /**
  * @brief Free all Data collections in a given list.
  *
diff --git a/src/libkshark-tepdata.c b/src/libkshark-tepdata.c
index e697296d..f30bff35 100644
--- a/src/libkshark-tepdata.c
+++ b/src/libkshark-tepdata.c
@@ -362,6 +362,9 @@ static ssize_t get_records(struct kshark_context *kshark_ctx,
 
 				pid = entry->pid;
 
+				/* Apply Id filtering. */
+				kshark_apply_filters(kshark_ctx, stream, entry);
+
 				/* Apply advanced event filtering. */
 				if (adv_filter && adv_filter->filters &&
 				    tep_filter_match(adv_filter, rec) != FILTER_MATCH)
@@ -1238,6 +1241,43 @@ out:
 	return peer_handle;
 }
 
+/** A list of built in default plugins for FTRACE (trace-cmd) data. */
+const char *tep_plugin_names[] = {
+	"sched_events",
+	"missed_events",
+	"kvm_combo",
+};
+
+/**
+ * Register to the data stream all default plugins for FTRACE (trace-cmd) data.
+ */
+int kshark_tep_handle_plugins(struct kshark_context *kshark_ctx, int sd)
+{
+	int i, n_tep_plugins = ARRAY_SIZE(tep_plugin_names);
+	struct kshark_plugin_list *plugin;
+	struct kshark_data_stream *stream;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return -EEXIST;
+
+	for (i = 0; i < n_tep_plugins; ++i) {
+		plugin = kshark_find_plugin_by_name(kshark_ctx->plugins,
+						    tep_plugin_names[i]);
+
+		if (plugin && plugin->process_interface) {
+			kshark_register_plugin_to_stream(stream,
+							 plugin->process_interface,
+							 true);
+		} else {
+			fprintf(stderr, "Plugin \"%s\" not found.\n",
+				tep_plugin_names[i]);
+		}
+	}
+
+	return kshark_handle_all_dpis(stream, KSHARK_PLUGIN_INIT);
+}
+
 /** The Process Id of the Idle tasks is zero. */
 #define LINUX_IDLE_TASK_PID	0
 
@@ -1302,6 +1342,210 @@ static inline char *set_tep_format(struct kshark_data_stream *stream)
 				      TEP_DATA_FORMAT_IDENTIFIER);
 }
 
+static struct tracecmd_input *get_top_input(struct kshark_context *kshark_ctx,
+					    int sd)
+{
+	struct kshark_data_stream *top_stream;
+
+	top_stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!top_stream)
+		return NULL;
+
+	return kshark_get_tep_input(top_stream);
+}
+
+/**
+ * @brief Get an array containing the names of all buffers in FTRACE data
+ *	  file.
+ *
+ * @param kshark_ctx: Input location for context pointer.
+ * @param sd: Data stream identifier of the top buffers in the FTRACE data
+ *	  file.
+ * @param n_buffers: Output location for the size of the outputted array,
+ *	    or a negative error code on failure.
+ *
+ * @returns Array of strings on success, or NULL on failure. The user is
+ *	    responsible for freeing the elements of the outputted array.
+ */
+char **kshark_tep_get_buffer_names(struct kshark_context *kshark_ctx, int sd,
+				   int *n_buffers)
+{
+	struct tracecmd_input *top_input;
+	char **buffer_names;
+	int i, n;
+
+	top_input = get_top_input(kshark_ctx, sd);
+	if (!top_input) {
+		*n_buffers = -EFAULT;
+		return NULL;
+	}
+
+	n = tracecmd_buffer_instances(top_input);
+	buffer_names = calloc(n, sizeof(char *));
+	if (!buffer_names) {
+		*n_buffers = -ENOMEM;
+		return NULL;
+	}
+
+	for (i = 0; i < n; ++i) {
+		buffer_names[i] =
+			strdup(tracecmd_buffer_instance_name(top_input, i));
+		if (!buffer_names[i])
+			goto free_all;
+	}
+
+	*n_buffers = n;
+	return buffer_names;
+
+ free_all:
+	for (i = 0; i < n; ++i)
+		free(buffer_names[i]);
+	free(buffer_names);
+
+	*n_buffers = -ENOMEM;
+	return NULL;
+}
+
+static void set_stream_fields(struct tracecmd_input *top_input, int i,
+			      const char *file,
+			      const char *name,
+			      struct kshark_data_stream *buffer_stream,
+			      struct tracecmd_input **buffer_input)
+{
+	*buffer_input = tracecmd_buffer_instance_handle(top_input, i);
+
+	buffer_stream->name = strdup(name);
+	buffer_stream->file = strdup(file);
+	set_tep_format(buffer_stream);
+}
+
+/**
+ * @brief Open a given buffers in FTRACE (trace-cmd) data file.
+ *
+ * @param kshark_ctx: Input location for context pointer.
+ * @param sd: Data stream identifier of the top buffers in the FTRACE data
+ *	  file.
+ * @param buffer_name: The name of the buffer to open.
+ *
+ * @returns Data stream identifier of the buffer on success. Otherwise a
+ *	    negative error code.
+ */
+int kshark_tep_open_buffer(struct kshark_context *kshark_ctx, int sd,
+			   const char *buffer_name)
+{
+	struct kshark_data_stream *top_stream, *buffer_stream;
+	struct tracecmd_input *top_input, *buffer_input;
+	int i, sd_buffer, n_buffers, ret = -ENODATA;
+	char **names;
+
+	top_stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!top_stream)
+		return -EFAULT;
+
+	top_input = kshark_get_tep_input(top_stream);
+	if (!top_input)
+		return -EFAULT;
+
+	names = kshark_tep_get_buffer_names(kshark_ctx, sd, &n_buffers);
+	if (!names)
+		return n_buffers;
+
+	sd_buffer = kshark_add_stream(kshark_ctx);
+	buffer_stream = kshark_get_data_stream(kshark_ctx, sd_buffer);
+	if (!buffer_stream)
+		return -EFAULT;
+
+	for (i = 0; i < n_buffers; ++i) {
+		if (strcmp(buffer_name, names[i]) == 0) {
+			set_stream_fields(top_input, i,
+					  top_stream->file,
+					  buffer_name,
+					  buffer_stream,
+					  &buffer_input);
+
+			if (!buffer_stream->name || !buffer_stream->file) {
+				free(buffer_stream->name);
+				free(buffer_stream->file);
+
+				ret = -ENOMEM;
+				break;
+			}
+
+			ret = kshark_tep_stream_init(buffer_stream,
+						     buffer_input);
+			break;
+		}
+	}
+
+	for (i = 0; i < n_buffers; ++i)
+		free(names[i]);
+	free(names);
+
+	return (ret < 0)? ret : buffer_stream->stream_id;
+}
+
+/**
+ * @brief Initialize data streams for all buffers in a FTRACE (trace-cmd) data
+ *	  file.
+ *
+ * @param kshark_ctx: Input location for context pointer.
+ * @param sd: Data stream identifier of the top buffers in the FTRACE data
+ *	  file.
+ *
+ * @returns The total number of data streams initialized on success. Otherwise
+ *	    a negative error code.
+ */
+int kshark_tep_init_all_buffers(struct kshark_context *kshark_ctx,
+				int sd)
+{
+	struct kshark_data_stream *top_stream, *buffer_stream;
+	struct tracecmd_input *buffer_input;
+	struct tracecmd_input *top_input;
+	int i, n_buffers, sd_buffer, ret;
+
+	top_stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!top_stream)
+		return -EFAULT;
+
+	top_input = kshark_get_tep_input(top_stream);
+	if (!top_input)
+		return -EFAULT;
+
+	n_buffers = tracecmd_buffer_instances(top_input);
+	for (i = 0; i < n_buffers; ++i) {
+		sd_buffer = kshark_add_stream(kshark_ctx);
+		if (sd_buffer < 0)
+			return -EFAULT;
+
+		buffer_stream = kshark_ctx->stream[sd_buffer];
+
+		set_stream_fields(top_input, i,
+				  top_stream->file,
+				  tracecmd_buffer_instance_name(top_input, i),
+				  buffer_stream,
+				  &buffer_input);
+
+		if (!buffer_stream->name || !buffer_stream->file) {
+			free(buffer_stream->name);
+			free(buffer_stream->file);
+			ret = -ENOMEM;
+			break;
+		}
+
+		ret = kshark_tep_stream_init(buffer_stream, buffer_input);
+		if (ret != 0)
+			return -EFAULT;
+	}
+
+	return n_buffers;
+}
+
+/** Is this a stream corresponding to the "top" buffer in the file. */
+bool kshark_tep_is_top_stream(struct kshark_data_stream *stream)
+{
+	return strcmp(stream->name, KS_UNNAMED) == 0;
+}
+
 /** Check is the file contains TEP tracing data. */
 bool kshark_tep_check_data(const char *file_name)
 {
@@ -1514,3 +1758,149 @@ char **kshark_tracecmd_local_plugins()
 {
 	return tracefs_tracers(tracefs_get_tracing_dir());
 }
+
+/**
+ * @brief Free an array, allocated by kshark_tracecmd_get_hostguest_mapping() API
+ *
+ *
+ * @param map: Array, allocated by kshark_tracecmd_get_hostguest_mapping() API
+ * @param count: Number of entries in the array
+ *
+ */
+void kshark_tracecmd_free_hostguest_map(struct kshark_host_guest_map *map, int count)
+{
+	int i;
+
+	if (!map)
+		return;
+	for (i = 0; i < count; i++) {
+		free(map[i].guest_name);
+		free(map[i].cpu_pid);
+		memset(&map[i], 0, sizeof(*map));
+	}
+	free(map);
+}
+
+/**
+ * @brief Get mapping of guest VCPU to host task, running that VCPU.
+ *	  Array of mappings for each guest is allocated and returned
+ *	  in map input parameter.
+ *
+ *
+ * @param map: Returns allocated array of kshark_host_guest_map structures, each
+ *	       one describing VCPUs mapping of one guest.
+ *
+ * @return The number of entries in the *map array, or a negative error code on
+ *	   failure.
+ */
+int kshark_tracecmd_get_hostguest_mapping(struct kshark_host_guest_map **map)
+{
+	struct kshark_host_guest_map *gmap = NULL;
+	struct tracecmd_input *peer_handle = NULL;
+	struct kshark_data_stream *peer_stream;
+	struct tracecmd_input *guest_handle = NULL;
+	struct kshark_data_stream *guest_stream;
+	struct kshark_context *kshark_ctx = NULL;
+	unsigned long long trace_id;
+	const char *name;
+	int vcpu_count;
+	const int *cpu_pid;
+	int *stream_ids;
+	int i, j, k;
+	int count = 0;
+	int ret;
+
+	if (!map || !kshark_instance(&kshark_ctx))
+		return -EFAULT;
+	if (*map)
+		return -EEXIST;
+
+	stream_ids = kshark_all_streams(kshark_ctx);
+	for (i = 0; i < kshark_ctx->n_streams; i++) {
+		guest_stream = kshark_get_data_stream(kshark_ctx, stream_ids[i]);
+		if (!guest_stream || !kshark_is_tep(guest_stream))
+			continue;
+		guest_handle = kshark_get_tep_input(guest_stream);
+		if (!guest_handle)
+			continue;
+		trace_id = tracecmd_get_traceid(guest_handle);
+		if (!trace_id)
+			continue;
+		for (j = 0; j < kshark_ctx->n_streams; j++) {
+			if (stream_ids[i] == stream_ids[j])
+				continue;
+			peer_stream = kshark_get_data_stream(kshark_ctx, stream_ids[j]);
+			if (!peer_stream || !kshark_is_tep(guest_stream))
+				continue;
+			peer_handle = kshark_get_tep_input(peer_stream);
+			if (!peer_handle)
+				continue;
+			ret = tracecmd_get_guest_cpumap(peer_handle, trace_id,
+							&name, &vcpu_count, &cpu_pid);
+			if (!ret && vcpu_count) {
+				gmap = realloc(*map,
+					       (count + 1) * sizeof(struct kshark_host_guest_map));
+				if (!gmap)
+					goto mem_error;
+				*map = gmap;
+				memset(&gmap[count], 0, sizeof(struct kshark_host_guest_map));
+				count++;
+				gmap[count - 1].guest_id = stream_ids[i];
+				gmap[count - 1].host_id = stream_ids[j];
+				gmap[count - 1].guest_name = strdup(name);
+				if (!gmap[count - 1].guest_name)
+					goto mem_error;
+				gmap[count - 1].vcpu_count = vcpu_count;
+				gmap[count - 1].cpu_pid = malloc(sizeof(int) * vcpu_count);
+				if (!gmap[count - 1].cpu_pid)
+					goto mem_error;
+				for (k = 0; k < vcpu_count; k++)
+					gmap[count - 1].cpu_pid[k] = cpu_pid[k];
+				break;
+			}
+		}
+	}
+
+	free(stream_ids);
+	return count;
+
+mem_error:
+	free(stream_ids);
+	if (*map) {
+		kshark_tracecmd_free_hostguest_map(*map, count);
+		*map = NULL;
+	}
+
+	return -ENOMEM;
+}
+
+/**
+ * @brief Find the data stream corresponding the top buffer of a FTRACE
+ *	  (trace-cmd) data file.
+ *
+ * @param kshark_ctx: Input location for context pointer.
+ * @param file: The name of the file.
+ *
+ * @returns Data stream identifier of the top buffers in the FTRACE data
+ *	    fileon success. Otherwise a negative error code.
+ */
+int kshark_tep_find_top_stream(struct kshark_context *kshark_ctx,
+			       const char *file)
+{
+	struct kshark_data_stream *top_stream = NULL, *stream;
+	int i, *stream_ids = kshark_all_streams(kshark_ctx);
+
+	for (i = 0; i < kshark_ctx->n_streams; ++i) {
+		stream = kshark_ctx->stream[stream_ids[i]];
+		if (strcmp(stream->file, file) == 0 &&
+		    kshark_tep_is_top_stream(stream))
+			top_stream = stream;
+	}
+
+	free(stream_ids);
+
+	if (!top_stream)
+		return -EEXIST;
+
+	return top_stream->stream_id;
+}
diff --git a/src/libkshark-tepdata.h b/src/libkshark-tepdata.h
index b7d6c163..1fe473bc 100644
--- a/src/libkshark-tepdata.h
+++ b/src/libkshark-tepdata.h
@@ -52,9 +52,60 @@ void kshark_tep_filter_reset(struct kshark_data_stream *stream);
 
 char **kshark_tracecmd_local_plugins();
 
+struct tep_handle;
+
+struct tep_handle *kshark_get_tep(struct kshark_data_stream *stream);
+
+struct tracecmd_input;
+
+struct tracecmd_input *kshark_get_tep_input(struct kshark_data_stream *stream);
+
+struct tep_record;
+
 ssize_t kshark_load_tep_records(struct kshark_context *kshark_ctx, int sd,
 				struct tep_record ***data_rows);
 
+/**
+ * Structure representing the mapping between the virtual CPUs and their
+ * corresponding processes in the host.
+ */
+struct kshark_host_guest_map {
+	/** ID of guest stream */
+	int guest_id;
+
+	/** ID of host stream */
+	int host_id;
+
+	/** Guest name */
+	char *guest_name;
+
+	/** Number of guest's CPUs in *cpu_pid array */
+	int vcpu_count;
+
+	/** Array of host task PIDs, index is the VCPU id */
+	int *cpu_pid;
+};
+
+void kshark_tracecmd_free_hostguest_map(struct kshark_host_guest_map *map,
+					int count);
+
+int kshark_tracecmd_get_hostguest_mapping(struct kshark_host_guest_map **map);
+
+char **kshark_tep_get_buffer_names(struct kshark_context *kshark_ctx, int sd,
+				   int *n_buffers);
+
+int kshark_tep_open_buffer(struct kshark_context *kshark_ctx, int sd,
+			   const char *buffer_name);
+
+int kshark_tep_init_all_buffers(struct kshark_context *kshark_ctx, int sd);
+
+int kshark_tep_handle_plugins(struct kshark_context *kshark_ctx, int sd);
+
+int kshark_tep_find_top_stream(struct kshark_context *kshark_ctx,
+			       const char *file);
+
+bool kshark_tep_is_top_stream(struct kshark_data_stream *stream);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/libkshark.c b/src/libkshark.c
index ef36d211..5b444418 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: LGPL-2.1
 
 /*
- * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
  */
 
  /**
@@ -9,8 +9,10 @@
  *  @brief   API for processing of tracing data.
  */
 
+#ifndef _GNU_SOURCE
 /** Use GNU C Library. */
-#define _GNU_SOURCE 1
+#define _GNU_SOURCE
+#endif // _GNU_SOURCE
 
 // C
 #include <stdlib.h>
@@ -37,23 +39,6 @@ static bool kshark_default_context(struct kshark_context **context)
 	kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS;
 	kshark_ctx->stream_info.max_stream_id = -1;
 
-	kshark_ctx->collections = NULL;
-	kshark_ctx->plugins = NULL;
-
-	kshark_ctx->show_task_filter = tracecmd_filter_id_hash_alloc();
-	kshark_ctx->hide_task_filter = tracecmd_filter_id_hash_alloc();
-
-	kshark_ctx->show_event_filter = tracecmd_filter_id_hash_alloc();
-	kshark_ctx->hide_event_filter = tracecmd_filter_id_hash_alloc();
-
-	kshark_ctx->show_cpu_filter = tracecmd_filter_id_hash_alloc();
-	kshark_ctx->hide_cpu_filter = tracecmd_filter_id_hash_alloc();
-
-	kshark_ctx->filter_mask = 0x0;
-
-	kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS;
-	kshark_ctx->stream_info.max_stream_id = -1;
-
 	/* Will free kshark_context_handler. */
 	kshark_free(NULL);
 
@@ -465,6 +450,12 @@ int kshark_close(struct kshark_context *kshark_ctx, int 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);
@@ -1039,103 +1030,134 @@ ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int sd,
 	return stream->tasks->count;
 }
 
-static bool filter_find(struct tracecmd_filter_id *filter, int pid,
+static bool filter_find(struct kshark_hash_id *filter, int pid,
 			bool test)
 {
 	return !filter || !filter->count ||
-		!!(unsigned long)tracecmd_filter_id_find(filter, pid) == test;
+	       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_task(struct kshark_context *kshark_ctx, int pid)
+static bool kshark_show_cpu(struct kshark_data_stream *stream, int cpu)
 {
-	return filter_find(kshark_ctx->show_task_filter, pid, true) &&
-	       filter_find(kshark_ctx->hide_task_filter, pid, false);
+	return filter_find(stream->show_cpu_filter, cpu, true) &&
+	       filter_find(stream->hide_cpu_filter, cpu, false);
 }
 
-static bool kshark_show_event(struct kshark_context *kshark_ctx, int pid)
+static struct kshark_hash_id *get_filter(struct kshark_context *kshark_ctx,
+					 int sd,
+					 enum kshark_filter_type filter_id)
 {
-	return filter_find(kshark_ctx->show_event_filter, pid, true) &&
-	       filter_find(kshark_ctx->hide_event_filter, pid, false);
+	struct kshark_data_stream *stream;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return NULL;
+
+	return kshark_get_filter(stream, filter_id);
 }
 
-static bool kshark_show_cpu(struct kshark_context *kshark_ctx, int cpu)
+/**
+ * @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)
 {
-	return filter_find(kshark_ctx->show_cpu_filter, cpu, true) &&
-	       filter_find(kshark_ctx->hide_cpu_filter, cpu, false);
+	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 filster specified by "filter_id".
+ * @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,
+void kshark_filter_add_id(struct kshark_context *kshark_ctx, int sd,
 			  int filter_id, int id)
 {
-	struct tracecmd_filter_id *filter;
+	struct kshark_hash_id *filter;
 
-	switch (filter_id) {
-		case KS_SHOW_CPU_FILTER:
-			filter = kshark_ctx->show_cpu_filter;
-			break;
-		case KS_HIDE_CPU_FILTER:
-			filter = kshark_ctx->hide_cpu_filter;
-			break;
-		case KS_SHOW_EVENT_FILTER:
-			filter = kshark_ctx->show_event_filter;
-			break;
-		case KS_HIDE_EVENT_FILTER:
-			filter = kshark_ctx->hide_event_filter;
-			break;
-		case KS_SHOW_TASK_FILTER:
-			filter = kshark_ctx->show_task_filter;
-			break;
-		case KS_HIDE_TASK_FILTER:
-			filter = kshark_ctx->hide_task_filter;
-			break;
-		default:
-			return;
+	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);
 	}
 
-	tracecmd_filter_id_add(filter, id);
+	if (n)
+		*n = 0;
+
+	return NULL;
 }
 
 /**
- * @brief Clear (reset) the filster specified by "filter_id".
+ * @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 filter_id)
+void kshark_filter_clear(struct kshark_context *kshark_ctx, int sd,
+			 int filter_id)
 {
-	struct tracecmd_filter_id *filter;
-
-	switch (filter_id) {
-		case KS_SHOW_CPU_FILTER:
-			filter = kshark_ctx->show_cpu_filter;
-			break;
-		case KS_HIDE_CPU_FILTER:
-			filter = kshark_ctx->hide_cpu_filter;
-			break;
-		case KS_SHOW_EVENT_FILTER:
-			filter = kshark_ctx->show_event_filter;
-			break;
-		case KS_HIDE_EVENT_FILTER:
-			filter = kshark_ctx->hide_event_filter;
-			break;
-		case KS_SHOW_TASK_FILTER:
-			filter = kshark_ctx->show_task_filter;
-			break;
-		case KS_HIDE_TASK_FILTER:
-			filter = kshark_ctx->hide_task_filter;
-			break;
-		default:
-			return;
-	}
+	struct kshark_hash_id *filter;
 
-	tracecmd_filter_id_clear(filter);
+	filter = get_filter(kshark_ctx, sd, filter_id);
+	if (filter)
+		kshark_hash_id_clear(filter);
 }
 
 /**
@@ -1145,7 +1167,7 @@ void kshark_filter_clear(struct kshark_context *kshark_ctx, int filter_id)
  *
  * @returns True if the Id filter is set, otherwise False.
  */
-bool kshark_this_filter_is_set(struct tracecmd_filter_id *filter)
+bool kshark_this_filter_is_set(struct kshark_hash_id *filter)
 {
 	return filter && filter->count;
 }
@@ -1154,17 +1176,49 @@ bool kshark_this_filter_is_set(struct tracecmd_filter_id *filter)
  * @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 is set, otherwise False.
+ * @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)
+bool kshark_filter_is_set(struct kshark_context *kshark_ctx, int sd)
 {
-	return kshark_this_filter_is_set(kshark_ctx->show_task_filter) ||
--              kshark_this_filter_is_set(kshark_ctx->hide_task_filter) ||
--              kshark_this_filter_is_set(kshark_ctx->show_cpu_filter) ||
--              kshark_this_filter_is_set(kshark_ctx->hide_cpu_filter) ||
--              kshark_this_filter_is_set(kshark_ctx->show_event_filter) ||
--              kshark_this_filter_is_set(kshark_ctx->hide_event_filter);
+	struct kshark_data_stream *stream;
+
+	stream = kshark_get_data_stream(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) {
@@ -1172,56 +1226,100 @@ static void set_all_visible(uint16_t *v) {
 	*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))
+			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]);
+	}
+}
+
 /**
  * @brief This function loops over the array of entries specified by "data"
- *	  and "n_entries" and sets the "visible" fields of each entry
- *	  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.
+ *	  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_entries(struct kshark_context *kshark_ctx,
-			   struct kshark_entry **data,
-			   size_t n_entries)
+void kshark_filter_stream_entries(struct kshark_context *kshark_ctx,
+				  int sd,
+				  struct kshark_entry **data,
+				  size_t n_entries)
 {
-	int i;
-
-	if (kshark_ctx->advanced_event_filter->filters) {
-		/* The advanced filter is set. */
-		fprintf(stderr,
-			"Failed to filter!\n");
-		fprintf(stderr,
-			"Reset the Advanced filter or reload the data.\n");
-		return;
-	}
-
-	if (!kshark_filter_is_set(kshark_ctx))
-		return;
-
-	/* Apply only the Id filters. */
-	for (i = 0; i < n_entries; ++i) {
-		/* Start with and entry which is visible everywhere. */
-		set_all_visible(&data[i]->visible);
-
-		/* Apply event filtering. */
-		if (!kshark_show_event(kshark_ctx, data[i]->event_id))
-			unset_event_filter_flag(kshark_ctx, data[i]);
-
-		/* Apply CPU filtering. */
-		if (!kshark_show_cpu(kshark_ctx, data[i]->cpu))
-			data[i]->visible &= ~kshark_ctx->filter_mask;
+	if (sd >= 0)
+		filter_entries(kshark_ctx, sd, data, n_entries);
+}
 
-		/* Apply task filtering. */
-		if (!kshark_show_task(kshark_ctx, data[i]->pid))
-			data[i]->visible &= ~kshark_ctx->filter_mask;
-	}
+/**
+ * @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);
 }
 
 /**
@@ -1238,6 +1336,7 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx,
 			      size_t n_entries)
 {
 	int i;
+
 	for (i = 0; i < n_entries; ++i)
 		set_all_visible(&data[i]->visible);
 }
@@ -1361,15 +1460,15 @@ void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec)
  * @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).
+ *	    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(uint64_t time,
-				 struct kshark_entry **data,
-				 size_t l, size_t h)
+ssize_t kshark_find_entry_by_time(int64_t time,
+				  struct kshark_entry **data,
+				  size_t l, size_t h)
 {
 	size_t mid;
 
@@ -1393,15 +1492,16 @@ ssize_t kshark_find_entry_by_time(uint64_t time,
  *
  * @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 pid)
+		      struct kshark_entry *e, int sd, int *pid)
 {
-	if (e->pid == pid)
+	if (e->stream_id == sd && e->pid == *pid)
 		return true;
 
 	return false;
@@ -1412,20 +1512,80 @@ bool kshark_match_pid(struct kshark_context *kshark_ctx,
  *
  * @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 cpu)
+		      struct kshark_entry *e, int sd, int *cpu)
 {
-	if (e->cpu == 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.
@@ -1434,8 +1594,9 @@ bool kshark_match_cpu(struct kshark_context *kshark_ctx,
  *		 where the search starts.
  * @param n: Number of array elements to search in.
  * @param cond: Matching condition function.
- * @param val: Matching condition value, used by the 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.
@@ -1446,7 +1607,7 @@ bool kshark_match_cpu(struct kshark_context *kshark_ctx,
  */
 struct kshark_entry_request *
 kshark_entry_request_alloc(size_t first, size_t n,
-			   matching_condition_func cond, int val,
+			   matching_condition_func cond, int sd, int *values,
 			   bool vis_only, int vis_mask)
 {
 	struct kshark_entry_request *req = malloc(sizeof(*req));
@@ -1461,7 +1622,8 @@ kshark_entry_request_alloc(size_t first, size_t n,
 	req->first = first;
 	req->n = n;
 	req->cond = cond;
-	req->val = val;
+	req->sd = sd;
+	req->values = values;
 	req->vis_only = vis_only;
 	req->vis_mask = vis_mask;
 
@@ -1515,7 +1677,7 @@ get_entry(const struct kshark_entry_request *req,
 	 */
 	assert((inc > 0 && start < end) || (inc < 0 && start > end));
 	for (i = start; i != end; i += inc) {
-		if (req->cond(kshark_ctx, data[i], req->val)) {
+		if (req->cond(kshark_ctx, data[i], req->sd, req->values)) {
 			/*
 			 * Data satisfying the condition has been found.
 			 */
diff --git a/src/libkshark.h b/src/libkshark.h
index 95f501de..0a560f14 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -14,6 +14,7 @@
 
 // C
 #include <stdint.h>
+#include <string.h>
 #include <pthread.h>
 #include <errno.h>
 
@@ -24,10 +25,6 @@
 extern "C" {
 #endif
 
-// trace-cmd
-#include "trace-cmd/trace-cmd.h"
-#include "trace-cmd/trace-filter-hash.h"
-
 // KernelShark
 #include "libkshark-plugin.h"
 
@@ -378,27 +375,6 @@ struct kshark_context {
 	/** Parameters of the stream descriptor array. */
 	struct kshark_stream_array_descriptor	stream_info;
 
-	/** A mutex, used to protect the access to the input file. */
-	pthread_mutex_t		input_mutex;
-
-	/** Hash of tasks to filter on. */
-	struct tracecmd_filter_id	*show_task_filter;
-
-	/** Hash of tasks to not display. */
-	struct tracecmd_filter_id	*hide_task_filter;
-
-	/** Hash of events to filter on. */
-	struct tracecmd_filter_id	*show_event_filter;
-
-	/** Hash of events to not display. */
-	struct tracecmd_filter_id	*hide_event_filter;
-
-	/** Hash of CPUs to filter on. */
-	struct tracecmd_filter_id	*show_cpu_filter;
-
-	/** Hash of CPUs to not display. */
-	struct tracecmd_filter_id	*hide_cpu_filter;
-
 	/**
 	 * Bit mask, controlling the visibility of the entries after filtering.
 	 * If given bit is set here, all entries which are filtered-out will
@@ -406,12 +382,6 @@ struct kshark_context {
 	 */
 	uint8_t				filter_mask;
 
-	/**
-	 * Filter allowing sophisticated filtering based on the content of
-	 * the event.
-	 */
-	struct tep_event_filter		*advanced_event_filter;
-
 	/** List of Data collections. */
 	struct kshark_entry_collection *collections;
 
@@ -570,14 +540,22 @@ enum kshark_filter_type {
 	KS_HIDE_CPU_FILTER,
 };
 
-void kshark_filter_add_id(struct kshark_context *kshark_ctx,
+struct kshark_hash_id *
+kshark_get_filter(struct kshark_data_stream *stream,
+		  enum kshark_filter_type filter_id);
+
+void kshark_filter_add_id(struct kshark_context *kshark_ctx, int sd,
 			  int filter_id, int id);
 
-void kshark_filter_clear(struct kshark_context *kshark_ctx, int filter_id);
+int *kshark_get_filter_ids(struct kshark_context *kshark_ctx, int sd,
+			   int filter_id, int *n);
+
+void kshark_filter_clear(struct kshark_context *kshark_ctx, int sd,
+			 int filter_id);
 
-bool kshark_this_filter_is_set(struct tracecmd_filter_id *filter);
+bool kshark_this_filter_is_set(struct kshark_hash_id *filter);
 
-bool kshark_filter_is_set(struct kshark_context *kshark_ctx);
+bool kshark_filter_is_set(struct kshark_context *kshark_ctx, int sd);
 
 static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx,
 					   struct kshark_entry *e)
@@ -594,9 +572,16 @@ static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx,
 	e->visible &= ~event_mask;
 }
 
-void kshark_filter_entries(struct kshark_context *kshark_ctx,
-			   struct kshark_entry **data,
-			   size_t n_entries);
+void kshark_apply_filters(struct kshark_context *kshark_ctx,
+			  struct kshark_data_stream *stream,
+			  struct kshark_entry *entry);
+
+void kshark_filter_stream_entries(struct kshark_context *kshark_ctx, int sd,
+				  struct kshark_entry **data,
+				  size_t n_entries);
+
+void kshark_filter_all_entries(struct kshark_context *kshark_ctx,
+			       struct kshark_entry **data, size_t n_entries);
 
 void kshark_clear_all_filters(struct kshark_context *kshark_ctx,
 			      struct kshark_entry **data,
@@ -607,10 +592,10 @@ void kshark_plugin_actions(struct kshark_data_stream *stream,
 
 /** Search failed identifiers. */
 enum kshark_search_failed {
-	/** All entries have timestamps greater timestamps. */
+	/** All entries have greater timestamps. */
 	BSEARCH_ALL_GREATER = -1,
 
-	/** All entries have timestamps smaller timestamps. */
+	/** All entries have smaller timestamps. */
 	BSEARCH_ALL_SMALLER = -2,
 };
 
@@ -626,15 +611,26 @@ enum kshark_search_failed {
 		}					\
 	})
 
-ssize_t kshark_find_entry_by_time(uint64_t time,
+ssize_t kshark_find_entry_by_time(int64_t time,
 				  struct kshark_entry **data_rows,
 				  size_t l, size_t h);
 
 bool kshark_match_pid(struct kshark_context *kshark_ctx,
-		      struct kshark_entry *e, int pid);
+		      struct kshark_entry *e, int sd, int *pid);
 
 bool kshark_match_cpu(struct kshark_context *kshark_ctx,
-		      struct kshark_entry *e, int cpu);
+		      struct kshark_entry *e, int sd, int *cpu);
+
+bool kshark_match_event_id(struct kshark_context *kshark_ctx,
+			   struct kshark_entry *e, int sd, int *event_id);
+
+bool kshark_match_event_and_pid(struct kshark_context *kshark_ctx,
+				struct kshark_entry *e,
+				int sd, int *values);
+
+bool kshark_match_event_and_cpu(struct kshark_context *kshark_ctx,
+				struct kshark_entry *e,
+				int sd, int *values);
 
 /**
  * Empty bin identifier.
@@ -652,7 +648,7 @@ bool kshark_match_cpu(struct kshark_context *kshark_ctx,
 /** Matching condition function type. To be user for data requests */
 typedef bool (matching_condition_func)(struct kshark_context*,
 				       struct kshark_entry*,
-				       int);
+				       int, int*);
 
 /**
  * Data request structure, defining the properties of the required
@@ -674,10 +670,13 @@ struct kshark_entry_request {
 	/** Matching condition function. */
 	matching_condition_func *cond;
 
+	/** Data stream identifier. */
+	int sd;
+
 	/**
 	 * Matching condition value, used by the Matching condition function.
 	 */
-	int val;
+	int *values;
 
 	/** If true, a visible entry is requested. */
 	bool vis_only;
@@ -691,7 +690,7 @@ struct kshark_entry_request {
 
 struct kshark_entry_request *
 kshark_entry_request_alloc(size_t first, size_t n,
-			   matching_condition_func cond, int val,
+			   matching_condition_func cond, int sd, int *values,
 			   bool vis_only, int vis_mask);
 
 void kshark_free_entry_request(struct kshark_entry_request *req);
@@ -708,8 +707,8 @@ kshark_get_entry_back(const struct kshark_entry_request *req,
 
 /**
  * Data collections are used to optimize the search for an entry having an
- * abstract property, defined by a Matching condition function and a value.
- * When a collection is processed, the data which is relevant for the
+ * abstract property, defined by a Matching condition function and an array of
+ * values. When a collection is processed, the data which is relevant for the
  * collection is enclosed in "Data intervals", defined by pairs of "Resume" and
  * "Break" points. It is guaranteed that the data outside of the intervals
  * contains no entries satisfying the abstract matching condition. However, the
@@ -726,11 +725,17 @@ struct kshark_entry_collection {
 	/** Matching condition function, used to define the collections. */
 	matching_condition_func *cond;
 
+	/** Data stream identifier. */
+	int stream_id;
+
 	/**
-	 * Matching condition value, used by the Matching condition finction
-	 * to define the collections.
+	 * Array of matching condition values, used by the Matching condition
+	 * finction to define the collection.
 	 */
-	int val;
+	int *values;
+
+	/** The suze of the array of matching condition values. */
+	int n_val;
 
 	/**
 	 * Array of indexes defining the beginning of each individual data
@@ -753,26 +758,30 @@ kshark_add_collection_to_list(struct kshark_context *kshark_ctx,
 			      struct kshark_entry **data,
 			      size_t n_rows,
 			      matching_condition_func cond,
-			      int val,
+			      int sd, int *values, size_t n_val,
 			      size_t margin);
 
 struct kshark_entry_collection *
 kshark_register_data_collection(struct kshark_context *kshark_ctx,
 				struct kshark_entry **data, size_t n_rows,
-				matching_condition_func cond, int val,
+				matching_condition_func cond,
+				int sd, int *values, size_t n_val,
 				size_t margin);
 
 void kshark_unregister_data_collection(struct kshark_entry_collection **col,
 				       matching_condition_func cond,
-				       int val);
+				       int sd, int *values, size_t n_val);
 
 struct kshark_entry_collection *
 kshark_find_data_collection(struct kshark_entry_collection *col,
 			    matching_condition_func cond,
-			    int val);
+			    int sd, int *values, size_t n_val);
 
 void kshark_reset_data_collection(struct kshark_entry_collection *col);
 
+void kshark_unregister_stream_collections(struct kshark_entry_collection **col,
+					  int sd);
+
 void kshark_free_collection_list(struct kshark_entry_collection *col);
 
 const struct kshark_entry *
@@ -914,17 +923,27 @@ bool kshark_export_adv_filters(struct kshark_context *kshark_ctx,
 bool kshark_import_adv_filters(struct kshark_context *kshark_ctx,
 			       struct kshark_config_doc *conf);
 
+bool kshark_export_event_filter(struct kshark_data_stream *stream,
+				enum kshark_filter_type filter_type,
+				const char *filter_name,
+				struct kshark_config_doc *conf);
+
+int kshark_import_event_filter(struct kshark_data_stream *stream,
+			       enum kshark_filter_type filter_type,
+			       const char *filter_name,
+			       struct kshark_config_doc *conf);
+
 bool kshark_export_user_mask(struct kshark_context *kshark_ctx,
 			     struct kshark_config_doc **conf);
 
 bool kshark_import_user_mask(struct kshark_context *kshark_ctx,
 			     struct kshark_config_doc *conf);
 
-bool kshark_export_filter_array(struct tracecmd_filter_id *filter,
+bool kshark_export_filter_array(struct kshark_hash_id *filter,
 				const char *filter_name,
 				struct kshark_config_doc *conf);
 
-bool kshark_import_filter_array(struct tracecmd_filter_id *filter,
+bool kshark_import_filter_array(struct kshark_hash_id *filter,
 				const char *filter_name,
 				struct kshark_config_doc *conf);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (12 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 13/20] kernel-shark: Complete the stream integration Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-19  2:00   ` Steven Rostedt
  2020-11-18 14:49 ` [PATCH v4 15/20] kernel-shark: Integrate the stream definitions with data model Yordan Karadzhov (VMware)
                   ` (5 subsequent siblings)
  19 siblings, 1 reply; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The C API provides loading of the trace data in two different forms.
The firs one is an array of kshark_entries and is being used by the
KernelShark GUI. The second is a matrix-like structure that has all
the fields of the kshark_entry stored in separate arrays, forming the
columns of the matrix. The second form of the data is used by
trace-cruncher. In this patch we add methods for merging of several
data streams into a single data set. Both kshark_entries and matrix
forms of the data are supported. This patch includes a simple example
that demonstrate how to open a file that contains multiple buffers.
Each buffers is loaded into a separate Data stream and those streams
are merged together.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 examples/CMakeLists.txt    |   4 +
 examples/multibufferload.c |  60 +++++++++
 src/libkshark.c            | 255 +++++++++++++++++++++++++++++++++++++
 src/libkshark.h            |  47 +++++++
 4 files changed, 366 insertions(+)
 create mode 100644 examples/multibufferload.c

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 8d40e42c..831eee24 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -8,6 +8,10 @@ message(STATUS "datafilter")
 add_executable(dfilter          datafilter.c)
 target_link_libraries(dfilter   kshark)
 
+message(STATUS "multibufferload")
+add_executable(mbload          multibufferload.c)
+target_link_libraries(mbload   kshark)
+
 # message(STATUS "datahisto")
 # add_executable(dhisto          datahisto.c)
 # target_link_libraries(dhisto   kshark)
diff --git a/examples/multibufferload.c b/examples/multibufferload.c
new file mode 100644
index 00000000..70b2733a
--- /dev/null
+++ b/examples/multibufferload.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libkshark.h"
+#include "libkshark-tepdata.h"
+
+const char *default_file = "trace.dat";
+
+void put_entry(struct kshark_entry *e)
+{
+	char *entry_str = kshark_dump_entry(e);
+	puts(entry_str);
+	free(entry_str);
+}
+
+int main(int argc, char **argv)
+{
+	struct kshark_context *kshark_ctx;
+	struct kshark_entry **data = NULL;
+	ssize_t r, n_rows;
+	int sd;
+
+	/* Create a new kshark session. */
+	kshark_ctx = NULL;
+	if (!kshark_instance(&kshark_ctx))
+		return 1;
+
+	/* Open a trace data file produced by trace-cmd. */
+	if (argc > 1)
+		sd = kshark_open(kshark_ctx, argv[1]);
+	else
+		sd = kshark_open(kshark_ctx, default_file);
+
+	if (sd < 0) {
+		kshark_free(kshark_ctx);
+		return 1;
+	}
+
+	/* Initialize data streams for all buffers in this file. */
+	kshark_tep_init_all_buffers(kshark_ctx, sd);
+
+	/* Load all buffers. */
+	n_rows = kshark_load_all_entries(kshark_ctx, &data);
+
+	/* Print to the screen the first 20 entries. */
+	for (r = 0; r < 20; ++r)
+		put_entry(data[r]);
+
+	/* Free the memory. */
+	for (r = 0; r < n_rows; ++r)
+		free(data[r]);
+	free(data);
+
+	kshark_close_all(kshark_ctx);
+
+	/* Close the session. */
+	kshark_free(kshark_ctx);
+
+	return 0;
+}
diff --git a/src/libkshark.c b/src/libkshark.c
index 5b444418..9675618e 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -1760,3 +1760,258 @@ kshark_get_entry_back(const struct kshark_entry_request *req,
 
 	return get_entry(req, data, index, req->first, end, -1);
 }
+
+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;
+}
diff --git a/src/libkshark.h b/src/libkshark.h
index 0a560f14..edf3dcf4 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -980,12 +980,59 @@ struct kshark_config_doc *kshark_open_config_file(const char *file_name,
 
 struct kshark_config_doc *kshark_json_to_conf(struct json_object *jobj);
 
+/** Structure representing a data set made of KernelShark entries. */
+struct kshark_entry_data_set {
+	/** Array of entries pointers. */
+	struct kshark_entry **data;
+
+	/** The size of the data set. */
+	ssize_t n_rows;
+};
+
+struct kshark_entry **
+kshark_merge_data_entries(struct kshark_entry_data_set *buffers,
+			  int n_buffers);
+
+ssize_t kshark_load_all_entries(struct kshark_context *kshark_ctx,
+				struct kshark_entry ***data_rows);
+
+ssize_t kshark_append_all_entries(struct kshark_context *kshark_ctx,
+				  struct kshark_entry **prior_data,
+				  ssize_t n_prior_rows,
+				  int first_streams,
+				  struct kshark_entry ***merged_data);
+
 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);
 
+/** Structure representing a data set made of data columns (arrays). */
+struct kshark_matrix_data_set {
+	/** Event Id column. */
+	int16_t *event_array;
+
+	/** CPU Id column. */
+	int16_t *cpu_array;
+
+	/** PID column. */
+	int32_t *pid_array;
+
+	/** Record offset column. */
+	int64_t *offset_array;
+
+	/** Timestamp column. */
+	int64_t *ts_array;
+
+	/** The size of the data set. */
+	ssize_t n_rows;
+};
+
+struct kshark_matrix_data_set
+kshark_merge_data_matrices(struct kshark_matrix_data_set *buffers,
+			   int n_buffers);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 15/20] kernel-shark: Integrate the stream definitions with data model
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (13 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:49 ` [PATCH v4 16/20] kernel-shark: Use only signed types for model defs Yordan Karadzhov (VMware)
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The visualization model is adapted in order to be able to distinguish
the entries belonging to different Data streams.
We re-enable the visualization model example as well.

Signen-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 examples/CMakeLists.txt |   8 ++--
 examples/datahisto.c    |  38 +++++++--------
 src/CMakeLists.txt      |   2 +-
 src/libkshark-model.c   | 102 ++++++++++++++++++++++++----------------
 src/libkshark-model.h   |  34 +++++++-------
 5 files changed, 102 insertions(+), 82 deletions(-)

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 831eee24..2f6acea2 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -12,10 +12,10 @@ message(STATUS "multibufferload")
 add_executable(mbload          multibufferload.c)
 target_link_libraries(mbload   kshark)
 
-# message(STATUS "datahisto")
-# add_executable(dhisto          datahisto.c)
-# target_link_libraries(dhisto   kshark)
-#
+message(STATUS "datahisto")
+add_executable(dhisto          datahisto.c)
+target_link_libraries(dhisto   kshark)
+
 # message(STATUS "confogio")
 # add_executable(confio          configio.c)
 # target_link_libraries(confio   kshark)
diff --git a/examples/datahisto.c b/examples/datahisto.c
index b177b088..568072d8 100644
--- a/examples/datahisto.c
+++ b/examples/datahisto.c
@@ -7,6 +7,7 @@
 // C
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 // KernelShark
 #include "libkshark.h"
@@ -16,7 +17,7 @@
 
 const char *default_file = "trace.dat";
 
-void dump_bin(struct kshark_trace_histo *histo, int bin,
+void dump_bin(struct kshark_trace_histo *histo, int bin, int sd,
 	      const char *type, int val)
 {
 	const struct kshark_entry *e_front, *e_back;
@@ -26,22 +27,22 @@ void dump_bin(struct kshark_trace_histo *histo, int bin,
 	printf("bin %i {\n", bin);
 	if (strcmp(type, "cpu") == 0) {
 		e_front = ksmodel_get_entry_front(histo, bin, true,
-						  kshark_match_cpu, val,
+						  kshark_match_cpu, sd, &val,
 						  NULL,
 						  &i_front);
 
 		e_back = ksmodel_get_entry_back(histo, bin, true,
-						kshark_match_cpu, val,
+						kshark_match_cpu, sd, &val,
 						NULL,
 						&i_back);
 	} else if (strcmp(type, "task") == 0) {
 		e_front = ksmodel_get_entry_front(histo, bin, true,
-						  kshark_match_pid, val,
+						  kshark_match_pid, sd, &val,
 						  NULL,
 						  &i_front);
 
 		e_back = ksmodel_get_entry_back(histo, bin, true,
-						kshark_match_pid, val,
+						kshark_match_pid, sd, &val,
 						NULL,
 						&i_back);
 	} else {
@@ -67,12 +68,12 @@ void dump_bin(struct kshark_trace_histo *histo, int bin,
 	puts("}\n");
 }
 
-void dump_histo(struct kshark_trace_histo *histo, const char *type, int val)
+void dump_histo(struct kshark_trace_histo *histo, int sd, const char *type, int val)
 {
 	size_t bin;
 
 	for (bin = 0; bin < histo->n_bins; ++bin)
-		dump_bin(histo, bin, type, val);
+		dump_bin(histo, bin, sd, type, val);
 }
 
 int main(int argc, char **argv)
@@ -81,8 +82,7 @@ int main(int argc, char **argv)
 	struct kshark_entry **data = NULL;
 	struct kshark_trace_histo histo;
 	ssize_t i, n_rows, n_tasks;
-	bool status;
-	int *pids;
+	int sd, *pids;
 
 	/* Create a new kshark session. */
 	kshark_ctx = NULL;
@@ -91,24 +91,24 @@ int main(int argc, char **argv)
 
 	/* Open a trace data file produced by trace-cmd. */
 	if (argc > 1)
-		status = kshark_open(kshark_ctx, argv[1]);
+		sd = kshark_open(kshark_ctx, argv[1]);
 	else
-		status = kshark_open(kshark_ctx, default_file);
+		sd = kshark_open(kshark_ctx, default_file);
 
-	if (!status) {
+	if (sd < 0) {
 		kshark_free(kshark_ctx);
 		return 1;
 	}
 
 	/* Load the content of the file into an array of entries. */
-	n_rows = kshark_load_data_entries(kshark_ctx, &data);
+	n_rows = kshark_load_entries(kshark_ctx, sd, &data);
 	if (n_rows < 1) {
 		kshark_free(kshark_ctx);
 		return 1;
 	}
 
 	/* Get a list of all tasks. */
-	n_tasks = kshark_get_task_pids(kshark_ctx, &pids);
+	n_tasks = kshark_get_task_pids(kshark_ctx, sd, &pids);
 
 	/* Initialize the Visualization Model. */
 	ksmodel_init(&histo);
@@ -119,7 +119,7 @@ int main(int argc, char **argv)
 	ksmodel_fill(&histo, data, n_rows);
 
 	/* Dump the raw bins. */
-	dump_histo(&histo, "", 0);
+	dump_histo(&histo, sd, "", 0);
 
 	puts("\n...\n\n");
 
@@ -127,13 +127,13 @@ int main(int argc, char **argv)
 	 * Change the state of the model. Do 50% Zoom-In and dump only CPU 0.
 	 */
 	ksmodel_zoom_in(&histo, .50, -1);
-	dump_histo(&histo, "cpu", 0);
+	dump_histo(&histo, sd, "cpu", 0);
 
 	puts("\n...\n\n");
 
 	/* Shift forward by two bins and this time dump only CPU 1. */
 	ksmodel_shift_forward(&histo, 2);
-	dump_histo(&histo, "cpu", 1);
+	dump_histo(&histo, sd, "cpu", 1);
 
 	puts("\n...\n\n");
 
@@ -142,7 +142,7 @@ int main(int argc, char **argv)
 	 * Task.
 	 */
 	ksmodel_zoom_out(&histo, .10, N_BINS - 1);
-	dump_histo(&histo, "task", pids[n_tasks - 1]);
+	dump_histo(&histo, sd, "task", pids[n_tasks - 1]);
 
 	/* Reset (clear) the model. */
 	ksmodel_clear(&histo);
@@ -154,7 +154,7 @@ int main(int argc, char **argv)
 	free(data);
 
 	/* Close the file. */
-	kshark_close(kshark_ctx);
+	kshark_close(kshark_ctx, sd);
 
 	/* Close the session. */
 	kshark_free(kshark_ctx);
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2bdab1a5..cbcc5701 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,7 +3,7 @@ message("\n src ...")
 message(STATUS "libkshark")
 add_library(kshark SHARED libkshark.c
                           libkshark-hash.c
-#                           libkshark-model.c
+                          libkshark-model.c
                           libkshark-plugin.c
                           libkshark-tepdata.c
 #                           libkshark-configio.c
diff --git a/src/libkshark-model.c b/src/libkshark-model.c
index babac2a2..97cff314 100644
--- a/src/libkshark-model.c
+++ b/src/libkshark-model.c
@@ -1,17 +1,19 @@
 // SPDX-License-Identifier: LGPL-2.1
 
 /*
- * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
  */
 
- /**
-  *  @file    libkshark-model.c
-  *  @brief   Visualization model for FTRACE (trace-cmd) data.
-  */
+/**
+ *  @file    libkshark-model.c
+ *  @brief   Time series visualization model for tracing data.
+ */
 
 // C
 #include <stdlib.h>
 #include <assert.h>
+#include <string.h>
+#include <stdio.h>
 
 // KernelShark
 #include "libkshark-model.h"
@@ -782,7 +784,8 @@ static bool ksmodel_is_visible(struct kshark_entry *e)
 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)
+				  matching_condition_func func,
+				  int sd, int *values)
 {
 	size_t first, n;
 
@@ -794,14 +797,15 @@ ksmodel_entry_front_request_alloc(struct kshark_trace_histo *histo,
 	first = ksmodel_first_index_at_bin(histo, bin);
 
 	return kshark_entry_request_alloc(first, n,
-					  func, val,
+					  func, sd, values,
 					  vis_only, KS_GRAPH_VIEW_FILTER_MASK);
 }
 
 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)
+				 matching_condition_func func,
+				 int sd, int *values)
 {
 	size_t first, n;
 
@@ -813,7 +817,7 @@ ksmodel_entry_back_request_alloc(struct kshark_trace_histo *histo,
 	first = ksmodel_last_index_at_bin(histo, bin);
 
 	return kshark_entry_request_alloc(first, n,
-					  func, val,
+					  func, sd, values,
 					  vis_only, KS_GRAPH_VIEW_FILTER_MASK);
 }
 
@@ -822,12 +826,13 @@ ksmodel_entry_back_request_alloc(struct kshark_trace_histo *histo,
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @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)
+				   int bin, int sd, int cpu)
 {
 	size_t i, n, first, not_found = KS_EMPTY_BIN;
 
@@ -838,7 +843,8 @@ ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo,
 	first = ksmodel_first_index_at_bin(histo, bin);
 
 	for (i = first; i < first + n; ++i) {
-		if (histo->data[i]->cpu == cpu) {
+		if (histo->data[i]->cpu == cpu &&
+		    histo->data[i]->stream_id == sd) {
 			if (ksmodel_is_visible(histo->data[i]))
 				return i;
 			else
@@ -854,12 +860,13 @@ ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo,
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @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)
+				   int bin, int sd, int pid)
 {
 	size_t i, n, first, not_found = KS_EMPTY_BIN;
 
@@ -870,7 +877,8 @@ ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo,
 	first = ksmodel_first_index_at_bin(histo, bin);
 
 	for (i = first; i < first + n; ++i) {
-		if (histo->data[i]->pid == pid) {
+		if (histo->data[i]->pid == pid &&
+		    histo->data[i]->stream_id == sd) {
 			if (ksmodel_is_visible(histo->data[i]))
 				return i;
 			else
@@ -890,8 +898,9 @@ ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo,
  * @param bin: Bin id.
  * @param vis_only: If true, a visible entry is requested.
  * @param func: Matching condition function.
- * @param val: Matching condition value, used by the Matching condition
- *	       function.
+ * @param sd: Data stream identifier.
+ * @param values: Matching condition values, used by the Matching condition
+ *                function.
  * @param col: Optional input location for Data collection.
  * @param index: Optional output location for the index of the requested
  *		 entry inside the array.
@@ -901,7 +910,7 @@ ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo,
 const struct kshark_entry *
 ksmodel_get_entry_front(struct kshark_trace_histo *histo,
 			int bin, bool vis_only,
-			matching_condition_func func, int val,
+			matching_condition_func func, int sd, int *values,
 			struct kshark_entry_collection *col,
 			ssize_t *index)
 {
@@ -913,7 +922,7 @@ ksmodel_get_entry_front(struct kshark_trace_histo *histo,
 
 	/* Set the position at the beginning of the bin and go forward. */
 	req = ksmodel_entry_front_request_alloc(histo, bin, vis_only,
-							    func, val);
+						func, sd, values);
 	if (!req)
 		return NULL;
 
@@ -937,8 +946,9 @@ ksmodel_get_entry_front(struct kshark_trace_histo *histo,
  * @param bin: Bin id.
  * @param vis_only: If true, a visible entry is requested.
  * @param func: Matching condition function.
- * @param val: Matching condition value, used by the Matching condition
- *	       function.
+ * @param sd: Data stream identifier.
+ * @param values: Matching condition values, used by the Matching condition
+ *                function.
  * @param col: Optional input location for Data collection.
  * @param index: Optional output location for the index of the requested
  *		 entry inside the array.
@@ -948,7 +958,7 @@ ksmodel_get_entry_front(struct kshark_trace_histo *histo,
 const struct kshark_entry *
 ksmodel_get_entry_back(struct kshark_trace_histo *histo,
 		       int bin, bool vis_only,
-		       matching_condition_func func, int val,
+		       matching_condition_func func, int sd, int *values,
 		       struct kshark_entry_collection *col,
 		       ssize_t *index)
 {
@@ -960,7 +970,7 @@ ksmodel_get_entry_back(struct kshark_trace_histo *histo,
 
 	/* Set the position at the end of the bin and go backwards. */
 	req = ksmodel_entry_back_request_alloc(histo, bin, vis_only,
-							   func, val);
+					       func, sd, values);
 	if (!req)
 		return NULL;
 
@@ -998,6 +1008,7 @@ static int ksmodel_get_entry_pid(const struct kshark_entry *entry)
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @param cpu: CPU Id.
  * @param vis_only: If true, a visible entry is requested.
  * @param col: Optional input location for Data collection.
@@ -1008,7 +1019,7 @@ static int ksmodel_get_entry_pid(const struct kshark_entry *entry)
  *	    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,
+			  int bin, int sd, int cpu, bool vis_only,
 			  struct kshark_entry_collection *col,
 			  ssize_t *index)
 {
@@ -1018,7 +1029,7 @@ int ksmodel_get_pid_front(struct kshark_trace_histo *histo,
 		return KS_EMPTY_BIN;
 
 	entry = ksmodel_get_entry_front(histo, bin, vis_only,
-					       kshark_match_cpu, cpu,
+					       kshark_match_cpu, sd, &cpu,
 					       col, index);
 
 	return ksmodel_get_entry_pid(entry);
@@ -1031,6 +1042,7 @@ int ksmodel_get_pid_front(struct kshark_trace_histo *histo,
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @param cpu: CPU Id.
  * @param vis_only: If true, a visible entry is requested.
  * @param col: Optional input location for Data collection.
@@ -1041,7 +1053,7 @@ int ksmodel_get_pid_front(struct kshark_trace_histo *histo,
  *	    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,
+			 int bin, int sd, int cpu, bool vis_only,
 			 struct kshark_entry_collection *col,
 			 ssize_t *index)
 {
@@ -1051,7 +1063,7 @@ int ksmodel_get_pid_back(struct kshark_trace_histo *histo,
 		return KS_EMPTY_BIN;
 
 	entry = ksmodel_get_entry_back(histo, bin, vis_only,
-					      kshark_match_cpu, cpu,
+					      kshark_match_cpu, sd, &cpu,
 					      col, index);
 
 	return ksmodel_get_entry_pid(entry);
@@ -1080,6 +1092,7 @@ static int ksmodel_get_entry_cpu(const struct kshark_entry *entry)
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @param pid: Process Id.
  * @param vis_only: If true, a visible entry is requested.
  * @param col: Optional input location for Data collection.
@@ -1090,7 +1103,7 @@ static int ksmodel_get_entry_cpu(const struct kshark_entry *entry)
  *	    Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN).
  */
 int ksmodel_get_cpu_front(struct kshark_trace_histo *histo,
-			  int bin, int pid, bool vis_only,
+			  int bin, int sd, int pid, bool vis_only,
 			  struct kshark_entry_collection *col,
 			  ssize_t *index)
 {
@@ -1100,7 +1113,7 @@ int ksmodel_get_cpu_front(struct kshark_trace_histo *histo,
 		return KS_EMPTY_BIN;
 
 	entry = ksmodel_get_entry_front(histo, bin, vis_only,
-					       kshark_match_pid, pid,
+					       kshark_match_pid, sd, &pid,
 					       col,
 					       index);
 	return ksmodel_get_entry_cpu(entry);
@@ -1113,6 +1126,7 @@ int ksmodel_get_cpu_front(struct kshark_trace_histo *histo,
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @param pid: Process Id.
  * @param vis_only: If true, a visible entry is requested.
  * @param col: Optional input location for Data collection.
@@ -1123,7 +1137,7 @@ int ksmodel_get_cpu_front(struct kshark_trace_histo *histo,
  *	    Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN).
  */
 int ksmodel_get_cpu_back(struct kshark_trace_histo *histo,
-			 int bin, int pid, bool vis_only,
+			 int bin, int sd, int pid, bool vis_only,
 			 struct kshark_entry_collection *col,
 			 ssize_t *index)
 {
@@ -1133,7 +1147,7 @@ int ksmodel_get_cpu_back(struct kshark_trace_histo *histo,
 		return KS_EMPTY_BIN;
 
 	entry = ksmodel_get_entry_back(histo, bin, vis_only,
-					      kshark_match_pid, pid,
+					      kshark_match_pid, sd, &pid,
 					      col,
 					      index);
 
@@ -1145,6 +1159,7 @@ int ksmodel_get_cpu_back(struct kshark_trace_histo *histo,
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @param cpu: Cpu Id.
  * @param col: Optional input location for Data collection.
  * @param index: Optional output location for the index of the requested
@@ -1153,7 +1168,7 @@ int ksmodel_get_cpu_back(struct kshark_trace_histo *histo,
  * @returns True, if a visible entry exists in this bin. Else false.
  */
 bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo,
-				     int bin, int cpu,
+				     int bin, int sd, int cpu,
 				     struct kshark_entry_collection *col,
 				     ssize_t *index)
 {
@@ -1166,7 +1181,7 @@ bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo,
 	/* Set the position at the beginning of the bin and go forward. */
 	req = ksmodel_entry_front_request_alloc(histo,
 						bin, true,
-						kshark_match_cpu, cpu);
+						kshark_match_cpu, sd, &cpu);
 	if (!req)
 		return false;
 
@@ -1198,6 +1213,7 @@ bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo,
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @param pid: Process Id of the task.
  * @param col: Optional input location for Data collection.
  * @param index: Optional output location for the index of the requested
@@ -1206,7 +1222,7 @@ bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo,
  * @returns True, if a visible entry exists in this bin. Else false.
  */
 bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo,
-				      int bin, int pid,
+				      int bin, int sd, int pid,
 				      struct kshark_entry_collection *col,
 				      ssize_t *index)
 {
@@ -1219,7 +1235,7 @@ bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo,
 	/* Set the position at the beginning of the bin and go forward. */
 	req = ksmodel_entry_front_request_alloc(histo,
 						bin, true,
-						kshark_match_pid, pid);
+						kshark_match_pid, sd, &pid);
 	if (!req)
 		return false;
 
@@ -1247,15 +1263,17 @@ bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo,
 }
 
 static bool match_cpu_missed_events(struct kshark_context *kshark_ctx,
-				    struct kshark_entry *e, int cpu)
+				    struct kshark_entry *e, int sd, int *cpu)
 {
-	return e->event_id == -EOVERFLOW && e->cpu == cpu;
+	return e->event_id == KS_EVENT_OVERFLOW &&
+	       e->cpu == *cpu && e->stream_id == sd;
 }
 
 static bool match_pid_missed_events(struct kshark_context *kshark_ctx,
-				    struct kshark_entry *e, int pid)
+				    struct kshark_entry *e, int sd, int *pid)
 {
-	return e->event_id == -EOVERFLOW && e->pid == pid;
+	return e->event_id == KS_EVENT_OVERFLOW &&
+	       e->pid == *pid && e->stream_id == sd;
 }
 
 /**
@@ -1264,6 +1282,7 @@ static bool match_pid_missed_events(struct kshark_context *kshark_ctx,
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @param cpu: CPU Id.
  * @param col: Optional input location for Data collection.
  * @param index: Optional output location for the index of the requested
@@ -1273,12 +1292,12 @@ static bool match_pid_missed_events(struct kshark_context *kshark_ctx,
  */
 const struct kshark_entry *
 ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo,
-			      int bin, int cpu,
+			      int bin, int sd, int cpu,
 			      struct kshark_entry_collection *col,
 			      ssize_t *index)
 {
 	return ksmodel_get_entry_front(histo, bin, true,
-				       match_cpu_missed_events, cpu,
+				       match_cpu_missed_events, sd, &cpu,
 				       col, index);
 }
 
@@ -1288,6 +1307,7 @@ ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo,
  *
  * @param histo: Input location for the model descriptor.
  * @param bin: Bin id.
+ * @param sd: Data stream identifier.
  * @param pid: Process Id of the task.
  * @param col: Optional input location for Data collection.
  * @param index: Optional output location for the index of the requested
@@ -1297,11 +1317,11 @@ ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo,
  */
 const struct kshark_entry *
 ksmodel_get_task_missed_events(struct kshark_trace_histo *histo,
-			       int bin, int pid,
+			       int bin, int sd, int pid,
 			       struct kshark_entry_collection *col,
 			       ssize_t *index)
 {
 	return ksmodel_get_entry_front(histo, bin, true,
-				       match_pid_missed_events, pid,
+				       match_pid_missed_events, sd, &pid,
 				       col, index);
 }
diff --git a/src/libkshark-model.h b/src/libkshark-model.h
index 47793b11..87e252eb 100644
--- a/src/libkshark-model.h
+++ b/src/libkshark-model.h
@@ -1,13 +1,13 @@
 /* SPDX-License-Identifier: LGPL-2.1 */
 
 /*
- * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
  */
 
- /**
-  *  @file    libkshark-model.h
-  *  @brief   Visualization model for FTRACE (trace-cmd) data.
-  */
+/**
+ *  @file    libkshark-model.h
+ *  @brief   Time series visualization model for tracing data.
+ */
 
 #ifndef _LIB_KSHARK_MODEL_H
 #define _LIB_KSHARK_MODEL_H
@@ -102,64 +102,64 @@ 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);
+				   int bin, int sd, int cpu);
 
 ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo,
-				   int bin, int pid);
+				   int bin, int sd, 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,
+			matching_condition_func func, int sd, int *values,
 			struct kshark_entry_collection *col,
 			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,
+		       matching_condition_func func, int sd, int *values,
 		       struct kshark_entry_collection *col,
 		       ssize_t *index);
 
 int ksmodel_get_pid_front(struct kshark_trace_histo *histo,
-			  int bin, int cpu, bool vis_only,
+			  int bin, int sd, int cpu, bool vis_only,
 			  struct kshark_entry_collection *col,
 			  ssize_t *index);
 
 int ksmodel_get_pid_back(struct kshark_trace_histo *histo,
-			 int bin, int cpu, bool vis_only,
+			 int bin, int sd, int cpu, bool vis_only,
 			 struct kshark_entry_collection *col,
 			 ssize_t *index);
 
 int ksmodel_get_cpu_front(struct kshark_trace_histo *histo,
-			  int bin, int pid, bool vis_only,
+			  int bin, int sd, int pid, bool vis_only,
 			  struct kshark_entry_collection *col,
 			  ssize_t *index);
 
 int ksmodel_get_cpu_back(struct kshark_trace_histo *histo,
-			 int bin, int pid, bool vis_only,
+			 int bin, int sd, int pid, bool vis_only,
 			 struct kshark_entry_collection *col,
 			 ssize_t *index);
 
 bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo,
-				     int bin, int cpu,
+				     int bin, int sd, int cpu,
 				     struct kshark_entry_collection *col,
 				     ssize_t *index);
 
 bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo,
-				      int bin, int pid,
+				      int bin, int sd, int pid,
 				      struct kshark_entry_collection *col,
 				      ssize_t *index);
 
 const struct kshark_entry *
 ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo,
-			      int bin, int cpu,
+			      int bin, int sd, int cpu,
 			      struct kshark_entry_collection *col,
 			      ssize_t *index);
 
 const struct kshark_entry *
 ksmodel_get_task_missed_events(struct kshark_trace_histo *histo,
-			       int bin, int pid,
+			       int bin, int sd, int pid,
 			       struct kshark_entry_collection *col,
 			       ssize_t *index);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 16/20] kernel-shark: Use only signed types for model defs
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (14 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 15/20] kernel-shark: Integrate the stream definitions with data model Yordan Karadzhov (VMware)
@ 2020-11-18 14:49 ` Yordan Karadzhov (VMware)
  2020-11-18 14:50 ` [PATCH v4 17/20] kernel-shark: Add ksmodel_get_bin() Yordan Karadzhov (VMware)
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:49 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

All variables that are used to describe the state of the model, like
the range of the model or the size of the bin must have signed integer
types. This is a continuation of the change started  by switching to
signed timestamps.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark-model.c | 12 ++++++------
 src/libkshark-model.h | 18 +++++++++---------
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/src/libkshark-model.c b/src/libkshark-model.c
index 97cff314..f3864bfd 100644
--- a/src/libkshark-model.c
+++ b/src/libkshark-model.c
@@ -91,10 +91,10 @@ static bool ksmodel_histo_alloc(struct kshark_trace_histo *histo, size_t n)
 }
 
 static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo,
-					size_t n, uint64_t min, uint64_t max,
+					size_t n, int64_t min, int64_t max,
 					bool force_in_range)
 {
-	uint64_t corrected_range, delta_range, range = max - min;
+	int64_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. */
@@ -163,7 +163,7 @@ static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo,
  * @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)
+			size_t n, int64_t min, int64_t max)
 {
 	ksmodel_set_in_range_bining(histo, n, min, max, false);
 }
@@ -256,7 +256,7 @@ static size_t ksmodel_set_upper_edge(struct kshark_trace_histo *histo)
 static void ksmodel_set_next_bin_edge(struct kshark_trace_histo *histo,
 				      size_t bin, size_t last_row)
 {
-	uint64_t time_min, time_max;
+	int64_t time_min, time_max;
 	size_t next_bin = bin + 1;
 	ssize_t row;
 
@@ -598,9 +598,9 @@ void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n)
  * @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, uint64_t ts)
+void ksmodel_jump_to(struct kshark_trace_histo *histo, int64_t ts)
 {
-	uint64_t min, max, range_min;
+	int64_t min, max, range_min;
 
 	if (ts > histo->min && ts < histo->max) {
 		/*
diff --git a/src/libkshark-model.h b/src/libkshark-model.h
index 87e252eb..b8624809 100644
--- a/src/libkshark-model.h
+++ b/src/libkshark-model.h
@@ -58,16 +58,16 @@ struct kshark_trace_histo {
 	 * Lower edge of the time-window to be visualized. Only entries having
 	 * timestamp >= min will be visualized.
 	 */
-	uint64_t		min;
+	int64_t			min;
 
 	/**
 	 * Upper edge of the time-window to be visualized. Only entries having
 	 * timestamp <= max will be visualized.
 	 */
-	uint64_t		max;
+	int64_t			max;
 
 	/** The size in time for each bin. */
-	uint64_t		bin_size;
+	int64_t			bin_size;
 
 	/** Number of bins. */
 	int			n_bins;
@@ -78,7 +78,7 @@ 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);
+			size_t n, int64_t min, int64_t max);
 
 void ksmodel_fill(struct kshark_trace_histo *histo,
 		  struct kshark_entry **data, size_t n);
@@ -89,7 +89,7 @@ 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, uint64_t ts);
+void ksmodel_jump_to(struct kshark_trace_histo *histo, int64_t ts);
 
 void ksmodel_zoom_out(struct kshark_trace_histo *histo,
 		      double r, int mark);
@@ -163,16 +163,16 @@ ksmodel_get_task_missed_events(struct kshark_trace_histo *histo,
 			       struct kshark_entry_collection *col,
 			       ssize_t *index);
 
-static inline double ksmodel_bin_time(struct kshark_trace_histo *histo,
+static inline int64_t ksmodel_bin_ts(struct kshark_trace_histo *histo,
 				      int bin)
 {
-	return (histo->min + bin*histo->bin_size) * 1e-9;
+	return (histo->min + bin*histo->bin_size);
 }
 
-static inline uint64_t ksmodel_bin_ts(struct kshark_trace_histo *histo,
+static inline double ksmodel_bin_time(struct kshark_trace_histo *histo,
 				      int bin)
 {
-	return (histo->min + bin*histo->bin_size);
+	return ksmodel_bin_ts(histo, bin) * 1e-9;
 }
 
 #ifdef __cplusplus
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 17/20] kernel-shark: Add ksmodel_get_bin()
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (15 preceding siblings ...)
  2020-11-18 14:49 ` [PATCH v4 16/20] kernel-shark: Use only signed types for model defs Yordan Karadzhov (VMware)
@ 2020-11-18 14:50 ` Yordan Karadzhov (VMware)
  2020-11-18 14:50 ` [PATCH v4 18/20] kernel-shark: Protect ksmodel_set_in_range_bining() Yordan Karadzhov (VMware)
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:50 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The method retrieves the Id of the bin of the model that contains
given entry.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark-model.c | 27 +++++++++++++++++++++++++++
 src/libkshark-model.h |  3 +++
 2 files changed, 30 insertions(+)

diff --git a/src/libkshark-model.c b/src/libkshark-model.c
index f3864bfd..2301b069 100644
--- a/src/libkshark-model.c
+++ b/src/libkshark-model.c
@@ -1325,3 +1325,30 @@ ksmodel_get_task_missed_events(struct kshark_trace_histo *histo,
 				       match_pid_missed_events, sd, &pid,
 				       col, index);
 }
+
+/**
+ * @brief Find the bin Id of a give entry.
+ *
+ * @param histo: Input location for the model descriptor.
+ * @param entry: Input location for entry.
+ *
+ * @returns If the timestamp of the entry is inside the range of the model the
+ * 	    function returns the Id of the bin it belongs to.
+ * 	    If the timestamp of the entry is outside of the range of the model
+ * 	    the function returns UPPER_OVERFLOW_BIN or LOWER_OVERFLOW_BIN
+ * 	    (negative values).
+ */
+int ksmodel_get_bin(struct kshark_trace_histo *histo,
+		    const struct kshark_entry *entry)
+{
+	if (entry->ts < histo->min)
+		return UPPER_OVERFLOW_BIN;
+
+	if (entry->ts > histo->max)
+		return LOWER_OVERFLOW_BIN;
+
+	if (entry->ts == histo->max)
+		return histo->n_bins - 1;
+
+	return  (entry->ts - histo->min) / histo->bin_size;
+}
diff --git a/src/libkshark-model.h b/src/libkshark-model.h
index b8624809..8989ee0e 100644
--- a/src/libkshark-model.h
+++ b/src/libkshark-model.h
@@ -175,6 +175,9 @@ static inline double ksmodel_bin_time(struct kshark_trace_histo *histo,
 	return ksmodel_bin_ts(histo, bin) * 1e-9;
 }
 
+int ksmodel_get_bin(struct kshark_trace_histo *histo,
+		    const struct kshark_entry *entry);
+
 #ifdef __cplusplus
 }
 #endif // __cplusplus
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 18/20] kernel-shark: Protect ksmodel_set_in_range_bining()
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (16 preceding siblings ...)
  2020-11-18 14:50 ` [PATCH v4 17/20] kernel-shark: Add ksmodel_get_bin() Yordan Karadzhov (VMware)
@ 2020-11-18 14:50 ` Yordan Karadzhov (VMware)
  2020-11-18 14:50 ` [PATCH v4 19/20] kernel-shark: Add methods for time calibration Yordan Karadzhov (VMware)
  2020-11-18 14:50 ` [PATCH v4 20/20] kernel-shark: Integrate streams with libkshark-configio Yordan Karadzhov (VMware)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:50 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Handle the case when the number of bins is zero or negative.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark-model.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/libkshark-model.c b/src/libkshark-model.c
index 2301b069..44f829d6 100644
--- a/src/libkshark-model.c
+++ b/src/libkshark-model.c
@@ -97,8 +97,19 @@ static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo,
 	int64_t corrected_range, delta_range, range = max - min;
 	struct kshark_entry *last;
 
+	if (n <= 0) {
+		histo->n_bins = histo->bin_size = 0;
+		histo->min = min;
+		histo->max = max;
+
+		free(histo->bin_count);
+		free(histo->map);
+
+		return;
+	}
+
 	/* The size of the bin must be >= 1, hence the range must be >= n. */
-	if (n == 0 || range < n) {
+	if (range < n) {
 		range = n;
 		max = min + n;
 	}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 19/20] kernel-shark: Add methods for time calibration
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (17 preceding siblings ...)
  2020-11-18 14:50 ` [PATCH v4 18/20] kernel-shark: Protect ksmodel_set_in_range_bining() Yordan Karadzhov (VMware)
@ 2020-11-18 14:50 ` Yordan Karadzhov (VMware)
  2020-11-18 14:50 ` [PATCH v4 20/20] kernel-shark: Integrate streams with libkshark-configio Yordan Karadzhov (VMware)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:50 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

We add an infrastructure for correcting the timestamps of the entries.
This is needed in order to correlate Data streams that have been
recorded using non-synchronized clocks. The infrastructure can handle
an arbitrary timestamps correction formula, however for the moment we
only provide calibration that adds a constant offset.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark-tepdata.c | 13 +++++-
 src/libkshark.c         | 99 +++++++++++++++++++++++++++++++++++++++++
 src/libkshark.h         | 27 +++++++++++
 3 files changed, 138 insertions(+), 1 deletion(-)

diff --git a/src/libkshark-tepdata.c b/src/libkshark-tepdata.c
index f30bff35..849edd10 100644
--- a/src/libkshark-tepdata.c
+++ b/src/libkshark-tepdata.c
@@ -338,6 +338,9 @@ static ssize_t get_records(struct kshark_context *kshark_ctx,
 					entry = &temp_rec->entry;
 					missed_events_action(stream, rec, entry);
 
+					/* Apply time calibration. */
+					kshark_postprocess_entry(stream, rec, entry);
+
 					entry->stream_id = stream->stream_id;
 
 					temp_next = &temp_rec->next;
@@ -360,6 +363,12 @@ static ssize_t get_records(struct kshark_context *kshark_ctx,
 
 				entry->stream_id = stream->stream_id;
 
+				/*
+				 * Post-process the content of the entry. This includes
+				 * time calibration and event-specific plugin actions.
+				 */
+				kshark_postprocess_entry(stream, rec, entry);
+
 				pid = entry->pid;
 
 				/* Apply Id filtering. */
@@ -527,8 +536,10 @@ static ssize_t tepdata_load_matrix(struct kshark_data_stream *stream,
 			if (cpu_array)
 				(*cpu_array)[count] = e->cpu;
 
-			if (ts_array)
+			if (ts_array) {
+				kshark_calib_entry(stream, e);
 				(*ts_array)[count] = e->ts;
+			}
 
 			if (pid_array)
 				(*pid_array)[count] = e->pid;
diff --git a/src/libkshark.c b/src/libkshark.c
index 9675618e..7af44420 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -131,6 +131,7 @@ static void kshark_stream_free(struct kshark_data_stream *stream)
 
 	kshark_hash_id_free(stream->tasks);
 
+	free(stream->calib_array);
 	free(stream->file);
 	free(stream->name);
 	free(stream->interface);
@@ -1364,6 +1365,37 @@ void kshark_plugin_actions(struct kshark_data_stream *stream,
 	}
 }
 
+/**
+ * @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)
@@ -1761,6 +1793,73 @@ kshark_get_entry_back(const struct kshark_entry_request *req,
 	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;
diff --git a/src/libkshark.h b/src/libkshark.h
index edf3dcf4..dce3dd2c 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -129,6 +129,12 @@ static const char top_name[] = { 0x1b, 0x00 }; // Non printable character
  */
 #define KS_UNNAMED	(char *) &top_name
 
+/**
+ * Timestamp calibration function type. To be user for system clock
+ * calibration.
+ */
+typedef void (*time_calib_func) (int64_t *, int64_t *);
+
 struct kshark_data_stream;
 
 /** A function type to be used by the method interface of the data stream. */
@@ -327,6 +333,15 @@ struct kshark_data_stream {
 	/** The number of plugins registered for this stream.*/
 	int			n_plugins;
 
+	/** System clock calibration function. */
+	time_calib_func		calib;
+
+	/** An array of time calibration constants. */
+	int64_t			*calib_array;
+
+	/** The size of the array of time calibration constants. */
+	size_t			calib_array_size;
+
 	/** List of Plugin's Event handlers. */
 	struct kshark_event_proc_handler	*event_handlers;
 
@@ -590,6 +605,12 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx,
 void kshark_plugin_actions(struct kshark_data_stream *stream,
 			   void *record, struct kshark_entry *entry);
 
+void kshark_calib_entry(struct kshark_data_stream *stream,
+			struct kshark_entry *entry);
+
+void kshark_postprocess_entry(struct kshark_data_stream *stream,
+			      void *record, struct kshark_entry *entry);
+
 /** Search failed identifiers. */
 enum kshark_search_failed {
 	/** All entries have greater timestamps. */
@@ -980,6 +1001,12 @@ struct kshark_config_doc *kshark_open_config_file(const char *file_name,
 
 struct kshark_config_doc *kshark_json_to_conf(struct json_object *jobj);
 
+void kshark_offset_calib(int64_t *ts, int64_t *atgv);
+
+void kshark_set_clock_offset(struct kshark_context *kshark_ctx,
+			     struct kshark_entry **entries, size_t size,
+			     int sd, int64_t offset);
+
 /** Structure representing a data set made of KernelShark entries. */
 struct kshark_entry_data_set {
 	/** Array of entries pointers. */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 20/20] kernel-shark: Integrate streams with libkshark-configio
  2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (18 preceding siblings ...)
  2020-11-18 14:50 ` [PATCH v4 19/20] kernel-shark: Add methods for time calibration Yordan Karadzhov (VMware)
@ 2020-11-18 14:50 ` Yordan Karadzhov (VMware)
  19 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-18 14:50 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The configuration input/output is adapted in order to be able to
work with the new version of the C API. Now it can handle multiple
Data streams. We are re-enabling the corresponding example as well.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 examples/CMakeLists.txt  |    8 +-
 examples/configio.c      |   20 +-
 src/CMakeLists.txt       |    2 +-
 src/libkshark-configio.c | 1208 ++++++++++++++++++++++++++++++++------
 src/libkshark.h          |   73 ++-
 5 files changed, 1093 insertions(+), 218 deletions(-)

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 2f6acea2..b8bc79a4 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -16,10 +16,10 @@ message(STATUS "datahisto")
 add_executable(dhisto          datahisto.c)
 target_link_libraries(dhisto   kshark)
 
-# message(STATUS "confogio")
-# add_executable(confio          configio.c)
-# target_link_libraries(confio   kshark)
-#
+message(STATUS "confogio")
+add_executable(confio          configio.c)
+target_link_libraries(confio   kshark)
+
 # message(STATUS "dataplot")
 # add_executable(dplot          dataplot.cpp)
 # target_link_libraries(dplot   kshark-plot)
diff --git a/examples/configio.c b/examples/configio.c
index faa116a1..9710d533 100644
--- a/examples/configio.c
+++ b/examples/configio.c
@@ -7,22 +7,26 @@ int main(int argc, char **argv)
 {
 	struct kshark_config_doc *conf, *filter, *hello;
 	struct kshark_context *kshark_ctx;
-	int *ids = NULL, i;
+	struct kshark_data_stream *stream;
+	int sd, *ids = NULL, i;
 
 	/* Create a new kshark session. */
 	kshark_ctx = NULL;
 	if (!kshark_instance(&kshark_ctx))
 		return 1;
 
+	sd = kshark_add_stream(kshark_ctx);
+	stream = kshark_ctx->stream[sd];
+
 	if (argc == 1) {
-		tracecmd_filter_id_add(kshark_ctx->show_task_filter, 314);
-		tracecmd_filter_id_add(kshark_ctx->show_task_filter, 42);
+		kshark_hash_id_add(stream->show_task_filter, 314);
+		kshark_hash_id_add(stream->show_task_filter, 42);
 
 		/* Create a new Confog. doc. */
 		conf = kshark_config_new("foo.bar.config", KS_CONFIG_JSON);
 
 		/* Add filter's info. */
-		filter = kshark_export_all_filters(kshark_ctx, KS_CONFIG_JSON);
+		filter = kshark_export_all_filters(kshark_ctx, sd, KS_CONFIG_JSON);
 		kshark_config_doc_add(conf, "Filters" ,filter);
 
 		/* Add "Hello Kernel" message. */
@@ -39,11 +43,11 @@ int main(int argc, char **argv)
 		/* Retrieve the filter's info. */
 		filter = kshark_config_alloc(KS_CONFIG_JSON);
 		if (kshark_config_doc_get(conf, "Filters" ,filter)) {
-			kshark_import_all_filters(kshark_ctx, filter);
+			kshark_import_all_filters(kshark_ctx, sd, filter);
 
 			/* Get the array of Ids to be fitered. */
-			ids = tracecmd_filter_ids(kshark_ctx->show_task_filter);
-			for (i = 0; i < kshark_ctx->show_task_filter->count; ++i)
+			ids = kshark_hash_ids(stream->show_task_filter);
+			for (i = 0; i < stream->show_task_filter->count; ++i)
 				printf("pid: %i\n", ids[i]);
 		}
 
@@ -58,7 +62,7 @@ int main(int argc, char **argv)
 	}
 
 	kshark_free_config_doc(conf);
-
+	kshark_close(kshark_ctx, sd);
 	kshark_free(kshark_ctx);
 
 	return 0;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cbcc5701..ae5de54d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -6,7 +6,7 @@ add_library(kshark SHARED libkshark.c
                           libkshark-model.c
                           libkshark-plugin.c
                           libkshark-tepdata.c
-#                           libkshark-configio.c
+                          libkshark-configio.c
                           libkshark-collection.c)
 
 target_link_libraries(kshark ${TRACECMD_LIBRARY}
diff --git a/src/libkshark-configio.c b/src/libkshark-configio.c
index cb7ca54b..98098da6 100644
--- a/src/libkshark-configio.c
+++ b/src/libkshark-configio.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: LGPL-2.1
 
 /*
- * Copyright (C) 2018 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ * Copyright (C) 2018 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
  */
 
  /**
@@ -13,15 +13,17 @@
 #ifndef _GNU_SOURCE
 /** Use GNU C Library. */
 #define _GNU_SOURCE
-
-#endif
+#endif // _GNU_SOURCE
 
 #include <stdio.h>
+#include <string.h>
 #include <sys/stat.h>
 
 // KernelShark
 #include "libkshark.h"
 #include "libkshark-model.h"
+#include "libkshark-plugin.h"
+#include "libkshark-tepdata.h"
 
 static struct json_object *kshark_json_config_alloc(const char *type)
 {
@@ -299,7 +301,7 @@ bool kshark_config_doc_get(struct kshark_config_doc *conf,
 		case KS_CONFIG_JSON:
 			json_object_put(val->conf_doc);
 			if (!get_jval(conf, key, &val->conf_doc))
-				goto fail;
+				goto not_found;
 
 			return true;
 		case KS_CONFIG_STRING:
@@ -308,7 +310,7 @@ bool kshark_config_doc_get(struct kshark_config_doc *conf,
 				goto fail;
 
 			if (!get_jval(conf, key, &tmp->conf_doc))
-				goto fail;
+				goto not_found;
 
 			val->conf_doc =
 				(char *) json_object_get_string(tmp->conf_doc);
@@ -331,7 +333,9 @@ bool kshark_config_doc_get(struct kshark_config_doc *conf,
 	return true;
 
  fail:
-	fprintf(stderr, "Failed to get config. document.\n");
+	fprintf(stderr, "Failed to get config. document <%s>.\n", key);
+
+ not_found:
 	return false;
 }
 
@@ -348,6 +352,19 @@ kshark_record_config_new(enum kshark_config_formats format)
 	return kshark_config_new("kshark.config.record", format);
 }
 
+/**
+ * @brief Create an empty Data Stream Configuration document. The type
+ * 	  description is set to "kshark.config.stream".
+ *
+ * @returns kshark_config_doc instance on success, otherwise NULL. Use
+ *	    kshark_free_config_doc() to free the object.
+ */
+struct kshark_config_doc *
+kshark_stream_config_new(enum kshark_config_formats format)
+{
+	return kshark_config_new("kshark.config.stream", format);
+}
+
 /**
  * @brief Create an empty Filter Configuration document. The type description
  *	  is set to "kshark.config.filter".
@@ -361,6 +378,19 @@ kshark_filter_config_new(enum kshark_config_formats format)
 	return kshark_config_new("kshark.config.filter", format);
 }
 
+/**
+ * @brief Create an empty Session Configuration document. The type description
+ *	  is set to "kshark.config.filter".
+ *
+ * @returns kshark_config_doc instance on success, otherwise NULL. Use
+ *	    kshark_free_config_doc() to free the object.
+ */
+struct kshark_config_doc *
+kshark_session_config_new(enum kshark_config_formats format)
+{
+	return kshark_config_new("kshark.config.session", format);
+}
+
 /**
  * @brief Create an empty Text Configuration document. The Text Configuration
  *	  documents do not use type descriptions.
@@ -419,10 +449,11 @@ bool kshark_type_check(struct kshark_config_doc *conf, const char *type)
 	}
 }
 
-static bool kshark_trace_file_to_json(const char *file,
+static bool kshark_trace_file_to_json(const char *file, const char *name,
 				      struct json_object *jobj)
 {
-	struct json_object *jfile_name, *jtime;
+	struct json_object *jfile_name, *jbuffer_name, *jtime;
+	char abs_path[FILENAME_MAX];
 	struct stat st;
 
 	if (!file || !jobj)
@@ -433,13 +464,21 @@ static bool kshark_trace_file_to_json(const char *file,
 		return false;
 	}
 
-	jfile_name = json_object_new_string(file);
+	if (!realpath(file, abs_path)) {
+		fprintf(stderr, "Unable to get absolute pathname for %s\n",
+			file);
+		return false;
+	}
+
+	jfile_name = json_object_new_string(abs_path);
+	jbuffer_name = json_object_new_string(name);
 	jtime = json_object_new_int64(st.st_mtime);
 
 	if (!jfile_name || !jtime)
 		goto fail;
 
 	json_object_object_add(jobj, "file", jfile_name);
+	json_object_object_add(jobj, "name", jbuffer_name);
 	json_object_object_add(jobj, "time", jtime);
 
 	return true;
@@ -458,13 +497,15 @@ static bool kshark_trace_file_to_json(const char *file,
  *	  Configuration document.
  *
  * @param file: The name of the file.
+ * @param name: The name of the data buffer.
  * @param format: Input location for the Configuration format identifier.
  *		  Currently only Json format is supported.
  *
- * @returns True on success, otherwise False.
+ * @returns kshark_config_doc instance on success, otherwise NULL. Use
+ *	    kshark_free_config_doc() to free the object.
  */
 struct kshark_config_doc *
-kshark_export_trace_file(const char *file,
+kshark_export_trace_file(const char *file, const char *name,
 			 enum kshark_config_formats format)
 {
 	/*  Create a new Configuration document. */
@@ -476,7 +517,7 @@ kshark_export_trace_file(const char *file,
 
 	switch (format) {
 	case KS_CONFIG_JSON:
-		kshark_trace_file_to_json(file, conf->conf_doc);
+		kshark_trace_file_to_json(file, name, conf->conf_doc);
 		return conf;
 
 	default:
@@ -486,19 +527,30 @@ kshark_export_trace_file(const char *file,
 	}
 }
 
-static bool kshark_trace_file_from_json(const char **file,
+static bool kshark_trace_file_from_json(const char **file, const char **name,
+					const char *type,
 					struct json_object *jobj)
 {
-	struct json_object *jfile_name, *jtime;
-	const char *file_str;
+	struct json_object *jfile_name, *jbuffer_name, *jtime;
+	const char *file_str, *name_str;
 	struct stat st;
+	char *header;
 	int64_t time;
+	bool type_OK = true;
 
 	if (!jobj)
 		return false;
 
-	if (!kshark_json_type_check(jobj, "kshark.config.data") ||
+	if (type) {
+		/* Make sure that the condition document has a correct type. */
+		type_OK = false;
+		if (asprintf(&header, "kshark.config.%s", type) >= 0)
+			type_OK = kshark_json_type_check(jobj, header);
+	}
+
+	if (!type_OK ||
 	    !json_object_object_get_ex(jobj, "file", &jfile_name) ||
+	    !json_object_object_get_ex(jobj, "name", &jbuffer_name) ||
 	    !json_object_object_get_ex(jobj, "time", &jtime)) {
 		fprintf(stderr,
 			"Failed to retrieve data file from json_object.\n");
@@ -506,6 +558,7 @@ static bool kshark_trace_file_from_json(const char **file,
 	}
 
 	file_str = json_object_get_string(jfile_name);
+	name_str = json_object_get_string(jbuffer_name);
 	time = json_object_get_int64(jtime);
 
 	if (stat(file_str, &st) != 0) {
@@ -514,15 +567,24 @@ static bool kshark_trace_file_from_json(const char **file,
 	}
 
 	if (st.st_mtime != time) {
-		fprintf(stderr,"Timestamp mismatch!\nFile %s", file_str);
+		fprintf(stderr, "Timestamp mismatch! (%li!=%li)\nFile %s\n",
+				time, st.st_mtime, file_str);
 		return false;
 	}
 
 	*file = file_str;
+	*name = name_str;
 
 	return true;
 }
 
+/* Quiet warnings over documenting simple structures */
+//! @cond Doxygen_Suppress
+
+#define TOP_BUFF_NAME	"top buffer"
+
+//! @endcond
+
 /**
  * @brief Read the name of a trace data file and its timestamp from a
  *	  Configuration document and check if such a file exists.
@@ -532,27 +594,379 @@ static bool kshark_trace_file_from_json(const char **file,
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported.
  *
- * @returns The name of the file on success, otherwise NULL. "conf" has
- *	    the ownership over the returned string.
+ * @returns The Id number of the data stream associated with the loaded file on
+ *	    success, otherwise -1. "conf" has the ownership over the returned
+ *	    string.
  */
-const char* kshark_import_trace_file(struct kshark_context *kshark_ctx,
-				     struct kshark_config_doc *conf)
+int kshark_import_trace_file(struct kshark_context *kshark_ctx,
+			     struct kshark_config_doc *conf)
 {
-	const char *file = NULL;
+	const char *file = NULL, *name = NULL;
+	int sd = -1;
+
 	switch (conf->format) {
 	case KS_CONFIG_JSON:
-		if (kshark_trace_file_from_json(&file, conf->conf_doc))
-			kshark_open(kshark_ctx, file);
+		if (kshark_trace_file_from_json(&file, &name, "data",
+						conf->conf_doc)) {
+			if (strcmp(name, KS_UNNAMED) == 0 ||
+			    strcmp(name, TOP_BUFF_NAME) == 0) {
+				sd = kshark_open(kshark_ctx, file);
+			} else {
+				int sd_top;
+
+				sd_top = kshark_tep_find_top_stream(kshark_ctx,
+								    file);
+				if (sd_top < 0) {
+					/*
+					 * The "top" steam (buffer) has to be
+					 * initialized first.
+					 */
+					sd_top = kshark_open(kshark_ctx, file);
+				}
+
+				if (sd_top >= 0)
+					sd = kshark_tep_open_buffer(kshark_ctx,
+								    sd_top,
+								    name);
+
+				if (sd >= 0)
+				kshark_tep_handle_plugins(kshark_ctx, sd);
+			}
+		}
+
+		break;
 
+	default:
+		fprintf(stderr, "Document format %d not supported\n",
+			conf->format);
 		break;
+	}
+
+	return sd;
+}
+
+static bool kshark_plugin_to_json(struct kshark_plugin_list *plugin,
+				  struct json_object *jobj)
+{
+	struct json_object *jname = json_object_new_string(plugin->name);
+
+	if (!kshark_trace_file_to_json(plugin->file, plugin->name, jobj) ||
+	    !jname) {
+		json_object_put(jname);
+		return false;
+	}
+
+	json_object_object_add(jobj, "name", jname);
+	return true;
+}
+
+/**
+ * @brief Record the name of a plugin's obj file and its timestamp into a
+ *	  Configuration document.
+ *
+ * @param plugin: The plugin to be expected.
+ * @param format: Input location for the Configuration format identifier.
+ *		  Currently only Json format is supported.
+ *
+ * @returns kshark_config_doc instance on success, otherwise NULL. Use
+ *	    kshark_free_config_doc() to free the object.
+ */
+struct kshark_config_doc *
+kshark_export_plugin_file(struct kshark_plugin_list *plugin,
+			  enum kshark_config_formats format)
+{
+	/*  Create a new Configuration document. */
+	struct kshark_config_doc *conf =
+		kshark_config_new("kshark.config.library", format);
+
+	if (!conf)
+		return NULL;
+
+	switch (format) {
+	case KS_CONFIG_JSON:
+		kshark_plugin_to_json(plugin, conf->conf_doc);
+		return conf;
+
+	default:
+		fprintf(stderr, "Document format %d not supported\n",
+			conf->format);
+		return NULL;
+	}
+}
+
+static bool kshark_all_plugins_to_json(struct kshark_context *kshark_ctx,
+				       struct json_object *jobj)
+{
+	struct kshark_plugin_list *plugin = kshark_ctx->plugins;
+	struct json_object *jfile, *jlist;
+
+	jlist = json_object_new_array();
+	if (!jlist)
+		return false;
+
+	while (plugin) {
+		jfile = json_object_new_object();
+		if (!kshark_trace_file_to_json(plugin->file, plugin->name, jfile))
+			goto fail;
+
+		json_object_array_add(jlist, jfile);
+		plugin = plugin->next;
+	}
+
+	json_object_object_add(jobj, "obj. files", jlist);
+
+	return true;
+
+ fail:
+	fprintf(stderr, "Failed to allocate memory for json_object.\n");
+	json_object_put(jobj);
+	json_object_put(jlist);
+	return false;
+}
+
+/**
+ * @brief Record the current list of registered plugins into a
+ *	  Configuration document.
+ *
+ * @param kshark_ctx: Input location for session context pointer.
+ * @param format: Input location for the Configuration format identifier.
+ *		  Currently only Json format is supported.
+ *
+ * @returns kshark_config_doc instance on success, otherwise NULL. Use
+ *	    kshark_free_config_doc() to free the object.
+ */
+struct kshark_config_doc *
+kshark_export_all_plugins(struct kshark_context *kshark_ctx,
+			  enum kshark_config_formats format)
+{
+	struct kshark_config_doc *conf =
+		kshark_config_new("kshark.config.plugins", KS_CONFIG_JSON);
+
+	if (!conf)
+		return NULL;
+
+	switch (format) {
+	case KS_CONFIG_JSON:
+		kshark_all_plugins_to_json(kshark_ctx, conf->conf_doc);
+		return conf;
+
+	default:
+		fprintf(stderr, "Document format %d not supported\n",
+			conf->format);
+		return NULL;
+	}
+}
+
+static bool kshark_plugin_from_json(struct kshark_context *kshark_ctx,
+				    struct json_object *jobj)
+{
+	const char *file, *name;
+
+	if (!kshark_trace_file_from_json(&file, &name, NULL, jobj)) {
+		fprintf(stderr, "Failed to import plugin!\n");
+		return false;
+	}
+
+	return !!(long) kshark_register_plugin(kshark_ctx, name, file);
+}
+
+static bool kshark_all_plugins_from_json(struct kshark_context *kshark_ctx,
+					 struct json_object *jobj)
+{
+	struct json_object *jlist, *jfile;
+	int i, n_plugins;
+
+	if (!kshark_ctx || !jobj)
+		return false;
+
+	if (!kshark_json_type_check(jobj, "kshark.config.plugins") ||
+	    !json_object_object_get_ex(jobj, "obj. files", &jlist) ||
+	    json_object_get_type(jlist) != json_type_array)
+		goto fail;
+
+	n_plugins = json_object_array_length(jlist);
+	for (i = 0; i < n_plugins; ++i) {
+		jfile = json_object_array_get_idx(jlist, i);
+		if (!jfile)
+			goto fail;
+
+		kshark_plugin_from_json(kshark_ctx, jfile);
+	}
+
+	return true;
+
+ fail:
+	json_object_put(jfile);
+	json_object_put(jlist);
+	return false;
+}
+
+/**
+ * @brief Load the list of registered plugins from a Configuration
+ *	  document.
+ *
+ * @param kshark_ctx: Input location for session context pointer.
+ * @param conf: Input location for the kshark_config_doc instance. Currently
+ *		only Json format is supported.
+ *
+ * @returns True, if plugins have been loaded. If the configuration
+ *	    document contains no data or in a case of an error, the function
+ *	    returns False.
+ */
+bool kshark_import_all_plugins(struct kshark_context *kshark_ctx,
+			       struct kshark_config_doc *conf)
+{
+	switch (conf->format) {
+	case KS_CONFIG_JSON:
+		return kshark_all_plugins_from_json(kshark_ctx,
+						    conf->conf_doc);
+
+	default:
+		fprintf(stderr, "Document format %d not supported\n",
+			conf->format);
+		return false;
+	}
+}
+
+static void kshark_stream_plugins_to_json(struct kshark_data_stream *stream,
+				          struct json_object *jobj)
+{
+	struct kshark_dpi_list *plugin = stream->plugins;
+	struct json_object *jlist, *jplg;
+	bool active;
+
+	jlist = json_object_new_array();
+	while (plugin) {
+		jplg = json_object_new_array();
+		json_object_array_add(jplg,
+				      json_object_new_string(plugin->interface->name));
+
+		active = plugin->status & KSHARK_PLUGIN_ENABLED;
+		json_object_array_add(jplg, json_object_new_boolean(active));
+
+		json_object_array_add(jlist, jplg);
+
+		plugin = plugin->next;
+	}
+
+	json_object_object_add(jobj, "registered", jlist);
+}
+
+/**
+ * @brief Record the current list of plugins registered for a given Data
+ *	  stream into a Configuration document.
+ *
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param format: Input location for the Configuration format identifier.
+ *		  Currently only Json format is supported.
+ *
+ * @returns kshark_config_doc instance on success, otherwise NULL. Use
+ *	    kshark_free_config_doc() to free the object.
+ */
+struct kshark_config_doc *
+kshark_export_stream_plugins(struct kshark_data_stream *stream,
+			     enum kshark_config_formats format)
+{
+	struct kshark_config_doc *conf =
+		kshark_config_new("kshark.config.plugins", KS_CONFIG_JSON);
+
+	if (!conf)
+		return NULL;
+
+	switch (format) {
+	case KS_CONFIG_JSON:
+		kshark_stream_plugins_to_json(stream, conf->conf_doc);
+		return conf;
 
 	default:
 		fprintf(stderr, "Document format %d not supported\n",
 			conf->format);
 		return NULL;
 	}
+}
+
+static bool kshark_stream_plugins_from_json(struct kshark_context *kshark_ctx,
+					    struct kshark_data_stream *stream,
+					    struct json_object *jobj)
+{
+	struct json_object *jlist, *jplg, *jname, *jstatus;
+	struct kshark_plugin_list *plugin;
+	struct kshark_dpi_list *dpi_list;
+	struct kshark_dpi *dpi;
+	int i, n_plugins;
+	bool active;
+
+	jplg = jname = jstatus = NULL;
+
+	if (!kshark_ctx || !stream || !jobj)
+		return false;
+
+	if (!kshark_json_type_check(jobj, "kshark.config.plugins") ||
+	    !json_object_object_get_ex(jobj, "registered", &jlist) ||
+	    json_object_get_type(jlist) != json_type_array)
+		goto fail;
 
-	return file;
+	n_plugins = json_object_array_length(jlist);
+	for (i = 0; i < n_plugins; ++i) {
+		jplg = json_object_array_get_idx(jlist, i);
+		if (!jplg ||
+		    json_object_get_type(jplg) != json_type_array ||
+		    json_object_array_length(jplg) != 2)
+			goto fail;
+
+		jname = json_object_array_get_idx(jplg, 0);
+		jstatus = json_object_array_get_idx(jplg, 1);
+		if (!jname || !jstatus)
+			goto fail;
+
+		plugin = kshark_find_plugin_by_name(kshark_ctx->plugins,
+						    json_object_get_string(jname));
+
+		if (plugin) {
+			active = json_object_get_boolean(jstatus);
+			dpi = plugin->process_interface;
+			dpi_list = kshark_register_plugin_to_stream(stream, dpi,
+								    active);
+
+			kshark_handle_dpi(stream, dpi_list, KSHARK_PLUGIN_INIT);
+		}
+	}
+
+	return true;
+
+ fail:
+	json_object_put(jplg);
+	json_object_put(jlist);
+	return false;
+}
+
+/**
+ * @brief Load the list of registered plugins for a given Data
+ *	  stream from a Configuration document.
+ *
+ * @param kshark_ctx: Input location for session context pointer.
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param conf: Input location for the kshark_config_doc instance. Currently
+ *		only Json format is supported.
+ *
+ * @returns True, if plugins have been loaded. If the configuration
+ *	    document contains no data or in a case of an error, the function
+ *	    returns False.
+ */
+bool kshark_import_stream_plugins(struct kshark_context *kshark_ctx,
+				  struct kshark_data_stream *stream,
+				  struct kshark_config_doc *conf)
+{
+	switch (conf->format) {
+	case KS_CONFIG_JSON:
+		return kshark_stream_plugins_from_json(kshark_ctx, stream,
+						       conf->conf_doc);
+
+	default:
+		fprintf(stderr, "Document format %d not supported\n",
+			conf->format);
+		return false;
+	}
 }
 
 static bool kshark_model_to_json(struct kshark_trace_histo *histo,
@@ -593,14 +1007,13 @@ static bool kshark_model_to_json(struct kshark_trace_histo *histo,
 /**
  * @brief Record the current configuration of the Vis. model into a
  *	  Configuration document.
- * Load the configuration of the Vis. model from a Configuration
- *	  document.
  *
  * @param histo: Input location for the Vis. model descriptor.
  * @param format: Input location for the kshark_config_doc instance. Currently
  *		  only Json format is supported.
  *
- * @returns True on success, otherwise False.
+ * @returns kshark_config_doc instance on success, otherwise NULL. Use
+ *	    free() to free the object.
  */
 struct kshark_config_doc *
 kshark_export_model(struct kshark_trace_histo *histo,
@@ -688,17 +1101,21 @@ bool kshark_import_model(struct kshark_trace_histo *histo,
 	}
 }
 
-static bool kshark_event_filter_to_json(struct tep_handle *pevent,
-					struct tracecmd_filter_id *filter,
+static bool kshark_event_filter_to_json(struct kshark_data_stream *stream,
+					enum kshark_filter_type filter_type,
 					const char *filter_name,
 					struct json_object *jobj)
 {
-	json_object *jfilter_data, *jevent, *jsystem, *jname;
-	struct tep_event *event;
-	int i, evt, *ids, nr_events;
-	char *temp;
+	json_object *jfilter_data, *jname;
+	struct kshark_hash_id *filter;
+	char *name_str;
+	int i, *ids;
+
+	filter = kshark_get_filter(stream, filter_type);
+	if (!filter)
+		return false;
 
-	jevent = jsystem = jname = NULL;
+	jname = NULL;
 
 	/*
 	 * If this Json document already contains a description of the filter,
@@ -707,7 +1124,7 @@ static bool kshark_event_filter_to_json(struct tep_handle *pevent,
 	json_del_if_exist(jobj, filter_name);
 
 	/* Get the array of Ids to be fitered. */
-	ids = tracecmd_filter_ids(filter);
+	ids = kshark_hash_ids(filter);
 	if (!ids)
 		return true;
 
@@ -716,32 +1133,15 @@ static bool kshark_event_filter_to_json(struct tep_handle *pevent,
 	if (!jfilter_data)
 		goto fail;
 
-	nr_events = tep_get_events_count(pevent);
 	for (i = 0; i < filter->count; ++i) {
-		for (evt = 0; evt < nr_events; ++evt) {
-			event = tep_get_event(pevent, evt);
-			if (event->id == ids[i]) {
-				jevent = json_object_new_object();
-
-				temp = event->system;
-				jsystem = json_object_new_string(temp);
-
-				temp = event->name;
-				jname = json_object_new_string(temp);
-
-				if (!jevent || !jsystem || !jname)
-					goto fail;
-
-				json_object_object_add(jevent, "system",
-							       jsystem);
-
-				json_object_object_add(jevent, "name",
-							       jname);
-
-				json_object_array_add(jfilter_data, jevent);
+		name_str = kshark_event_from_id(stream->stream_id,
+						ids[i]);
+		if (name_str) {
+			jname = json_object_new_string(name_str);
+			if (!jname)
+				goto fail;
 
-				break;
-			}
+			json_object_array_add(jfilter_data, jname);
 		}
 	}
 
@@ -755,8 +1155,6 @@ static bool kshark_event_filter_to_json(struct tep_handle *pevent,
  fail:
 	fprintf(stderr, "Failed to allocate memory for json_object.\n");
 	json_object_put(jfilter_data);
-	json_object_put(jevent);
-	json_object_put(jsystem);
 	json_object_put(jname);
 	free(ids);
 
@@ -767,22 +1165,22 @@ static bool kshark_event_filter_to_json(struct tep_handle *pevent,
  * @brief Record the current configuration of an Event Id filter into a
  *	  Configuration document.
  *
- * @param pevent: Input location for the Page event.
- * @param filter: Input location for an Id filter.
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param filter_type: Identifier of the filter.
  * @param filter_name: The name of the filter to show up in the Json document.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported.
  *
  * @returns True on success, otherwise False.
  */
-bool kshark_export_event_filter(struct tep_handle *pevent,
-				struct tracecmd_filter_id *filter,
+bool kshark_export_event_filter(struct kshark_data_stream *stream,
+				enum kshark_filter_type filter_type,
 				const char *filter_name,
 				struct kshark_config_doc *conf)
 {
 	switch (conf->format) {
 	case KS_CONFIG_JSON:
-		return kshark_event_filter_to_json(pevent, filter,
+		return kshark_event_filter_to_json(stream, filter_type,
 						   filter_name,
 						   conf->conf_doc);
 
@@ -793,15 +1191,19 @@ bool kshark_export_event_filter(struct tep_handle *pevent,
 	}
 }
 
-static int kshark_event_filter_from_json(struct tep_handle *pevent,
-					  struct tracecmd_filter_id *filter,
-					  const char *filter_name,
-					  struct json_object *jobj)
+static int kshark_event_filter_from_json(struct kshark_data_stream *stream,
+					 enum kshark_filter_type filter_type,
+					 const char *filter_name,
+					 struct json_object *jobj)
 {
-	json_object *jfilter, *jevent, *jsystem, *jname;
-	const char *system_str, *name_str;
-	struct tep_event *event;
-	int i, length, count = 0;
+	int i, length, event_id, count = 0;
+	struct kshark_hash_id *filter;
+	json_object *jfilter, *jevent;
+	const char *name_str;
+
+	filter = kshark_get_filter(stream, filter_type);
+	if (!filter)
+		return 0;
 
 	/*
 	 * Use the name of the filter to find the array of events associated
@@ -819,20 +1221,13 @@ static int kshark_event_filter_from_json(struct tep_handle *pevent,
 	length = json_object_array_length(jfilter);
 	for (i = 0; i < length; ++i) {
 		jevent = json_object_array_get_idx(jfilter, i);
-
-		if (!json_object_object_get_ex(jevent, "system", &jsystem) ||
-		    !json_object_object_get_ex(jevent, "name", &jname))
-			goto fail;
-
-		system_str = json_object_get_string(jsystem);
-		name_str = json_object_get_string(jname);
-
-		event = tep_find_event_by_name(pevent, system_str, name_str);
-		if (!event)
+		name_str = json_object_get_string(jevent);
+		event_id = kshark_find_event_id(stream, name_str);
+		if (event_id < 0)
 			continue;
 
-		tracecmd_filter_id_add(filter, event->id);
-		++count;
+		kshark_hash_id_add(filter, event_id);
+		count++;
 	}
 
 	if (count != length)
@@ -842,32 +1237,32 @@ static int kshark_event_filter_from_json(struct tep_handle *pevent,
 
  fail:
 	fprintf(stderr, "Failed to load event filter from json_object.\n");
-	tracecmd_filter_id_clear(filter);
+	kshark_hash_id_clear(filter);
 	return 0;
 }
 
 /**
  * @brief Load from Configuration document the configuration of an Event Id filter.
  *
- * @param pevent: Input location for the Page event.
- * @param filter: Input location for an Id filter.
+ * @param stream: Input location for a Trace data stream pointer.
+ * @param filter_type: Identifier of the filter.
  * @param filter_name: The name of the filter as showing up in the Config.
  *	               document.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported.
  *
  * @returns The total number of events added to the filter. If not all events
- *	    listed in the input configuration have been added successfully,
- *	    the returned number is negative.
+ *         listed in the input configuration have been added successfully,
+ *         the returned number is negative.
  */
-int kshark_import_event_filter(struct tep_handle *pevent,
-			       struct tracecmd_filter_id *filter,
+int kshark_import_event_filter(struct kshark_data_stream *stream,
+			       enum kshark_filter_type filter_type,
 			       const char *filter_name,
 			       struct kshark_config_doc *conf)
 {
 	switch (conf->format) {
 	case KS_CONFIG_JSON:
-		return kshark_event_filter_from_json(pevent, filter,
+		return kshark_event_filter_from_json(stream, filter_type,
 						     filter_name,
 						     conf->conf_doc);
 
@@ -878,7 +1273,7 @@ int kshark_import_event_filter(struct tep_handle *pevent,
 	}
 }
 
-static bool kshark_filter_array_to_json(struct tracecmd_filter_id *filter,
+static bool kshark_filter_array_to_json(struct kshark_hash_id *filter,
 					const char *filter_name,
 					struct json_object *jobj)
 {
@@ -892,7 +1287,7 @@ static bool kshark_filter_array_to_json(struct tracecmd_filter_id *filter,
 	json_del_if_exist(jobj, filter_name);
 
 	/* Get the array of Ids to be filtered. */
-	ids = tracecmd_filter_ids(filter);
+	ids = kshark_hash_ids(filter);
 	if (!ids)
 		return true;
 
@@ -936,7 +1331,7 @@ static bool kshark_filter_array_to_json(struct tracecmd_filter_id *filter,
  *
  * @returns True on success, otherwise False.
  */
-bool kshark_export_filter_array(struct tracecmd_filter_id *filter,
+bool kshark_export_filter_array(struct kshark_hash_id *filter,
 				const char *filter_name,
 				struct kshark_config_doc *conf)
 {
@@ -952,7 +1347,7 @@ bool kshark_export_filter_array(struct tracecmd_filter_id *filter,
 	}
 }
 
-static bool kshark_filter_array_from_json(struct tracecmd_filter_id *filter,
+static bool kshark_filter_array_from_json(struct kshark_hash_id *filter,
 					  const char *filter_name,
 					  struct json_object *jobj)
 {
@@ -978,7 +1373,7 @@ static bool kshark_filter_array_from_json(struct tracecmd_filter_id *filter,
 		if (!jpid)
 			goto fail;
 
-		tracecmd_filter_id_add(filter, json_object_get_int(jpid));
+		kshark_hash_id_add(filter, json_object_get_int(jpid));
 	}
 
 	return true;
@@ -1002,7 +1397,7 @@ static bool kshark_filter_array_from_json(struct tracecmd_filter_id *filter,
  *	    document contains no data for this particular filter or in a case
  *	    of an error, the function returns False.
  */
-bool kshark_import_filter_array(struct tracecmd_filter_id *filter,
+bool kshark_import_filter_array(struct kshark_hash_id *filter,
 				const char *filter_name,
 				struct kshark_config_doc *conf)
 {
@@ -1018,16 +1413,14 @@ bool kshark_import_filter_array(struct tracecmd_filter_id *filter,
 	}
 }
 
-static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx,
+static bool kshark_adv_filters_to_json(struct kshark_data_stream *stream,
 				       struct json_object *jobj)
 {
-	struct tep_event_filter *adv_filter = kshark_ctx->advanced_event_filter;
-	json_object *jfilter_data, *jevent, *jsystem, *jname, *jfilter;
-	struct tep_event **events;
-	char *str;
-	int i;
+	json_object *jfilter_data, *jevent, *jname, *jfilter;
+	char *filter_str;
+	int *events, i;
 
-	jevent = jsystem = jname = jfilter = NULL;
+	jevent = jname = jfilter = NULL;
 
 	/*
 	 * If this Json document already contains a description of the model,
@@ -1035,8 +1428,7 @@ static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx,
 	 */
 	json_del_if_exist(jobj, KS_ADV_EVENT_FILTER_NAME);
 
-	if (!kshark_ctx->advanced_event_filter ||
-	    !kshark_ctx->advanced_event_filter->filters)
+	if (!kshark_tep_filter_is_set(stream))
 		return true;
 
 	/* Create a Json array and fill the Id values into it. */
@@ -1044,24 +1436,23 @@ static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx,
 	if (!jfilter_data)
 		goto fail;
 
-	events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM);
+	events = kshark_get_all_event_ids(stream);
 	if (!events)
 		return false;
 
-	for (i = 0; events[i]; i++) {
-		str = tep_filter_make_string(adv_filter,
-					     events[i]->id);
-		if (!str)
+	for (i = 0; i < stream->n_events; ++i) {
+		filter_str = kshark_tep_filter_make_string(stream, events[i]);
+		if (!filter_str)
 			continue;
 
 		jevent = json_object_new_object();
-		jsystem = json_object_new_string(events[i]->system);
-		jname = json_object_new_string(events[i]->name);
-		jfilter = json_object_new_string(str);
-		if (!jevent || !jsystem || !jname || !jfilter)
+		jname = json_object_new_string(kshark_event_from_id(stream->stream_id,
+								    events[i]));
+
+		jfilter = json_object_new_string(filter_str);
+		if (!jevent || !jname || !jfilter)
 			goto fail;
 
-		json_object_object_add(jevent, "system", jsystem);
 		json_object_object_add(jevent, "name", jname);
 		json_object_object_add(jevent, "condition", jfilter);
 
@@ -1077,7 +1468,6 @@ static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx,
 	fprintf(stderr, "Failed to allocate memory for json_object.\n");
 	json_object_put(jfilter_data);
 	json_object_put(jevent);
-	json_object_put(jsystem);
 	json_object_put(jname);
 	json_object_put(jfilter);
 
@@ -1089,15 +1479,27 @@ static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx,
  *	  Configuration document.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported. If NULL, a new Adv. Filter
  *		Configuration document will be created.
  *
  * @returns True on success, otherwise False.
  */
-bool kshark_export_adv_filters(struct kshark_context *kshark_ctx,
+bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, int sd,
 			       struct kshark_config_doc **conf)
 {
+	struct kshark_data_stream *stream =
+		kshark_get_data_stream(kshark_ctx, sd);
+
+	if (!stream)
+		return false;
+
+	if (!kshark_is_tep(stream)) {
+		/* Nothing to export. */
+		return true;
+	}
+
 	if (!*conf)
 		*conf = kshark_filter_config_new(KS_CONFIG_JSON);
 
@@ -1106,7 +1508,7 @@ bool kshark_export_adv_filters(struct kshark_context *kshark_ctx,
 
 	switch ((*conf)->format) {
 	case KS_CONFIG_JSON:
-		return kshark_adv_filters_to_json(kshark_ctx,
+		return kshark_adv_filters_to_json(stream,
 						  (*conf)->conf_doc);
 
 	default:
@@ -1116,11 +1518,10 @@ bool kshark_export_adv_filters(struct kshark_context *kshark_ctx,
 	}
 }
 
-static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx,
+static bool kshark_adv_filters_from_json(struct kshark_data_stream *stream,
 					 struct json_object *jobj)
 {
-	struct tep_event_filter *adv_filter = kshark_ctx->advanced_event_filter;
-	json_object *jfilter, *jsystem, *jname, *jcond;
+	json_object *jfilter, *jname, *jcond;
 	int i, length, n, ret = 0;
 	char *filter_str = NULL;
 
@@ -1142,13 +1543,11 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx,
 	for (i = 0; i < length; ++i) {
 		jfilter = json_object_array_get_idx(jfilter, i);
 
-		if (!json_object_object_get_ex(jfilter, "system", &jsystem) ||
-		    !json_object_object_get_ex(jfilter, "name", &jname) ||
+		if (!json_object_object_get_ex(jfilter, "name", &jname) ||
 		    !json_object_object_get_ex(jfilter, "condition", &jcond))
 			goto fail;
 
-		n = asprintf(&filter_str, "%s/%s:%s",
-			     json_object_get_string(jsystem),
+		n = asprintf(&filter_str, "%s:%s",
 			     json_object_get_string(jname),
 			     json_object_get_string(jcond));
 
@@ -1157,8 +1556,7 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx,
 			goto fail;
 		}
 
-		ret = tep_filter_add_filter_str(adv_filter,
-						filter_str);
+		ret = kshark_tep_add_filter_str(stream, filter_str);
 		if (ret < 0)
 			goto fail;
 	}
@@ -1167,16 +1565,6 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx,
 
  fail:
 	fprintf(stderr, "Failed to laod Advanced filters.\n");
-	if (ret < 0) {
-		char error_str[200];
-		int error_status =
-			tep_strerror(kshark_ctx->pevent, ret, error_str,
-				     sizeof(error_str));
-
-		if (error_status == 0)
-			fprintf(stderr, "filter failed due to: %s\n",
-					error_str);
-	}
 
 	free(filter_str);
 	return false;
@@ -1187,6 +1575,7 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx,
  *	  filter.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported.
  *
@@ -1194,12 +1583,18 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx,
  *	    document contains no data for the Adv. filter or in a case of
  *	    an error, the function returns False.
  */
-bool kshark_import_adv_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, int sd,
 			       struct kshark_config_doc *conf)
 {
+	struct kshark_data_stream *stream =
+		kshark_get_data_stream(kshark_ctx, sd);
+
+	if (!stream)
+		return false;
+
 	switch (conf->format) {
 	case KS_CONFIG_JSON:
-		return kshark_adv_filters_from_json(kshark_ctx,
+		return kshark_adv_filters_from_json(stream,
 						    conf->conf_doc);
 
 	default:
@@ -1212,8 +1607,10 @@ bool kshark_import_adv_filters(struct kshark_context *kshark_ctx,
 static bool kshark_user_mask_to_json(struct kshark_context *kshark_ctx,
 				     struct json_object *jobj)
 {
-	uint8_t mask = kshark_ctx->filter_mask;
 	json_object *jmask;
+	uint8_t mask;
+
+	mask = kshark_ctx->filter_mask;
 
 	jmask = json_object_new_int((int) mask);
 	if (!jmask)
@@ -1246,8 +1643,7 @@ bool kshark_export_user_mask(struct kshark_context *kshark_ctx,
 
 	switch ((*conf)->format) {
 	case KS_CONFIG_JSON:
-		return kshark_user_mask_to_json(kshark_ctx,
-						(*conf)->conf_doc);
+		return kshark_user_mask_to_json(kshark_ctx, (*conf)->conf_doc);
 
 	default:
 		fprintf(stderr, "Document format %d not supported\n",
@@ -1296,8 +1692,7 @@ bool kshark_import_user_mask(struct kshark_context *kshark_ctx,
 {
 	switch (conf->format) {
 	case KS_CONFIG_JSON:
-		return kshark_user_mask_from_json(kshark_ctx,
-						  conf->conf_doc);
+		return kshark_user_mask_from_json(kshark_ctx, conf->conf_doc);
 
 	default:
 		fprintf(stderr, "Document format %d not supported\n",
@@ -1306,11 +1701,133 @@ bool kshark_import_user_mask(struct kshark_context *kshark_ctx,
 	}
 }
 
+static bool kshark_calib_array_from_json(struct kshark_context *kshark_ctx,
+					 int sd, struct json_object *jobj)
+{
+	json_object *jcalib_argv, *jcalib;
+	int64_t *calib_argv = NULL;
+	int i, calib_length;
+
+	if (!json_object_object_get_ex(jobj, "calib. array", &jcalib_argv) ||
+	    json_object_get_type(jcalib_argv) != json_type_array)
+		return false;
+
+	calib_length = json_object_array_length(jcalib_argv);
+	if (!calib_length)
+		return false;
+
+	calib_argv = calloc(calib_length, sizeof(*calib_argv));
+	for (i = 0; i < calib_length; ++i) {
+		jcalib = json_object_array_get_idx(jcalib_argv, i);
+		calib_argv[i] = json_object_get_int64(jcalib);
+	}
+
+	kshark_ctx->stream[sd]->calib = kshark_offset_calib;
+	kshark_ctx->stream[sd]->calib_array = calib_argv;
+	kshark_ctx->stream[sd]->calib_array_size = calib_length;
+
+	return true;
+}
+
+/**
+ * @brief Load from Configuration document the value of the time calibration
+ *	  constants into a Configuration document.
+ *
+ * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
+ * @param conf: Input location for the kshark_config_doc instance. Currently
+ *		only Json format is supported. If NULL, a new Configuration
+ *		document will be created.
+ *
+ * @returns True on success, otherwise False.
+ */
+bool kshark_import_calib_array(struct kshark_context *kshark_ctx, int sd,
+			       struct kshark_config_doc *conf)
+{
+	switch (conf->format) {
+	case KS_CONFIG_JSON:
+		return kshark_calib_array_from_json(kshark_ctx, sd, conf->conf_doc);
+
+	default:
+		fprintf(stderr, "Document format %d not supported\n",
+			conf->format);
+		return false;
+	}
+}
+
+static bool kshark_calib_array_to_json(struct kshark_context *kshark_ctx,
+				       int sd, struct json_object *jobj)
+{
+	json_object *jval = NULL, *jcalib = NULL;
+	struct kshark_data_stream *stream;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream || !stream->calib_array_size)
+		goto fail;
+
+	jcalib = json_object_new_array();
+	if (!jcalib)
+		goto fail;
+
+	for (size_t i = 0; i < stream->calib_array_size; ++i) {
+		jval = json_object_new_int64(stream->calib_array[i]);
+		if (!jval)
+			goto fail;
+
+		json_object_array_add(jcalib, jval);
+	}
+
+	/* Add the mask to the filter config document. */
+	json_object_object_add(jobj, "calib. array", jcalib);
+
+	return true;
+
+ fail:
+	json_object_put(jval);
+	json_object_put(jcalib);
+
+	return false;
+}
+
+/**
+ * @brief Record the current values of the time calibration constants into
+ *	  a Configuration document.
+ *
+ * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
+ * @param conf: Input location for the kshark_config_doc instance. Currently
+ *		only Json format is supported. If NULL, a new Configuration
+ *		document will be created.
+ *
+ * @returns True on success, otherwise False.
+ */
+bool kshark_export_calib_array(struct kshark_context *kshark_ctx, int sd,
+			       struct kshark_config_doc **conf)
+{
+	if (!*conf)
+		*conf = kshark_stream_config_new(KS_CONFIG_JSON);
+
+	if (!*conf)
+		return false;
+
+	switch ((*conf)->format) {
+	case KS_CONFIG_JSON:
+		return kshark_calib_array_to_json(kshark_ctx, sd,
+						  (*conf)->conf_doc);
+
+	default:
+		fprintf(stderr, "Document format %d not supported\n",
+			(*conf)->format);
+		return false;
+	}
+}
+
 /**
  * @brief Record the current configuration of "show task" and "hide task"
  *	  filters into a Json document.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported. If NULL, a new Filter
  *		Configuration document will be created.
@@ -1318,10 +1835,15 @@ bool kshark_import_user_mask(struct kshark_context *kshark_ctx,
  * @returns True, if a filter has been recorded. If both filters contain
  *	    no Id values or in a case of an error, the function returns False.
  */
-bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx,
+bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, int sd,
 				     struct kshark_config_doc **conf)
 {
-	bool ret = true;
+	struct kshark_data_stream *stream =
+		kshark_get_data_stream(kshark_ctx, sd);
+	bool ret;
+
+	if (!stream)
+		return false;
 
 	if (!*conf)
 		*conf = kshark_filter_config_new(KS_CONFIG_JSON);
@@ -1330,15 +1852,16 @@ bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx,
 		return false;
 
 	/* Save a filter only if it contains Id values. */
-	if (kshark_this_filter_is_set(kshark_ctx->show_event_filter))
-		ret &= kshark_export_event_filter(kshark_ctx->pevent,
-						  kshark_ctx->show_event_filter,
+	ret = true;
+	if (kshark_this_filter_is_set(stream->show_event_filter))
+		ret &= kshark_export_event_filter(stream,
+						  KS_SHOW_EVENT_FILTER,
 						  KS_SHOW_EVENT_FILTER_NAME,
 						  *conf);
 
-	if (kshark_this_filter_is_set(kshark_ctx->hide_event_filter))
-		ret &= kshark_export_event_filter(kshark_ctx->pevent,
-						  kshark_ctx->hide_event_filter,
+	if (kshark_this_filter_is_set(stream->hide_event_filter))
+		ret &= kshark_export_event_filter(stream,
+						  KS_HIDE_EVENT_FILTER,
 						  KS_HIDE_EVENT_FILTER_NAME,
 						  *conf);
 
@@ -1350,6 +1873,7 @@ bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx,
  *	  filters into a Configuration document.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported. If NULL, a new Filter
  *		Configuration document will be created.
@@ -1357,10 +1881,15 @@ bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx,
  * @returns True, if a filter has been recorded. If both filters contain
  *	    no Id values or in a case of an error, the function returns False.
  */
-bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx,
+bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, int sd,
 				    struct kshark_config_doc **conf)
 {
-	bool ret = true;
+	struct kshark_data_stream *stream =
+		kshark_get_data_stream(kshark_ctx, sd);
+	bool ret;
+
+	if (!stream)
+		return false;
 
 	if (!*conf)
 		*conf = kshark_filter_config_new(KS_CONFIG_JSON);
@@ -1369,25 +1898,26 @@ bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx,
 		return false;
 
 	/* Save a filter only if it contains Id values. */
-	if (kshark_this_filter_is_set(kshark_ctx->show_task_filter))
-		ret &= kshark_export_filter_array(kshark_ctx->show_task_filter,
+	ret = true;
+	if (kshark_this_filter_is_set(stream->show_task_filter))
+		ret &= kshark_export_filter_array(stream->show_task_filter,
 						  KS_SHOW_TASK_FILTER_NAME,
 						  *conf);
 
-	if (kshark_this_filter_is_set(kshark_ctx->hide_task_filter))
-		ret &= kshark_export_filter_array(kshark_ctx->hide_task_filter,
+	if (kshark_this_filter_is_set(stream->hide_task_filter))
+		ret &= kshark_export_filter_array(stream->hide_task_filter,
 						  KS_HIDE_TASK_FILTER_NAME,
 						  *conf);
 
 	return ret;
 }
 
-
 /**
  * @brief Record the current configuration of "show cpu" and "hide cpu"
  *	  filters into a Configuration document.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported. If NULL, a new Filter
  *		Configuration document will be created.
@@ -1395,10 +1925,15 @@ bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx,
  * @returns True, if a filter has been recorded. If both filters contain
  *	    no Id values or in a case of an error, the function returns False.
  */
-bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx,
+bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, int sd,
 				   struct kshark_config_doc **conf)
 {
-	bool ret = true;
+	struct kshark_data_stream *stream =
+		kshark_get_data_stream(kshark_ctx, sd);
+	bool ret;
+
+	if (!stream)
+		return false;
 
 	if (!*conf)
 		*conf = kshark_filter_config_new(KS_CONFIG_JSON);
@@ -1407,13 +1942,14 @@ bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx,
 		return false;
 
 	/* Save a filter only if it contains Id values. */
-	if (kshark_this_filter_is_set(kshark_ctx->show_cpu_filter))
-		ret &= kshark_export_filter_array(kshark_ctx->show_cpu_filter,
+	ret = true;
+	if (kshark_this_filter_is_set(stream->show_cpu_filter))
+		ret &= kshark_export_filter_array(stream->show_cpu_filter,
 						  KS_SHOW_CPU_FILTER_NAME,
 						  *conf);
 
-	if (kshark_this_filter_is_set(kshark_ctx->hide_cpu_filter))
-		ret &= kshark_export_filter_array(kshark_ctx->hide_cpu_filter,
+	if (kshark_this_filter_is_set(stream->hide_cpu_filter))
+		ret &= kshark_export_filter_array(stream->hide_cpu_filter,
 						  KS_HIDE_CPU_FILTER_NAME,
 						  *conf);
 
@@ -1425,6 +1961,7 @@ bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx,
  *	  and "hide event" filters.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported.
  *
@@ -1432,18 +1969,23 @@ bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx,
  *	    document contains no data for any event filter or in a case
  *	    of an error, the function returns False.
  */
-bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, int sd,
 				     struct kshark_config_doc *conf)
 {
+	struct kshark_data_stream *stream =
+		kshark_get_data_stream(kshark_ctx, sd);
 	bool ret = false;
 
-	ret |= kshark_import_event_filter(kshark_ctx->pevent,
-					  kshark_ctx->hide_event_filter,
+	if (!stream)
+		return false;
+
+	ret |= kshark_import_event_filter(stream,
+					  KS_HIDE_EVENT_FILTER,
 					  KS_HIDE_EVENT_FILTER_NAME,
 					  conf);
 
-	ret |= kshark_import_event_filter(kshark_ctx->pevent,
-					  kshark_ctx->show_event_filter,
+	ret |= kshark_import_event_filter(stream,
+					  KS_SHOW_EVENT_FILTER,
 					  KS_SHOW_EVENT_FILTER_NAME,
 					  conf);
 
@@ -1455,6 +1997,7 @@ bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx,
  *	  and "hide task" filters.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported.
  *
@@ -1462,16 +2005,21 @@ bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx,
  *	    document contains no data for any task filter or in a case of an
  *	    error, the function returns False.
  */
-bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, int sd,
 				    struct kshark_config_doc *conf)
 {
+	struct kshark_data_stream *stream =
+		kshark_get_data_stream(kshark_ctx, sd);
 	bool ret = false;
 
-	ret |= kshark_import_filter_array(kshark_ctx->hide_task_filter,
+	if (!stream)
+		return false;
+
+	ret |= kshark_import_filter_array(stream->hide_task_filter,
 					  KS_HIDE_TASK_FILTER_NAME,
 					  conf);
 
-	ret |= kshark_import_filter_array(kshark_ctx->show_task_filter,
+	ret |= kshark_import_filter_array(stream->show_task_filter,
 					  KS_SHOW_TASK_FILTER_NAME,
 					  conf);
 
@@ -1483,6 +2031,7 @@ bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx,
  *	  and "hide cpu" filters.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported.
  *
@@ -1490,16 +2039,21 @@ bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx,
  *	    document contains no data for any cpu filter or in a case of an
  *	    error, the function returns False.
  */
-bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, int sd,
 				    struct kshark_config_doc *conf)
 {
+	struct kshark_data_stream *stream =
+		kshark_get_data_stream(kshark_ctx, sd);
 	bool ret = false;
 
-	ret |= kshark_import_filter_array(kshark_ctx->hide_cpu_filter,
+	if (!stream)
+		return false;
+
+	ret |= kshark_import_filter_array(stream->hide_cpu_filter,
 					  KS_HIDE_CPU_FILTER_NAME,
 					  conf);
 
-	ret |= kshark_import_filter_array(kshark_ctx->show_cpu_filter,
+	ret |= kshark_import_filter_array(stream->show_cpu_filter,
 					  KS_SHOW_CPU_FILTER_NAME,
 					  conf);
 
@@ -1511,6 +2065,7 @@ bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx,
  *	  configuration of all filters.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param format: Input location for the kshark_config_doc instance. Currently
  *		  only Json format is supported.
  *
@@ -1518,7 +2073,7 @@ bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx,
  *	    kshark_free_config_doc() to free the object.
  */
 struct kshark_config_doc *
-kshark_export_all_filters(struct kshark_context *kshark_ctx,
+kshark_export_all_filters(struct kshark_context *kshark_ctx, int sd,
 			  enum kshark_config_formats format)
 {
 	/*  Create a new Configuration document. */
@@ -1527,11 +2082,11 @@ kshark_export_all_filters(struct kshark_context *kshark_ctx,
 
 	/* Save a filter only if it contains Id values. */
 	if (!conf ||
-	    !kshark_export_all_event_filters(kshark_ctx, &conf) ||
-	    !kshark_export_all_task_filters(kshark_ctx, &conf) ||
-	    !kshark_export_all_cpu_filters(kshark_ctx, &conf) ||
+	    !kshark_export_all_event_filters(kshark_ctx, sd, &conf) ||
+	    !kshark_export_all_task_filters(kshark_ctx, sd, &conf) ||
+	    !kshark_export_all_cpu_filters(kshark_ctx, sd, &conf) ||
 	    !kshark_export_user_mask(kshark_ctx, &conf) ||
-	    !kshark_export_adv_filters(kshark_ctx, &conf)) {
+	    !kshark_export_adv_filters(kshark_ctx, sd, &conf)) {
 		kshark_free_config_doc(conf);
 		return NULL;
 	}
@@ -1543,6 +2098,7 @@ kshark_export_all_filters(struct kshark_context *kshark_ctx,
  * @brief Load from a Configuration document the configuration of all filters.
  *
  * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
  * @param conf: Input location for the kshark_config_doc instance. Currently
  *		only Json format is supported.
  *
@@ -1550,19 +2106,289 @@ kshark_export_all_filters(struct kshark_context *kshark_ctx,
  *	    document contains no data for any filter or in a case of an error,
  *	    the function returns False.
  */
-bool kshark_import_all_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_all_filters(struct kshark_context *kshark_ctx, int sd,
 			       struct kshark_config_doc *conf)
 {
 	bool ret;
-	ret = kshark_import_all_task_filters(kshark_ctx, conf);
-	ret |= kshark_import_all_cpu_filters(kshark_ctx, conf);
-	ret |= kshark_import_all_event_filters(kshark_ctx, conf);
+
+	ret = kshark_import_all_task_filters(kshark_ctx, sd, conf);
+	ret |= kshark_import_all_cpu_filters(kshark_ctx, sd, conf);
+	ret |= kshark_import_all_event_filters(kshark_ctx, sd, conf);
 	ret |= kshark_import_user_mask(kshark_ctx, conf);
-	ret |= kshark_import_adv_filters(kshark_ctx, conf);
+	ret |= kshark_import_adv_filters(kshark_ctx, sd, conf);
 
 	return ret;
 }
 
+/**
+ * @brief Create a Data Stream Configuration document.
+ *
+ * @param kshark_ctx: Input location for session context pointer.
+ * @param sd: Data stream identifier.
+ * @param format: Input location for the kshark_config_doc instance. Currently
+ *		  only Json format is supported.
+ *
+ * @returns kshark_config_doc instance on success, otherwise NULL. Use
+ *	    kshark_free_config_doc() to free the object.
+ */
+struct kshark_config_doc *
+kshark_export_dstream(struct kshark_context *kshark_ctx, int sd,
+		      enum kshark_config_formats format)
+{
+	struct kshark_config_doc *file_conf, *filter_conf, *sd_conf, *plg_conf;
+	struct kshark_config_doc *dstream_conf;
+	struct kshark_data_stream *stream;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return NULL;
+
+	/*  Create new Configuration documents. */
+	dstream_conf = kshark_stream_config_new(format);
+	sd_conf = kshark_config_alloc(KS_CONFIG_JSON);
+
+	sd_conf->conf_doc = json_object_new_int(sd);
+
+	filter_conf = kshark_export_all_filters(kshark_ctx, sd, format);
+
+	if (kshark_is_tep(stream) && kshark_tep_is_top_stream(stream))
+		file_conf = kshark_export_trace_file(stream->file,
+						     TOP_BUFF_NAME,
+						     format);
+	else
+		file_conf = kshark_export_trace_file(stream->file,
+						     stream->name,
+						     format);
+
+	plg_conf = kshark_export_stream_plugins(stream, format);
+
+	if (!dstream_conf ||
+	    !sd_conf ||
+	    !filter_conf ||
+	    !file_conf ||
+	    !plg_conf)
+		goto fail;
+
+	kshark_config_doc_add(dstream_conf, "stream id", sd_conf);
+	kshark_config_doc_add(dstream_conf, "data", file_conf);
+	kshark_config_doc_add(dstream_conf, "filters", filter_conf);
+	kshark_config_doc_add(dstream_conf, "plugins", plg_conf);
+
+	if (stream->calib_array_size)
+		kshark_export_calib_array(kshark_ctx, sd, &dstream_conf);
+
+	return dstream_conf;
+
+ fail:
+	kshark_free_config_doc(dstream_conf);
+	kshark_free_config_doc(filter_conf);
+	kshark_free_config_doc(file_conf);
+	kshark_free_config_doc(plg_conf);
+	kshark_free_config_doc(sd_conf);
+
+	return NULL;
+}
+
+/**
+ * @brief Load Data Stream from a Configuration document.
+ *
+ * @param kshark_ctx: Input location for session context pointer.
+ * @param conf: Input location for the kshark_config_doc instance. Currently
+ *		only Json format is supported.
+ *
+ * @returns The Stream Id on the loaded Data Stream on success, otherwise a
+ *	    negative error code.
+ */
+int kshark_import_dstream(struct kshark_context *kshark_ctx,
+			  struct kshark_config_doc *conf)
+{
+	struct kshark_config_doc *file_conf, *filter_conf, *plg_conf;
+	struct kshark_data_stream *stream;
+	bool ret = false;
+	int sd = -EFAULT;
+
+	if (!kshark_type_check(conf, "kshark.config.stream"))
+		return sd;
+
+	file_conf = kshark_config_alloc(KS_CONFIG_JSON);
+	filter_conf = kshark_config_alloc(KS_CONFIG_JSON);
+	plg_conf = kshark_config_alloc(KS_CONFIG_JSON);
+	if (!file_conf || !filter_conf || !plg_conf) {
+		fprintf(stderr,
+			"Failed to allocate memory for Json document.\n");
+		goto free;
+	}
+
+	if (kshark_config_doc_get(conf, "data", file_conf) &&
+	    kshark_config_doc_get(conf, "filters", filter_conf) &&
+	    kshark_config_doc_get(conf, "plugins", plg_conf)) {
+		sd = kshark_import_trace_file(kshark_ctx, file_conf);
+		if (sd < 0) {
+			fprintf(stderr,
+				"Failed to import data file form Json document.\n");
+			goto free;
+		}
+
+		stream = kshark_ctx->stream[sd];
+		kshark_import_calib_array(kshark_ctx, sd, conf);
+		ret = kshark_import_all_filters(kshark_ctx, sd,
+						filter_conf);
+		if (!ret) {
+			fprintf(stderr,
+				"Failed to import filters form Json document.\n");
+			kshark_close(kshark_ctx, sd);
+			sd = -EFAULT;
+			goto free;
+		}
+
+		ret = kshark_import_stream_plugins(kshark_ctx, stream, plg_conf);
+
+		if (!ret) {
+			fprintf(stderr,
+				"Failed to import stream plugins form Json document.\n");
+			kshark_close(kshark_ctx, sd);
+			sd = -EFAULT;
+			goto free;
+		}
+	}
+
+ free:
+	/* Free only the kshark_config_doc objects. */
+	free(file_conf);
+	free(filter_conf);
+	free(plg_conf);
+
+	return sd;
+}
+
+static bool
+kshark_export_all_dstreams_to_json(struct kshark_context *kshark_ctx,
+				   struct json_object *jobj)
+{
+	int *stream_ids = kshark_all_streams(kshark_ctx);
+	struct kshark_config_doc *dstream_conf;
+	struct json_object *jall_streams;
+
+	json_del_if_exist(jobj, KS_DSTREAMS_NAME);
+	jall_streams = json_object_new_array();
+
+	for (int i = 0; i < kshark_ctx->n_streams; ++i) {
+		dstream_conf = kshark_export_dstream(kshark_ctx, stream_ids[i],
+						     KS_CONFIG_JSON);
+		if (!dstream_conf)
+			goto fail;
+
+		json_object_array_put_idx(jall_streams, i, dstream_conf->conf_doc);
+
+		/* Free only the kshark_config_doc object. */
+		free(dstream_conf);
+	}
+
+	free(stream_ids);
+
+	json_object_object_add(jobj, KS_DSTREAMS_NAME, jall_streams);
+
+	return true;
+
+ fail:
+	json_object_put(jall_streams);
+	free(stream_ids);
+
+	return false;
+}
+
+/**
+ * @brief Record the current configuration for all Data Streams into a Json
+ *	  document.
+ *
+ * @param kshark_ctx: Input location for session context pointer.
+ * @param conf: Input location for the kshark_config_doc instance. Currently
+ *		only Json format is supported. If NULL, a new Configuration
+ *		document will be created.
+ *
+ * @returns True on success, otherwise False.
+ */
+bool kshark_export_all_dstreams(struct kshark_context *kshark_ctx,
+				struct kshark_config_doc **conf)
+{
+	if (!*conf)
+		*conf = kshark_session_config_new(KS_CONFIG_JSON);
+
+	if (!*conf)
+		return false;
+
+	switch ((*conf)->format) {
+	case KS_CONFIG_JSON:
+		return kshark_export_all_dstreams_to_json(kshark_ctx,
+							  (*conf)->conf_doc);
+
+	default:
+		fprintf(stderr, "Document format %d not supported\n",
+			(*conf)->format);
+		return false;
+	}
+}
+
+static ssize_t
+kshark_import_all_dstreams_from_json(struct kshark_context *kshark_ctx,
+				     struct json_object *jobj,
+				     struct kshark_entry ***data_rows)
+{
+	struct kshark_config_doc dstream_conf;
+	json_object *jall_streams, *jstream;
+	int sd, i, length;
+
+	if (!json_object_object_get_ex(jobj, KS_DSTREAMS_NAME, &jall_streams) ||
+	    json_object_get_type(jall_streams) != json_type_array)
+		return -EFAULT;
+
+	length = json_object_array_length(jall_streams);
+	if (!length)
+		return -EFAULT;
+
+	dstream_conf.format = KS_CONFIG_JSON;
+	for (i = 0; i < length; ++i) {
+		jstream = json_object_array_get_idx(jall_streams, i);
+		dstream_conf.conf_doc = jstream;
+		sd = kshark_import_dstream(kshark_ctx, &dstream_conf);
+
+		if (sd < 0)
+			return -EFAULT;
+	}
+
+	return kshark_load_all_entries(kshark_ctx, data_rows);
+}
+
+/**
+ * @brief Load all Data Streams from a Configuration document.
+ *
+ * @param kshark_ctx: Input location for session context pointer.
+ * @param conf: Input location for the kshark_config_doc instance. Currently
+ *		only Json format is supported.
+ * @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_import_all_dstreams(struct kshark_context *kshark_ctx,
+				   struct kshark_config_doc *conf,
+				   struct kshark_entry ***data_rows)
+{
+	switch (conf->format) {
+	case KS_CONFIG_JSON:
+		return kshark_import_all_dstreams_from_json(kshark_ctx,
+							    conf->conf_doc,
+							    data_rows);
+
+	default:
+		fprintf(stderr, "Document format %d not supported\n",
+			conf->format);
+		return -EFAULT;
+	}
+}
+
 static bool kshark_save_json_file(const char *file_name,
 				  struct json_object *jobj)
 {
diff --git a/src/libkshark.h b/src/libkshark.h
index dce3dd2c..dd4f2b75 100644
--- a/src/libkshark.h
+++ b/src/libkshark.h
@@ -895,6 +895,12 @@ enum kshark_config_formats {
  */
 #define KS_DATA_SOURCE_NAME		"trace data"
 
+/**
+ * Field name for the Configuration document describing all currently loaded
+ * data streams.
+ */
+#define KS_DSTREAMS_NAME		"data streams"
+
 struct kshark_config_doc *
 kshark_config_alloc(enum kshark_config_formats);
 
@@ -906,9 +912,15 @@ void kshark_free_config_doc(struct kshark_config_doc *conf);
 struct kshark_config_doc *
 kshark_record_config_new(enum kshark_config_formats);
 
+struct kshark_config_doc *
+kshark_stream_config_new(enum kshark_config_formats);
+
 struct kshark_config_doc *
 kshark_filter_config_new(enum kshark_config_formats);
 
+struct kshark_config_doc *
+kshark_session_config_new(enum kshark_config_formats format);
+
 struct kshark_config_doc *kshark_string_config_alloc(void);
 
 bool kshark_type_check(struct kshark_config_doc *conf, const char *type);
@@ -924,24 +936,42 @@ bool kshark_config_doc_get(struct kshark_config_doc *conf,
 struct kshark_trace_histo;
 
 struct kshark_config_doc *
-kshark_export_trace_file(const char *file,
+kshark_export_trace_file(const char *file, const char *name,
 			 enum kshark_config_formats format);
 
-const char *kshark_import_trace_file(struct kshark_context *kshark_ctx,
-				     struct kshark_config_doc *conf);
+int kshark_import_trace_file(struct kshark_context *kshark_ctx,
+			     struct kshark_config_doc *conf);
+
+struct kshark_config_doc *
+kshark_export_plugin_file(struct kshark_plugin_list *plugin,
+			  enum kshark_config_formats format);
+
+struct kshark_config_doc *
+kshark_export_all_plugins(struct kshark_context *kshark_ctx,
+			  enum kshark_config_formats format);
+
+bool kshark_import_all_plugins(struct kshark_context *kshark_ctx,
+			       struct kshark_config_doc *conf);
+
+struct kshark_config_doc *
+kshark_export_stream_plugins(struct kshark_data_stream *stream,
+			     enum kshark_config_formats format);
+
+bool kshark_import_stream_plugins(struct kshark_context *kshark_ctx,
+				  struct kshark_data_stream *stream,
+				  struct kshark_config_doc *conf);
 
 struct kshark_config_doc *
 kshark_export_model(struct kshark_trace_histo *histo,
 		     enum kshark_config_formats format);
 
-
 bool kshark_import_model(struct kshark_trace_histo *histo,
 			 struct kshark_config_doc *conf);
 
-bool kshark_export_adv_filters(struct kshark_context *kshark_ctx,
+bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, int sd,
 			       struct kshark_config_doc **conf);
 
-bool kshark_import_adv_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, int sd,
 			       struct kshark_config_doc *conf);
 
 bool kshark_export_event_filter(struct kshark_data_stream *stream,
@@ -968,31 +998,46 @@ bool kshark_import_filter_array(struct kshark_hash_id *filter,
 				const char *filter_name,
 				struct kshark_config_doc *conf);
 
-bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx,
+bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, int sd,
 				     struct kshark_config_doc **conf);
 
-bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx,
+bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, int sd,
 				    struct kshark_config_doc **conf);
 
-bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx,
+bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, int sd,
 				   struct kshark_config_doc **conf);
 
 struct kshark_config_doc *
-kshark_export_all_filters(struct kshark_context *kshark_ctx,
+kshark_export_all_filters(struct kshark_context *kshark_ctx, int sd,
 			  enum kshark_config_formats format);
 
-bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, int sd,
 				     struct kshark_config_doc *conf);
 
-bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, int sd,
 				    struct kshark_config_doc *conf);
 
-bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, int sd,
 				   struct kshark_config_doc *conf);
 
-bool kshark_import_all_filters(struct kshark_context *kshark_ctx,
+bool kshark_import_all_filters(struct kshark_context *kshark_ctx, int sd,
 			       struct kshark_config_doc *conf);
 
+struct kshark_config_doc *
+kshark_export_dstream(struct kshark_context *kshark_ctx, int sd,
+		      enum kshark_config_formats format);
+
+int kshark_import_dstream(struct kshark_context *kshark_ctx,
+			  struct kshark_config_doc *conf);
+
+bool kshark_export_all_dstreams(struct kshark_context *kshark_ctx,
+				struct kshark_config_doc **conf);
+
+ssize_t kshark_import_all_dstreams(struct kshark_context *kshark_ctx,
+				   struct kshark_config_doc *conf,
+				   struct kshark_entry ***data_rows);
+
+
 bool kshark_save_config_file(const char *file_name,
 			     struct kshark_config_doc *conf);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams
  2020-11-18 14:49 ` [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams Yordan Karadzhov (VMware)
@ 2020-11-18 22:13   ` Steven Rostedt
  2020-11-19 16:01     ` Yordan Karadzhov (VMware)
  2020-11-18 22:17   ` Steven Rostedt
  1 sibling, 1 reply; 35+ messages in thread
From: Steven Rostedt @ 2020-11-18 22:13 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Wed, 18 Nov 2020 16:49:49 +0200
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> +static void *index_to_ptr(unsigned int index)
> +{
> +	unsigned long p;
> +
> +	p = INVALID_STREAM_MASK | index;
> +
> +	return (void *) p;

I'm curious to why you add a space between the *) and the p?

This is now your project, so I wont complain about formats like this.
You can decide it. But typecasts in Linux are expected to be together
with what they typecast.

Again, no change required, we just need to be consistent with it.
(makes it easier to grep for)

-- Steve


> +}
> +

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams
  2020-11-18 14:49 ` [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams Yordan Karadzhov (VMware)
  2020-11-18 22:13   ` Steven Rostedt
@ 2020-11-18 22:17   ` Steven Rostedt
  2020-11-19 16:04     ` Yordan Karadzhov (VMware)
  1 sibling, 1 reply; 35+ messages in thread
From: Steven Rostedt @ 2020-11-18 22:17 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Wed, 18 Nov 2020 16:49:49 +0200
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

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

This wont work on a 32 bit machine, as pointers are 32 bits not 64.

> +
> +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);

On 32 bit, the above will always fail, because unsigned long is 32
bits (as is void *), but INVALID_STREAM_MASK is 64 bits. And the two
could never equal each other.

-- Steve


> +
> +	return p && v;
> +}
> +

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 08/20] kernel-shark: Add stream interface for trace-cmd data
  2020-11-18 14:49 ` [PATCH v4 08/20] kernel-shark: Add stream interface for trace-cmd data Yordan Karadzhov (VMware)
@ 2020-11-19  1:11   ` Steven Rostedt
  2020-11-19 16:06     ` Yordan Karadzhov (VMware)
  0 siblings, 1 reply; 35+ messages in thread
From: Steven Rostedt @ 2020-11-19  1:11 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Wed, 18 Nov 2020 16:49:51 +0200
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> +static char *tepdata_get_task(struct kshark_data_stream *stream,
> +			      const struct kshark_entry *entry)
> +{
> +	struct kshark_generic_stream_interface *interface = stream->interface;
> +	const char *task;
> +	char *buffer;
> +	int pid;
> +
> +	if (!interface)
> +		return NULL;
> +
> +	pid = interface->get_pid(stream, entry);
> +	task = tep_data_comm_from_pid(kshark_get_tep(stream), pid);


> +	if (asprintf(&buffer, "%s", task)  <= 0)
> +		return NULL;
> +
> +	return buffer;

That's a pretty fancy way to implement:

	return strdup(task ? : "(nil)");

 ;-)

If you wanted to show "(nil)" when task is NULL.

Otherwise, if you want to return NULL if task is NULL, then you could
simplify it further to just:

	return task ? strdup(task) : NULL;


-- Steve


> +}

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 10/20] kernel-shark: Start using data streams
  2020-11-18 14:49 ` [PATCH v4 10/20] kernel-shark: Start using data streams Yordan Karadzhov (VMware)
@ 2020-11-19  1:29   ` Steven Rostedt
  2020-11-19 16:08     ` Yordan Karadzhov (VMware)
  0 siblings, 1 reply; 35+ messages in thread
From: Steven Rostedt @ 2020-11-19  1:29 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Wed, 18 Nov 2020 16:49:53 +0200
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:


> @@ -327,6 +302,34 @@ int kshark_add_stream(struct kshark_context *kshark_ctx)
>  	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;
> +
> +	if (!stream || !kshark_instance(&kshark_ctx))
> +		return -EFAULT;
> +
> +	stream->file = strdup(file);

Should probably check the result of the allocation of file.

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

Why pass in file, if it is going to equal stream->file?

Couldn't kshark_tep_init_input() just use whatever is in the stream->file?

-- Steve


> +	}
> +
> +	return -ENODATA;
> +}
> +
>  /**
>   * @brief Remove Data stream.
>   *

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams
  2020-11-18 14:49 ` [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams Yordan Karadzhov (VMware)
@ 2020-11-19  2:00   ` Steven Rostedt
  2020-11-19 16:14     ` Yordan Karadzhov (VMware)
  0 siblings, 1 reply; 35+ messages in thread
From: Steven Rostedt @ 2020-11-19  2:00 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Wed, 18 Nov 2020 16:49:57 +0200
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> The C API provides loading of the trace data in two different forms.
> The firs one is an array of kshark_entries and is being used by the

  "first one"

> KernelShark GUI. The second is a matrix-like structure that has all
> the fields of the kshark_entry stored in separate arrays, forming the
> columns of the matrix. The second form of the data is used by
> trace-cruncher. In this patch we add methods for merging of several
> data streams into a single data set. Both kshark_entries and matrix
> forms of the data are supported. This patch includes a simple example
> that demonstrate how to open a file that contains multiple buffers.
> Each buffers is loaded into a separate Data stream and those streams
> are merged together.
> 
> Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
> ---
>  examples/CMakeLists.txt    |   4 +
>  examples/multibufferload.c |  60 +++++++++
>  src/libkshark.c            | 255 +++++++++++++++++++++++++++++++++++++
>  src/libkshark.h            |  47 +++++++
>  4 files changed, 366 insertions(+)
>  create mode 100644 examples/multibufferload.c
> 
> diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
> index 8d40e42c..831eee24 100644
> --- a/examples/CMakeLists.txt
> +++ b/examples/CMakeLists.txt
> @@ -8,6 +8,10 @@ message(STATUS "datafilter")
>  add_executable(dfilter          datafilter.c)
>  target_link_libraries(dfilter   kshark)
>  
> +message(STATUS "multibufferload")
> +add_executable(mbload          multibufferload.c)
> +target_link_libraries(mbload   kshark)
> +
>  # message(STATUS "datahisto")
>  # add_executable(dhisto          datahisto.c)
>  # target_link_libraries(dhisto   kshark)
> diff --git a/examples/multibufferload.c b/examples/multibufferload.c
> new file mode 100644
> index 00000000..70b2733a
> --- /dev/null
> +++ b/examples/multibufferload.c
> @@ -0,0 +1,60 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +#include "libkshark.h"
> +#include "libkshark-tepdata.h"
> +
> +const char *default_file = "trace.dat";
> +
> +void put_entry(struct kshark_entry *e)
> +{
> +	char *entry_str = kshark_dump_entry(e);
> +	puts(entry_str);
> +	free(entry_str);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	struct kshark_context *kshark_ctx;
> +	struct kshark_entry **data = NULL;
> +	ssize_t r, n_rows;
> +	int sd;
> +
> +	/* Create a new kshark session. */
> +	kshark_ctx = NULL;
> +	if (!kshark_instance(&kshark_ctx))
> +		return 1;
> +
> +	/* Open a trace data file produced by trace-cmd. */
> +	if (argc > 1)
> +		sd = kshark_open(kshark_ctx, argv[1]);
> +	else
> +		sd = kshark_open(kshark_ctx, default_file);

I'm confused. It doesn't look like this merges more than one stream.

-- Steve

> +
> +	if (sd < 0) {
> +		kshark_free(kshark_ctx);
> +		return 1;
> +	}
> +
> +	/* Initialize data streams for all buffers in this file. */
> +	kshark_tep_init_all_buffers(kshark_ctx, sd);
> +
> +	/* Load all buffers. */
> +	n_rows = kshark_load_all_entries(kshark_ctx, &data);
> +
> +	/* Print to the screen the first 20 entries. */
> +	for (r = 0; r < 20; ++r)
> +		put_entry(data[r]);
> +
> +	/* Free the memory. */
> +	for (r = 0; r < n_rows; ++r)
> +		free(data[r]);
> +	free(data);
> +
> +	kshark_close_all(kshark_ctx);
> +
> +	/* Close the session. */
> +	kshark_free(kshark_ctx);
> +
> +	return 0;
> +}

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams
  2020-11-18 22:13   ` Steven Rostedt
@ 2020-11-19 16:01     ` Yordan Karadzhov (VMware)
  2020-11-19 16:38       ` Steven Rostedt
  0 siblings, 1 reply; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-19 16:01 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel



On 19.11.20 г. 0:13 ч., Steven Rostedt wrote:
> On Wed, 18 Nov 2020 16:49:49 +0200
> "Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:
> 
>> +static void *index_to_ptr(unsigned int index)
>> +{
>> +	unsigned long p;
>> +
>> +	p = INVALID_STREAM_MASK | index;
>> +
>> +	return (void *) p;
> 
> I'm curious to why you add a space between the *) and the p?
> 
> This is now your project, so I wont complain about formats like this.
> You can decide it. But typecasts in Linux are expected to be together
> with what they typecast.
> 
Hi Steven,

I have no strong preference about having or not having this extra space.
For me, having the space makes the C-like casting look more similar to a 
new declaration on contrary to the C++-like casting that will be 
something like this

	unsigned long i(340);
	char c = char(i);

and looks more like a constructor call.
Of course C++ will not let you cast number to pointer this way.

Thanks!
Yordan

> Again, no change required, we just need to be consistent with it.
> (makes it easier to grep for)
> 
> -- Steve
> 
> 
>> +}
>> +

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams
  2020-11-18 22:17   ` Steven Rostedt
@ 2020-11-19 16:04     ` Yordan Karadzhov (VMware)
  0 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-19 16:04 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel



On 19.11.20 г. 0:17 ч., Steven Rostedt wrote:
> On Wed, 18 Nov 2020 16:49:49 +0200
> "Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:
> 
>> +/**
>> + * Bit mask (16-63) used when converting indexes to pointers and vise-versa.
>> + */
>> +#define INVALID_STREAM_MASK	(~((uint64_t) INDEX_MASK))
> 
> This wont work on a 32 bit machine, as pointers are 32 bits not 64.
> 

You are right. I will change this to

#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);
> 
> On 32 bit, the above will always fail, because unsigned long is 32
> bits (as is void *), but INVALID_STREAM_MASK is 64 bits. And the two
> could never equal each other.
> 

Thanks a lot!
Yordan


> -- Steve
> 
> 
>> +
>> +	return p && v;
>> +}
>> +

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 08/20] kernel-shark: Add stream interface for trace-cmd data
  2020-11-19  1:11   ` Steven Rostedt
@ 2020-11-19 16:06     ` Yordan Karadzhov (VMware)
  0 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-19 16:06 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel



On 19.11.20 г. 3:11 ч., Steven Rostedt wrote:
> On Wed, 18 Nov 2020 16:49:51 +0200
> "Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:
> 
>> +static char *tepdata_get_task(struct kshark_data_stream *stream,
>> +			      const struct kshark_entry *entry)
>> +{
>> +	struct kshark_generic_stream_interface *interface = stream->interface;
>> +	const char *task;
>> +	char *buffer;
>> +	int pid;
>> +
>> +	if (!interface)
>> +		return NULL;
>> +
>> +	pid = interface->get_pid(stream, entry);
>> +	task = tep_data_comm_from_pid(kshark_get_tep(stream), pid);
> 
> 
>> +	if (asprintf(&buffer, "%s", task)  <= 0)
>> +		return NULL;
>> +
>> +	return buffer;
> 
> That's a pretty fancy way to implement:
> 
> 	return strdup(task ? : "(nil)");
> 
>   ;-)
> 
> If you wanted to show "(nil)" when task is NULL.
> 
> Otherwise, if you want to return NULL if task is NULL, then you could
> simplify it further to just:
> 
> 	return task ? strdup(task) : NULL;
> 

I am taking this one for v5.

Thanks a lot!
Yordan

> 
> -- Steve
> 
> 
>> +}

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 10/20] kernel-shark: Start using data streams
  2020-11-19  1:29   ` Steven Rostedt
@ 2020-11-19 16:08     ` Yordan Karadzhov (VMware)
  0 siblings, 0 replies; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-19 16:08 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel



On 19.11.20 г. 3:29 ч., Steven Rostedt wrote:
> On Wed, 18 Nov 2020 16:49:53 +0200
> "Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:
> 
> 
>> @@ -327,6 +302,34 @@ int kshark_add_stream(struct kshark_context *kshark_ctx)
>>   	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;
>> +
>> +	if (!stream || !kshark_instance(&kshark_ctx))
>> +		return -EFAULT;
>> +
>> +	stream->file = strdup(file);
> 
> Should probably check the result of the allocation of file.
> 
>> +
>> +	if (kshark_tep_check_data(file)) {
>> +		kshark_set_data_format(stream->data_format,
>> +				       TEP_DATA_FORMAT_IDENTIFIER);
>> +
>> +		return kshark_tep_init_input(stream, file);
> 
> Why pass in file, if it is going to equal stream->file?
> 
> Couldn't kshark_tep_init_input() just use whatever is in the stream->file?
> 

Will change both in v5.
Thanks a lot!
Y.



> -- Steve
> 
> 
>> +	}
>> +
>> +	return -ENODATA;
>> +}
>> +
>>   /**
>>    * @brief Remove Data stream.
>>    *

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams
  2020-11-19  2:00   ` Steven Rostedt
@ 2020-11-19 16:14     ` Yordan Karadzhov (VMware)
  2020-11-19 16:43       ` Steven Rostedt
  0 siblings, 1 reply; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-19 16:14 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel



On 19.11.20 г. 4:00 ч., Steven Rostedt wrote:
> On Wed, 18 Nov 2020 16:49:57 +0200
> "Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:
> 
>> The C API provides loading of the trace data in two different forms.
>> The firs one is an array of kshark_entries and is being used by the
> 
>    "first one"
> 
>> KernelShark GUI. The second is a matrix-like structure that has all
>> the fields of the kshark_entry stored in separate arrays, forming the
>> columns of the matrix. The second form of the data is used by
>> trace-cruncher. In this patch we add methods for merging of several
>> data streams into a single data set. Both kshark_entries and matrix
>> forms of the data are supported. This patch includes a simple example
>> that demonstrate how to open a file that contains multiple buffers.
>> Each buffers is loaded into a separate Data stream and those streams
>> are merged together.
>>
>> Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
>> ---
>>   examples/CMakeLists.txt    |   4 +
>>   examples/multibufferload.c |  60 +++++++++
>>   src/libkshark.c            | 255 +++++++++++++++++++++++++++++++++++++
>>   src/libkshark.h            |  47 +++++++
>>   4 files changed, 366 insertions(+)
>>   create mode 100644 examples/multibufferload.c
>>
>> diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
>> index 8d40e42c..831eee24 100644
>> --- a/examples/CMakeLists.txt
>> +++ b/examples/CMakeLists.txt
>> @@ -8,6 +8,10 @@ message(STATUS "datafilter")
>>   add_executable(dfilter          datafilter.c)
>>   target_link_libraries(dfilter   kshark)
>>   
>> +message(STATUS "multibufferload")
>> +add_executable(mbload          multibufferload.c)
>> +target_link_libraries(mbload   kshark)
>> +
>>   # message(STATUS "datahisto")
>>   # add_executable(dhisto          datahisto.c)
>>   # target_link_libraries(dhisto   kshark)
>> diff --git a/examples/multibufferload.c b/examples/multibufferload.c
>> new file mode 100644
>> index 00000000..70b2733a
>> --- /dev/null
>> +++ b/examples/multibufferload.c
>> @@ -0,0 +1,60 @@
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +
>> +#include "libkshark.h"
>> +#include "libkshark-tepdata.h"
>> +
>> +const char *default_file = "trace.dat";
>> +
>> +void put_entry(struct kshark_entry *e)
>> +{
>> +	char *entry_str = kshark_dump_entry(e);
>> +	puts(entry_str);
>> +	free(entry_str);
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +	struct kshark_context *kshark_ctx;
>> +	struct kshark_entry **data = NULL;
>> +	ssize_t r, n_rows;
>> +	int sd;
>> +
>> +	/* Create a new kshark session. */
>> +	kshark_ctx = NULL;
>> +	if (!kshark_instance(&kshark_ctx))
>> +		return 1;
>> +
>> +	/* Open a trace data file produced by trace-cmd. */
>> +	if (argc > 1)
>> +		sd = kshark_open(kshark_ctx, argv[1]);
>> +	else
>> +		sd = kshark_open(kshark_ctx, default_file);
> 
> I'm confused. It doesn't look like this merges more than one stream.


Hi Steven,

Here it only opens the "top" buffer in the file.

> 
> -- Steve
> 
>> +
>> +	if (sd < 0) {
>> +		kshark_free(kshark_ctx);
>> +		return 1;
>> +	}
>> +
>> +	/* Initialize data streams for all buffers in this file. */
>> +	kshark_tep_init_all_buffers(kshark_ctx, sd);

And here is the place where it opens in separate streams the other 
buffers that are in the same file.


>> +
>> +	/* Load all buffers. */
>> +	n_rows = kshark_load_all_entries(kshark_ctx, &data);
>> +

Here it loads and merges the entries from all buffers.

Thanks!
Yordan

>> +	/* Print to the screen the first 20 entries. */
>> +	for (r = 0; r < 20; ++r)
>> +		put_entry(data[r]);
>> +
>> +	/* Free the memory. */
>> +	for (r = 0; r < n_rows; ++r)
>> +		free(data[r]);
>> +	free(data);
>> +
>> +	kshark_close_all(kshark_ctx);
>> +
>> +	/* Close the session. */
>> +	kshark_free(kshark_ctx);
>> +
>> +	return 0;
>> +}

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams
  2020-11-19 16:01     ` Yordan Karadzhov (VMware)
@ 2020-11-19 16:38       ` Steven Rostedt
  0 siblings, 0 replies; 35+ messages in thread
From: Steven Rostedt @ 2020-11-19 16:38 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Thu, 19 Nov 2020 18:01:30 +0200
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> Hi Steven,
> 
> I have no strong preference about having or not having this extra space.
> For me, having the space makes the C-like casting look more similar to a 
> new declaration on contrary to the C++-like casting that will be 
> something like this
> 
> 	unsigned long i(340);
> 	char c = char(i);
> 
> and looks more like a constructor call.
> Of course C++ will not let you cast number to pointer this way.

As long as you are consistent, but I'm just letting you know the
appropriate style for the Linux kernel, which is no space:

	type x = (type)y;

But again, this is really up to you, but just be aware if you submit to the
kernel, the above is the expected style.

-- Steve

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams
  2020-11-19 16:14     ` Yordan Karadzhov (VMware)
@ 2020-11-19 16:43       ` Steven Rostedt
  2020-11-19 17:48         ` Yordan Karadzhov (VMware)
  0 siblings, 1 reply; 35+ messages in thread
From: Steven Rostedt @ 2020-11-19 16:43 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Thu, 19 Nov 2020 18:14:36 +0200
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> >   
> >> +
> >> +	if (sd < 0) {
> >> +		kshark_free(kshark_ctx);
> >> +		return 1;
> >> +	}
> >> +
> >> +	/* Initialize data streams for all buffers in this file. */
> >> +	kshark_tep_init_all_buffers(kshark_ctx, sd);  
> 
> And here is the place where it opens in separate streams the other 
> buffers that are in the same file.
> 

Ah so it requires that the file have buffers recorded. OK, I'll try it out.

-- Steve



^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams
  2020-11-19 16:43       ` Steven Rostedt
@ 2020-11-19 17:48         ` Yordan Karadzhov (VMware)
  2020-11-19 20:43           ` Steven Rostedt
  0 siblings, 1 reply; 35+ messages in thread
From: Yordan Karadzhov (VMware) @ 2020-11-19 17:48 UTC (permalink / raw)
  To: Steven Rostedt; +Cc: linux-trace-devel



On 19.11.20 г. 18:43 ч., Steven Rostedt wrote:
> On Thu, 19 Nov 2020 18:14:36 +0200
> "Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:
> 
>>>    
>>>> +
>>>> +	if (sd < 0) {
>>>> +		kshark_free(kshark_ctx);
>>>> +		return 1;
>>>> +	}
>>>> +
>>>> +	/* Initialize data streams for all buffers in this file. */
>>>> +	kshark_tep_init_all_buffers(kshark_ctx, sd);
>>
>> And here is the place where it opens in separate streams the other
>> buffers that are in the same file.
>>
> 
> Ah so it requires that the file have buffers recorded. OK, I'll try it out.

Hi Steven,
If you want to open a second file, just replace 
kshark_tep_init_all_buffers() with another call of kshark_open().

I will wait to see if you will find more problems before sending v5.
Thanks a lot!
Y.

> 
> -- Steve
> 
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams
  2020-11-19 17:48         ` Yordan Karadzhov (VMware)
@ 2020-11-19 20:43           ` Steven Rostedt
  0 siblings, 0 replies; 35+ messages in thread
From: Steven Rostedt @ 2020-11-19 20:43 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Thu, 19 Nov 2020 19:48:29 +0200
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> If you want to open a second file, just replace 
> kshark_tep_init_all_buffers() with another call of kshark_open().

This is fine, I just used a trace.dat with multiple buffers.

> 
> I will wait to see if you will find more problems before sending v5.

No need to wait, you are good to go.

-- Steve

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2020-11-19 20:44 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-18 14:49 [PATCH v4 00/20] Start KernelShark v2 transformation Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 01/20] kernel-shark: Use only signed types in kshark_entry Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 02/20] kernel-shark: Add stream_id to kshark_entry Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 03/20] kernel-shark: Introduce libkshark-hash Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 04/20] kernel-shark: Introduce Data streams Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 05/20] kernel-shark: Rename static methods in libkshark Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 06/20] kernel-shark: Add basic methods for Data streams Yordan Karadzhov (VMware)
2020-11-18 22:13   ` Steven Rostedt
2020-11-19 16:01     ` Yordan Karadzhov (VMware)
2020-11-19 16:38       ` Steven Rostedt
2020-11-18 22:17   ` Steven Rostedt
2020-11-19 16:04     ` Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 07/20] kernel-shark: Housekeeping before implementing stream interface Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 08/20] kernel-shark: Add stream interface for trace-cmd data Yordan Karadzhov (VMware)
2020-11-19  1:11   ` Steven Rostedt
2020-11-19 16:06     ` Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 09/20] kernel-shark: Start introducing KernelShark 2.0 Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 10/20] kernel-shark: Start using data streams Yordan Karadzhov (VMware)
2020-11-19  1:29   ` Steven Rostedt
2020-11-19 16:08     ` Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 11/20] kernel-shark: Remove dead code Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 12/20] kernel-shark: Redesign the plugin interface Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 13/20] kernel-shark: Complete the stream integration Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 14/20] kernel-shark: Provide merging of multiple data streams Yordan Karadzhov (VMware)
2020-11-19  2:00   ` Steven Rostedt
2020-11-19 16:14     ` Yordan Karadzhov (VMware)
2020-11-19 16:43       ` Steven Rostedt
2020-11-19 17:48         ` Yordan Karadzhov (VMware)
2020-11-19 20:43           ` Steven Rostedt
2020-11-18 14:49 ` [PATCH v4 15/20] kernel-shark: Integrate the stream definitions with data model Yordan Karadzhov (VMware)
2020-11-18 14:49 ` [PATCH v4 16/20] kernel-shark: Use only signed types for model defs Yordan Karadzhov (VMware)
2020-11-18 14:50 ` [PATCH v4 17/20] kernel-shark: Add ksmodel_get_bin() Yordan Karadzhov (VMware)
2020-11-18 14:50 ` [PATCH v4 18/20] kernel-shark: Protect ksmodel_set_in_range_bining() Yordan Karadzhov (VMware)
2020-11-18 14:50 ` [PATCH v4 19/20] kernel-shark: Add methods for time calibration Yordan Karadzhov (VMware)
2020-11-18 14:50 ` [PATCH v4 20/20] kernel-shark: Integrate streams with libkshark-configio Yordan Karadzhov (VMware)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.