From patchwork Thu Jul 5 15:37:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 10758649 Return-Path: Received: from mail-wr1-f66.google.com ([209.85.221.66]:36209 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753345AbeGEPia (ORCPT ); Thu, 5 Jul 2018 11:38:30 -0400 Received: by mail-wr1-f66.google.com with SMTP id h9-v6so1420389wro.3 for ; Thu, 05 Jul 2018 08:38:29 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [RFC 1/3] kernel-shark-qt: Add generic instruments for searching inside the trace data Date: Thu, 5 Jul 2018 18:37:42 +0300 Message-Id: <20180705153744.28356-1-y.karadz@gmail.com> Sender: linux-trace-devel-owner@vger.kernel.org List-ID: Content-Length: 12772 This patch introduces the instrumentation for data extraction, used by the visualization model of the Qt-based KernelShark. The effectiveness of these instruments for searching has a dominant effect over the performance of the model, so let's spend some time and explain this in details. The first type of instruments provides binary search inside a sorted in time arrays of kshark_entries or trace_records. The search returns the first element of the array, having timestamp bigger than a reference time value. The time complexity of these searches is log(n). The second type of instruments provides searching for the first (in time) entry, satisfying an abstract Matching condition. Since the array is sorted in time, but we search for an abstract property, for this search the array is considered unsorted, thus we have to iterate and check all elements of the array one by one. If we search for a type of entries, which are well presented in the array, the time complexity of the search is constant, because no matter how big is the array the search only goes through small number of entries at the beginning of the array (or at the end, if we search backwards), before it finds the first match. However if we search for sparse, or even nonexistent entries, the time complexity becomes linear. These explanations will start making more sense with the following patches. Signed-off-by: Yordan Karadzhov (VMware) --- kernel-shark-qt/src/libkshark.c | 261 +++++++++++++++++++++++++++++++- kernel-shark-qt/src/libkshark.h | 76 +++++++++- 2 files changed, 334 insertions(+), 3 deletions(-) diff --git a/kernel-shark-qt/src/libkshark.c b/kernel-shark-qt/src/libkshark.c index 13d7e6d..ebd096a 100644 --- a/kernel-shark-qt/src/libkshark.c +++ b/kernel-shark-qt/src/libkshark.c @@ -761,7 +761,7 @@ static const char *kshark_get_info(struct pevent *pe, * @returns The returned string contains a semicolon-separated list of data * fields. */ -char* kshark_dump_entry(struct kshark_entry *entry) +char* kshark_dump_entry(const struct kshark_entry *entry) { const char *event_name, *task, *lat, *info; struct kshark_context *kshark_ctx; @@ -808,3 +808,262 @@ char* kshark_dump_entry(struct kshark_entry *entry) return NULL; } + +/** + * @brief Binary search inside a time-sorted array of kshark_entries. + * @param time: The value of time to search for. + * @param data_rows: Input location for the trace data. + * @param l: Array index specifying the lower edge of the range to search in. + * @param h: Array index specifying the upper edge of the range to search in. + * @returns On success, the first kshark_entry inside the range, having a + timestamp equal or bigger than "time". In the case when no + kshark_entry has been found inside the range, the function will + return the value of "l" or "h". + */ +size_t kshark_find_entry_by_time(uint64_t time, + struct kshark_entry **data_rows, + size_t l, size_t h) +{ + size_t mid; + + if (data_rows[l]->ts >= time) + return l; + + if (data_rows[h]->ts < time) + return h; + + while (h - l > 1) { + mid = (l + h) / 2; + if (data_rows[mid]->ts < time) + l = mid; + else + h = mid; + } + + return h; +} + +/** + * @brief Binary search inside a time-sorted array of pevent_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 pevent_record inside the range, having a + timestamp equal or bigger than "time". In the case when no + pevent_record has been found inside the range, the function will + return the value of "l" or "h". + */ +size_t kshark_find_record_by_time(uint64_t time, + struct pevent_record **data, + size_t l, size_t h) +{ + size_t mid; + + if (data[l]->ts >= time) + return l; + + if (data[h]->ts < time) + return h; + + while (h - l > 1) { + mid = (l + h) / 2; + if (data[mid]->ts < time) + l = mid; + else + h = mid; + } + + return h; +} + +/** + * @brief Simple Pid matching finction to be user for data requests. + * @param kshark_ctx: Input location for the session context pointer. + * @param e: kshark_entry to be checked. + * @param pid: Matching condition value. + * @returns True if the Pid of the entry matches the value of "pid". + * Else false. + */ +bool kshark_check_pid(struct kshark_context *kshark_ctx, + struct kshark_entry *e, int pid) +{ + if (e->pid == pid) + return true; + + return false; +} + +/** + * @brief Simple Cpu matching finction to be user for data requests. + * @param kshark_ctx: Input location for the session context pointer. + * @param e: kshark_entry to be checked. + * @param cpu: Matching condition value. + * @returns True if the Cpu of the entry matches the value of "cpu". + * Else false. + */ +bool kshark_check_cpu(struct kshark_context *kshark_ctx, + struct kshark_entry *e, int cpu) +{ + if (e->cpu == cpu) + return true; + + return false; +} + +/** + * @brief Create Data request. The user has to free the returned + * kshark_entry_request. + * @param first: Array index specifying the position inside the array from + * where the search starts. + * @param n: Number of array elements to search in. + * @param cond: Matching condition finction. + * @param val: Matching condition value, used by the Matching condition + * finction. + * @param vis_only: If true, a visible entry is requested. + * @param vis_mask: If "vis_only" is true, use this mask to specify the level + * of visibility of the requested entry + * @returns Pointer to kshark_entry_request on success, or NULL on failure. + */ +struct kshark_entry_request * +kshark_entry_request_alloc(size_t first, size_t n, + matching_condition_func cond, int val, + bool vis_only, int vis_mask) +{ + struct kshark_entry_request *req = malloc(sizeof(*req)); + + if (!req) { + fprintf(stderr, + "Failed to allocate memory for entry request.\n"); + return NULL; + } + + req->first = first; + req->n = n; + req->cond = cond; + req->val = val; + req->vis_only = vis_only; + req->vis_mask = vis_mask; + + return req; +} + +/** Dummy entry, used to indicate the existence of filtered entries. */ +const struct kshark_entry dummy_entry = {/* visible */ 0x00, + /* cpu */ KS_FILTERED_BIN, + /* pid */ KS_FILTERED_BIN, + /* event_id */ -1, + /* offset */ 0, + /* ts */ 0, + /* next */ NULL}; + +/** + * @brief Search for an entry satisfying the requirements of a given Data + * request. Start from the position provided by the request and go + * searching in the direction of the increasing timestamps (front). + * @param req: Input location for Data request. + * @param data: Input location for the trace data. + * @param index: Optional output location for the index of the returned + * entry inside the array. + * @returns Pointer to the first entry satisfying the matching condition. + * In the special case when some entries, satisfying the Matching + * condition finction have been found, but all these entries have + * been discarded because of the visibility criteria (filtered + * entries), the function returns a pointer to a special + * "Dummy entry". + */ +const struct kshark_entry * +kshark_get_entry_front(const struct kshark_entry_request *req, + struct kshark_entry **data, + ssize_t *index) +{ + struct kshark_context *kshark_ctx = NULL; + const struct kshark_entry *e = NULL; + size_t i, last; + + if (!kshark_instance(&kshark_ctx)) + return e; + + last = req->first + req->n; + for (i = req->first; i < last; ++i) { + if (req->cond(kshark_ctx, data[i], req->val)) { + /* + * Data satisfying the condition has been found. + */ + if (req->vis_only && + !(data[i]->visible & req->vis_mask)) { + /* This data entry has been filtered. */ + e = &dummy_entry; + } else { + e = data[i]; + break; + } + } + } + + if (index) { + if (e) + *index = (e->event_id >= 0)? i : KS_FILTERED_BIN; + else + *index = KS_EMPTY_BIN; + } + + return e; +} + +/** + * @brief Search for an entry satisfying the requirements of a given Data + * request. Start from the position provided by the request and go + * searching in the direction of the decreasing timestamps (back). + * @param req: Input location for Data request. + * @param data: Input location for the trace data. + * @param index: Optional output location for the index of the returned + * entry inside the array. + * @returns Pointer to the first entry satisfying the matching condition. + * In the special case when some entries, satisfying the Matching + * condition finction , have been found, but all these entries have + * been discarded because of the visibility criteria (filtered + * entries), the function returns a pointer to a special + * "Dummy entry". + */ +const struct kshark_entry * +kshark_get_entry_back(const struct kshark_entry_request *req, + struct kshark_entry **data, + ssize_t *index) +{ + struct kshark_context *kshark_ctx = NULL; + const struct kshark_entry *e = NULL; + ssize_t i, last; + + if (!kshark_instance(&kshark_ctx)) + return e; + + last = req->first - req->n + 1; + if (last < 0) + last = 0; + + for (i = req->first; i >= last; --i) { + if (req->cond(kshark_ctx, data[i], req->val)) { + /* + * Data satisfying the condition has been found. + */ + if (req->vis_only && + !(data[i]->visible & req->vis_mask)) { + /* This data entry has been filtered. */ + e = &dummy_entry; + } else { + e = data[i]; + break; + } + } + } + + if (index) { + if (e) + *index = (e->event_id >= 0)? i : KS_FILTERED_BIN; + else + *index = KS_EMPTY_BIN; + } + + return e; +} diff --git a/kernel-shark-qt/src/libkshark.h b/kernel-shark-qt/src/libkshark.h index 6ed2a1e..3b62231 100644 --- a/kernel-shark-qt/src/libkshark.h +++ b/kernel-shark-qt/src/libkshark.h @@ -42,7 +42,7 @@ struct kshark_entry { uint8_t visible; /** The CPU core of the record. */ - uint8_t cpu; + int8_t cpu; /** The PID of the task the record was generated. */ int16_t pid; @@ -133,7 +133,7 @@ void kshark_close(struct kshark_context *kshark_ctx); void kshark_free(struct kshark_context *kshark_ctx); -char* kshark_dump_entry(struct kshark_entry *entry); +char* kshark_dump_entry(const struct kshark_entry *entry); /** Bit masks used to control the visibility of the entry after filtering. */ enum kshark_filter_masks { @@ -190,6 +190,78 @@ void kshark_filter_entries(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_entries); +size_t kshark_find_entry_by_time(uint64_t time, + struct kshark_entry **data_rows, + size_t l, size_t h); + +size_t kshark_find_record_by_time(uint64_t time, + struct pevent_record **data_rows, + size_t l, size_t h); + +bool kshark_check_pid(struct kshark_context *kshark_ctx, + struct kshark_entry *e, int pid); + +bool kshark_check_cpu(struct kshark_context *kshark_ctx, + struct kshark_entry *e, int cpu); + +/** Empty bin identifier. */ +#define KS_EMPTY_BIN -1 + +/** Filtered bin identifier. */ +#define KS_FILTERED_BIN -2 + +/** Matching condition finction type. To be user for data requests */ +typedef bool (matching_condition_func)(struct kshark_context*, + struct kshark_entry*, + int); + +/** + * Data request structure, defining the properties of the required + * kshark_entry. + */ +struct kshark_entry_request { + /** + * Array index specifying the position inside the array from where + * the search starts. + */ + size_t first; + + /** Number of array elements to search in. */ + size_t n; + + /** Matching condition finction. */ + matching_condition_func *cond; + + /** + * Matching condition value, used by the Matching condition finction. + */ + int val; + + /** If true, a visible entry is requested. */ + bool vis_only; + + /** + * If "vis_only" is true, use this mask to specify the level of + * visibility of the requested entry. + */ + uint8_t vis_mask; +}; + +struct kshark_entry_request * +kshark_entry_request_alloc(size_t first, size_t n, + matching_condition_func cond, int val, + bool vis_only, int vis_mask); + +const struct kshark_entry * +kshark_get_entry_front(const struct kshark_entry_request *req, + struct kshark_entry **data, + ssize_t *index); + +const struct kshark_entry * +kshark_get_entry_back(const struct kshark_entry_request *req, + struct kshark_entry **data, + ssize_t *index); + #ifdef __cplusplus } #endif From patchwork Thu Jul 5 15:37:43 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 10758651 Return-Path: Received: from mail-wm0-f66.google.com ([74.125.82.66]:37238 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753607AbeGEPik (ORCPT ); Thu, 5 Jul 2018 11:38:40 -0400 Received: by mail-wm0-f66.google.com with SMTP id n17-v6so11718905wmh.2 for ; Thu, 05 Jul 2018 08:38:39 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [RFC 2/3] kernel-shark-qt: Introduce the visualization model used by the Qt-based KS Date: Thu, 5 Jul 2018 18:37:43 +0300 Message-Id: <20180705153744.28356-2-y.karadz@gmail.com> In-Reply-To: <20180705153744.28356-1-y.karadz@gmail.com> References: <20180705153744.28356-1-y.karadz@gmail.com> Sender: linux-trace-devel-owner@vger.kernel.org List-ID: Content-Length: 38401 The model, used by the Qt-based KernelShark for visualization of trace data is build over the concept of "Data Bins". When visualizing a large data-set of trace records, we are limited by the number of screen pixels available for drawing. The model divides the data-set into data-units, also called Bins. The Bin has to be defined in such a way that the entire content of one Bin can be summarized and visualized by a single graphical element. This model uses the timestamp of the trace records, as a criteria for forming Bins. When the Model has to visualize all records inside a given time-window, it divides this time-window into N smaller, uniformly sized subintervals and then defines that one Bin contains all trace records, having timestamps falling into one of this subintervals. Because the model operates over an array of trace records, sorted in time, the content of each Bin can be retrieved by simply knowing the index of the first element inside this Bin and the index of the first element of the next Bin. This means that knowing the index of the first element in each Bin is enough to determine the State of the model. The State of the model can be modified by its five basic operations: Zoon-In, Zoom-Out, Shift-Forward, Shift-Backward and Jump-To. After each of these operations, the new State of the model is retrieved, by using binary search to find the index of the first element in each Bin. This means that each one of the five basic operations of the model has log(n) time complexity (see previous change log). In order to keep the visualization of the new state of the model as efficient as possible, the model needs a way to summarize and visualize the content of the Bins in constant time. This is achieved by limiting ourself to only checking the content of the records at the beginning and at the end of the Bin. As explaned in the previous change log, this approach has the very counter-intuitive effect of making the update of the sparse (or empty) Graphs much slower. The problem of the Sparse Graphs will be addressed in another patch, where "Data Collections" will be introduced. The following patch will introduces a simple example, demonstrating the usage of the API of the Model. Signed-off-by: Yordan Karadzhov (VMware) --- kernel-shark-qt/src/CMakeLists.txt | 3 +- kernel-shark-qt/src/libkshark-model.c | 1131 +++++++++++++++++++++++++ kernel-shark-qt/src/libkshark-model.h | 138 +++ 3 files changed, 1271 insertions(+), 1 deletion(-) create mode 100644 kernel-shark-qt/src/libkshark-model.c create mode 100644 kernel-shark-qt/src/libkshark-model.h diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt index ed3c60e..ec22f63 100644 --- a/kernel-shark-qt/src/CMakeLists.txt +++ b/kernel-shark-qt/src/CMakeLists.txt @@ -1,7 +1,8 @@ message("\n src ...") message(STATUS "libkshark") -add_library(kshark SHARED libkshark.c) +add_library(kshark SHARED libkshark.c + libkshark-model.c) target_link_libraries(kshark ${CMAKE_DL_LIBS} ${TRACEEVENT_LIBRARY} diff --git a/kernel-shark-qt/src/libkshark-model.c b/kernel-shark-qt/src/libkshark-model.c new file mode 100644 index 0000000..5f3583f --- /dev/null +++ b/kernel-shark-qt/src/libkshark-model.c @@ -0,0 +1,1131 @@ +// SPDX-License-Identifier: LGPL-2.1 + +/* + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov + */ + + /** + * @file libkshark.c + * @brief Visualization model for FTRACE (trace-cmd) data. + */ + +// C +#include + +// KernelShark +#include "libkshark-model.h" + +/** + * @brief Initialize the Visualization model. + * @param histo: Input location for the model descriptor. + */ +void ksmodel_init(struct kshark_trace_histo *histo) +{ + /* + * Initialize an empty histo. The histo will have no bins and will + * contain no data. + */ + histo->bin_size = 0; + histo->min = 0; + histo->max = 0; + histo->n_bins = 0; + + histo->bin_count = NULL; + histo->map = NULL; +} + +/** + * @brief Clear (reset) the Visualization model. + * @param histo: Input location for the model descriptor. + */ +void ksmodel_clear(struct kshark_trace_histo *histo) +{ + /* Reset the histo. It will have no bins and will contain no data. */ + histo->bin_size = 0; + histo->min = 0; + histo->max = 0; + histo->n_bins = 0; + + free(histo->map); + histo->map = NULL; + + free(histo->bin_count); + histo->bin_count = NULL; +} + +static void ksmodel_reset_bins(struct kshark_trace_histo *histo, + size_t first, size_t last) +{ + size_t i; + + /* Reset the content of the bins. */ + for (i = first; i <= last; ++i) { + histo->map[i] = KS_EMPTY_BIN; + histo->bin_count[i] = 0; + } +} + +static void ksmodel_histo_alloc(struct kshark_trace_histo *histo, size_t n) +{ + free(histo->bin_count); + free(histo->map); + + /* Create bins. Two overflow bins are added. */ + histo->map = calloc(n + 2, sizeof(*histo->map)); + histo->bin_count = calloc(n + 2, sizeof(*histo->bin_count)); + + if (!histo->map || !histo->bin_count) { + ksmodel_clear(histo); + fprintf(stderr, "Failed to allocate memory for a histo.\n"); + return; + } + + histo->n_bins = n; +} + +static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo, + size_t n, uint64_t min, uint64_t max, + bool force_in_range) +{ + uint64_t corrected_range, delta_range, range = max - min; + struct kshark_entry *last; + + /* The size of the bin must be >= 1, hence the range must be >= n. */ + if (n == 0 || range < n) + return; + + /* + * If the number of bins changes, allocate memory for the descriptor + * of the model. + */ + if (n != histo->n_bins) { + ksmodel_histo_alloc(histo, n); + } + + /* Reset the content of all bins (including overflow bins) to zero. */ + ksmodel_reset_bins(histo, 0, histo->n_bins + 1); + + if (range % n == 0) { + /* + * The range is multiple of the number of bin and needs no + * adjustment. This is very unlikely to happen but still ... + */ + histo->min = min; + histo->max = max; + histo->bin_size = range / n; + } else { + /* + * The range needs adjustment. The new range will be slightly + * bigger, compared to the requested one. + */ + histo->bin_size = range / n + 1; + corrected_range = histo->bin_size * n; + delta_range = corrected_range - range; + histo->min = min - delta_range / 2; + histo->max = histo->min + corrected_range; + + if (!force_in_range) + return; + + /* + * Make sure that the new range doesn't go outside of the time + * interval of the dataset. + */ + last = histo->data[histo->data_size - 1]; + if (histo->min < histo->data[0]->ts) { + histo->min = histo->data[0]->ts; + histo->max = histo->min + corrected_range; + } else if (histo->max > last->ts) { + histo->max = last->ts; + histo->min = histo->max - corrected_range; + } + } +} + +/** + * @brief Prepare the bining of the Visualization model. + * @param histo: Input location for the model descriptor. + * @param n: Number of bins. + * @param min: Lower edge of the time-window to be visualized. + * @param max: Upper edge of the time-window to be visualized. + */ +void ksmodel_set_bining(struct kshark_trace_histo *histo, + size_t n, uint64_t min, uint64_t max) +{ + ksmodel_set_in_range_bining(histo, n, min, max, false); +} + +static size_t ksmodel_set_lower_edge(struct kshark_trace_histo *histo) +{ + /* + * Find the index of the first entry inside + * the range (timestamp > min). + */ + size_t row = kshark_find_entry_by_time(histo->min, + histo->data, + 0, + histo->data_size - 1); + + if (row != 0) { + /* + * The first entry inside the range is not the first entry + * of the dataset. This means that the Lower Overflow bin + * contains data. + */ + + /* Lower Overflow bin starts at "0". */ + histo->map[histo->n_bins + 1] = 0; + + /* + * The number of entries inside the Lower Overflow bin is + * equal to the index of the first entry inside the range. + */ + histo->bin_count[histo->n_bins + 1] = row; + } else { + /* + * Lower Overflow bin is empty. The number of entries is + * already set to "0". + */ + histo->map[histo->n_bins + 1] = KS_EMPTY_BIN; + } + + /* + * Now check if the first entry inside the range falls into the + * first bin. + */ + if (histo->data[row]->ts < histo->min + histo->bin_size) { + /* + * It is inside the fisrs bin. Set the beginning + * of the fisrs bin. + */ + histo->map[0] = row; + } else { + /* The fisrs bin is empty. */ + histo->map[0] = KS_EMPTY_BIN; + } + + return row; +} + +static size_t ksmodel_set_upper_edge(struct kshark_trace_histo *histo) +{ + /* + * Find the index of the first entry outside + * the range (timestamp > max). + */ + size_t row = kshark_find_entry_by_time(histo->max, + histo->data, + 0, + histo->data_size - 1); + + if (row < histo->data_size - 1 || + (row == histo->data_size - 1 && + histo->data[histo->data_size - 1]->ts > histo->max)) { + /* + * The Upper Overflow bin contains data. Set its beginning + * and the number of entries. + */ + histo->map[histo->n_bins] = row; + histo->bin_count[histo->n_bins] = histo->data_size - row; + } else { + /* + * Upper Overflow bin is empty. The number of entries is + * already set to "0". + */ + histo->map[histo->n_bins] = KS_EMPTY_BIN; + } + + return row; +} + +static void ksmodel_set_next_bin_edge(struct kshark_trace_histo *histo, + size_t bin) +{ + size_t time, row, next_bin = bin + 1; + + /* Calculate the beginning of the next bin. */ + time = histo->min + next_bin * histo->bin_size; + + /* + * Find the index of the first entry inside + * the next bin (timestamp > time). + */ + row = kshark_find_entry_by_time(time,histo->data, 0, + histo->data_size - 1); + + /* + * The timestamp of the very last entry of the dataset can be exactly + * equal to the value of the upper edge of the range. This is very + * likely to happen when we use ksmodel_set_in_range_bining(). In this + * case we have to increase the size of the very last bin in order to + * make sure that the last entry of the dataset will fall into it. + */ + if (next_bin == histo->n_bins - 1) + ++time; + + if (histo->data[row]->ts >= time + histo->bin_size) { + /* The bin is empty. */ + histo->map[next_bin] = KS_EMPTY_BIN; + return; + } + + /* Set the index of the first entry. */ + histo->map[next_bin] = row; +} + +static void ksmodel_set_bin_counts(struct kshark_trace_histo *histo) +{ + size_t i = 0, prev_not_empty; + + /* + * Find the first bin which contain data. Start by checking the + * Lower Overflow bin. + */ + if (histo->map[histo->n_bins + 1] != KS_EMPTY_BIN) { + prev_not_empty = histo->n_bins + 1; + } else { + while (histo->map[i] < 0) { + ++i; + } + + prev_not_empty = i++; + } + + /* + * Starting from the first not empty bin, loop over all bins and + * calculate the number of entries. + */ + while (i < histo->n_bins) { + if (histo->map[i] != KS_EMPTY_BIN) { + histo->bin_count[prev_not_empty] = + histo->map[i] - histo->map[prev_not_empty]; + + prev_not_empty = i; + } + + ++i; + } + + /* Check if the Upper Overflow bin contains data. */ + if (histo->map[histo->n_bins] == KS_EMPTY_BIN) { + /* + * The Upper Overflow bin is empty. Use the size of the + * dataset to calculate the content of the previouse not + * empty bin. + */ + histo->bin_count[prev_not_empty] = histo->data_size - + histo->map[prev_not_empty]; + } else { + /* + * Use the index of the first entry inside the Upper Overflow + * bin to calculate the content of the previouse not empty + * bin. + */ + histo->bin_count[prev_not_empty] = histo->map[histo->n_bins] - + histo->map[prev_not_empty]; + } +} + +/** + * @brief Provide the Visualization model with data. Calculate the current + * state of the model. + * @param histo: Input location for the model descriptor. + * @param data: Input location for the trace data. + * @param n: Number of bins. + */ +void ksmodel_fill(struct kshark_trace_histo *histo, + struct kshark_entry **data, size_t n) +{ + size_t bin; + + histo->data_size = n; + histo->data = data; + + if (histo->n_bins == 0 || + histo->bin_size == 0 || + histo->data_size == 0) { + /* + * Something is wrong with this histo. + * Most likely the binning is not set. + */ + ksmodel_clear(histo); + fprintf(stderr, + "Unable to fill the model with data.\n"); + fprintf(stderr, + "Try to set the bining of the model first.\n"); + + return; + } + + /* Set the Lower Overflow bin */ + ksmodel_set_lower_edge(histo); + + /* + * Loop over the dataset and set the beginning of all individual bins. + */ + bin = 0; + while (bin < histo->n_bins) { + ksmodel_set_next_bin_edge(histo, bin); + ++bin; + } + + /* Set the Upper Overflow bin. */ + ksmodel_set_upper_edge(histo); + + /* Calculate the number of entries in each bin. */ + ksmodel_set_bin_counts(histo); +} + +/** + * @brief Get the total number of entries in a given bin. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @returns The number of entries in this bin. + */ +size_t ksmodel_bin_count(struct kshark_trace_histo *histo, int bin) +{ + if (bin >= 0 && bin < (int) histo->n_bins) + return histo->bin_count[bin]; + + if (bin == UPPER_OVERFLOW_BIN) + return histo->bin_count[histo->n_bins]; + + if (bin == LOWER_OVERFLOW_BIN) + return histo->bin_count[histo->n_bins + 1]; + + return 0; +} + +/** + * @brief Shift the time-window of the model forward. Recalculate the current + * state of the model. + * @param histo: Input location for the model descriptor. + * @param n: Number of bins to shift. + */ +void ksmodel_shift_forward(struct kshark_trace_histo *histo, size_t n) +{ + size_t bin; + + if (!histo->data_size) + return; + + if (ksmodel_bin_count(histo, UPPER_OVERFLOW_BIN) == 0) { + /* + * The Upper Overflow bin is empty. This means that we are at + * the upper edge of the dataset already. Do nothing in this + * case. + */ + return; + } + + if (n >= histo->n_bins) { + /* + * No overlap between the new and the old ranges. Recalculate + * all bins from scratch. First calculate the new range. + */ + histo->min += n * histo->bin_size; + histo->max += n * histo->bin_size; + + ksmodel_set_bining(histo, histo->n_bins, histo->min, + histo->max); + + ksmodel_fill(histo, histo->data, histo->data_size); + return; + } + + /* Set the new Lower Overflow bin */ + ksmodel_set_lower_edge(histo); + + /* + * Copy the content of all overlaping bins starting from bin "0" + * of the new histo. + */ + bin = 0; + while (bin < histo->n_bins - n) { + histo->map[bin] = histo->map[bin + n]; + histo->bin_count[bin] = histo->bin_count[bin + n]; + ++bin; + } + + histo->map[bin] = histo->map[bin + n]; + histo->bin_count[bin] = 0; + + /* Calculate only the content of the new (non-overlapping) bins. */ + ksmodel_reset_bins(histo, bin + 1, histo->n_bins); + while (bin < histo->n_bins) { + ksmodel_set_next_bin_edge(histo, bin); + ++bin; + } + + /* + * Set the new Upper Overflow bin and calculate the number of entries + * in each bin. + */ + ksmodel_set_upper_edge(histo); + ksmodel_set_bin_counts(histo); +} + +/** + * @brief Shift the time-window of the model backward. Recalculate the current + * state of the model. + * @param histo: Input location for the model descriptor. + * @param n: Number of bins to shift. + */ +void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n) +{ + size_t bin; + + if (!histo->data_size) + return; + + if (ksmodel_bin_count(histo, LOWER_OVERFLOW_BIN) == 0) { + /* + * The Lower Overflow bin is empty. This means that we are at + * the Lower edge of the dataset already. Do nothing in this + * case. + */ + return; + } + + if (n >= histo->n_bins) { + /* + * No overlap between the new and the old range. Recalculate + * all bins from scratch. First calculate the new range. + */ + histo->min -= n * histo->bin_size; + histo->max -= n * histo->bin_size; + + ksmodel_set_bining(histo, histo->n_bins, histo->min, + histo->max); + + ksmodel_fill(histo, histo->data, histo->data_size); + return; + } + + /* + * Copy the content of all overlaping bins starting from the last bin + * of the new histo. + */ + bin = histo->n_bins - 1; + while (1) { + if (bin > histo->n_bins - n && histo->map[bin] >= 0) { + histo->map[histo->n_bins] = histo->map[bin]; + histo->bin_count[histo->n_bins] += + histo->bin_count[bin]; + } + + histo->map[bin] = histo->map[bin-n]; + histo->bin_count[bin] = histo->bin_count[bin-n]; + + if (bin == n) + break; + + --bin; + } + + /* Reset all new bins, including overflow bins. */ + ksmodel_reset_bins(histo, 0, bin); + ksmodel_reset_bins(histo, histo->n_bins, histo->n_bins + 1); + + /* Set the new Lower Overflow bin */ + ksmodel_set_lower_edge(histo); + + /* Calculate only the content of the new (non-overlapping) bins. */ + bin = 0; + while (bin < n) { + ksmodel_set_next_bin_edge(histo, bin); + ++bin; + } + + /* + * Set the new Upper Overflow bin and calculate the number of entries + * in each bin. + */ + ksmodel_set_upper_edge(histo); + ksmodel_set_bin_counts(histo); +} + +/** + * @brief Move the time-window of the model to a given location. Recalculate + * the current state of the model. + * @param histo: Input location for the model descriptor. + * @param ts: position in time to be visualized. + */ +void ksmodel_jump_to(struct kshark_trace_histo *histo, size_t ts) +{ + size_t min, max, range_min; + + if (ts > histo->min && ts < histo->max) { + /* + * The new position is already inside the range. + * Do nothing in this case. + */ + return; + } + + /* + * Calculate the new range without changing the size and the number + * of bins. + */ + min = ts - histo->n_bins * histo->bin_size / 2; + + /* Make sure that the range does not go outside of the dataset. */ + if (min < histo->data[0]->ts) + min = histo->data[0]->ts; + + range_min = histo->data[histo->data_size - 1]->ts - + histo->n_bins * histo->bin_size; + + if (min > range_min) + min = range_min; + + max = min + histo->n_bins * histo->bin_size; + + /* Use the new range to recalculate all bins from scratch. */ + ksmodel_set_bining(histo, histo->n_bins, min, max); + ksmodel_fill(histo, histo->data, histo->data_size); +} + +/** + * @brief Extend the time-window of the model. Recalculate the current state + * of the model. + * @param histo: Input location for the model descriptor. + * @param r: Scale factor of the zoom-out. + * @param mark: Focus point of the zoom-out. + */ +void ksmodel_zoom_out(struct kshark_trace_histo *histo, + double r, int mark) +{ + size_t range, min, max, delta_min; + double delta_tot; + + if (!histo->data_size) + return; + + /* + * If the marker is not set, assume that the focal point of the zoom + * is the center of the range. + */ + if (mark < 0) + mark = histo->n_bins / 2; + + /* + * Calculate the new range of the histo. Use the bin of the marker + * as a focal point for the zoomout. With this the maker will stay + * inside the same bin in the new histo. + */ + range = histo->max - histo->min; + delta_tot = range * r; + delta_min = delta_tot * mark / histo->n_bins; + + min = histo->min - delta_min; + max = histo->max + (size_t) delta_tot - delta_min; + + /* Make sure the new range doesn't go outside of the dataset. */ + if (min < histo->data[0]->ts) + min = histo->data[0]->ts; + + if (max > histo->data[histo->data_size - 1]->ts) + max = histo->data[histo->data_size - 1]->ts; + + /* + * Use the new range to recalculate all bins from scratch. Enforce + * "In Range" adjustment of the range of the model, in order to avoid + * slowly drifting outside of the data-set in the case when the very + * first or the very last entry is used as a focal point. + */ + ksmodel_set_in_range_bining(histo, histo->n_bins, min, max, true); + ksmodel_fill(histo, histo->data, histo->data_size); +} + +/** + * @brief Shrink the time-window of the model. Recalculate the current state + * of the model. + * @param histo: Input location for the model descriptor. + * @param r: Scale factor of the zoom-in. + * @param mark: Focus point of the zoom-in. + */ +void ksmodel_zoom_in(struct kshark_trace_histo *histo, + double r, int mark) +{ + size_t range, min, max, delta_min; + double delta_tot; + + if (!histo->data_size) + return; + + /* + * If the marker is not set, assume that the focal point of the zoom + * is the center of the range. + */ + if (mark < 0) + mark = histo->n_bins / 2; + + range = histo->max - histo->min; + + /* Avoid overzooming. */ + if (range < histo->n_bins * 4) + return; + + /* + * Calculate the new range of the histo. Use the bin of the marker + * as a focal point for the zoomin. With this the maker will stay + * inside the same bin in the new histo. + */ + delta_tot = range * r; + if (mark == (int)histo->n_bins - 1) + delta_min = delta_tot; + else if (mark == 0) + delta_min = 0; + else + delta_min = delta_tot * mark / histo->n_bins; + + min = histo->min + delta_min; + max = histo->max - (size_t) delta_tot + delta_min; + + /* + * Use the new range to recalculate all bins from scratch. Enforce + * "In Range" adjustment of the range of the model, in order to avoid + * slowly drifting outside of the data-set in the case when the very + * first or the very last entry is used as a focal point. + */ + ksmodel_set_in_range_bining(histo, histo->n_bins, min, max, true); + ksmodel_fill(histo, histo->data, histo->data_size); +} + +/** + * @brief Get the index of the first entry in a given bin. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @returns Index of the first entry in this bin. If the bin is empty the + * function returns negative error identifier (KS_EMPTY_BIN). + */ +ssize_t ksmodel_first_index_at_bin(struct kshark_trace_histo *histo, int bin) +{ + if (bin >= 0 && bin < (int) histo->n_bins) + return histo->map[bin]; + + if (bin == UPPER_OVERFLOW_BIN) + return histo->map[histo->n_bins]; + + if (bin == LOWER_OVERFLOW_BIN) + return histo->map[histo->n_bins + 1]; + + return KS_EMPTY_BIN; +} + +/** + * @brief Get the index of the last entry in a given bin. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @returns Index of the last entry in this bin. If the bin is empty the + * function returns negative error identifier (KS_EMPTY_BIN). + */ +ssize_t ksmodel_last_index_at_bin(struct kshark_trace_histo *histo, int bin) +{ + ssize_t index = ksmodel_first_index_at_bin(histo, bin); + size_t count = ksmodel_bin_count(histo, bin); + + if (index >= 0 && count) + index += count - 1; + + return index; +} + +static bool ksmodel_is_visible(struct kshark_entry *e) +{ + if ((e->visible & KS_GRAPH_VIEW_FILTER_MASK) && + (e->visible & KS_EVENT_VIEW_FILTER_MASK)) + return true; + + return false; +} + +static struct kshark_entry_request * +ksmodel_entry_front_request_alloc(struct kshark_trace_histo *histo, + int bin, bool vis_only, + matching_condition_func func, int val) +{ + struct kshark_entry_request *req; + size_t first, n; + + /* Get the number of entries in this bin. */ + n = ksmodel_bin_count(histo, bin); + if (!n) + return NULL; + + first = ksmodel_first_index_at_bin(histo, bin); + + req = kshark_entry_request_alloc(first, n, + func, val, + vis_only, KS_GRAPH_VIEW_FILTER_MASK); + + return req; +} + +static struct kshark_entry_request * +ksmodel_entry_back_request_alloc(struct kshark_trace_histo *histo, + int bin, bool vis_only, + matching_condition_func func, int val) +{ + struct kshark_entry_request *req; + size_t first, n; + + /* Get the number of entries in this bin. */ + n = ksmodel_bin_count(histo, bin); + if (!n) + return NULL; + + first = ksmodel_last_index_at_bin(histo, bin); + + req = kshark_entry_request_alloc(first, n, + func, val, + vis_only, KS_GRAPH_VIEW_FILTER_MASK); + + return req; +} + +/** + * @brief Get the index of the first entry from a given Cpu in a given bin. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @param cpu: Cpu Id. + * @returns Index of the first entry from a given Cpu in this bin. + */ +ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo, + int bin, int cpu) +{ + size_t i, n, first, not_found = KS_EMPTY_BIN; + + n = ksmodel_bin_count(histo, bin); + if (!n) + return not_found; + + first = ksmodel_first_index_at_bin(histo, bin); + + for (i = first; i < first + n; ++i) { + if (histo->data[i]->cpu == cpu) { + if (ksmodel_is_visible(histo->data[i])) + return i; + else + not_found = KS_FILTERED_BIN; + } + } + + return not_found; +} + +/** + * @brief Get the index of the first entry from a given Task in a given bin. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @param pid: Process Id of a task. + * @returns Index of the first entry from a given Task in this bin. + */ +ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo, + int bin, int pid) +{ + size_t i, n, first, not_found = KS_EMPTY_BIN; + + n = ksmodel_bin_count(histo, bin); + if (!n) + return not_found; + + first = ksmodel_first_index_at_bin(histo, bin); + + for (i = first; i < first + n; ++i) { + if (histo->data[i]->pid == pid) { + if (ksmodel_is_visible(histo->data[i])) + return i; + else + not_found = KS_FILTERED_BIN; + } + } + + return not_found; +} + +/** + * @brief In a given bin, start from the front end of the bin and go towards + * the back end, searching for an entry satisfying the Matching + * condition defined by a Matching condition function. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @param vis_only: If true, a visible entry is requested. + * @param func: Matching condition finction. + * @param val: Matching condition value, used by the Matching condition + * finction. + * @param index: Optional output location for the index of the requested + * entry inside the array. + * @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL. + */ +const struct kshark_entry * +ksmodel_get_entry_front(struct kshark_trace_histo *histo, + int bin, bool vis_only, + matching_condition_func func, int val, + ssize_t *index) +{ + struct kshark_entry_request *req; + const struct kshark_entry *entry; + + /* Set the position at the beginning of the bin and go forward. */ + req = ksmodel_entry_front_request_alloc(histo, bin, vis_only, + func, val); + if (!req) + return NULL; + + entry = kshark_get_entry_front(req, histo->data, index); + free(req); + + return entry; +} + +/** + * @brief In a given bin, start from the back end of the bin and go towards + * the front end, searching for an entry satisfying the Matching + * condition defined by a Matching condition function. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @param vis_only: If true, a visible entry is requested. + * @param func: Matching condition finction. + * @param val: Matching condition value, used by the Matching condition + * finction. + * @param index: Optional output location for the index of the requested + * entry inside the array. + * @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL. + */ +const struct kshark_entry * +ksmodel_get_entry_back(struct kshark_trace_histo *histo, + int bin, bool vis_only, + matching_condition_func func, int val, + ssize_t *index) +{ + struct kshark_entry_request *req; + const struct kshark_entry *entry; + + /* Set the position at the end of the bin and go backwards. */ + req = ksmodel_entry_back_request_alloc(histo, bin, vis_only, + func, val); + if (!req) + return NULL; + + entry = kshark_get_entry_back(req, histo->data, index); + free(req); + + return entry; +} + +static int ksmodel_get_entry_pid(const struct kshark_entry *entry) +{ + if (!entry) { + /* No data has been found. */ + return KS_EMPTY_BIN; + } + + if (!entry->visible) { + /* Some data has been found, but it is filtered-out. */ + return KS_FILTERED_BIN; + } + + return entry->pid; +} + +/** + * @brief In a given bin, start from the front end of the bin and go towards + * the back end, searching for an entry from a given Cpu. Return + * the Process Id of the task of the entry found. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @param cpu: Cpu Id. + * @param vis_only: If true, a visible entry is requested. + * @param index: Optional output location for the index of the requested + * entry inside the array. + * @returns Process Id of the task if an entry has been found. Else a negative + * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). + */ +int ksmodel_get_pid_front(struct kshark_trace_histo *histo, + int bin, int cpu, bool vis_only, + ssize_t *index) +{ + struct kshark_entry_request *req; + const struct kshark_entry *entry; + + /* Set the position at the beginning of the bin and go forward. */ + req = ksmodel_entry_front_request_alloc(histo, + bin, vis_only, + kshark_check_cpu, cpu); + if (!req) + return KS_EMPTY_BIN; + + entry = kshark_get_entry_front(req, histo->data, index); + free(req); + + return ksmodel_get_entry_pid(entry); +} + +/** + * @brief In a given bin, start from the back end of the bin and go towards + * the front end, searching for an entry from a given Cpu. Return + * the Process Id of the task of the entry found. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @param cpu: Cpu Id. + * @param vis_only: If true, a visible entry is requested. + * @param index: Optional output location for the index of the requested + * entry inside the array. + * @returns Process Id of the task if an entry has been found. Else a negative + * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). + */ +int ksmodel_get_pid_back(struct kshark_trace_histo *histo, + int bin, int cpu, bool vis_only, + ssize_t *index) +{ + struct kshark_entry_request *req; + const struct kshark_entry *entry; + + /* Set the position at the end of the bin and go backwards. */ + req = ksmodel_entry_back_request_alloc(histo, + bin, vis_only, + kshark_check_cpu, cpu); + if (!req) + return KS_EMPTY_BIN; + + entry = kshark_get_entry_back(req, histo->data, index); + free(req); + + return ksmodel_get_entry_pid(entry); +} + +static int ksmodel_get_entry_cpu(const struct kshark_entry *entry) +{ + if (!entry) { + /* No data has been found. */ + return KS_EMPTY_BIN; + } + + if (!entry->visible) { + /* Some data has been found, but it is filtered-out. */ + return KS_FILTERED_BIN; + } + + return entry->cpu; +} + +/** + * @brief Get the Cpu Id of the first entry from a given Task in a given bin. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @param pid: Process Id of a task. + * @param vis_only: If true, a visible entry is requested. + * @param index: Optional output location for the index of the requested + * entry inside the array. + * @returns Cpu Id of the first entry from a given Task in this bin. + */ +int ksmodel_get_cpu(struct kshark_trace_histo *histo, + int bin, int pid, bool vis_only, + ssize_t *index) +{ + struct kshark_entry_request *req; + const struct kshark_entry *entry; + size_t n; + + /* Get the number of entries in this bin. */ + n = ksmodel_bin_count(histo, bin); + if (!n) + return KS_EMPTY_BIN; + + /* Create an entry request but keep the starting position unset. */ + req = kshark_entry_request_alloc(0, n, + kshark_check_pid, pid, + vis_only, KS_GRAPH_VIEW_FILTER_MASK); + + if (bin == UPPER_OVERFLOW_BIN) { + /* + * Set the position at the end of the Lower Overflow bin and + * go backwards. + */ + req->first = ksmodel_bin_count(histo, bin) - 1; + entry = kshark_get_entry_back(req, histo->data, index); + } else { + /* + * Set the position at the beginning of the bin and go + * forward. + */ + req->first = ksmodel_first_index_at_bin(histo, bin); + entry = kshark_get_entry_front(req, histo->data, index); + } + + free(req); + + return ksmodel_get_entry_cpu(entry); +} + +/** + * @brief Check if a visible entry from a given Cpu exist in this bin. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @param cpu: Cpu Id. + * @param index: Optional output location for the index of the requested + * entry inside the array. + * @returns True, if a visible entry from the Cpu exists in this bin. + * Else false. + */ +bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo, + int bin, int cpu, ssize_t *index) +{ + struct kshark_entry_request *req; + const struct kshark_entry *entry; + + /* Set the position at the beginning of the bin and go forward. */ + req = ksmodel_entry_front_request_alloc(histo, + bin, true, + kshark_check_cpu, cpu); + if (!req) + return false; + + req->vis_mask = KS_EVENT_VIEW_FILTER_MASK; + entry = kshark_get_entry_front(req, histo->data, index); + free(req); + + if (!entry || !entry->visible) { + /* No visible entry has been found. */ + return false; + } + + return true; +} + +/** + * @brief Check if a visible entry from a given Task exist in this bin. + * @param histo: Input location for the model descriptor. + * @param bin: Bin id. + * @param pid: Process Id of the task. + * @param index: Optional output location for the index of the requested + * entry inside the array. + * @returns True, if a visible entry from the task exists in this bin. + * Else false. + */ +bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo, + int bin, int pid, ssize_t *index) +{ + struct kshark_entry_request *req; + const struct kshark_entry *entry; + + /* Set the position at the beginning of the bin and go forward. */ + req = ksmodel_entry_front_request_alloc(histo, + bin, true, + kshark_check_pid, pid); + if (!req) + return false; + + req->vis_mask = KS_EVENT_VIEW_FILTER_MASK; + entry = kshark_get_entry_front(req, histo->data, index); + free(req); + + if (!entry || !entry->visible) { + /* No visible entry has been found. */ + return false; + } + + return true; +} diff --git a/kernel-shark-qt/src/libkshark-model.h b/kernel-shark-qt/src/libkshark-model.h new file mode 100644 index 0000000..9b2c54c --- /dev/null +++ b/kernel-shark-qt/src/libkshark-model.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ + +/* + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov + */ + + /** + * @file libkshark-model.h + * @brief Visualization model for FTRACE (trace-cmd) data. + */ + +#ifndef _LIB_KSHARK_MODEL_H +#define _LIB_KSHARK_MODEL_H + +// KernelShark +#include "libkshark.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** Overflow Bin identifiers. */ +enum OverflowBin { + /** Identifier of the Upper Overflow Bin. */ + UPPER_OVERFLOW_BIN = -1, + + /** Identifier of the Lower Overflow Bin. */ + LOWER_OVERFLOW_BIN = -2, +}; + +/** Structure describing the current state of the visualization model. */ +struct kshark_trace_histo { + /** Trace data. */ + struct kshark_entry **data; + + /** The size of the data. */ + size_t data_size; + + /** The index of the first entry in each bin. */ + ssize_t *map; + + /** Number of entries in each bin. */ + size_t *bin_count; + + /** Lower edge of the time-window to be visualized. */ + uint64_t min; + + /** Upper edge of the time-window to be visualized. */ + uint64_t max; + + /** The size of the bins. */ + uint64_t bin_size; + + /** Number of bins. */ + size_t n_bins; +}; + +void ksmodel_init(struct kshark_trace_histo *histo); + +void ksmodel_clear(struct kshark_trace_histo *histo); + +void ksmodel_set_bining(struct kshark_trace_histo *histo, + size_t n, uint64_t min, uint64_t max); + +void ksmodel_fill(struct kshark_trace_histo *histo, + struct kshark_entry **data, size_t n); + +size_t ksmodel_bin_count(struct kshark_trace_histo *histo, int bin); + +void ksmodel_shift_forward(struct kshark_trace_histo *histo, size_t n); + +void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n); + +void ksmodel_jump_to(struct kshark_trace_histo *histo, size_t ts); + +void ksmodel_zoom_out(struct kshark_trace_histo *histo, + double r, int mark); + +void ksmodel_zoom_in(struct kshark_trace_histo *histo, + double r, int mark); + +ssize_t ksmodel_first_index_at_bin(struct kshark_trace_histo *histo, int bin); + +ssize_t ksmodel_last_index_at_bin(struct kshark_trace_histo *histo, int bin); + +ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo, + int bin, int cpu); + +ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo, + int bin, int pid); + +const struct kshark_entry * +ksmodel_get_entry_front(struct kshark_trace_histo *histo, + int bin, bool vis_only, + matching_condition_func func, int val, + ssize_t *index); + +const struct kshark_entry * +ksmodel_get_entry_back(struct kshark_trace_histo *histo, + int bin, bool vis_only, + matching_condition_func func, int val, + ssize_t *index); + +int ksmodel_get_pid_front(struct kshark_trace_histo *histo, + int bin, int cpu, bool vis_only, + ssize_t *index); + +int ksmodel_get_pid_back(struct kshark_trace_histo *histo, + int bin, int cpu, bool vis_only, + ssize_t *index); + +int ksmodel_get_cpu(struct kshark_trace_histo *histo, + int bin, int pid, bool vis_only, + ssize_t *index); + +bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo, + int bin, int cpu, ssize_t *index); + +bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo, + int bin, int pid, ssize_t *index); + +static inline double ksmodel_bin_time(struct kshark_trace_histo *histo, + int bin) +{ + return (histo->min + bin*histo->bin_size) * 1e-9; +} + +static inline uint64_t ksmodel_bin_ts(struct kshark_trace_histo *histo, + int bin) +{ + return (histo->min + bin*histo->bin_size); +} + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif From patchwork Thu Jul 5 15:37:44 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 10758653 Return-Path: Received: from mail-wm0-f66.google.com ([74.125.82.66]:50371 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753264AbeGEPit (ORCPT ); Thu, 5 Jul 2018 11:38:49 -0400 Received: by mail-wm0-f66.google.com with SMTP id v25-v6so12046095wmc.0 for ; Thu, 05 Jul 2018 08:38:48 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [RFC 3/3] kernel-shark-qt: Add an example showing how to manipulate the Vis. model. Date: Thu, 5 Jul 2018 18:37:44 +0300 Message-Id: <20180705153744.28356-3-y.karadz@gmail.com> In-Reply-To: <20180705153744.28356-1-y.karadz@gmail.com> References: <20180705153744.28356-1-y.karadz@gmail.com> Sender: linux-trace-devel-owner@vger.kernel.org List-ID: Content-Length: 4756 This patch introduces a basic example, showing how to initialize the Visualization model and to use the API to perform some of the basic operations. Signed-off-by: Yordan Karadzhov (VMware) --- kernel-shark-qt/examples/CMakeLists.txt | 4 + kernel-shark-qt/examples/datahisto.c | 155 ++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 kernel-shark-qt/examples/datahisto.c diff --git a/kernel-shark-qt/examples/CMakeLists.txt b/kernel-shark-qt/examples/CMakeLists.txt index 009fd1e..6906eba 100644 --- a/kernel-shark-qt/examples/CMakeLists.txt +++ b/kernel-shark-qt/examples/CMakeLists.txt @@ -7,3 +7,7 @@ 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) diff --git a/kernel-shark-qt/examples/datahisto.c b/kernel-shark-qt/examples/datahisto.c new file mode 100644 index 0000000..1db2b02 --- /dev/null +++ b/kernel-shark-qt/examples/datahisto.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2018 VMware Inc, Yordan Karadzhov + */ + +// C +#include +#include + +// KernelShark +#include "libkshark.h" +#include "libkshark-model.h" + +#define N_BINS 5 + +const char *default_file = "trace.dat"; + +void dump_bin(struct kshark_trace_histo *histo, int bin, + const char *type, int val) +{ + const struct kshark_entry *e_front, *e_back; + char *entry_str; + ssize_t i_front, i_back; + + printf("bin %i {\n", bin); + if (strcmp(type, "cpu") == 0) { + e_front = ksmodel_get_entry_front(histo, bin, true, + kshark_check_cpu, val, + &i_front); + + e_back = ksmodel_get_entry_back(histo, bin, true, + kshark_check_cpu, val, + &i_back); + } else if (strcmp(type, "task") == 0) { + e_front = ksmodel_get_entry_front(histo, bin, true, + kshark_check_pid, val, + &i_front); + + e_back = ksmodel_get_entry_back(histo, bin, true, + kshark_check_pid, val, + &i_back); + } else { + i_front = ksmodel_first_index_at_bin(histo, bin); + e_front = histo->data[i_front]; + + i_back = ksmodel_last_index_at_bin(histo, bin); + e_back = histo->data[i_back]; + } + + if (i_front == KS_EMPTY_BIN) { + puts ("EMPTY BIN"); + } else { + entry_str = kshark_dump_entry(e_front); + printf("%li -> %s\n", i_front, entry_str); + free(entry_str); + + entry_str = kshark_dump_entry(e_back); + printf("%li -> %s\n", i_back, entry_str); + free(entry_str); + } + + puts("}\n"); +} + +void dump_histo(struct kshark_trace_histo *histo, const char *type, int val) +{ + size_t bin; + + for (bin = 0; bin < histo->n_bins; ++bin) + dump_bin(histo, bin, type, val); +} + +int main(int argc, char **argv) +{ + struct kshark_context *kshark_ctx; + struct kshark_entry **data = NULL; + struct kshark_trace_histo histo; + size_t i, n_rows, n_tasks; + bool status; + int *pids; + + /* 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) + status = kshark_open(kshark_ctx, argv[1]); + else + status = kshark_open(kshark_ctx, default_file); + + if (!status) { + 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); + + /* Get a list of all tasks. */ + n_tasks = kshark_get_task_pids(kshark_ctx, &pids); + + /* Initialize the Visualization Model. */ + ksmodel_init(&histo); + ksmodel_set_bining(&histo, N_BINS, data[0]->ts, + data[n_rows - 1]->ts); + + /* Fill the model with data and calculate its state. */ + ksmodel_fill(&histo, data, n_rows); + + /* Dump the raw bins. */ + dump_histo(&histo, "", 0); + + puts("\n...\n\n"); + + /* + * 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); + + 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); + + puts("\n...\n\n"); + + /* + * Do 10% Zoom-Out, using the last bin as a focal point. Dump the last + * Task. + */ + ksmodel_zoom_out(&histo, .10, N_BINS - 1); + dump_histo(&histo, "task", pids[n_tasks - 1]); + + /* Reset (clear) the model. */ + ksmodel_clear(&histo); + + /* Free the memory. */ + for (i = 0; i < n_rows; ++i) + free(data[i]); + + free(data); + + /* Close the file. */ + kshark_close(kshark_ctx); + + /* Close the session. */ + kshark_free(kshark_ctx); + + return 0; +}