xref: /linux/tools/perf/util/values.c (revision 7685b334d1e4927cc73b62c65293ba65748d9c52)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <inttypes.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <linux/zalloc.h>
8 
9 #include "values.h"
10 #include "debug.h"
11 #include "evsel.h"
12 
13 int perf_read_values_init(struct perf_read_values *values)
14 {
15 	values->threads_max = 16;
16 	values->pid = malloc(values->threads_max * sizeof(*values->pid));
17 	values->tid = malloc(values->threads_max * sizeof(*values->tid));
18 	values->value = zalloc(values->threads_max * sizeof(*values->value));
19 	if (!values->pid || !values->tid || !values->value) {
20 		pr_debug("failed to allocate read_values threads arrays");
21 		goto out_free_pid;
22 	}
23 	values->threads = 0;
24 
25 	values->counters_max = 16;
26 	values->counters = malloc(values->counters_max * sizeof(*values->counters));
27 	if (!values->counters) {
28 		pr_debug("failed to allocate read_values counters array");
29 		goto out_free_counter;
30 	}
31 	values->num_counters = 0;
32 
33 	return 0;
34 
35 out_free_counter:
36 	zfree(&values->counters);
37 out_free_pid:
38 	zfree(&values->pid);
39 	zfree(&values->tid);
40 	zfree(&values->value);
41 	return -ENOMEM;
42 }
43 
44 void perf_read_values_destroy(struct perf_read_values *values)
45 {
46 	int i;
47 
48 	if (!values->threads_max || !values->counters_max)
49 		return;
50 
51 	for (i = 0; i < values->threads; i++)
52 		zfree(&values->value[i]);
53 	zfree(&values->value);
54 	zfree(&values->pid);
55 	zfree(&values->tid);
56 	zfree(&values->counters);
57 }
58 
59 static int perf_read_values__enlarge_threads(struct perf_read_values *values)
60 {
61 	int nthreads_max = values->threads_max * 2;
62 	void *npid = realloc(values->pid, nthreads_max * sizeof(*values->pid)),
63 	     *ntid = realloc(values->tid, nthreads_max * sizeof(*values->tid)),
64 	     *nvalue = realloc(values->value, nthreads_max * sizeof(*values->value));
65 
66 	if (!npid || !ntid || !nvalue)
67 		goto out_err;
68 
69 	values->threads_max = nthreads_max;
70 	values->pid = npid;
71 	values->tid = ntid;
72 	values->value = nvalue;
73 	return 0;
74 out_err:
75 	free(npid);
76 	free(ntid);
77 	free(nvalue);
78 	pr_debug("failed to enlarge read_values threads arrays");
79 	return -ENOMEM;
80 }
81 
82 static int perf_read_values__findnew_thread(struct perf_read_values *values,
83 					    u32 pid, u32 tid)
84 {
85 	int i;
86 
87 	for (i = 0; i < values->threads; i++)
88 		if (values->pid[i] == pid && values->tid[i] == tid)
89 			return i;
90 
91 	if (values->threads == values->threads_max) {
92 		i = perf_read_values__enlarge_threads(values);
93 		if (i < 0)
94 			return i;
95 	}
96 
97 	i = values->threads;
98 
99 	values->value[i] = zalloc(values->counters_max * sizeof(**values->value));
100 	if (!values->value[i]) {
101 		pr_debug("failed to allocate read_values counters array");
102 		return -ENOMEM;
103 	}
104 	values->pid[i] = pid;
105 	values->tid[i] = tid;
106 	values->threads = i + 1;
107 
108 	return i;
109 }
110 
111 static int perf_read_values__enlarge_counters(struct perf_read_values *values)
112 {
113 	int counters_max = values->counters_max * 2;
114 	struct evsel **new_counters = realloc(values->counters,
115 					     counters_max * sizeof(*values->counters));
116 
117 	if (!new_counters) {
118 		pr_debug("failed to enlarge read_values counters array");
119 		goto out_enomem;
120 	}
121 
122 	for (int i = 0; i < values->threads; i++) {
123 		u64 *value = realloc(values->value[i], counters_max * sizeof(**values->value));
124 
125 		if (!value) {
126 			pr_debug("failed to enlarge read_values ->values array");
127 			goto out_free_counters;
128 		}
129 
130 		for (int j = values->counters_max; j < counters_max; j++)
131 			value[j] = 0;
132 
133 		values->value[i] = value;
134 	}
135 
136 	values->counters_max = counters_max;
137 	values->counters = new_counters;
138 
139 	return 0;
140 out_free_counters:
141 	free(new_counters);
142 out_enomem:
143 	return -ENOMEM;
144 }
145 
146 static int perf_read_values__findnew_counter(struct perf_read_values *values,
147 					     struct evsel *evsel)
148 {
149 	int i;
150 
151 	for (i = 0; i < values->num_counters; i++)
152 		if (values->counters[i] == evsel)
153 			return i;
154 
155 	if (values->num_counters == values->counters_max) {
156 		int err = perf_read_values__enlarge_counters(values);
157 
158 		if (err)
159 			return err;
160 	}
161 
162 	i = values->num_counters++;
163 	values->counters[i] = evsel;
164 
165 	return i;
166 }
167 
168 int perf_read_values_add_value(struct perf_read_values *values,
169 				u32 pid, u32 tid,
170 				struct evsel *evsel, u64 value)
171 {
172 	int tindex, cindex;
173 
174 	tindex = perf_read_values__findnew_thread(values, pid, tid);
175 	if (tindex < 0)
176 		return tindex;
177 	cindex = perf_read_values__findnew_counter(values, evsel);
178 	if (cindex < 0)
179 		return cindex;
180 
181 	values->value[tindex][cindex] += value;
182 	return 0;
183 }
184 
185 static void perf_read_values__display_pretty(FILE *fp,
186 					     struct perf_read_values *values)
187 {
188 	int i, j;
189 	int pidwidth, tidwidth;
190 	int *counterwidth;
191 
192 	counterwidth = malloc(values->num_counters * sizeof(*counterwidth));
193 	if (!counterwidth) {
194 		fprintf(fp, "INTERNAL ERROR: Failed to allocate counterwidth array\n");
195 		return;
196 	}
197 	tidwidth = 3;
198 	pidwidth = 3;
199 	for (j = 0; j < values->num_counters; j++)
200 		counterwidth[j] = strlen(evsel__name(values->counters[j]));
201 	for (i = 0; i < values->threads; i++) {
202 		int width;
203 
204 		width = snprintf(NULL, 0, "%d", values->pid[i]);
205 		if (width > pidwidth)
206 			pidwidth = width;
207 		width = snprintf(NULL, 0, "%d", values->tid[i]);
208 		if (width > tidwidth)
209 			tidwidth = width;
210 		for (j = 0; j < values->num_counters; j++) {
211 			width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]);
212 			if (width > counterwidth[j])
213 				counterwidth[j] = width;
214 		}
215 	}
216 
217 	fprintf(fp, "# %*s  %*s", pidwidth, "PID", tidwidth, "TID");
218 	for (j = 0; j < values->num_counters; j++)
219 		fprintf(fp, "  %*s", counterwidth[j], evsel__name(values->counters[j]));
220 	fprintf(fp, "\n");
221 
222 	for (i = 0; i < values->threads; i++) {
223 		fprintf(fp, "  %*d  %*d", pidwidth, values->pid[i],
224 			tidwidth, values->tid[i]);
225 		for (j = 0; j < values->num_counters; j++)
226 			fprintf(fp, "  %*" PRIu64,
227 				counterwidth[j], values->value[i][j]);
228 		fprintf(fp, "\n");
229 	}
230 	free(counterwidth);
231 }
232 
233 static void perf_read_values__display_raw(FILE *fp,
234 					  struct perf_read_values *values)
235 {
236 	int width, pidwidth, tidwidth, namewidth, rawwidth, countwidth;
237 	int i, j;
238 
239 	tidwidth = 3; /* TID */
240 	pidwidth = 3; /* PID */
241 	namewidth = 4; /* "Name" */
242 	rawwidth = 3; /* "Raw" */
243 	countwidth = 5; /* "Count" */
244 
245 	for (i = 0; i < values->threads; i++) {
246 		width = snprintf(NULL, 0, "%d", values->pid[i]);
247 		if (width > pidwidth)
248 			pidwidth = width;
249 		width = snprintf(NULL, 0, "%d", values->tid[i]);
250 		if (width > tidwidth)
251 			tidwidth = width;
252 	}
253 	for (j = 0; j < values->num_counters; j++) {
254 		width = strlen(evsel__name(values->counters[j]));
255 		if (width > namewidth)
256 			namewidth = width;
257 		width = snprintf(NULL, 0, "%x", values->counters[j]->core.idx);
258 		if (width > rawwidth)
259 			rawwidth = width;
260 	}
261 	for (i = 0; i < values->threads; i++) {
262 		for (j = 0; j < values->num_counters; j++) {
263 			width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]);
264 			if (width > countwidth)
265 				countwidth = width;
266 		}
267 	}
268 
269 	fprintf(fp, "# %*s  %*s  %*s  %*s  %*s\n",
270 		pidwidth, "PID", tidwidth, "TID",
271 		namewidth, "Name", rawwidth, "Raw",
272 		countwidth, "Count");
273 	for (i = 0; i < values->threads; i++)
274 		for (j = 0; j < values->num_counters; j++)
275 			fprintf(fp, "  %*d  %*d  %*s  %*x  %*" PRIu64,
276 				pidwidth, values->pid[i],
277 				tidwidth, values->tid[i],
278 				namewidth, evsel__name(values->counters[j]),
279 				rawwidth, values->counters[j]->core.idx,
280 				countwidth, values->value[i][j]);
281 }
282 
283 void perf_read_values_display(FILE *fp, struct perf_read_values *values, int raw)
284 {
285 	if (raw)
286 		perf_read_values__display_raw(fp, values);
287 	else
288 		perf_read_values__display_pretty(fp, values);
289 }
290