diff options
author | Steven Rostedt <srostedt@redhat.com> | 2009-12-01 11:23:28 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2009-12-17 21:43:46 -0500 |
commit | 43e0833d8468c9d9b496abde1845dddff106d11e (patch) | |
tree | 96086c05b35ff7dbdd3bb296bb8d749a6c0e293f | |
parent | ab91bd24b899d73d7552c5d1947656a3b446660e (diff) |
Add new trace view store model to improve performance
With the amount of data needed for loading a trace file, we must
implement our own tree view model.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | trace-view-store.c | 848 | ||||
-rw-r--r-- | trace-view-store.h | 104 |
3 files changed, 953 insertions, 1 deletions
@@ -30,7 +30,7 @@ trace-util.o:: $(HEADERS) | |||
30 | trace-ftrace.o:: $(HEADERS) | 30 | trace-ftrace.o:: $(HEADERS) |
31 | trace-input.o:: $(HEADERS) | 31 | trace-input.o:: $(HEADERS) |
32 | 32 | ||
33 | trace-cmd:: trace-cmd.o trace-read.o trace-view.o | 33 | trace-cmd:: trace-cmd.o trace-read.o trace-view.o trace-view-store.o |
34 | $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS) | 34 | $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS) |
35 | 35 | ||
36 | .PHONY: view_depends | 36 | .PHONY: view_depends |
diff --git a/trace-view-store.c b/trace-view-store.c new file mode 100644 index 0000000..55bf2e7 --- /dev/null +++ b/trace-view-store.c | |||
@@ -0,0 +1,848 @@ | |||
1 | #include "trace-view-store.h" | ||
2 | #include <stdlib.h> | ||
3 | |||
4 | /* boring declarations of local functions */ | ||
5 | |||
6 | static void trace_view_store_init (TraceViewStore *pkg_tree); | ||
7 | |||
8 | static void trace_view_store_class_init (TraceViewStoreClass *klass); | ||
9 | |||
10 | static void trace_view_store_tree_model_init (GtkTreeModelIface *iface); | ||
11 | |||
12 | static void trace_view_store_finalize (GObject *object); | ||
13 | |||
14 | static GtkTreeModelFlags trace_view_store_get_flags (GtkTreeModel *tree_model); | ||
15 | |||
16 | static gint trace_view_store_get_n_columns (GtkTreeModel *tree_model); | ||
17 | |||
18 | static GType trace_view_store_get_column_type (GtkTreeModel *tree_model, | ||
19 | gint index); | ||
20 | |||
21 | static gboolean trace_view_store_get_iter (GtkTreeModel *tree_model, | ||
22 | GtkTreeIter *iter, | ||
23 | GtkTreePath *path); | ||
24 | |||
25 | static GtkTreePath *trace_view_store_get_path (GtkTreeModel *tree_model, | ||
26 | GtkTreeIter *iter); | ||
27 | |||
28 | static void trace_view_store_get_value (GtkTreeModel *tree_model, | ||
29 | GtkTreeIter *iter, | ||
30 | gint column, | ||
31 | GValue *value); | ||
32 | |||
33 | static gboolean trace_view_store_iter_next (GtkTreeModel *tree_model, | ||
34 | GtkTreeIter *iter); | ||
35 | |||
36 | static gboolean trace_view_store_iter_children (GtkTreeModel *tree_model, | ||
37 | GtkTreeIter *iter, | ||
38 | GtkTreeIter *parent); | ||
39 | |||
40 | static gboolean trace_view_store_iter_has_child (GtkTreeModel *tree_model, | ||
41 | GtkTreeIter *iter); | ||
42 | |||
43 | static gint trace_view_store_iter_n_children (GtkTreeModel *tree_model, | ||
44 | GtkTreeIter *iter); | ||
45 | |||
46 | static gboolean trace_view_store_iter_nth_child (GtkTreeModel *tree_model, | ||
47 | GtkTreeIter *iter, | ||
48 | GtkTreeIter *parent, | ||
49 | gint n); | ||
50 | |||
51 | static gboolean trace_view_store_iter_parent (GtkTreeModel *tree_model, | ||
52 | GtkTreeIter *iter, | ||
53 | GtkTreeIter *child); | ||
54 | |||
55 | |||
56 | |||
57 | static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */ | ||
58 | |||
59 | |||
60 | /***************************************************************************** | ||
61 | * | ||
62 | * trace_view_store_get_type: here we register our new type and its interfaces | ||
63 | * with the type system. If you want to implement | ||
64 | * additional interfaces like GtkTreeSortable, you | ||
65 | * will need to do it here. | ||
66 | * | ||
67 | *****************************************************************************/ | ||
68 | |||
69 | GType | ||
70 | trace_view_store_get_type (void) | ||
71 | { | ||
72 | static GType trace_view_store_type = 0; | ||
73 | |||
74 | /* Some boilerplate type registration stuff */ | ||
75 | if (trace_view_store_type == 0) | ||
76 | { | ||
77 | static const GTypeInfo trace_view_store_info = | ||
78 | { | ||
79 | sizeof (TraceViewStoreClass), | ||
80 | NULL, /* base_init */ | ||
81 | NULL, /* base_finalize */ | ||
82 | (GClassInitFunc) trace_view_store_class_init, | ||
83 | NULL, /* class finalize */ | ||
84 | NULL, /* class_data */ | ||
85 | sizeof (TraceViewStore), | ||
86 | 0, /* n_preallocs */ | ||
87 | (GInstanceInitFunc) trace_view_store_init | ||
88 | }; | ||
89 | static const GInterfaceInfo tree_model_info = | ||
90 | { | ||
91 | (GInterfaceInitFunc) trace_view_store_tree_model_init, | ||
92 | NULL, | ||
93 | NULL | ||
94 | }; | ||
95 | |||
96 | /* First register the new derived type with the GObject type system */ | ||
97 | trace_view_store_type = g_type_register_static (G_TYPE_OBJECT, "TraceViewStore", | ||
98 | &trace_view_store_info, (GTypeFlags)0); | ||
99 | |||
100 | /* Now register our GtkTreeModel interface with the type system */ | ||
101 | g_type_add_interface_static (trace_view_store_type, GTK_TYPE_TREE_MODEL, &tree_model_info); | ||
102 | } | ||
103 | |||
104 | return trace_view_store_type; | ||
105 | } | ||
106 | |||
107 | |||
108 | /***************************************************************************** | ||
109 | * | ||
110 | * trace_view_store_class_init: more boilerplate GObject/GType stuff. | ||
111 | * Init callback for the type system, | ||
112 | * called once when our new class is created. | ||
113 | * | ||
114 | *****************************************************************************/ | ||
115 | |||
116 | static void | ||
117 | trace_view_store_class_init (TraceViewStoreClass *klass) | ||
118 | { | ||
119 | GObjectClass *object_class; | ||
120 | |||
121 | parent_class = (GObjectClass*) g_type_class_peek_parent (klass); | ||
122 | object_class = (GObjectClass*) klass; | ||
123 | |||
124 | object_class->finalize = trace_view_store_finalize; | ||
125 | } | ||
126 | |||
127 | /***************************************************************************** | ||
128 | * | ||
129 | * trace_view_store_tree_model_init: init callback for the interface registration | ||
130 | * in trace_view_store_get_type. Here we override | ||
131 | * the GtkTreeModel interface functions that | ||
132 | * we implement. | ||
133 | * | ||
134 | *****************************************************************************/ | ||
135 | |||
136 | static void | ||
137 | trace_view_store_tree_model_init (GtkTreeModelIface *iface) | ||
138 | { | ||
139 | iface->get_flags = trace_view_store_get_flags; | ||
140 | iface->get_n_columns = trace_view_store_get_n_columns; | ||
141 | iface->get_column_type = trace_view_store_get_column_type; | ||
142 | iface->get_iter = trace_view_store_get_iter; | ||
143 | iface->get_path = trace_view_store_get_path; | ||
144 | iface->get_value = trace_view_store_get_value; | ||
145 | iface->iter_next = trace_view_store_iter_next; | ||
146 | iface->iter_children = trace_view_store_iter_children; | ||
147 | iface->iter_has_child = trace_view_store_iter_has_child; | ||
148 | iface->iter_n_children = trace_view_store_iter_n_children; | ||
149 | iface->iter_nth_child = trace_view_store_iter_nth_child; | ||
150 | iface->iter_parent = trace_view_store_iter_parent; | ||
151 | } | ||
152 | |||
153 | |||
154 | /***************************************************************************** | ||
155 | * | ||
156 | * trace_view_store_init: this is called everytime a new trace view store object | ||
157 | * instance is created (we do that in trace_view_store_new). | ||
158 | * Initialise the list structure's fields here. | ||
159 | * | ||
160 | *****************************************************************************/ | ||
161 | |||
162 | static void | ||
163 | trace_view_store_init (TraceViewStore *trace_view_store) | ||
164 | { | ||
165 | trace_view_store->n_columns = TRACE_VIEW_STORE_N_COLUMNS; | ||
166 | |||
167 | trace_view_store->column_types[0] = G_TYPE_UINT; /* CPU */ | ||
168 | trace_view_store->column_types[1] = G_TYPE_STRING; /* TS */ | ||
169 | trace_view_store->column_types[2] = G_TYPE_STRING; /* COMM */ | ||
170 | trace_view_store->column_types[3] = G_TYPE_UINT; /* PID */ | ||
171 | trace_view_store->column_types[4] = G_TYPE_STRING; /* LAT */ | ||
172 | trace_view_store->column_types[5] = G_TYPE_STRING; /* EVENT */ | ||
173 | trace_view_store->column_types[6] = G_TYPE_STRING; /* INFO */ | ||
174 | |||
175 | g_assert (TRACE_VIEW_STORE_N_COLUMNS == 7); | ||
176 | |||
177 | trace_view_store->num_rows = 0; | ||
178 | trace_view_store->rows = NULL; | ||
179 | |||
180 | /* Set all columns visible */ | ||
181 | trace_view_store->visible_column_mask = (1 << TRACE_VIEW_STORE_N_COLUMNS) - 1; | ||
182 | |||
183 | trace_view_store->stamp = g_random_int(); /* Random int to check whether an iter belongs to our model */ | ||
184 | |||
185 | } | ||
186 | |||
187 | |||
188 | /***************************************************************************** | ||
189 | * | ||
190 | * trace_view_store_finalize: this is called just before a trace view store is | ||
191 | * destroyed. Free dynamically allocated memory here. | ||
192 | * | ||
193 | *****************************************************************************/ | ||
194 | |||
195 | static void | ||
196 | trace_view_store_finalize (GObject *object) | ||
197 | { | ||
198 | /* TraceViewStore *trace_view_store = TRACE_VIEW_STORE(object); */ | ||
199 | |||
200 | /* free all records and free all memory used by the list */ | ||
201 | #warning IMPLEMENT | ||
202 | |||
203 | /* must chain up - finalize parent */ | ||
204 | (* parent_class->finalize) (object); | ||
205 | } | ||
206 | |||
207 | |||
208 | /***************************************************************************** | ||
209 | * | ||
210 | * trace_view_store_get_flags: tells the rest of the world whether our tree model | ||
211 | * has any special characteristics. In our case, | ||
212 | * we have a list model (instead of a tree), and each | ||
213 | * tree iter is valid as long as the row in question | ||
214 | * exists, as it only contains a pointer to our struct. | ||
215 | * | ||
216 | *****************************************************************************/ | ||
217 | |||
218 | static GtkTreeModelFlags | ||
219 | trace_view_store_get_flags (GtkTreeModel *tree_model) | ||
220 | { | ||
221 | g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), (GtkTreeModelFlags)0); | ||
222 | |||
223 | return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST); | ||
224 | } | ||
225 | |||
226 | |||
227 | /***************************************************************************** | ||
228 | * | ||
229 | * trace_view_store_get_n_columns: tells the rest of the world how many data | ||
230 | * columns we export via the tree model interface | ||
231 | * | ||
232 | *****************************************************************************/ | ||
233 | |||
234 | static gint | ||
235 | trace_view_store_get_n_columns (GtkTreeModel *tree_model) | ||
236 | { | ||
237 | g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), 0); | ||
238 | |||
239 | return TRACE_VIEW_STORE(tree_model)->n_columns; | ||
240 | } | ||
241 | |||
242 | |||
243 | /***************************************************************************** | ||
244 | * | ||
245 | * trace_view_store_get_column_type: tells the rest of the world which type of | ||
246 | * data an exported model column contains | ||
247 | * | ||
248 | *****************************************************************************/ | ||
249 | |||
250 | static GType | ||
251 | trace_view_store_get_column_type (GtkTreeModel *tree_model, | ||
252 | gint index) | ||
253 | { | ||
254 | g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), G_TYPE_INVALID); | ||
255 | g_return_val_if_fail (index < TRACE_VIEW_STORE(tree_model)->n_columns && index >= 0, G_TYPE_INVALID); | ||
256 | |||
257 | return TRACE_VIEW_STORE(tree_model)->column_types[index]; | ||
258 | } | ||
259 | |||
260 | |||
261 | /***************************************************************************** | ||
262 | * | ||
263 | * trace_view_store_get_iter: converts a tree path (physical position) into a | ||
264 | * tree iter structure (the content of the iter | ||
265 | * fields will only be used internally by our model). | ||
266 | * We simply store a pointer to our TraceViewRecord | ||
267 | * structure that represents that row in the tree iter. | ||
268 | * | ||
269 | *****************************************************************************/ | ||
270 | |||
271 | static gboolean | ||
272 | trace_view_store_get_iter (GtkTreeModel *tree_model, | ||
273 | GtkTreeIter *iter, | ||
274 | GtkTreePath *path) | ||
275 | { | ||
276 | TraceViewStore *trace_view_store; | ||
277 | TraceViewRecord *record; | ||
278 | gint *indices, n, depth; | ||
279 | |||
280 | g_assert(TRACE_VIEW_IS_LIST(tree_model)); | ||
281 | g_assert(path!=NULL); | ||
282 | |||
283 | trace_view_store = TRACE_VIEW_STORE(tree_model); | ||
284 | |||
285 | indices = gtk_tree_path_get_indices(path); | ||
286 | depth = gtk_tree_path_get_depth(path); | ||
287 | |||
288 | /* we do not allow children */ | ||
289 | g_assert(depth == 1); /* depth 1 = top level; a list only has top level nodes and no children */ | ||
290 | |||
291 | n = indices[0]; /* the n-th top level row */ | ||
292 | |||
293 | if ( n >= trace_view_store->num_rows || n < 0 ) | ||
294 | return FALSE; | ||
295 | |||
296 | record = trace_view_store->rows[n]; | ||
297 | |||
298 | g_assert(record != NULL); | ||
299 | g_assert(record->pos == n); | ||
300 | |||
301 | /* We simply store a pointer to our custom record in the iter */ | ||
302 | iter->stamp = trace_view_store->stamp; | ||
303 | iter->user_data = record; | ||
304 | iter->user_data2 = NULL; /* unused */ | ||
305 | iter->user_data3 = NULL; /* unused */ | ||
306 | |||
307 | return TRUE; | ||
308 | } | ||
309 | |||
310 | |||
311 | /***************************************************************************** | ||
312 | * | ||
313 | * trace_view_store_get_path: converts a tree iter into a tree path (ie. the | ||
314 | * physical position of that row in the list). | ||
315 | * | ||
316 | *****************************************************************************/ | ||
317 | |||
318 | static GtkTreePath * | ||
319 | trace_view_store_get_path (GtkTreeModel *tree_model, | ||
320 | GtkTreeIter *iter) | ||
321 | { | ||
322 | GtkTreePath *path; | ||
323 | TraceViewRecord *record; | ||
324 | TraceViewStore *trace_view_store; | ||
325 | |||
326 | g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), NULL); | ||
327 | g_return_val_if_fail (iter != NULL, NULL); | ||
328 | g_return_val_if_fail (iter->user_data != NULL, NULL); | ||
329 | |||
330 | trace_view_store = TRACE_VIEW_STORE(tree_model); | ||
331 | |||
332 | record = (TraceViewRecord*) iter->user_data; | ||
333 | |||
334 | path = gtk_tree_path_new(); | ||
335 | gtk_tree_path_append_index(path, record->pos); | ||
336 | |||
337 | return path; | ||
338 | } | ||
339 | |||
340 | |||
341 | /***************************************************************************** | ||
342 | * | ||
343 | * trace_view_store_get_value: Returns a row's exported data columns | ||
344 | * (_get_value is what gtk_tree_model_get uses) | ||
345 | * | ||
346 | *****************************************************************************/ | ||
347 | |||
348 | static void | ||
349 | trace_view_store_get_value (GtkTreeModel *tree_model, | ||
350 | GtkTreeIter *iter, | ||
351 | gint column, | ||
352 | GValue *value) | ||
353 | { | ||
354 | TraceViewRecord *record; | ||
355 | TraceViewStore *trace_view_store; | ||
356 | struct trace_seq s; | ||
357 | struct pevent *pevent; | ||
358 | struct event *event; | ||
359 | struct record *data; | ||
360 | const gchar *comm; | ||
361 | gchar *str; | ||
362 | guint64 secs, usecs; | ||
363 | gint val; | ||
364 | int cpu; | ||
365 | |||
366 | g_return_if_fail (TRACE_VIEW_IS_LIST (tree_model)); | ||
367 | g_return_if_fail (iter != NULL); | ||
368 | g_return_if_fail (column < TRACE_VIEW_STORE(tree_model)->n_columns); | ||
369 | |||
370 | g_value_init (value, TRACE_VIEW_STORE(tree_model)->column_types[column]); | ||
371 | |||
372 | trace_view_store = TRACE_VIEW_STORE(tree_model); | ||
373 | |||
374 | pevent = tracecmd_get_pevent(trace_view_store->handle); | ||
375 | |||
376 | record = *(TraceViewRecord**)iter->user_data; | ||
377 | |||
378 | g_return_if_fail ( record != NULL ); | ||
379 | |||
380 | if(record->pos >= trace_view_store->num_rows) | ||
381 | g_return_if_reached(); | ||
382 | |||
383 | /* If all columns are visible just use what was passed in */ | ||
384 | if (trace_view_store->visible_column_mask != | ||
385 | ((1 << TRACE_VIEW_STORE_N_COLUMNS) - 1)) { | ||
386 | guint i; | ||
387 | |||
388 | column++; /* make 0 drop out */ | ||
389 | |||
390 | for (i = 0; column && i < TRACE_VIEW_STORE_N_COLUMNS; i++) { | ||
391 | if (!trace_view_store->visible_column_mask & (1 << i)) | ||
392 | continue; | ||
393 | |||
394 | column--; | ||
395 | } | ||
396 | |||
397 | g_return_if_fail(column); | ||
398 | |||
399 | column = i; | ||
400 | } | ||
401 | |||
402 | switch(column) | ||
403 | { | ||
404 | case TRACE_VIEW_STORE_COL_CPU: | ||
405 | g_value_set_uint(value, record->cpu); | ||
406 | break; | ||
407 | |||
408 | case TRACE_VIEW_STORE_COL_TS: | ||
409 | usecs = record->timestamp; | ||
410 | usecs /= 1000; | ||
411 | secs = usecs / 1000000ULL; | ||
412 | usecs -= secs * 1000000ULL; | ||
413 | str = g_strdup_printf("%lu.%06lu", secs, usecs); | ||
414 | g_value_set_string(value, str); | ||
415 | g_free(str); | ||
416 | break; | ||
417 | |||
418 | case TRACE_VIEW_STORE_COL_COMM: | ||
419 | case TRACE_VIEW_STORE_COL_PID: | ||
420 | case TRACE_VIEW_STORE_COL_LAT: | ||
421 | case TRACE_VIEW_STORE_COL_EVENT: | ||
422 | case TRACE_VIEW_STORE_COL_INFO: | ||
423 | |||
424 | data = tracecmd_read_at(trace_view_store->handle, record->offset, &cpu); | ||
425 | if (cpu != record->cpu) { | ||
426 | free(data); | ||
427 | return; | ||
428 | } | ||
429 | |||
430 | switch (column) { | ||
431 | case TRACE_VIEW_STORE_COL_COMM: | ||
432 | case TRACE_VIEW_STORE_COL_PID: | ||
433 | val = pevent_data_pid(pevent, data); | ||
434 | if (column == TRACE_VIEW_STORE_COL_PID) | ||
435 | g_value_set_uint(value, val); | ||
436 | else { | ||
437 | comm = pevent_data_comm_from_pid(pevent, val); | ||
438 | g_value_set_string(value, comm); | ||
439 | } | ||
440 | break; | ||
441 | |||
442 | case TRACE_VIEW_STORE_COL_LAT: | ||
443 | trace_seq_init(&s); | ||
444 | pevent_data_lat_fmt(pevent, &s, data, data->size); | ||
445 | g_value_set_string(value, s.buffer); | ||
446 | break; | ||
447 | |||
448 | case TRACE_VIEW_STORE_COL_EVENT: | ||
449 | case TRACE_VIEW_STORE_COL_INFO: | ||
450 | val = pevent_data_type(pevent, data); | ||
451 | event = pevent_data_event_from_type(pevent, val); | ||
452 | if (column == TRACE_VIEW_STORE_COL_EVENT) { | ||
453 | g_value_set_string(value, event->name); | ||
454 | break; | ||
455 | } | ||
456 | |||
457 | |||
458 | trace_seq_init(&s); | ||
459 | pevent_event_info(&s, event, cpu, data, data->size, | ||
460 | record->timestamp); | ||
461 | g_value_set_string(value, s.buffer); | ||
462 | break; | ||
463 | } | ||
464 | free(data); | ||
465 | } | ||
466 | } | ||
467 | |||
468 | |||
469 | /***************************************************************************** | ||
470 | * | ||
471 | * trace_view_store_iter_next: Takes an iter structure and sets it to point | ||
472 | * to the next row. | ||
473 | * | ||
474 | *****************************************************************************/ | ||
475 | |||
476 | static gboolean | ||
477 | trace_view_store_iter_next (GtkTreeModel *tree_model, | ||
478 | GtkTreeIter *iter) | ||
479 | { | ||
480 | TraceViewRecord *record, *nextrecord; | ||
481 | TraceViewStore *trace_view_store; | ||
482 | |||
483 | g_return_val_if_fail (TRACE_VIEW_IS_LIST (tree_model), FALSE); | ||
484 | |||
485 | if (iter == NULL || iter->user_data == NULL) | ||
486 | return FALSE; | ||
487 | |||
488 | trace_view_store = TRACE_VIEW_STORE(tree_model); | ||
489 | |||
490 | record = (TraceViewRecord *) iter->user_data; | ||
491 | |||
492 | /* Is this the last record in the list? */ | ||
493 | if ((record->pos + 1) >= trace_view_store->num_rows) | ||
494 | return FALSE; | ||
495 | |||
496 | nextrecord = trace_view_store->rows[(record->pos + 1)]; | ||
497 | |||
498 | g_assert ( nextrecord != NULL ); | ||
499 | g_assert ( nextrecord->pos == (record->pos + 1) ); | ||
500 | |||
501 | iter->stamp = trace_view_store->stamp; | ||
502 | iter->user_data = nextrecord; | ||
503 | |||
504 | return TRUE; | ||
505 | } | ||
506 | |||
507 | |||
508 | /***************************************************************************** | ||
509 | * | ||
510 | * trace_view_store_iter_children: Returns TRUE or FALSE depending on whether | ||
511 | * the row specified by 'parent' has any children. | ||
512 | * If it has children, then 'iter' is set to | ||
513 | * point to the first child. Special case: if | ||
514 | * 'parent' is NULL, then the first top-level | ||
515 | * row should be returned if it exists. | ||
516 | * | ||
517 | *****************************************************************************/ | ||
518 | |||
519 | static gboolean | ||
520 | trace_view_store_iter_children (GtkTreeModel *tree_model, | ||
521 | GtkTreeIter *iter, | ||
522 | GtkTreeIter *parent) | ||
523 | { | ||
524 | TraceViewStore *trace_view_store; | ||
525 | |||
526 | g_return_val_if_fail (parent == NULL || parent->user_data != NULL, FALSE); | ||
527 | |||
528 | /* this is a list, nodes have no children */ | ||
529 | if (parent) | ||
530 | return FALSE; | ||
531 | |||
532 | /* parent == NULL is a special case; we need to return the first top-level row */ | ||
533 | |||
534 | g_return_val_if_fail (TRACE_VIEW_IS_LIST (tree_model), FALSE); | ||
535 | |||
536 | trace_view_store = TRACE_VIEW_STORE(tree_model); | ||
537 | |||
538 | /* No rows => no first row */ | ||
539 | if (trace_view_store->num_rows == 0) | ||
540 | return FALSE; | ||
541 | |||
542 | /* Set iter to first item in list */ | ||
543 | iter->stamp = trace_view_store->stamp; | ||
544 | iter->user_data = trace_view_store->rows[0]; | ||
545 | |||
546 | return TRUE; | ||
547 | } | ||
548 | |||
549 | |||
550 | /***************************************************************************** | ||
551 | * | ||
552 | * trace_view_store_iter_has_child: Returns TRUE or FALSE depending on whether | ||
553 | * the row specified by 'iter' has any children. | ||
554 | * We only have a list and thus no children. | ||
555 | * | ||
556 | *****************************************************************************/ | ||
557 | |||
558 | static gboolean | ||
559 | trace_view_store_iter_has_child (GtkTreeModel *tree_model, | ||
560 | GtkTreeIter *iter) | ||
561 | { | ||
562 | return FALSE; | ||
563 | } | ||
564 | |||
565 | |||
566 | /***************************************************************************** | ||
567 | * | ||
568 | * trace_view_store_iter_n_children: Returns the number of children the row | ||
569 | * specified by 'iter' has. This is usually 0, | ||
570 | * as we only have a list and thus do not have | ||
571 | * any children to any rows. A special case is | ||
572 | * when 'iter' is NULL, in which case we need | ||
573 | * to return the number of top-level nodes, | ||
574 | * ie. the number of rows in our list. | ||
575 | * | ||
576 | *****************************************************************************/ | ||
577 | |||
578 | static gint | ||
579 | trace_view_store_iter_n_children (GtkTreeModel *tree_model, | ||
580 | GtkTreeIter *iter) | ||
581 | { | ||
582 | TraceViewStore *trace_view_store; | ||
583 | |||
584 | g_return_val_if_fail (TRACE_VIEW_IS_LIST (tree_model), -1); | ||
585 | g_return_val_if_fail (iter == NULL || iter->user_data != NULL, FALSE); | ||
586 | |||
587 | trace_view_store = TRACE_VIEW_STORE(tree_model); | ||
588 | |||
589 | /* special case: if iter == NULL, return number of top-level rows */ | ||
590 | if (!iter) | ||
591 | return trace_view_store->num_rows; | ||
592 | |||
593 | return 0; /* otherwise, this is easy again for a list */ | ||
594 | } | ||
595 | |||
596 | |||
597 | /***************************************************************************** | ||
598 | * | ||
599 | * trace_view_store_iter_nth_child: If the row specified by 'parent' has any | ||
600 | * children, set 'iter' to the n-th child and | ||
601 | * return TRUE if it exists, otherwise FALSE. | ||
602 | * A special case is when 'parent' is NULL, in | ||
603 | * which case we need to set 'iter' to the n-th | ||
604 | * row if it exists. | ||
605 | * | ||
606 | *****************************************************************************/ | ||
607 | |||
608 | static gboolean | ||
609 | trace_view_store_iter_nth_child (GtkTreeModel *tree_model, | ||
610 | GtkTreeIter *iter, | ||
611 | GtkTreeIter *parent, | ||
612 | gint n) | ||
613 | { | ||
614 | TraceViewRecord *record; | ||
615 | TraceViewStore *trace_view_store; | ||
616 | |||
617 | g_return_val_if_fail (TRACE_VIEW_IS_LIST (tree_model), FALSE); | ||
618 | |||
619 | trace_view_store = TRACE_VIEW_STORE(tree_model); | ||
620 | |||
621 | /* a list has only top-level rows */ | ||
622 | if(parent) | ||
623 | return FALSE; | ||
624 | |||
625 | /* special case: if parent == NULL, set iter to n-th top-level row */ | ||
626 | |||
627 | if( n >= trace_view_store->num_rows ) | ||
628 | return FALSE; | ||
629 | |||
630 | record = trace_view_store->rows[n]; | ||
631 | |||
632 | g_assert( record != NULL ); | ||
633 | g_assert( record->pos == n ); | ||
634 | |||
635 | iter->stamp = trace_view_store->stamp; | ||
636 | iter->user_data = record; | ||
637 | |||
638 | return TRUE; | ||
639 | } | ||
640 | |||
641 | |||
642 | /***************************************************************************** | ||
643 | * | ||
644 | * trace_view_store_iter_parent: Point 'iter' to the parent node of 'child'. As | ||
645 | * we have a list and thus no children and no | ||
646 | * parents of children, we can just return FALSE. | ||
647 | * | ||
648 | *****************************************************************************/ | ||
649 | |||
650 | static gboolean | ||
651 | trace_view_store_iter_parent (GtkTreeModel *tree_model, | ||
652 | GtkTreeIter *iter, | ||
653 | GtkTreeIter *child) | ||
654 | { | ||
655 | return FALSE; | ||
656 | } | ||
657 | |||
658 | |||
659 | /***************************************************************************** | ||
660 | * | ||
661 | * merge_sort_rows_ts: Merge sort the data by time stamp. | ||
662 | * | ||
663 | * | ||
664 | *****************************************************************************/ | ||
665 | |||
666 | static void merge_sort_rows_ts(TraceViewStore *store) | ||
667 | { | ||
668 | guint64 ts; | ||
669 | gint next; | ||
670 | guint *indexes; | ||
671 | guint count = 0; | ||
672 | gint cpu; | ||
673 | guint i; | ||
674 | |||
675 | |||
676 | indexes = g_new0(guint, store->cpus); | ||
677 | |||
678 | /* Now sort these by timestamp */ | ||
679 | do { | ||
680 | next = -1; | ||
681 | ts = 0; | ||
682 | for (cpu = 0; cpu < store->cpus; cpu++) { | ||
683 | if (indexes[cpu] == store->cpu_items[cpu]) | ||
684 | continue; | ||
685 | i = indexes[cpu]; | ||
686 | if (!ts || store->cpu_list[cpu][i].timestamp < ts) { | ||
687 | ts = store->cpu_list[cpu][i].timestamp; | ||
688 | next = cpu; | ||
689 | } | ||
690 | } | ||
691 | if (next >= 0) { | ||
692 | i = indexes[next]++; | ||
693 | store->rows[count] = &store->cpu_list[next][i]; | ||
694 | store->cpu_list[next][i].pos = count++; | ||
695 | } | ||
696 | } while (next >= 0); | ||
697 | |||
698 | g_free(indexes); | ||
699 | } | ||
700 | |||
701 | /***************************************************************************** | ||
702 | * | ||
703 | * trace_view_store_new: This is what you use in your own code to create a | ||
704 | * new trace view store tree model for you to use. | ||
705 | * | ||
706 | *****************************************************************************/ | ||
707 | |||
708 | TraceViewStore * | ||
709 | trace_view_store_new (struct tracecmd_input *handle) | ||
710 | { | ||
711 | TraceViewStore *newstore; | ||
712 | struct record *data; | ||
713 | gint cpu, count, total=0; | ||
714 | struct temp { | ||
715 | guint64 offset; | ||
716 | guint64 ts; | ||
717 | struct temp *next; | ||
718 | } *list, **next, *rec; | ||
719 | |||
720 | newstore = (TraceViewStore*) g_object_new (TRACE_VIEW_STORE_TYPE, NULL); | ||
721 | |||
722 | g_assert( newstore != NULL ); | ||
723 | |||
724 | newstore->handle = handle; | ||
725 | newstore->cpus = tracecmd_cpus(handle); | ||
726 | |||
727 | newstore->cpu_list = g_new(TraceViewRecord *, newstore->cpus); | ||
728 | g_assert(newstore->cpu_list != NULL); | ||
729 | |||
730 | newstore->cpu_items = g_new(gint, newstore->cpus); | ||
731 | g_assert(newstore->cpu_items != NULL); | ||
732 | |||
733 | for (cpu = 0; cpu < newstore->cpus; cpu++) { | ||
734 | |||
735 | count = 0; | ||
736 | list = NULL; | ||
737 | next = &list; | ||
738 | |||
739 | do { | ||
740 | data = tracecmd_read_data(handle, cpu); | ||
741 | if (data) { | ||
742 | *next = rec = g_malloc(sizeof(*rec)); | ||
743 | g_assert(rec != NULL); | ||
744 | rec->offset = data->offset; | ||
745 | rec->ts = data->ts; | ||
746 | rec->next = NULL; | ||
747 | next = &rec->next; | ||
748 | free(data); | ||
749 | } | ||
750 | count++; | ||
751 | } while (data); | ||
752 | |||
753 | if (count) { | ||
754 | TraceViewRecord *trec; | ||
755 | struct temp *t; | ||
756 | gint i; | ||
757 | |||
758 | rec = list; | ||
759 | |||
760 | trec = g_new(TraceViewRecord, count); | ||
761 | for (i = 0; i < count; i++) { | ||
762 | g_assert(rec != NULL); | ||
763 | trec[i].cpu = cpu; | ||
764 | trec[i].timestamp = rec->ts; | ||
765 | trec[i].offset = rec->offset; | ||
766 | trec[i].visible = 1; | ||
767 | trec[i].pos = i; | ||
768 | t = rec; | ||
769 | rec = rec->next; | ||
770 | g_free(t); | ||
771 | } | ||
772 | g_assert(rec == NULL); | ||
773 | |||
774 | newstore->cpu_list[cpu] = trec; | ||
775 | } else | ||
776 | newstore->cpu_list[cpu] = NULL; | ||
777 | |||
778 | newstore->cpu_items[cpu] = count; | ||
779 | |||
780 | total += count; | ||
781 | } | ||
782 | |||
783 | newstore->num_rows = total; | ||
784 | newstore->rows = g_malloc(sizeof(*newstore->rows) * total); | ||
785 | |||
786 | merge_sort_rows_ts(newstore); | ||
787 | |||
788 | return newstore; | ||
789 | } | ||
790 | |||
791 | |||
792 | /***************************************************************************** | ||
793 | * | ||
794 | * trace_view_store_append_record: Empty lists are boring. This function can | ||
795 | * be used in your own code to add rows to the | ||
796 | * list. Note how we emit the "row-inserted" | ||
797 | * signal after we have appended the row | ||
798 | * internally, so the tree view and other | ||
799 | * interested objects know about the new row. | ||
800 | * | ||
801 | *****************************************************************************/ | ||
802 | |||
803 | #if 0 | ||
804 | void | ||
805 | trace_view_store_append_record (TraceViewStore *trace_view_store, | ||
806 | const gchar *name, | ||
807 | guint year_born) | ||
808 | { | ||
809 | GtkTreeIter iter; | ||
810 | GtkTreePath *path; | ||
811 | TraceViewRecord *newrecord; | ||
812 | gulong newsize; | ||
813 | guint pos; | ||
814 | |||
815 | g_return_if_fail (TRACE_VIEW_IS_LIST(trace_view_store)); | ||
816 | g_return_if_fail (name != NULL); | ||
817 | |||
818 | pos = trace_view_store->num_rows; | ||
819 | |||
820 | trace_view_store->num_rows++; | ||
821 | |||
822 | newsize = trace_view_store->num_rows * sizeof(TraceViewRecord*); | ||
823 | |||
824 | trace_view_store->rows = g_realloc(trace_view_store->rows, newsize); | ||
825 | |||
826 | newrecord = g_new0(TraceViewRecord, 1); | ||
827 | |||
828 | newrecord->name = g_strdup(name); | ||
829 | newrecord->name_collate_key = g_utf8_collate_key(name,-1); /* for fast sorting, used later */ | ||
830 | newrecord->year_born = year_born; | ||
831 | |||
832 | trace_view_store->rows[pos] = newrecord; | ||
833 | newrecord->pos = pos; | ||
834 | |||
835 | /* inform the tree view and other interested objects | ||
836 | * (e.g. tree row references) that we have inserted | ||
837 | * a new row, and where it was inserted */ | ||
838 | |||
839 | path = gtk_tree_path_new(); | ||
840 | gtk_tree_path_append_index(path, newrecord->pos); | ||
841 | |||
842 | trace_view_store_get_iter(GTK_TREE_MODEL(trace_view_store), &iter, path); | ||
843 | |||
844 | gtk_tree_model_row_inserted(GTK_TREE_MODEL(trace_view_store), path, &iter); | ||
845 | |||
846 | gtk_tree_path_free(path); | ||
847 | } | ||
848 | #endif | ||
diff --git a/trace-view-store.h b/trace-view-store.h new file mode 100644 index 0000000..73a38e7 --- /dev/null +++ b/trace-view-store.h | |||
@@ -0,0 +1,104 @@ | |||
1 | #ifndef _trace_view_store_h_included_ | ||
2 | #define _trace_view_store_h_included_ | ||
3 | |||
4 | #include <gtk/gtk.h> | ||
5 | #include "trace-cmd.h" | ||
6 | |||
7 | /* Some boilerplate GObject defines. 'klass' is used | ||
8 | * instead of 'class', because 'class' is a C++ keyword */ | ||
9 | |||
10 | #define TRACE_VIEW_STORE_TYPE (trace_view_store_get_type ()) | ||
11 | #define TRACE_VIEW_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACE_VIEW_STORE_TYPE, TraceViewStore)) | ||
12 | #define TRACE_VIEW_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TRACE_VIEW_STORE_TYPE, TraceViewStoreClass)) | ||
13 | #define TRACE_VIEW_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACE_VIEW_STORE_TYPE)) | ||
14 | #define TRACE_VIEW_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACE_VIEW_STORE_TYPE)) | ||
15 | #define TRACE_VIEW_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACE_VIEW_STORE_TYPE, TraceViewStoreClass)) | ||
16 | |||
17 | /* The data columns that we export via the tree model interface */ | ||
18 | |||
19 | enum | ||
20 | { | ||
21 | TRACE_VIEW_STORE_COL_CPU, | ||
22 | TRACE_VIEW_STORE_COL_TS, | ||
23 | TRACE_VIEW_STORE_COL_COMM, | ||
24 | TRACE_VIEW_STORE_COL_PID, | ||
25 | TRACE_VIEW_STORE_COL_LAT, | ||
26 | TRACE_VIEW_STORE_COL_EVENT, | ||
27 | TRACE_VIEW_STORE_COL_INFO, | ||
28 | TRACE_VIEW_STORE_N_COLUMNS, | ||
29 | } ; | ||
30 | |||
31 | |||
32 | typedef struct _TraceViewRecord TraceViewRecord; | ||
33 | typedef struct _TraceViewStore TraceViewStore; | ||
34 | typedef struct _TraceViewStoreClass TraceViewStoreClass; | ||
35 | |||
36 | |||
37 | |||
38 | /* TraceViewRecord: this structure represents a row */ | ||
39 | |||
40 | struct _TraceViewRecord | ||
41 | { | ||
42 | /* What we need from the record */ | ||
43 | guint64 timestamp; | ||
44 | guint64 offset; | ||
45 | gint cpu; | ||
46 | |||
47 | /* admin stuff used by the trace view store model */ | ||
48 | gint visible; | ||
49 | guint pos; /* pos within the array */ | ||
50 | }; | ||
51 | |||
52 | |||
53 | |||
54 | /* TraceViewStore: this structure contains everything we need for our | ||
55 | * model implementation. You can add extra fields to | ||
56 | * this structure, e.g. hashtables to quickly lookup | ||
57 | * rows or whatever else you might need, but it is | ||
58 | * crucial that 'parent' is the first member of the | ||
59 | * structure. */ | ||
60 | |||
61 | struct _TraceViewStore | ||
62 | { | ||
63 | GObject parent; /* this MUST be the first member */ | ||
64 | |||
65 | guint num_rows; /* number of rows that we have */ | ||
66 | TraceViewRecord **rows; /* a dynamically allocated array of pointers to | ||
67 | * the TraceViewRecord structure for each row */ | ||
68 | |||
69 | guint visible_column_mask; | ||
70 | gint n_columns; /* number of columns visible */ | ||
71 | |||
72 | GType column_types[TRACE_VIEW_STORE_N_COLUMNS]; | ||
73 | |||
74 | /* Tracecmd specific info */ | ||
75 | struct tracecmd_input *handle; | ||
76 | int cpus; | ||
77 | |||
78 | TraceViewRecord **cpu_list; | ||
79 | gint *cpu_items; | ||
80 | |||
81 | gint stamp; /* Random integer to check whether an iter belongs to our model */ | ||
82 | }; | ||
83 | |||
84 | |||
85 | |||
86 | /* TraceViewStoreClass: more boilerplate GObject stuff */ | ||
87 | |||
88 | struct _TraceViewStoreClass | ||
89 | { | ||
90 | GObjectClass parent_class; | ||
91 | }; | ||
92 | |||
93 | |||
94 | GType trace_view_store_get_type (void); | ||
95 | |||
96 | TraceViewStore *trace_view_store_new (struct tracecmd_input *handle); | ||
97 | |||
98 | #if 0 | ||
99 | void trace_view_store_append_record (TraceViewStore *trace_view_store, | ||
100 | const gchar *name, | ||
101 | guint year_born); | ||
102 | #endif | ||
103 | |||
104 | #endif /* _trace_view_store_h_included_ */ | ||