summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/comm.c
blob: 21b7ff382c3f0dfb2e0bff43074adae3c0b10973 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "comm.h"
#include "util.h"
#include <stdlib.h>
#include <stdio.h>
#include <linux/atomic.h>

struct comm_str {
	char *str;
	struct rb_node rb_node;
	atomic_t refcnt;
};

/* Should perhaps be moved to struct machine */
static struct rb_root comm_str_root;

static struct comm_str *comm_str__get(struct comm_str *cs)
{
	if (cs)
		atomic_inc(&cs->refcnt);
	return cs;
}

static void comm_str__put(struct comm_str *cs)
{
	if (cs && atomic_dec_and_test(&cs->refcnt)) {
		rb_erase(&cs->rb_node, &comm_str_root);
		zfree(&cs->str);
		free(cs);
	}
}

static struct comm_str *comm_str__alloc(const char *str)
{
	struct comm_str *cs;

	cs = zalloc(sizeof(*cs));
	if (!cs)
		return NULL;

	cs->str = strdup(str);
	if (!cs->str) {
		free(cs);
		return NULL;
	}

	atomic_set(&cs->refcnt, 0);

	return cs;
}

static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root)
{
	struct rb_node **p = &root->rb_node;
	struct rb_node *parent = NULL;
	struct comm_str *iter, *new;
	int cmp;

	while (*p != NULL) {
		parent = *p;
		iter = rb_entry(parent, struct comm_str, rb_node);

		cmp = strcmp(str, iter->str);
		if (!cmp)
			return iter;

		if (cmp < 0)
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}

	new = comm_str__alloc(str);
	if (!new)
		return NULL;

	rb_link_node(&new->rb_node, parent, p);
	rb_insert_color(&new->rb_node, root);

	return new;
}

struct comm *comm__new(const char *str, u64 timestamp, bool exec)
{
	struct comm *comm = zalloc(sizeof(*comm));

	if (!comm)
		return NULL;

	comm->start = timestamp;
	comm->exec = exec;

	comm->comm_str = comm_str__findnew(str, &comm_str_root);
	if (!comm->comm_str) {
		free(comm);
		return NULL;
	}

	comm_str__get(comm->comm_str);

	return comm;
}

int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec)
{
	struct comm_str *new, *old = comm->comm_str;

	new = comm_str__findnew(str, &comm_str_root);
	if (!new)
		return -ENOMEM;

	comm_str__get(new);
	comm_str__put(old);
	comm->comm_str = new;
	comm->start = timestamp;
	if (exec)
		comm->exec = true;

	return 0;
}

void comm__free(struct comm *comm)
{
	comm_str__put(comm->comm_str);
	free(comm);
}

const char *comm__str(const struct comm *comm)
{
	return comm->comm_str->str;
}