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
perf_read_values_init(struct perf_read_values * values)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
perf_read_values_destroy(struct perf_read_values * values)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
perf_read_values__enlarge_threads(struct perf_read_values * values)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
perf_read_values__findnew_thread(struct perf_read_values * values,u32 pid,u32 tid)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
perf_read_values__enlarge_counters(struct perf_read_values * values)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
perf_read_values__findnew_counter(struct perf_read_values * values,struct evsel * evsel)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
perf_read_values_add_value(struct perf_read_values * values,u32 pid,u32 tid,struct evsel * evsel,u64 value)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
perf_read_values__display_pretty(FILE * fp,struct perf_read_values * values)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
perf_read_values__display_raw(FILE * fp,struct perf_read_values * values)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
perf_read_values_display(FILE * fp,struct perf_read_values * values,int raw)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