18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * call-path.h: Manipulate a tree data structure containing function call paths
48c2ecf20Sopenharmony_ci * Copyright (c) 2014, Intel Corporation.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/rbtree.h>
88c2ecf20Sopenharmony_ci#include <linux/list.h>
98c2ecf20Sopenharmony_ci#include <linux/zalloc.h>
108c2ecf20Sopenharmony_ci#include <stdlib.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "call-path.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic void call_path__init(struct call_path *cp, struct call_path *parent,
158c2ecf20Sopenharmony_ci			    struct symbol *sym, u64 ip, bool in_kernel)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	cp->parent = parent;
188c2ecf20Sopenharmony_ci	cp->sym = sym;
198c2ecf20Sopenharmony_ci	cp->ip = sym ? 0 : ip;
208c2ecf20Sopenharmony_ci	cp->db_id = 0;
218c2ecf20Sopenharmony_ci	cp->in_kernel = in_kernel;
228c2ecf20Sopenharmony_ci	RB_CLEAR_NODE(&cp->rb_node);
238c2ecf20Sopenharmony_ci	cp->children = RB_ROOT;
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct call_path_root *call_path_root__new(void)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct call_path_root *cpr;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	cpr = zalloc(sizeof(struct call_path_root));
318c2ecf20Sopenharmony_ci	if (!cpr)
328c2ecf20Sopenharmony_ci		return NULL;
338c2ecf20Sopenharmony_ci	call_path__init(&cpr->call_path, NULL, NULL, 0, false);
348c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&cpr->blocks);
358c2ecf20Sopenharmony_ci	return cpr;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_civoid call_path_root__free(struct call_path_root *cpr)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct call_path_block *pos, *n;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
438c2ecf20Sopenharmony_ci		list_del_init(&pos->node);
448c2ecf20Sopenharmony_ci		free(pos);
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci	free(cpr);
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic struct call_path *call_path__new(struct call_path_root *cpr,
508c2ecf20Sopenharmony_ci					struct call_path *parent,
518c2ecf20Sopenharmony_ci					struct symbol *sym, u64 ip,
528c2ecf20Sopenharmony_ci					bool in_kernel)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct call_path_block *cpb;
558c2ecf20Sopenharmony_ci	struct call_path *cp;
568c2ecf20Sopenharmony_ci	size_t n;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (cpr->next < cpr->sz) {
598c2ecf20Sopenharmony_ci		cpb = list_last_entry(&cpr->blocks, struct call_path_block,
608c2ecf20Sopenharmony_ci				      node);
618c2ecf20Sopenharmony_ci	} else {
628c2ecf20Sopenharmony_ci		cpb = zalloc(sizeof(struct call_path_block));
638c2ecf20Sopenharmony_ci		if (!cpb)
648c2ecf20Sopenharmony_ci			return NULL;
658c2ecf20Sopenharmony_ci		list_add_tail(&cpb->node, &cpr->blocks);
668c2ecf20Sopenharmony_ci		cpr->sz += CALL_PATH_BLOCK_SIZE;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	n = cpr->next++ & CALL_PATH_BLOCK_MASK;
708c2ecf20Sopenharmony_ci	cp = &cpb->cp[n];
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	call_path__init(cp, parent, sym, ip, in_kernel);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return cp;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistruct call_path *call_path__findnew(struct call_path_root *cpr,
788c2ecf20Sopenharmony_ci				     struct call_path *parent,
798c2ecf20Sopenharmony_ci				     struct symbol *sym, u64 ip, u64 ks)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct rb_node **p;
828c2ecf20Sopenharmony_ci	struct rb_node *node_parent = NULL;
838c2ecf20Sopenharmony_ci	struct call_path *cp;
848c2ecf20Sopenharmony_ci	bool in_kernel = ip >= ks;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (sym)
878c2ecf20Sopenharmony_ci		ip = 0;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (!parent)
908c2ecf20Sopenharmony_ci		return call_path__new(cpr, parent, sym, ip, in_kernel);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	p = &parent->children.rb_node;
938c2ecf20Sopenharmony_ci	while (*p != NULL) {
948c2ecf20Sopenharmony_ci		node_parent = *p;
958c2ecf20Sopenharmony_ci		cp = rb_entry(node_parent, struct call_path, rb_node);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		if (cp->sym == sym && cp->ip == ip)
988c2ecf20Sopenharmony_ci			return cp;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
1018c2ecf20Sopenharmony_ci			p = &(*p)->rb_left;
1028c2ecf20Sopenharmony_ci		else
1038c2ecf20Sopenharmony_ci			p = &(*p)->rb_right;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	cp = call_path__new(cpr, parent, sym, ip, in_kernel);
1078c2ecf20Sopenharmony_ci	if (!cp)
1088c2ecf20Sopenharmony_ci		return NULL;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	rb_link_node(&cp->rb_node, node_parent, p);
1118c2ecf20Sopenharmony_ci	rb_insert_color(&cp->rb_node, &parent->children);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return cp;
1148c2ecf20Sopenharmony_ci}
115