perf tools: Add template for generating rbtree resort class

Sometimes we want to sort an existing rbtree by a different key,
introduce a template for that, that needs only to be provided the
rbtree root and the number of entries in it.

To do that a new rbtree will be created with extra space for each entry,
where possibly pre-calculated keys will be stored to be used in the
resort process and also later, when using the newly sorted rbtree.

Please check the following two changesets to see it in use for resorting
stats for threads and its syscalls in 'perf trace --summary'.

Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Milian Wolff <milian.wolff@kdab.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-9l6e1q34lmf3wwdeewstyakg@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2016-05-05 11:44:28 -03:00
Родитель d2c1103440
Коммит f58c253564
1 изменённых файлов: 149 добавлений и 0 удалений

149
tools/perf/util/rb_resort.h Normal file
Просмотреть файл

@ -0,0 +1,149 @@
#ifndef _PERF_RESORT_RB_H_
#define _PERF_RESORT_RB_H_
/*
* Template for creating a class to resort an existing rb_tree according to
* a new sort criteria, that must be present in the entries of the source
* rb_tree.
*
* (c) 2016 Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Quick example, resorting threads by its shortname:
*
* First define the prefix (threads) to be used for the functions and data
* structures created, and provide an expression for the sorting, then the
* fields to be present in each of the entries in the new, sorted, rb_tree.
*
* The body of the init function should collect the fields, maybe
* pre-calculating them from multiple entries in the original 'entry' from
* the rb_tree used as a source for the entries to be sorted:
DEFINE_RB_RESORT_RB(threads, strcmp(a->thread->shortname,
b->thread->shortname) < 0,
struct thread *thread;
)
{
entry->thread = rb_entry(nd, struct thread, rb_node);
}
* After this it is just a matter of instantiating it and iterating it,
* for a few data structures with existing rb_trees, such as 'struct machine',
* helpers are available to get the rb_root and the nr_entries:
DECLARE_RESORT_RB_MACHINE_THREADS(threads, machine_ptr);
* This will instantiate the new rb_tree and a cursor for it, that can be used as:
struct rb_node *nd;
resort_rb__for_each(nd, threads) {
struct thread *t = threads_entry;
printf("%s: %d\n", t->shortname, t->tid);
}
* Then delete it:
resort_rb__delete(threads);
* The name of the data structures and functions will have a _sorted suffix
* right before the method names, i.e. will look like:
*
* struct threads_sorted_entry {}
* threads_sorted__insert()
*/
#define DEFINE_RESORT_RB(__name, __comp, ...) \
struct __name##_sorted_entry { \
struct rb_node rb_node; \
__VA_ARGS__ \
}; \
static void __name##_sorted__init_entry(struct rb_node *nd, \
struct __name##_sorted_entry *entry); \
\
static int __name##_sorted__cmp(struct rb_node *nda, struct rb_node *ndb) \
{ \
struct __name##_sorted_entry *a, *b; \
a = rb_entry(nda, struct __name##_sorted_entry, rb_node); \
b = rb_entry(ndb, struct __name##_sorted_entry, rb_node); \
return __comp; \
} \
\
struct __name##_sorted { \
struct rb_root entries; \
struct __name##_sorted_entry nd[0]; \
}; \
\
static void __name##_sorted__insert(struct __name##_sorted *sorted, \
struct rb_node *sorted_nd) \
{ \
struct rb_node **p = &sorted->entries.rb_node, *parent = NULL; \
while (*p != NULL) { \
parent = *p; \
if (__name##_sorted__cmp(sorted_nd, parent)) \
p = &(*p)->rb_left; \
else \
p = &(*p)->rb_right; \
} \
rb_link_node(sorted_nd, parent, p); \
rb_insert_color(sorted_nd, &sorted->entries); \
} \
\
static void __name##_sorted__sort(struct __name##_sorted *sorted, \
struct rb_root *entries) \
{ \
struct rb_node *nd; \
unsigned int i = 0; \
for (nd = rb_first(entries); nd; nd = rb_next(nd)) { \
struct __name##_sorted_entry *snd = &sorted->nd[i++]; \
__name##_sorted__init_entry(nd, snd); \
__name##_sorted__insert(sorted, &snd->rb_node); \
} \
} \
\
static struct __name##_sorted *__name##_sorted__new(struct rb_root *entries, \
int nr_entries) \
{ \
struct __name##_sorted *sorted; \
sorted = malloc(sizeof(*sorted) + sizeof(sorted->nd[0]) * nr_entries); \
if (sorted) { \
sorted->entries = RB_ROOT; \
__name##_sorted__sort(sorted, entries); \
} \
return sorted; \
} \
\
static void __name##_sorted__delete(struct __name##_sorted *sorted) \
{ \
free(sorted); \
} \
\
static void __name##_sorted__init_entry(struct rb_node *nd, \
struct __name##_sorted_entry *entry)
#define DECLARE_RESORT_RB(__name) \
struct __name##_sorted_entry *__name##_entry; \
struct __name##_sorted *__name = __name##_sorted__new
#define resort_rb__for_each(__nd, __name) \
for (__nd = rb_first(&__name->entries); \
__name##_entry = rb_entry(__nd, struct __name##_sorted_entry, \
rb_node), __nd; \
__nd = rb_next(__nd))
#define resort_rb__delete(__name) \
__name##_sorted__delete(__name), __name = NULL
/*
* Helpers for other classes that contains both an rbtree and the
* number of entries in it:
*/
/* For 'struct intlist' */
#define DECLARE_RESORT_RB_INTLIST(__name, __ilist) \
DECLARE_RESORT_RB(__name)(&__ilist->rblist.entries, \
__ilist->rblist.nr_entries)
/* For 'struct machine->threads' */
#define DECLARE_RESORT_RB_MACHINE_THREADS(__name, __machine) \
DECLARE_RESORT_RB(__name)(&__machine->threads, __machine->nr_threads)
#endif /* _PERF_RESORT_RB_H_ */