![Davidlohr Bueso](/assets/img/avatar_default.png)
At the cost of an extra pointer, we can avoid the O(logN) cost of finding the first element in the tree (smallest node), which is something required for any of the strlist or intlist traversals (XXX_for_each_entry()). There are a number of users in perf of these (particularly strlists), including probes, and buildid. Signed-off-by: Davidlohr Bueso <dbueso@suse.de> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Namhyung Kim <namhyung@kernel.org> Link: http://lkml.kernel.org/r/20181206191819.30182-5-dave@stgolabs.net Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
152 lines
5 KiB
C
152 lines
5 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#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_entry(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_entry(__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.rb_root, \
|
|
__ilist->rblist.nr_entries)
|
|
|
|
/* For 'struct machine->threads' */
|
|
#define DECLARE_RESORT_RB_MACHINE_THREADS(__name, __machine, hash_bucket) \
|
|
DECLARE_RESORT_RB(__name)(&__machine->threads[hash_bucket].entries.rb_root, \
|
|
__machine->threads[hash_bucket].nr)
|
|
|
|
#endif /* _PERF_RESORT_RB_H_ */
|