From: "Yordan Karadzhov (VMware)" <y.karadz@gmail.com>
To: rostedt@goodmis.org
Cc: linux-trace-devel@vger.kernel.org,
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com>
Subject: [PATCH 3/7] kernel-shark-qt: Add API for loading trace.dat files
Date: Mon, 25 Jun 2018 18:01:17 +0300 [thread overview]
Message-ID: <20180625150121.14291-4-y.karadz@gmail.com> (raw)
In-Reply-To: <20180625150121.14291-1-y.karadz@gmail.com>
This patch introduces the first component of the C API used
by the new Qt-based version of KernelShark. The patch includes
only the part of the API responsible for loading data files
generated by trace-cmd. The following patch will introduces
an example, demonstrating the usage of this part of the API.
Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
kernel-shark-qt/src/CMakeLists.txt | 9 +
kernel-shark-qt/src/libkshark.c | 438 +++++++++++++++++++++++++++++
kernel-shark-qt/src/libkshark.h | 157 +++++++++++
3 files changed, 604 insertions(+)
create mode 100644 kernel-shark-qt/src/libkshark.c
create mode 100644 kernel-shark-qt/src/libkshark.h
diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index 8c66424..ed3c60e 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -1,4 +1,13 @@
message("\n src ...")
+message(STATUS "libkshark")
+add_library(kshark SHARED libkshark.c)
+
+target_link_libraries(kshark ${CMAKE_DL_LIBS}
+ ${TRACEEVENT_LIBRARY}
+ ${TRACECMD_LIBRARY})
+
+set_target_properties(kshark PROPERTIES SUFFIX ".so.${KS_VERSION_STRING}")
+
configure_file( ${KS_DIR}/build/deff.h.cmake
${KS_DIR}/src/KsDeff.h)
diff --git a/kernel-shark-qt/src/libkshark.c b/kernel-shark-qt/src/libkshark.c
new file mode 100644
index 0000000..f112a50
--- /dev/null
+++ b/kernel-shark-qt/src/libkshark.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+// C
+#define _GNU_SOURCE 1
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+// trace-cmd
+#include "trace-filter-hash.h"
+
+// KernelShark
+#include "libkshark.h"
+
+static __thread struct trace_seq seq;
+
+static struct kshark_context *kshark_context_handler = NULL;
+
+static bool kshark_default_context(struct kshark_context **context)
+{
+ struct kshark_context *kshark_ctx;
+
+ kshark_ctx = calloc(1, sizeof(*kshark_ctx));
+ if (!kshark_ctx)
+ return false;
+
+ /* Free the existing context (if any). */
+ if (*context && *context != kshark_context_handler) {
+ free(*context);
+ *context = NULL;
+ }
+
+ if (kshark_context_handler) {
+ free(kshark_context_handler);
+ kshark_context_handler = NULL;
+ }
+
+ kshark_context_handler = kshark_ctx;
+ *context = kshark_ctx;
+
+ return true;
+}
+
+bool kshark_instance(struct kshark_context **context)
+{
+ if (!seq.buffer)
+ trace_seq_init(&seq);
+
+ if (*context == NULL && kshark_context_handler == NULL) {
+ // No kshark_context exists. Create a default one.
+ bool status = kshark_default_context(context);
+ if (status)
+ return status;
+ } else if (*context != NULL) {
+ // Use the context provided by the user.
+ if (kshark_context_handler)
+ free(kshark_context_handler);
+
+ kshark_context_handler = *context;
+ } else {
+ /*
+ * No context is provided by the user, but the context handler
+ * is already set. Use the context handler.
+ */
+ *context = kshark_context_handler;
+ }
+
+ return true;
+}
+
+static void kshark_free_task_list(struct kshark_context *kshark_ctx)
+{
+ struct kshark_task_list *task;
+
+ while (kshark_ctx->tasks) {
+ task = kshark_ctx->tasks;
+ kshark_ctx->tasks = kshark_ctx->tasks->next;
+ free(task);
+ }
+
+ task = kshark_ctx->tasks = NULL;
+}
+
+bool kshark_open(struct kshark_context *kshark_ctx, const char *file)
+{
+ kshark_free_task_list(kshark_ctx);
+ struct tracecmd_input *handle = tracecmd_open(file);
+ if (!handle)
+ return false;
+
+ pthread_mutex_init(&kshark_ctx->input_mutex, NULL);
+
+ kshark_ctx->handle = handle;
+ kshark_ctx->pevt = tracecmd_get_pevent(handle);
+
+ /*
+ * Turn off function trace indent and turn on show parent
+ * if possible.
+ */
+ trace_util_add_option("ftrace:parent", "1");
+ trace_util_add_option("ftrace:indent", "0");
+
+ return true;
+}
+
+void kshark_close(struct kshark_context *kshark_ctx)
+{
+ if (!kshark_ctx || !kshark_ctx->handle)
+ return;
+
+ tracecmd_close(kshark_ctx->handle);
+ kshark_ctx->handle = NULL;
+ kshark_ctx->pevt = NULL;
+
+ pthread_mutex_destroy(&kshark_ctx->input_mutex);
+}
+
+void kshark_free(struct kshark_context *kshark_ctx)
+{
+ if (kshark_ctx == NULL && kshark_context_handler == NULL)
+ return;
+
+ if (kshark_ctx == NULL) {
+ kshark_ctx = kshark_context_handler;
+ kshark_context_handler = NULL;
+ }
+
+ kshark_free_task_list(kshark_ctx);
+
+ if (seq.buffer)
+ trace_seq_destroy(&seq);
+
+ free(kshark_ctx);
+
+ if (kshark_ctx == kshark_context_handler)
+ kshark_context_handler = NULL;
+
+ kshark_ctx = NULL;
+}
+
+static struct kshark_task_list *
+kshark_find_task(struct kshark_context *kshark_ctx, int pid)
+{
+ struct kshark_task_list *list = kshark_ctx->tasks;
+ while (list) {
+ if (list->pid == pid)
+ return list;
+
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+static struct kshark_task_list *
+kshark_add_task(struct kshark_context *kshark_ctx, int pid)
+{
+ struct kshark_task_list *list = kshark_find_task(kshark_ctx, pid);
+ if (list)
+ return list;
+
+ list = malloc(sizeof(*list));
+ list->pid = pid;
+ list->next = kshark_ctx->tasks;
+ kshark_ctx->tasks = list;
+
+ return list;
+}
+
+static void kshark_set_entry_values(struct kshark_context *kshark_ctx,
+ struct pevent_record *record,
+ struct kshark_entry *entry)
+{
+ // Offset of the record
+ entry->offset = record->offset;
+
+ // CPU Id of the record
+ entry->cpu = record->cpu;
+
+ // Time stamp of the record
+ entry->ts = record->ts;
+
+ // Event Id of the record
+ entry->event_id = pevent_data_type(kshark_ctx->pevt, record);
+
+ /*
+ * Is visible mask. This default value means that the entry
+ * is visible everywhere.
+ */
+ entry->visible = 0xFF;
+
+ // Process Id of the record
+ entry->pid = pevent_data_pid(kshark_ctx->pevt, record);
+}
+
+size_t kshark_load_data_entries(struct kshark_context *kshark_ctx,
+ struct kshark_entry ***data_rows)
+{
+ int n_cpus = tracecmd_cpus(kshark_ctx->handle);
+ int cpu;
+ size_t count, total = 0;
+ struct pevent_record *rec;
+
+ if (*data_rows)
+ free(*data_rows);
+
+ struct kshark_entry *entry, **next;
+ struct kshark_entry **cpu_list = calloc(n_cpus, sizeof(struct kshark_entry *));
+
+ for (cpu = 0; cpu < n_cpus; ++cpu) {
+ count = 0;
+ cpu_list[cpu] = NULL;
+ next = &cpu_list[cpu];
+
+ rec = tracecmd_read_cpu_first(kshark_ctx->handle, cpu);
+ while (rec) {
+ *next = entry = malloc( sizeof(struct kshark_entry) );
+ assert(entry != NULL);
+
+ kshark_set_entry_values(kshark_ctx, rec, entry);
+ kshark_add_task(kshark_ctx, entry->pid);
+
+ entry->next = NULL;
+ next = &(entry->next);
+ free_record(rec);
+
+ ++count;
+ rec = tracecmd_read_data(kshark_ctx->handle, cpu);
+ }
+
+ total += count;
+ }
+
+ struct kshark_entry **rows;
+ rows = calloc(total, sizeof(struct kshark_entry *));
+ if(!rows) {
+ fprintf(stderr, "Failed to allocate memory during data loading.\n");
+ return 0;
+ }
+
+ count = 0;
+ int next_cpu;
+ uint64_t ts;
+ while (count < total) {
+ ts = 0;
+ next_cpu = -1;
+ for (cpu = 0; cpu < n_cpus; ++cpu) {
+ if (!cpu_list[cpu])
+ continue;
+
+ if (!ts || cpu_list[cpu]->ts < ts) {
+ ts = cpu_list[cpu]->ts;
+ next_cpu = cpu;
+ }
+ }
+
+ if (next_cpu >= 0) {
+ rows[count] = cpu_list[next_cpu];
+ cpu_list[next_cpu] = cpu_list[next_cpu]->next;
+ }
+ ++count;
+ }
+
+ free(cpu_list);
+ *data_rows = rows;
+ return total;
+}
+
+size_t kshark_load_data_records(struct kshark_context *kshark_ctx,
+ struct pevent_record ***data_rows)
+{
+ int n_cpus = tracecmd_cpus(kshark_ctx->handle);
+ int cpu;
+ size_t count, total = 0;
+ struct pevent_record *data;
+
+ struct temp {
+ struct pevent_record *rec;
+ struct temp *next;
+ } **cpu_list, **temp_next, *temp_rec;
+
+ cpu_list = calloc(n_cpus, sizeof(struct temp *));
+
+ for (cpu = 0; cpu < n_cpus; ++cpu) {
+ count = 0;
+ cpu_list[cpu] = NULL;
+ temp_next = &cpu_list[cpu];
+
+ data = tracecmd_read_cpu_first(kshark_ctx->handle, cpu);
+ while (data) {
+ *temp_next = temp_rec = malloc(sizeof(*temp_rec));
+ assert(temp_rec != NULL);
+
+ kshark_add_task(kshark_ctx,
+ pevent_data_pid(kshark_ctx->pevt, data));
+ temp_rec->rec = data;
+ temp_rec->next = NULL;
+ temp_next = &(temp_rec->next);
+
+ ++count;
+ data = tracecmd_read_data(kshark_ctx->handle, cpu);
+ }
+
+ total += count;
+ }
+
+ struct pevent_record **rows;
+ rows = calloc(total, sizeof(struct pevent_record *));
+ if(!rows) {
+ fprintf(stderr, "Failed to allocate memory during data loading.\n");
+ return 0;
+ }
+
+ count = 0;
+ int next_cpu;
+ uint64_t ts;
+ while (count < total) {
+ ts = 0;
+ next_cpu = -1;
+ for (cpu = 0; cpu < n_cpus; ++cpu) {
+ if (!cpu_list[cpu])
+ continue;
+
+ if (!ts || cpu_list[cpu]->rec->ts < ts) {
+ ts = cpu_list[cpu]->rec->ts;
+ next_cpu = cpu;
+ }
+ }
+
+ if (next_cpu >= 0) {
+ rows[count] = cpu_list[next_cpu]->rec;
+ temp_rec = cpu_list[next_cpu];
+ cpu_list[next_cpu] = cpu_list[next_cpu]->next;
+ free (temp_rec);
+ }
+
+ ++count;
+ }
+
+ free(cpu_list);
+ *data_rows = rows;
+ return total;
+}
+
+static struct pevent_record *kshark_read_at(struct kshark_context *kshark_ctx,
+ uint64_t offset)
+{
+ pthread_mutex_lock(&kshark_ctx->input_mutex);
+
+ struct pevent_record *data = tracecmd_read_at(kshark_ctx->handle,
+ offset, NULL);
+
+ pthread_mutex_unlock(&kshark_ctx->input_mutex);
+
+ return data;
+}
+
+static const char *kshark_get_latency(struct pevent *pe,
+ struct pevent_record *record)
+{
+ if (!record)
+ return NULL;
+
+ trace_seq_reset(&seq);
+ pevent_data_lat_fmt(pe, &seq, record);
+ return seq.buffer;
+}
+
+static const char *kshark_get_info(struct pevent *pe,
+ struct pevent_record *record,
+ struct event_format *event)
+{
+ if (!record || !event)
+ return NULL;
+
+ trace_seq_reset(&seq);
+ pevent_event_info(&seq, event, record);
+
+ // Remove the trailing newline from the Info string.
+ char *pos;
+ if ((pos = strchr(seq.buffer, '\n')) != NULL)
+ *pos = '\0';
+
+ return seq.buffer;
+}
+
+char* kshark_dump_entry(struct kshark_entry *entry)
+{
+ struct kshark_context *kshark_ctx = NULL;
+ kshark_instance(&kshark_ctx);
+
+ if (!seq.buffer)
+ trace_seq_init(&seq);
+
+ trace_seq_reset(&seq);
+
+ char *tmp_str, *entry_str;
+ int size_tmp, size = 0;
+
+ struct pevent_record *data = kshark_read_at(kshark_ctx, entry->offset);
+
+ int event_id = pevent_data_type(kshark_ctx->pevt, data);
+ struct event_format *event =
+ pevent_data_event_from_type(kshark_ctx->pevt, event_id);
+
+ const char *event_name = (event)? event->name : "[UNKNOWN EVENT]";
+ const char *task = pevent_data_comm_from_pid(kshark_ctx->pevt, entry->pid);
+ const char *lat = kshark_get_latency(kshark_ctx->pevt, data);
+
+ size_tmp = asprintf(&tmp_str, "%li %s-%i; CPU %i; %s;",
+ entry->ts,
+ task,
+ entry->pid,
+ entry->cpu,
+ lat);
+
+ const char *info = kshark_get_info(kshark_ctx->pevt, data, event);
+ if (size_tmp) {
+ size = asprintf(&entry_str, "%s %s; %s; 0x%x",
+ tmp_str,
+ event_name,
+ info,
+ entry->visible);
+
+ free(tmp_str);
+ }
+
+ free_record(data);
+
+ if (size > 0)
+ return entry_str;
+
+ return NULL;
+}
diff --git a/kernel-shark-qt/src/libkshark.h b/kernel-shark-qt/src/libkshark.h
new file mode 100644
index 0000000..d6e41bb
--- /dev/null
+++ b/kernel-shark-qt/src/libkshark.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+ /**
+ * @file libkshark.h
+ * @brief API for processing of FTRACE (trace-cmd) data.
+ */
+
+#ifndef _LIB_KSHARK_H
+#define _LIB_KSHARK_H
+
+// C
+#include <stdint.h>
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// trace-cmd
+#include "trace-cmd.h"
+#include "event-parse.h"
+
+/**
+ * Kernel Shark entry contains all information from one trace record, needed in order to
+ * visualize the time-series of trace records. The part of the data which is not directly
+ * required for the visualization (latency, record info etc.) is available on-demand via
+ * the offset into the treace file.
+ */
+struct kshark_entry {
+ /**
+ * A bit mask controlling the visibility of the entry. A value of OxFF would mean
+ * that the entry is visible everywhere.
+ */
+ uint8_t visible;
+
+ /** The CPU core of the record. */
+ uint8_t cpu;
+
+ /** The PID of the task the record was generated. */
+ int16_t pid;
+
+ /** Unique Id ot the trace event type. */
+ int event_id;
+
+ /** The offset into the treace file, used to find the record. */
+ uint64_t offset;
+
+ /**
+ * The time of the record in nano seconds. The value is taken from the timestamps
+ * within the trace data file, which are architecture dependent. The time usually
+ * is the timestamp from when the system started.
+ */
+ uint64_t ts;
+
+ /** Pointer to the next (in time) kshark_entry on the same CPU core. */
+ struct kshark_entry *next;
+};
+
+/** Linked list of tasks. */
+struct kshark_task_list {
+ /** Pointer to the next task's PID. */
+ struct kshark_task_list *next;
+
+ /** PID of a task. */
+ int pid;
+};
+
+/** Structure representing a kshark session. */
+struct kshark_context {
+ /** Input handle for the trace data file. */
+ struct tracecmd_input *handle;
+
+ /** Page event used to parse the page. */
+ struct pevent *pevt;
+
+ /** List of task's PIDs. */
+ struct kshark_task_list *tasks;
+
+ /** A mutex, used to protect the access to the input file. */
+ pthread_mutex_t input_mutex;
+};
+
+/**
+ * @brief Initialize a kshark session. This function must be called before calling any
+ * other kshark function. If the session has been initialized, this function can be used
+ * to obtain the session's context.
+ * @param kshark_ctx: Optional input/output location for context pointer. Only valid on
+ * return true.
+ * @returns true on success, or false on failure.
+
+ */
+bool kshark_instance(struct kshark_context **kshark_ctx);
+
+/**
+ * @brief Open and prepare for reading a trace data file specified by "file". If the
+ * specified file does not exist, or contains no trace data, the function returns false.
+ * @param kshark_ctx: Input location for context pointer.
+ * @param file: The file to load.
+ * @returns true on success, or false on failure.
+ */
+bool kshark_open(struct kshark_context *kshark_ctx, const char *file);
+
+/**
+ * @brief Load the content of the trace data file into an array of kshark_entries. This
+ * function provides fast loading, however the "latency" and the "info" fields can be
+ * accessed only via the offset into the file. This makes the access to these two fields
+ * much slower.
+ * @param kshark_ctx: Input location for context pointer.
+ * @param data_rows: Output location for the trace data. The user is responsible for
+ * freeing the elements of the outputted array.
+ * @returns The size of the outputted data.
+ */
+size_t kshark_load_data_entries(struct kshark_context *kshark_ctx,
+ struct kshark_entry ***data_rows);
+
+/**
+ * @brief Load the content of the trace data file into an array of pevent_records. Use
+ * this function only if you need fast access to all fields of the record.
+ * @param kshark_ctx: Input location for the session context pointer.
+ * @param data_rows: Output location for the trace data. Use free_record() to free
+ * the elements of the outputted array.
+ * @returns The size of the outputted data.
+ */
+size_t kshark_load_data_records(struct kshark_context *kshark_ctx,
+ struct pevent_record ***data_rows);
+
+/**
+ * @brief Close the trace data file and free the trace data handle.
+ * @param kshark_ctx: Input location for the session context pointer.
+ */
+void kshark_close(struct kshark_context *kshark_ctx);
+
+/**
+ * @brief Deinitialize kshark session. Should be called after closing all open trace data
+ * files and before your application terminates.
+ * @param kshark_ctx: Optional input location for session context pointer.
+ */
+void kshark_free(struct kshark_context *kshark_ctx);
+
+/**
+ * @brief Dump into a sting the content of one entry. The function allocates a null
+ * terminated string and return a pointer to this string. The user has to free the
+ * returned string.
+ * @param entry: A Kernel Shark entry to be printed.
+ * @returns The returned string contains a semicolon-separated list of data fields.
+ */
+char* kshark_dump_entry(struct kshark_entry *entry);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIB_KSHARK_H
--
2.17.1
next prev parent reply other threads:[~2018-06-25 15:02 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-06-25 15:01 [PATCH 0/7] Introduce the very basic part of the C API of KS-1.0 Yordan Karadzhov (VMware)
2018-06-25 15:01 ` [PATCH 1/7] kernel-shark-qt: Add Cmake build system for the Qt based KernelShark Yordan Karadzhov (VMware)
2018-06-25 16:06 ` Steven Rostedt
2018-06-26 14:23 ` Yordan Karadzhov (VMware)
2018-06-25 15:01 ` Yordan Karadzhov (VMware) [this message]
2018-06-25 18:30 ` [PATCH 3/7] kernel-shark-qt: Add API for loading trace.dat files Steven Rostedt
2018-06-26 14:47 ` Yordan Karadzhov (VMware)
2018-06-26 15:16 ` Steven Rostedt
2018-06-26 15:26 ` Yordan Karadzhov (VMware)
2018-06-25 15:01 ` [PATCH 4/7] kernel-shark-qt: Add an example showing how to load trace data Yordan Karadzhov (VMware)
2018-06-25 18:34 ` Steven Rostedt
2018-06-25 15:01 ` [PATCH 5/7] kernel-shark-qt: Add a README file to trace-cmd/kernel-shark-qt Yordan Karadzhov (VMware)
2018-06-25 18:38 ` Steven Rostedt
2018-06-26 14:51 ` Yordan Karadzhov (VMware)
2018-06-26 15:18 ` Steven Rostedt
2018-06-25 15:01 ` [PATCH 6/7] kernel-shark-qt: Add filtering to the C API of KernelShark Yordan Karadzhov (VMware)
2018-06-25 19:07 ` Steven Rostedt
2018-06-25 15:01 ` [PATCH 7/7] kernel-shark-qt: Add an example showing how to filter trace data Yordan Karadzhov (VMware)
[not found] ` <20180625150121.14291-3-y.karadz@gmail.com>
2018-06-25 16:09 ` [PATCH 2/7] kernel-shark-qt: Automatic generation of doxygen documentation Steven Rostedt
2018-06-26 14:29 ` Yordan Karadzhov (VMware)
2018-06-26 15:00 ` Steven Rostedt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180625150121.14291-4-y.karadz@gmail.com \
--to=y.karadz@gmail.com \
--cc=linux-trace-devel@vger.kernel.org \
--cc=rostedt@goodmis.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).