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