blob: f3a4c6549dc6ab787def641a09a8af0db0f35126 [file] [log] [blame]
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* Implemented a fixed row size to speed up list.
* Copyright (C) 2010 Darren Hart <dvhltc@us.ibm.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include "trace-view-store.h"
#include <stdlib.h>
#include <string.h>
#include "cpu.h"
#include "trace-filter.h"
/* boring declarations of local functions */
static void trace_view_store_init (TraceViewStore *pkg_tree);
static void trace_view_store_class_init (TraceViewStoreClass *klass);
static void trace_view_store_tree_model_init (GtkTreeModelIface *iface);
static void trace_view_store_finalize (GObject *object);
static gboolean trace_view_store_get_iter (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path);
static GtkTreePath *trace_view_store_get_path (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean trace_view_store_iter_next (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean trace_view_store_iter_children (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent);
static gboolean trace_view_store_iter_has_child (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gint trace_view_store_iter_n_children (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean trace_view_store_iter_nth_child (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n);
static gboolean trace_view_store_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child);
static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */
/*****************************************************************************
*
* trace_view_store_get_type: here we register our new type and its interfaces
* with the type system. If you want to implement
* additional interfaces like GtkTreeSortable, you
* will need to do it here.
*
*****************************************************************************/
GType
trace_view_store_get_type (void)
{
static GType trace_view_store_type = 0;
/* Some boilerplate type registration stuff */
if (trace_view_store_type == 0)
{
static const GTypeInfo trace_view_store_info =
{
sizeof (TraceViewStoreClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) trace_view_store_class_init,
NULL, /* class finalize */
NULL, /* class_data */
sizeof (TraceViewStore),
0, /* n_preallocs */
(GInstanceInitFunc) trace_view_store_init
};
static const GInterfaceInfo tree_model_info =
{
(GInterfaceInitFunc) trace_view_store_tree_model_init,
NULL,
NULL
};
/* First register the new derived type with the GObject type system */
trace_view_store_type = g_type_register_static (G_TYPE_OBJECT, "TraceViewStore",
&trace_view_store_info, (GTypeFlags)0);
/* Now register our GtkTreeModel interface with the type system */
g_type_add_interface_static (trace_view_store_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
}
return trace_view_store_type;
}
/*****************************************************************************
*
* trace_view_store_class_init: more boilerplate GObject/GType stuff.
* Init callback for the type system,
* called once when our new class is created.
*
*****************************************************************************/
static void
trace_view_store_class_init (TraceViewStoreClass *klass)
{
GObjectClass *object_class;
parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
object_class = (GObjectClass*) klass;
object_class->finalize = trace_view_store_finalize;
}
/*****************************************************************************
*
* trace_view_store_tree_model_init: init callback for the interface registration
* in trace_view_store_get_type. Here we override
* the GtkTreeModel interface functions that
* we implement.
*
*****************************************************************************/
static void
trace_view_store_tree_model_init (GtkTreeModelIface *iface)
{
iface->get_flags = trace_view_store_get_flags;
iface->get_n_columns = trace_view_store_get_n_columns;
iface->get_column_type = trace_view_store_get_column_type;
iface->get_iter = trace_view_store_get_iter;
iface->get_path = trace_view_store_get_path;
iface->get_value = trace_view_store_get_value;
iface->iter_next = trace_view_store_iter_next;
iface->iter_children = trace_view_store_iter_children;
iface->iter_has_child = trace_view_store_iter_has_child;
iface->iter_n_children = trace_view_store_iter_n_children;
iface->iter_nth_child = trace_view_store_iter_nth_child;
iface->iter_parent = trace_view_store_iter_parent;
}
/*****************************************************************************
*
* trace_view_store_init: this is called everytime a new trace view store object
* instance is created (we do that in trace_view_store_new).
* Initialise the list structure's fields here.
*
*****************************************************************************/
static void
trace_view_store_init (TraceViewStore *trace_view_store)
{
trace_view_store->n_columns = TRACE_VIEW_STORE_N_COLUMNS;
trace_view_store->column_types[0] = G_TYPE_UINT; /* INDEX */
trace_view_store->column_types[1] = G_TYPE_UINT; /* CPU */
trace_view_store->column_types[2] = G_TYPE_STRING; /* TS */
trace_view_store->column_types[3] = G_TYPE_STRING; /* COMM */
trace_view_store->column_types[4] = G_TYPE_UINT; /* PID */
trace_view_store->column_types[5] = G_TYPE_STRING; /* LAT */
trace_view_store->column_types[6] = G_TYPE_STRING; /* EVENT */
trace_view_store->column_types[7] = G_TYPE_STRING; /* INFO */
g_assert (TRACE_VIEW_STORE_N_COLUMNS == 8);
trace_view_store->num_rows = 0;
trace_view_store->rows = NULL;
trace_view_store->spin = NULL;
trace_view_store->page = 1;
trace_view_store->pages = 1;
trace_view_store->rows_per_page = TRACE_VIEW_DEFAULT_MAX_ROWS;
trace_view_store->num_rows = 0;
trace_view_store->start_row = 0;
trace_view_store->visible_rows = 0;
trace_view_store->actual_rows = 0;
/* Set all columns visible */
trace_view_store->visible_column_mask = (1 << TRACE_VIEW_STORE_N_COLUMNS) - 1;
trace_view_store->stamp = g_random_int(); /* Random int to check whether an iter belongs to our model */
}
/*****************************************************************************
*
* trace_view_store_finalize: this is called just before a trace view store is
* destroyed. Free dynamically allocated memory here.
*
*****************************************************************************/
static void
trace_view_store_finalize (GObject *object)
{
TraceViewStore *store = TRACE_VIEW_STORE(object);
gint cpu;
/* free all records and free all memory used by the list */
for (cpu = 0; cpu < store->cpus; cpu++)
g_free(store->cpu_list[cpu]);
g_free(store->cpu_list);
g_free(store->cpu_mask);
g_free(store->rows);
g_free(store->cpu_items);
filter_task_hash_free(store->task_filter);
if (store->spin) {
g_object_unref(store->spin);
store->spin = NULL;
}
pevent_filter_free(store->event_filter);
tracecmd_close(store->handle);
/* must chain up - finalize parent */
(* parent_class->finalize) (object);
}
/*****************************************************************************
*
* trace_view_store_get_flags: tells the rest of the world whether our tree model
* has any special characteristics. In our case,
* we have a list model (instead of a tree), and each
* tree iter is valid as long as the row in question
* exists, as it only contains a pointer to our struct.
*
*****************************************************************************/
GtkTreeModelFlags
trace_view_store_get_flags (GtkTreeModel *tree_model)
{
g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), (GtkTreeModelFlags)0);
return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
}
/*****************************************************************************
*
* trace_view_store_get_n_columns: tells the rest of the world how many data
* columns we export via the tree model interface
*
*****************************************************************************/
gint
trace_view_store_get_n_columns (GtkTreeModel *tree_model)
{
g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), 0);
return TRACE_VIEW_STORE(tree_model)->n_columns;
}
/*****************************************************************************
*
* get_visible_column: Return the index of the visible columns
*
*****************************************************************************/
static gint get_visible_column(TraceViewStore *trace_view, gint column)
{
guint i;
/* If all columns are visible just use what was passed in */
if (trace_view->visible_column_mask == ((1 << TRACE_VIEW_STORE_N_COLUMNS) - 1))
return column;
column++; /* make 0 drop out */
for (i = 0; column && i < TRACE_VIEW_STORE_N_COLUMNS; i++) {
if (!(trace_view->visible_column_mask & (1 << i)))
continue;
column--;
}
g_assert(column == 0);
/* We upped column, so me must dec the return */
return i - 1;
}
/*****************************************************************************
*
* trace_view_store_get_column_type: tells the rest of the world which type of
* data an exported model column contains
*
*****************************************************************************/
GType
trace_view_store_get_column_type (GtkTreeModel *tree_model,
gint index)
{
g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), G_TYPE_INVALID);
g_return_val_if_fail (index < TRACE_VIEW_STORE(tree_model)->n_columns && index >= 0, G_TYPE_INVALID);
index = get_visible_column(TRACE_VIEW_STORE(tree_model), index);
return TRACE_VIEW_STORE(tree_model)->column_types[index];
}
/*****************************************************************************
*
* trace_view_store_get_iter: converts a tree path (physical position) into a
* tree iter structure (the content of the iter
* fields will only be used internally by our model).
* We simply store a pointer to our TraceViewRecord
* structure that represents that row in the tree iter.
*
*****************************************************************************/
static gboolean
trace_view_store_get_iter (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path)
{
TraceViewStore *trace_view_store;
TraceViewRecord *record;
gint *indices, n, depth;
g_assert(TRACE_VIEW_IS_LIST(tree_model));
g_assert(path!=NULL);
trace_view_store = TRACE_VIEW_STORE(tree_model);
indices = gtk_tree_path_get_indices(path);
depth = gtk_tree_path_get_depth(path);
/* we do not allow children */
g_assert(depth == 1); /* depth 1 = top level; a list only has top level nodes and no children */
n = indices[0]; /* the n-th top level row */
record = trace_view_store_get_visible_row(trace_view_store, n);
if (!record)
return FALSE;
/* We simply store a pointer to our custom record in the iter */
iter->stamp = trace_view_store->stamp;
iter->user_data = record;
iter->user_data2 = NULL; /* unused */
iter->user_data3 = NULL; /* unused */
return TRUE;
}
/*****************************************************************************
*
* trace_view_store_get_path: converts a tree iter into a tree path (ie. the
* physical position of that row in the list).
*
*****************************************************************************/
static GtkTreePath *
trace_view_store_get_path (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
GtkTreePath *path;
TraceViewRecord *record;
TraceViewStore *store;
g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), NULL);
g_return_val_if_fail (iter != NULL, NULL);
g_return_val_if_fail (iter->user_data != NULL, NULL);
store = TRACE_VIEW_STORE(tree_model);
record = (TraceViewRecord*) iter->user_data;
path = gtk_tree_path_new();
gtk_tree_path_append_index(path, record->pos - store->start_row);
return path;
}
/*****************************************************************************
*
* trace_view_store_get_value: Returns a row's exported data columns
* (_get_value is what gtk_tree_model_get uses)
*
*****************************************************************************/
void
trace_view_store_get_value (GtkTreeModel *tree_model,
GtkTreeIter *iter,
gint column,
GValue *value)
{
TraceViewRecord *record;
TraceViewStore *trace_view_store;
struct trace_seq s;
struct pevent *pevent;
struct event_format *event;
struct pevent_record *data;
const gchar *comm;
gchar *str;
guint64 secs, usecs;
gint val;
int cpu;
g_return_if_fail (TRACE_VIEW_IS_LIST (tree_model));
g_return_if_fail (iter != NULL);
g_return_if_fail (column < TRACE_VIEW_STORE(tree_model)->n_columns);
g_value_init (value, TRACE_VIEW_STORE(tree_model)->column_types[column]);
trace_view_store = TRACE_VIEW_STORE(tree_model);
pevent = tracecmd_get_pevent(trace_view_store->handle);
record = (TraceViewRecord*)iter->user_data;
g_return_if_fail ( record != NULL );
column = get_visible_column(TRACE_VIEW_STORE(tree_model), column);
switch(column)
{
case TRACE_VIEW_STORE_COL_INDEX:
g_value_set_uint(value, record->pos);
break;
case TRACE_VIEW_STORE_COL_CPU:
g_value_set_uint(value, record->cpu);
break;
case TRACE_VIEW_STORE_COL_TS:
usecs = record->timestamp;
usecs /= 1000;
secs = usecs / 1000000ULL;
usecs -= secs * 1000000ULL;
str = g_strdup_printf("%llu.%06llu",
(long long)secs, (long long)usecs);
g_value_set_string(value, str);
g_free(str);
break;
case TRACE_VIEW_STORE_COL_COMM:
case TRACE_VIEW_STORE_COL_PID:
case TRACE_VIEW_STORE_COL_LAT:
case TRACE_VIEW_STORE_COL_EVENT:
case TRACE_VIEW_STORE_COL_INFO:
data = tracecmd_read_at(trace_view_store->handle, record->offset, &cpu);
g_assert(data != NULL);
if (cpu != record->cpu) {
free_record(data);
return;
}
switch (column) {
case TRACE_VIEW_STORE_COL_COMM:
case TRACE_VIEW_STORE_COL_PID:
val = pevent_data_pid(pevent, data);
if (column == TRACE_VIEW_STORE_COL_PID)
g_value_set_uint(value, val);
else {
comm = pevent_data_comm_from_pid(pevent, val);
g_value_set_string(value, comm);
}
break;
case TRACE_VIEW_STORE_COL_LAT:
trace_seq_init(&s);
pevent_data_lat_fmt(pevent, &s, data);
g_value_set_string(value, s.buffer);
trace_seq_destroy(&s);
break;
case TRACE_VIEW_STORE_COL_EVENT:
case TRACE_VIEW_STORE_COL_INFO:
val = pevent_data_type(pevent, data);
event = pevent_data_event_from_type(pevent, val);
if (!event) {
if (column == TRACE_VIEW_STORE_COL_EVENT)
g_value_set_string(value, "[UNKNOWN EVENT]");
break;
}
if (column == TRACE_VIEW_STORE_COL_EVENT) {
g_value_set_string(value, event->name);
break;
}
trace_seq_init(&s);
pevent_event_info(&s, event, data);
g_value_set_string(value, s.buffer);
trace_seq_destroy(&s);
break;
}
free_record(data);
}
}
void trace_view_store_clear_all_events_enabled(TraceViewStore *store)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
pevent_filter_clear_trivial(store->event_filter, FILTER_TRIVIAL_BOTH);
store->all_events = 0;
}
void trace_view_store_set_all_events_enabled(TraceViewStore *store)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
if (store->all_events)
return;
/*
* All enabled means that we don't need to look at
* the system events, so free those arrays.
*/
pevent_filter_reset(store->event_filter);
store->all_events = 1;
}
/*****************************************************************************
*
* trace_view_store_iter_next: Takes an iter structure and sets it to point
* to the next row.
*
*****************************************************************************/
static gboolean
trace_view_store_iter_next (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
TraceViewRecord *record, *nextrecord;
TraceViewStore *trace_view_store;
gint pos;
g_return_val_if_fail (TRACE_VIEW_IS_LIST (tree_model), FALSE);
if (iter == NULL || iter->user_data == NULL)
return FALSE;
trace_view_store = TRACE_VIEW_STORE(tree_model);
record = (TraceViewRecord *) iter->user_data;
pos = record->pos - trace_view_store->start_row;
/* Is this the last record in the list? */
if ((pos + 1) >= trace_view_store->num_rows)
return FALSE;
nextrecord = trace_view_store->rows[(record->pos + 1)];
g_assert ( nextrecord != NULL );
g_assert ( nextrecord->pos == (record->pos + 1) );
iter->stamp = trace_view_store->stamp;
iter->user_data = nextrecord;
return TRUE;
}
/*****************************************************************************
*
* trace_view_store_iter_children: Returns TRUE or FALSE depending on whether
* the row specified by 'parent' has any children.
* If it has children, then 'iter' is set to
* point to the first child. Special case: if
* 'parent' is NULL, then the first top-level
* row should be returned if it exists.
*
*****************************************************************************/
static gboolean
trace_view_store_iter_children (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent)
{
TraceViewStore *trace_view_store;
g_return_val_if_fail (parent == NULL || parent->user_data != NULL, FALSE);
/* this is a list, nodes have no children */
if (parent)
return FALSE;
/* parent == NULL is a special case; we need to return the first top-level row */
g_return_val_if_fail (TRACE_VIEW_IS_LIST (tree_model), FALSE);
trace_view_store = TRACE_VIEW_STORE(tree_model);
/* No rows => no first row */
if (trace_view_store->num_rows == 0)
return FALSE;
/* Set iter to first item in list */
iter->stamp = trace_view_store->stamp;
iter->user_data = trace_view_store->rows[0];
return TRUE;
}
/*****************************************************************************
*
* trace_view_store_iter_has_child: Returns TRUE or FALSE depending on whether
* the row specified by 'iter' has any children.
* We only have a list and thus no children.
*
*****************************************************************************/
static gboolean
trace_view_store_iter_has_child (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
return FALSE;
}
/*****************************************************************************
*
* trace_view_store_iter_n_children: Returns the number of children the row
* specified by 'iter' has. This is usually 0,
* as we only have a list and thus do not have
* any children to any rows. A special case is
* when 'iter' is NULL, in which case we need
* to return the number of top-level nodes,
* ie. the number of rows in our list.
*
*****************************************************************************/
static gint
trace_view_store_iter_n_children (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
TraceViewStore *trace_view_store;
g_return_val_if_fail (TRACE_VIEW_IS_LIST (tree_model), -1);
g_return_val_if_fail (iter == NULL || iter->user_data != NULL, FALSE);
trace_view_store = TRACE_VIEW_STORE(tree_model);
/* special case: if iter == NULL, return number of top-level rows */
if (!iter)
return trace_view_store->num_rows;
return 0; /* otherwise, this is easy again for a list */
}
/*****************************************************************************
*
* trace_view_store_iter_nth_child: If the row specified by 'parent' has any
* children, set 'iter' to the n-th child and
* return TRUE if it exists, otherwise FALSE.
* A special case is when 'parent' is NULL, in
* which case we need to set 'iter' to the n-th
* row if it exists.
*
*****************************************************************************/
static gboolean
trace_view_store_iter_nth_child (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n)
{
TraceViewRecord *record;
TraceViewStore *trace_view_store;
g_return_val_if_fail (TRACE_VIEW_IS_LIST (tree_model), FALSE);
trace_view_store = TRACE_VIEW_STORE(tree_model);
/* a list has only top-level rows */
if(parent)
return FALSE;
/* special case: if parent == NULL, set iter to n-th top-level row */
if( n >= trace_view_store->num_rows )
return FALSE;
record = trace_view_store->rows[trace_view_store->start_row + n];
g_assert( record != NULL );
g_assert( record->pos - trace_view_store->start_row == n );
iter->stamp = trace_view_store->stamp;
iter->user_data = record;
return TRUE;
}
/*****************************************************************************
*
* trace_view_store_iter_parent: Point 'iter' to the parent node of 'child'. As
* we have a list and thus no children and no
* parents of children, we can just return FALSE.
*
*****************************************************************************/
static gboolean
trace_view_store_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child)
{
return FALSE;
}
static int mask_cpu_isset(TraceViewStore *store, gint cpu)
{
return cpu_isset(store->cpu_mask, cpu);
}
static void mask_cpu_set(TraceViewStore *store, gint cpu)
{
cpu_set(store->cpu_mask, cpu);
}
static void mask_cpu_clear(TraceViewStore *store, gint cpu)
{
cpu_clear(store->cpu_mask, cpu);
}
static void mask_set_cpus(TraceViewStore *store, gint cpus)
{
set_cpus(store->cpu_mask, cpus);
}
static void update_page(TraceViewStore *store)
{
if (!store->spin)
return;
gtk_spin_button_set_range(GTK_SPIN_BUTTON(store->spin),
1, store->pages);
}
/*****************************************************************************
*
* merge_sort_rows_ts: Merge sort the data by time stamp.
*
*
*****************************************************************************/
static void merge_sort_rows_ts(TraceViewStore *store)
{
guint64 ts;
gint next;
guint *indexes;
guint count = 0;
gint cpu;
guint i;
indexes = g_new0(guint, store->cpus);
/* Now sort these by timestamp */
do {
next = -1;
ts = 0;
for (cpu = 0; cpu < store->cpus; cpu++) {
if (!store->all_cpus && !mask_cpu_isset(store, cpu))
continue;
try_again:
if (indexes[cpu] == store->cpu_items[cpu])
continue;
i = indexes[cpu];
if (!store->cpu_list[cpu][i].visible) {
indexes[cpu]++;
goto try_again;
}
if (!ts || store->cpu_list[cpu][i].timestamp < ts) {
ts = store->cpu_list[cpu][i].timestamp;
next = cpu;
}
}
if (next >= 0) {
i = indexes[next]++;
store->rows[count] = &store->cpu_list[next][i];
store->cpu_list[next][i].pos = count++;
}
} while (next >= 0);
store->visible_rows = count;
store->start_row = 0;
store->pages = (count / store->rows_per_page) + 1;
if (store->page > 1) {
if (count < store->page * store->rows_per_page)
store->page = store->pages;
/* still greater? */
if (store->page > 1) {
store->start_row =
(store->page - 1) * store->rows_per_page;
g_assert(store->start_row < count);
}
}
store->num_rows = count > (store->start_row + store->rows_per_page) ?
store->rows_per_page :
count - store->start_row;
update_page(store);
g_free(indexes);
}
/*****************************************************************************
*
* trace_view_store_new: This is what you use in your own code to create a
* new trace view store tree model for you to use.
*
*****************************************************************************/
TraceViewStore *
trace_view_store_new (struct tracecmd_input *handle)
{
TraceViewStore *newstore;
struct pevent_record *data;
gint cpu, count, total=0;
struct temp {
guint64 offset;
guint64 ts;
struct temp *next;
} *list, **next, *rec;
newstore = (TraceViewStore*) g_object_new (TRACE_VIEW_STORE_TYPE, NULL);
g_assert( newstore != NULL );
newstore->handle = handle;
newstore->cpus = tracecmd_cpus(handle);
tracecmd_ref(handle);
newstore->event_filter = pevent_filter_alloc(tracecmd_get_pevent(handle));
newstore->cpu_list = g_new(TraceViewRecord *, newstore->cpus);
g_assert(newstore->cpu_list != NULL);
newstore->cpu_items = g_new(gint, newstore->cpus);
g_assert(newstore->cpu_items != NULL);
newstore->all_cpus = 1;
newstore->all_events = 1;
newstore->cpu_mask = g_new0(guint64, (newstore->cpus >> 6) + 1);
g_assert(newstore->cpu_mask != NULL);
mask_set_cpus(newstore, newstore->cpus);
for (cpu = 0; cpu < newstore->cpus; cpu++) {
count = 0;
list = NULL;
next = &list;
data = tracecmd_read_cpu_first(handle, cpu);
while (data) {
*next = rec = g_malloc(sizeof(*rec));
g_assert(rec != NULL);
rec->offset = data->offset;
rec->ts = data->ts;
rec->next = NULL;
next = &rec->next;
free_record(data);
count++;
data = tracecmd_read_data(handle, cpu);
}
if (count) {
TraceViewRecord *trec;
struct temp *t;
gint i;
rec = list;
trec = g_new(TraceViewRecord, count);
g_assert(trec != NULL);
for (i = 0; i < count; i++) {
g_assert(rec != NULL);
trec[i].cpu = cpu;
trec[i].timestamp = rec->ts;
trec[i].offset = rec->offset;
trec[i].visible = 1;
trec[i].pos = i;
t = rec;
rec = rec->next;
g_free(t);
}
g_assert(rec == NULL);
newstore->cpu_list[cpu] = trec;
} else
newstore->cpu_list[cpu] = NULL;
newstore->cpu_items[cpu] = count;
total += count;
}
newstore->actual_rows = total;
newstore->rows = g_malloc(sizeof(*newstore->rows) * total + 1);
merge_sort_rows_ts(newstore);
return newstore;
}
void trace_view_store_set_spin_button(TraceViewStore *store, GtkWidget *spin)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
g_return_if_fail (GTK_IS_SPIN_BUTTON (spin));
if (store->spin)
g_object_unref(store->spin);
store->spin = spin;
g_object_ref(spin);
gtk_spin_button_set_increments(GTK_SPIN_BUTTON(store->spin),
1.0, 5.0);
update_page(store);
}
/* --- helper functions --- */
gboolean trace_view_store_cpu_isset(TraceViewStore *store, gint cpu)
{
g_return_val_if_fail (TRACE_VIEW_IS_LIST (store), FALSE);
g_return_val_if_fail (cpu >= 0 || cpu < store->cpus, FALSE);
if (mask_cpu_isset(store, cpu))
return TRUE;
return FALSE;
}
void trace_view_store_set_all_cpus(TraceViewStore *store)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
if (store->all_cpus)
return;
mask_set_cpus(store, store->cpus);
store->all_cpus = 1;
merge_sort_rows_ts(store);
}
void trace_view_store_set_cpu(TraceViewStore *store, gint cpu)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
g_return_if_fail (cpu >= 0 || cpu < store->cpus);
if (store->all_cpus || mask_cpu_isset(store, cpu))
return;
mask_cpu_set(store, cpu);
merge_sort_rows_ts(store);
}
void trace_view_store_clear_cpu(TraceViewStore *store, gint cpu)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
g_return_if_fail (cpu >= 0 || cpu < store->cpus);
if (!mask_cpu_isset(store, cpu))
return;
store->all_cpus = 0;
mask_cpu_clear(store, cpu);
merge_sort_rows_ts(store);
}
void trace_view_store_set_page(TraceViewStore *store, gint page)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
g_return_if_fail (page >= 0 || page < store->pages);
store->page = page;
store->start_row = (page - 1) * store->rows_per_page;
g_assert(store->start_row < store->visible_rows);
store->num_rows = store->start_row + store->rows_per_page <
store->visible_rows ? store->rows_per_page :
store->visible_rows - store->start_row;
}
static int rows_ts_cmp(const void *a, const void *b)
{
/* a is just a key, but b is a pointer to a record pointer */
const TraceViewRecord *ta = a;
const TraceViewRecord *tb = *(TraceViewRecord **)b;
const TraceViewRecord *tb1 = *((TraceViewRecord **)b+1);
/* match inbetween too */
if ((ta->timestamp == tb->timestamp) ||
(ta->timestamp > tb->timestamp &&
ta->timestamp < tb1->timestamp))
return 0;
if (ta->timestamp < tb->timestamp)
return -1;
return 1;
}
static TraceViewRecord *
search_for_record_by_timestamp(TraceViewStore *store, guint64 ts)
{
TraceViewRecord key;
TraceViewRecord *rec, **prec;
if (!store->visible_rows)
return NULL;
if (ts < store->rows[0]->timestamp)
return NULL;
if (ts >= store->rows[store->visible_rows-1]->timestamp)
return store->rows[store->visible_rows-1];
key.timestamp = ts;
prec = bsearch(&key, store->rows, store->visible_rows - 1,
sizeof(store->rows[0]), rows_ts_cmp);
g_assert(prec != NULL);
rec = *prec;
return rec;
}
gint trace_view_store_get_timestamp_visible_row(TraceViewStore *store, guint64 ts)
{
TraceViewRecord *rec;
g_return_val_if_fail (TRACE_VIEW_IS_LIST (store), 0);
rec = search_for_record_by_timestamp(store, ts);
if (!rec)
return 0;
return rec->pos - (store->page - 1) * store->rows_per_page;
}
gint trace_view_store_get_timestamp_page(TraceViewStore *store, guint64 ts)
{
TraceViewRecord *rec;
g_return_val_if_fail (TRACE_VIEW_IS_LIST (store), 0);
rec = search_for_record_by_timestamp(store, ts);
if (!rec)
return 1;
return rec->pos / store->rows_per_page + 1;
}
static TraceViewRecord *get_row(TraceViewStore *store, gint row)
{
TraceViewRecord *record;
gint index = row - store->start_row;
g_return_val_if_fail(index >= 0 && index < store->visible_rows, NULL);
record = store->rows[row];
g_assert(record != NULL);
g_assert(record->pos == row);
return record;
}
TraceViewRecord *
trace_view_store_get_row(TraceViewStore *store, gint row)
{
g_return_val_if_fail(TRACE_VIEW_IS_LIST(store), NULL);
return get_row(store, row);
}
TraceViewRecord *
trace_view_store_get_visible_row(TraceViewStore *store, gint row)
{
g_return_val_if_fail(TRACE_VIEW_IS_LIST(store), NULL);
/* If we don't have any visible rows, return NULL */
if (!store->visible_rows)
return NULL;
row += store->start_row;
return get_row(store, row);
}
TraceViewRecord *
trace_view_store_get_actual_row(TraceViewStore *store, gint row)
{
g_return_val_if_fail (TRACE_VIEW_IS_LIST (store), NULL);
g_return_val_if_fail (row >= 0, NULL);
g_return_val_if_fail (row < store->actual_rows, NULL);
if (!store->rows)
return NULL;
return store->rows[row];
}
gint trace_view_store_get_num_actual_rows(TraceViewStore *store)
{
g_return_val_if_fail (TRACE_VIEW_IS_LIST (store), -1);
return store->actual_rows;
}
gint get_next_pid(TraceViewStore *store, struct pevent *pevent, struct pevent_record *record)
{
unsigned long long val;
int ret;
ret = pevent_read_number_field(store->sched_switch_next_field, record->data, &val);
return val;
}
gint get_wakeup_pid(TraceViewStore *store, struct pevent *pevent, struct pevent_record *record)
{
unsigned long long val;
int ret;
ret = pevent_read_number_field(store->sched_wakeup_pid_field, record->data, &val);
return val;
}
gint get_wakeup_new_pid(TraceViewStore *store, struct pevent *pevent, struct pevent_record *record)
{
unsigned long long val;
int ret;
ret = pevent_read_number_field(store->sched_wakeup_new_pid_field, record->data, &val);
return val;
}
static gboolean view_task(TraceViewStore *store, gint pid)
{
return (!store->task_filter ||
!filter_task_count(store->task_filter) ||
filter_task_find_pid(store->task_filter, pid)) &&
(!store->hide_tasks ||
!filter_task_count(store->hide_tasks) ||
!filter_task_find_pid(store->hide_tasks, pid));
}
static gboolean show_task(TraceViewStore *store, struct pevent *pevent,
struct pevent_record *record, gint pid)
{
gint event_id;
if (view_task(store, pid))
return TRUE;
event_id = pevent_data_type(pevent, record);
if (store->sched_switch_next_field &&
event_id == store->sched_switch_event->id) {
/* show sched switch to task */
pid = get_next_pid(store, pevent, record);
if (view_task(store, pid))
return TRUE;
}
if (store->sched_wakeup_pid_field &&
event_id == store->sched_wakeup_event->id) {
/* show sched switch to task */
pid = get_wakeup_pid(store, pevent, record);
if (view_task(store, pid))
return TRUE;
}
if (store->sched_wakeup_new_pid_field &&
event_id == store->sched_wakeup_new_event->id) {
/* show sched switch to task */
pid = get_wakeup_new_pid(store, pevent, record);
if (view_task(store, pid))
return TRUE;
}
return FALSE;
}
static void update_filter_tasks(TraceViewStore *store)
{
struct tracecmd_input *handle;
struct pevent *pevent;
struct pevent_record *record;
gint pid;
gint cpu;
gint i;
handle = store->handle;
pevent = tracecmd_get_pevent(store->handle);
if (!store->sched_switch_event) {
store->sched_switch_event =
pevent_find_event_by_name(pevent, "sched", "sched_switch");
if (store->sched_switch_event)
store->sched_switch_next_field =
pevent_find_any_field(store->sched_switch_event,
"next_pid");
store->sched_wakeup_event =
pevent_find_event_by_name(pevent, "sched", "sched_wakeup");
if (store->sched_wakeup_event)
store->sched_wakeup_pid_field =
pevent_find_any_field(store->sched_wakeup_event,
"pid");
store->sched_wakeup_new_event =
pevent_find_event_by_name(pevent, "sched", "sched_wakeup");
if (store->sched_wakeup_new_event)
store->sched_wakeup_new_pid_field =
pevent_find_any_field(store->sched_wakeup_new_event,
"pid");
}
for (cpu = 0; cpu < store->cpus; cpu++) {
record = tracecmd_read_cpu_first(handle, cpu);
for (i = 0; i < store->cpu_items[cpu]; i++) {
g_assert(record->offset == store->cpu_list[cpu][i].offset);
/* The record may be filtered by the events */
if (!store->all_events) {
int ret;
ret = pevent_filter_match(store->event_filter,
record);
if (ret != FILTER_MATCH) {
store->cpu_list[cpu][i].visible = 0;
goto skip;
}
}
pid = pevent_data_pid(pevent, record);
if (show_task(store, pevent, record, pid))
store->cpu_list[cpu][i].visible = 1;
else
store->cpu_list[cpu][i].visible = 0;
skip:
free_record(record);
record = tracecmd_read_data(handle, cpu);
}
g_assert(record == NULL);
}
merge_sort_rows_ts(store);
}
void trace_view_store_filter_tasks(TraceViewStore *store, struct filter_task *filter)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
/* We may pass in the store->task_filter. Don't free it if we do */
if (store->task_filter && store->task_filter != filter)
filter_task_hash_free(store->task_filter);
if (store->task_filter != filter)
store->task_filter = filter_task_hash_copy(filter);
update_filter_tasks(store);
}
void trace_view_store_hide_tasks(TraceViewStore *store, struct filter_task *filter)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
/* We may pass in the store->task_filter. Don't free it if we do */
if (store->hide_tasks && store->hide_tasks != filter)
filter_task_hash_free(store->hide_tasks);
if (store->hide_tasks != filter)
store->hide_tasks = filter_task_hash_copy(filter);
update_filter_tasks(store);
}
void trace_view_store_update_filter(TraceViewStore *store)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
update_filter_tasks(store);
}
void trace_view_store_assign_filters(TraceViewStore *store,
struct filter_task *task_filter,
struct filter_task *hide_tasks)
{
g_return_if_fail (TRACE_VIEW_IS_LIST (store));
/* We may pass in the store->task_filter. Don't free it if we do */
if (store->task_filter && store->task_filter != task_filter)
filter_task_hash_free(store->task_filter);
if (store->hide_tasks && store->hide_tasks != hide_tasks)
filter_task_hash_free(store->hide_tasks);
if (store->hide_tasks != hide_tasks)
store->hide_tasks = filter_task_hash_copy(hide_tasks);
if (store->task_filter != task_filter)
store->task_filter = filter_task_hash_copy(task_filter);
}
/*****************************************************************************
*
* trace_view_store_append_record: Empty lists are boring. This function can
* be used in your own code to add rows to the
* list. Note how we emit the "row-inserted"
* signal after we have appended the row
* internally, so the tree view and other
* interested objects know about the new row.
*
*****************************************************************************/
#if 0
void
trace_view_store_append_record (TraceViewStore *trace_view_store,
const gchar *name,
guint year_born)
{
GtkTreeIter iter;
GtkTreePath *path;
TraceViewRecord *newrecord;
gulong newsize;
guint pos;
g_return_if_fail (TRACE_VIEW_IS_LIST(trace_view_store));
g_return_if_fail (name != NULL);
pos = trace_view_store->num_rows;
trace_view_store->num_rows++;
newsize = trace_view_store->num_rows * sizeof(TraceViewRecord*);
trace_view_store->rows = g_realloc(trace_view_store->rows, newsize);
newrecord = g_new0(TraceViewRecord, 1);
newrecord->name = g_strdup(name);
newrecord->name_collate_key = g_utf8_collate_key(name,-1); /* for fast sorting, used later */
newrecord->year_born = year_born;
trace_view_store->rows[pos] = newrecord;
newrecord->pos = pos;
/* inform the tree view and other interested objects
* (e.g. tree row references) that we have inserted
* a new row, and where it was inserted */
path = gtk_tree_path_new();
gtk_tree_path_append_index(path, newrecord->pos);
trace_view_store_get_iter(GTK_TREE_MODEL(trace_view_store), &iter, path);
gtk_tree_model_row_inserted(GTK_TREE_MODEL(trace_view_store), path, &iter);
gtk_tree_path_free(path);
}
#endif