| 1 | |
| 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 = calloc(values->threads_max, sizeof(*values->pid));
|
| 17 | values->tid = calloc(values->threads_max, sizeof(*values->tid));
|
| 18 | values->value = calloc(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] = calloc(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;
|
| 240 | pidwidth = 3;
|
| 241 | namewidth = 4;
|
| 242 | rawwidth = 3;
|
| 243 | countwidth = 5;
|
| 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 | }
|