1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 #include "drm_pmu.h"
3 #include "counts.h"
4 #include "cpumap.h"
5 #include "debug.h"
6 #include "evsel.h"
7 #include "pmu.h"
8 #include <perf/threadmap.h>
9 #include <api/fs/fs.h>
10 #include <api/io.h>
11 #include <ctype.h>
12 #include <dirent.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <linux/unistd.h>
16 #include <linux/kcmp.h>
17 #include <linux/zalloc.h>
18 #include <sys/stat.h>
19 #include <sys/syscall.h>
20 #include <sys/sysmacros.h>
21 #include <sys/types.h>
22
23 enum drm_pmu_unit {
24 DRM_PMU_UNIT_BYTES,
25 DRM_PMU_UNIT_CAPACITY,
26 DRM_PMU_UNIT_CYCLES,
27 DRM_PMU_UNIT_HZ,
28 DRM_PMU_UNIT_NS,
29
30 DRM_PMU_UNIT_MAX,
31 };
32
33 struct drm_pmu_event {
34 const char *name;
35 const char *desc;
36 enum drm_pmu_unit unit;
37 };
38
39 struct drm_pmu {
40 struct perf_pmu pmu;
41 struct drm_pmu_event *events;
42 int num_events;
43 };
44
45 static const char * const drm_pmu_unit_strs[DRM_PMU_UNIT_MAX] = {
46 "bytes",
47 "capacity",
48 "cycles",
49 "hz",
50 "ns",
51 };
52
53 static const char * const drm_pmu_scale_unit_strs[DRM_PMU_UNIT_MAX] = {
54 "1bytes",
55 "1capacity",
56 "1cycles",
57 "1hz",
58 "1ns",
59 };
60
perf_pmu__is_drm(const struct perf_pmu * pmu)61 bool perf_pmu__is_drm(const struct perf_pmu *pmu)
62 {
63 return pmu && pmu->type >= PERF_PMU_TYPE_DRM_START &&
64 pmu->type <= PERF_PMU_TYPE_DRM_END;
65 }
66
evsel__is_drm(const struct evsel * evsel)67 bool evsel__is_drm(const struct evsel *evsel)
68 {
69 return perf_pmu__is_drm(evsel->pmu);
70 }
71
add_drm_pmu(struct list_head * pmus,char * line,size_t line_len)72 static struct drm_pmu *add_drm_pmu(struct list_head *pmus, char *line, size_t line_len)
73 {
74 struct drm_pmu *drm;
75 struct perf_pmu *pmu;
76 const char *name;
77 __u32 max_drm_pmu_type = 0, type;
78 int i = 12;
79
80 if (line[line_len - 1] == '\n')
81 line[line_len - 1] = '\0';
82 while (isspace(line[i]))
83 i++;
84
85 line[--i] = '_';
86 line[--i] = 'm';
87 line[--i] = 'r';
88 line[--i] = 'd';
89 name = &line[i];
90
91 list_for_each_entry(pmu, pmus, list) {
92 if (!perf_pmu__is_drm(pmu))
93 continue;
94 if (pmu->type > max_drm_pmu_type)
95 max_drm_pmu_type = pmu->type;
96 if (!strcmp(pmu->name, name)) {
97 /* PMU already exists. */
98 return NULL;
99 }
100 }
101
102 if (max_drm_pmu_type != 0)
103 type = max_drm_pmu_type + 1;
104 else
105 type = PERF_PMU_TYPE_DRM_START;
106
107 if (type > PERF_PMU_TYPE_DRM_END) {
108 zfree(&drm);
109 pr_err("Unable to encode DRM PMU type for %s\n", name);
110 return NULL;
111 }
112
113 drm = zalloc(sizeof(*drm));
114 if (!drm)
115 return NULL;
116
117 if (perf_pmu__init(&drm->pmu, type, name) != 0) {
118 perf_pmu__delete(&drm->pmu);
119 return NULL;
120 }
121
122 drm->pmu.cpus = perf_cpu_map__new("0");
123 if (!drm->pmu.cpus) {
124 perf_pmu__delete(&drm->pmu);
125 return NULL;
126 }
127 return drm;
128 }
129
130
starts_with(const char * str,const char * prefix)131 static bool starts_with(const char *str, const char *prefix)
132 {
133 return !strncmp(prefix, str, strlen(prefix));
134 }
135
add_event(struct drm_pmu_event ** events,int * num_events,const char * line,enum drm_pmu_unit unit,const char * desc)136 static int add_event(struct drm_pmu_event **events, int *num_events,
137 const char *line, enum drm_pmu_unit unit, const char *desc)
138 {
139 const char *colon = strchr(line, ':');
140 struct drm_pmu_event *tmp;
141
142 if (!colon)
143 return -EINVAL;
144
145 tmp = reallocarray(*events, *num_events + 1, sizeof(struct drm_pmu_event));
146 if (!tmp)
147 return -ENOMEM;
148 tmp[*num_events].unit = unit;
149 tmp[*num_events].desc = desc;
150 tmp[*num_events].name = strndup(line, colon - line);
151 if (!tmp[*num_events].name)
152 return -ENOMEM;
153 (*num_events)++;
154 *events = tmp;
155 return 0;
156 }
157
read_drm_pmus_cb(void * args,int fdinfo_dir_fd,const char * fd_name)158 static int read_drm_pmus_cb(void *args, int fdinfo_dir_fd, const char *fd_name)
159 {
160 struct list_head *pmus = args;
161 char buf[640];
162 struct io io;
163 char *line = NULL;
164 size_t line_len;
165 struct drm_pmu *drm = NULL;
166 struct drm_pmu_event *events = NULL;
167 int num_events = 0;
168
169 io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf));
170 if (io.fd == -1) {
171 /* Failed to open file, ignore. */
172 return 0;
173 }
174
175 while (io__getline(&io, &line, &line_len) > 0) {
176 if (starts_with(line, "drm-driver:")) {
177 drm = add_drm_pmu(pmus, line, line_len);
178 if (!drm)
179 break;
180 continue;
181 }
182 /*
183 * Note the string matching below is alphabetical, with more
184 * specific matches appearing before less specific.
185 */
186 if (starts_with(line, "drm-active-")) {
187 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES,
188 "Total memory active in one or more engines");
189 continue;
190 }
191 if (starts_with(line, "drm-cycles-")) {
192 add_event(&events, &num_events, line, DRM_PMU_UNIT_CYCLES,
193 "Busy cycles");
194 continue;
195 }
196 if (starts_with(line, "drm-engine-capacity-")) {
197 add_event(&events, &num_events, line, DRM_PMU_UNIT_CAPACITY,
198 "Engine capacity");
199 continue;
200 }
201 if (starts_with(line, "drm-engine-")) {
202 add_event(&events, &num_events, line, DRM_PMU_UNIT_NS,
203 "Utilization in ns");
204 continue;
205 }
206 if (starts_with(line, "drm-maxfreq-")) {
207 add_event(&events, &num_events, line, DRM_PMU_UNIT_HZ,
208 "Maximum frequency");
209 continue;
210 }
211 if (starts_with(line, "drm-purgeable-")) {
212 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES,
213 "Size of resident and purgeable memory buffers");
214 continue;
215 }
216 if (starts_with(line, "drm-resident-")) {
217 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES,
218 "Size of resident memory buffers");
219 continue;
220 }
221 if (starts_with(line, "drm-shared-")) {
222 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES,
223 "Size of shared memory buffers");
224 continue;
225 }
226 if (starts_with(line, "drm-total-cycles-")) {
227 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES,
228 "Total busy cycles");
229 continue;
230 }
231 if (starts_with(line, "drm-total-")) {
232 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES,
233 "Size of shared and private memory");
234 continue;
235 }
236 if (verbose > 1 && starts_with(line, "drm-") &&
237 !starts_with(line, "drm-client-id:") &&
238 !starts_with(line, "drm-pdev:"))
239 pr_debug("Unhandled DRM PMU fdinfo line match '%s'\n", line);
240 }
241 if (drm) {
242 drm->events = events;
243 drm->num_events = num_events;
244 list_add_tail(&drm->pmu.list, pmus);
245 }
246 free(line);
247 if (io.fd != -1)
248 close(io.fd);
249 return 0;
250 }
251
drm_pmu__exit(struct perf_pmu * pmu)252 void drm_pmu__exit(struct perf_pmu *pmu)
253 {
254 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu);
255
256 free(drm->events);
257 }
258
drm_pmu__have_event(const struct perf_pmu * pmu,const char * name)259 bool drm_pmu__have_event(const struct perf_pmu *pmu, const char *name)
260 {
261 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu);
262
263 if (!starts_with(name, "drm-"))
264 return false;
265
266 for (int i = 0; i < drm->num_events; i++) {
267 if (!strcasecmp(drm->events[i].name, name))
268 return true;
269 }
270 return false;
271 }
272
drm_pmu__for_each_event(const struct perf_pmu * pmu,void * state,pmu_event_callback cb)273 int drm_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb)
274 {
275 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu);
276
277 for (int i = 0; i < drm->num_events; i++) {
278 char encoding_buf[128];
279 struct pmu_event_info info = {
280 .pmu = pmu,
281 .name = drm->events[i].name,
282 .alias = NULL,
283 .scale_unit = drm_pmu_scale_unit_strs[drm->events[i].unit],
284 .desc = drm->events[i].desc,
285 .long_desc = NULL,
286 .encoding_desc = encoding_buf,
287 .topic = "drm",
288 .pmu_name = pmu->name,
289 .event_type_desc = "DRM event",
290 };
291 int ret;
292
293 snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=0x%x/", pmu->name, i);
294
295 ret = cb(state, &info);
296 if (ret)
297 return ret;
298 }
299 return 0;
300 }
301
drm_pmu__num_events(const struct perf_pmu * pmu)302 size_t drm_pmu__num_events(const struct perf_pmu *pmu)
303 {
304 const struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu);
305
306 return drm->num_events;
307 }
308
drm_pmu__index_for_event(const struct drm_pmu * drm,const char * name)309 static int drm_pmu__index_for_event(const struct drm_pmu *drm, const char *name)
310 {
311 for (int i = 0; i < drm->num_events; i++) {
312 if (!strcmp(drm->events[i].name, name))
313 return i;
314 }
315 return -1;
316 }
317
drm_pmu__config_term(const struct drm_pmu * drm,struct perf_event_attr * attr,struct parse_events_term * term,struct parse_events_error * err)318 static int drm_pmu__config_term(const struct drm_pmu *drm,
319 struct perf_event_attr *attr,
320 struct parse_events_term *term,
321 struct parse_events_error *err)
322 {
323 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
324 int i = drm_pmu__index_for_event(drm, term->config);
325
326 if (i >= 0) {
327 attr->config = i;
328 return 0;
329 }
330 }
331 if (err) {
332 char *err_str;
333
334 parse_events_error__handle(err, term->err_val,
335 asprintf(&err_str,
336 "unexpected drm event term (%s) %s",
337 parse_events__term_type_str(term->type_term),
338 term->config) < 0
339 ? strdup("unexpected drm event term")
340 : err_str,
341 NULL);
342 }
343 return -EINVAL;
344 }
345
drm_pmu__config_terms(const struct perf_pmu * pmu,struct perf_event_attr * attr,struct parse_events_terms * terms,struct parse_events_error * err)346 int drm_pmu__config_terms(const struct perf_pmu *pmu,
347 struct perf_event_attr *attr,
348 struct parse_events_terms *terms,
349 struct parse_events_error *err)
350 {
351 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu);
352 struct parse_events_term *term;
353
354 list_for_each_entry(term, &terms->terms, list) {
355 if (drm_pmu__config_term(drm, attr, term, err))
356 return -EINVAL;
357 }
358
359 return 0;
360 }
361
drm_pmu__check_alias(const struct perf_pmu * pmu,struct parse_events_terms * terms,struct perf_pmu_info * info,struct parse_events_error * err)362 int drm_pmu__check_alias(const struct perf_pmu *pmu, struct parse_events_terms *terms,
363 struct perf_pmu_info *info, struct parse_events_error *err)
364 {
365 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu);
366 struct parse_events_term *term =
367 list_first_entry(&terms->terms, struct parse_events_term, list);
368
369 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
370 int i = drm_pmu__index_for_event(drm, term->config);
371
372 if (i >= 0) {
373 info->unit = drm_pmu_unit_strs[drm->events[i].unit];
374 info->scale = 1;
375 return 0;
376 }
377 }
378 if (err) {
379 char *err_str;
380
381 parse_events_error__handle(err, term->err_val,
382 asprintf(&err_str,
383 "unexpected drm event term (%s) %s",
384 parse_events__term_type_str(term->type_term),
385 term->config) < 0
386 ? strdup("unexpected drm event term")
387 : err_str,
388 NULL);
389 }
390 return -EINVAL;
391 }
392
393 struct minor_info {
394 unsigned int *minors;
395 int minors_num, minors_len;
396 };
397
for_each_drm_fdinfo_in_dir(int (* cb)(void * args,int fdinfo_dir_fd,const char * fd_name),void * args,int proc_dir,const char * pid_name,struct minor_info * minors)398 static int for_each_drm_fdinfo_in_dir(int (*cb)(void *args, int fdinfo_dir_fd, const char *fd_name),
399 void *args, int proc_dir, const char *pid_name,
400 struct minor_info *minors)
401 {
402 char buf[256];
403 DIR *fd_dir;
404 struct dirent *fd_entry;
405 int fd_dir_fd, fdinfo_dir_fd = -1;
406
407
408 scnprintf(buf, sizeof(buf), "%s/fd", pid_name);
409 fd_dir_fd = openat(proc_dir, buf, O_DIRECTORY);
410 if (fd_dir_fd == -1)
411 return 0; /* Presumably lost race to open. */
412 fd_dir = fdopendir(fd_dir_fd);
413 if (!fd_dir) {
414 close(fd_dir_fd);
415 return -ENOMEM;
416 }
417 while ((fd_entry = readdir(fd_dir)) != NULL) {
418 struct stat stat;
419 unsigned int minor;
420 bool is_dup = false;
421 int ret;
422
423 if (fd_entry->d_type != DT_LNK)
424 continue;
425
426 if (fstatat(fd_dir_fd, fd_entry->d_name, &stat, 0) != 0)
427 continue;
428
429 if ((stat.st_mode & S_IFMT) != S_IFCHR || major(stat.st_rdev) != 226)
430 continue;
431
432 minor = minor(stat.st_rdev);
433 for (int i = 0; i < minors->minors_num; i++) {
434 if (minor(stat.st_rdev) == minors->minors[i]) {
435 is_dup = true;
436 break;
437 }
438 }
439 if (is_dup)
440 continue;
441
442 if (minors->minors_num == minors->minors_len) {
443 unsigned int *tmp = reallocarray(minors->minors, minors->minors_len + 4,
444 sizeof(unsigned int));
445
446 if (tmp) {
447 minors->minors = tmp;
448 minors->minors_len += 4;
449 }
450 }
451 minors->minors[minors->minors_num++] = minor;
452 if (fdinfo_dir_fd == -1) {
453 /* Open fdinfo dir if we have a DRM fd. */
454 scnprintf(buf, sizeof(buf), "%s/fdinfo", pid_name);
455 fdinfo_dir_fd = openat(proc_dir, buf, O_DIRECTORY);
456 if (fdinfo_dir_fd == -1)
457 continue;
458 }
459 ret = cb(args, fdinfo_dir_fd, fd_entry->d_name);
460 if (ret)
461 return ret;
462 }
463 if (fdinfo_dir_fd != -1)
464 close(fdinfo_dir_fd);
465 closedir(fd_dir);
466 return 0;
467 }
468
for_each_drm_fdinfo(bool skip_all_duplicates,int (* cb)(void * args,int fdinfo_dir_fd,const char * fd_name),void * args)469 static int for_each_drm_fdinfo(bool skip_all_duplicates,
470 int (*cb)(void *args, int fdinfo_dir_fd, const char *fd_name),
471 void *args)
472 {
473 DIR *proc_dir;
474 struct dirent *proc_entry;
475 int ret;
476 /*
477 * minors maintains an array of DRM minor device numbers seen for a pid,
478 * or for all pids if skip_all_duplicates is true, so that duplicates
479 * are ignored.
480 */
481 struct minor_info minors = {
482 .minors = NULL,
483 .minors_num = 0,
484 .minors_len = 0,
485 };
486
487 proc_dir = opendir(procfs__mountpoint());
488 if (!proc_dir)
489 return 0;
490
491 /* Walk through the /proc directory. */
492 while ((proc_entry = readdir(proc_dir)) != NULL) {
493 if (proc_entry->d_type != DT_DIR ||
494 !isdigit(proc_entry->d_name[0]))
495 continue;
496 if (!skip_all_duplicates) {
497 /* Reset the seen minor numbers for each pid. */
498 minors.minors_num = 0;
499 }
500 ret = for_each_drm_fdinfo_in_dir(cb, args,
501 dirfd(proc_dir), proc_entry->d_name,
502 &minors);
503 if (ret)
504 break;
505 }
506 free(minors.minors);
507 closedir(proc_dir);
508 return ret;
509 }
510
perf_pmus__read_drm_pmus(struct list_head * pmus)511 int perf_pmus__read_drm_pmus(struct list_head *pmus)
512 {
513 return for_each_drm_fdinfo(/*skip_all_duplicates=*/true, read_drm_pmus_cb, pmus);
514 }
515
evsel__drm_pmu_open(struct evsel * evsel,struct perf_thread_map * threads,int start_cpu_map_idx,int end_cpu_map_idx)516 int evsel__drm_pmu_open(struct evsel *evsel,
517 struct perf_thread_map *threads,
518 int start_cpu_map_idx, int end_cpu_map_idx)
519 {
520 (void)evsel;
521 (void)threads;
522 (void)start_cpu_map_idx;
523 (void)end_cpu_map_idx;
524 return 0;
525 }
526
read_count_and_apply_unit(const char * count_and_unit,enum drm_pmu_unit unit)527 static uint64_t read_count_and_apply_unit(const char *count_and_unit, enum drm_pmu_unit unit)
528 {
529 char *unit_ptr = NULL;
530 uint64_t count = strtoul(count_and_unit, &unit_ptr, 10);
531
532 if (!unit_ptr)
533 return 0;
534
535 while (isblank(*unit_ptr))
536 unit_ptr++;
537
538 switch (unit) {
539 case DRM_PMU_UNIT_BYTES:
540 if (*unit_ptr == '\0')
541 assert(count == 0); /* Generally undocumented, happens for 0. */
542 else if (!strcmp(unit_ptr, "KiB"))
543 count *= 1024;
544 else if (!strcmp(unit_ptr, "MiB"))
545 count *= 1024 * 1024;
546 else
547 pr_err("Unexpected bytes unit '%s'\n", unit_ptr);
548 break;
549 case DRM_PMU_UNIT_CAPACITY:
550 /* No units expected. */
551 break;
552 case DRM_PMU_UNIT_CYCLES:
553 /* No units expected. */
554 break;
555 case DRM_PMU_UNIT_HZ:
556 if (!strcmp(unit_ptr, "Hz"))
557 count *= 1;
558 else if (!strcmp(unit_ptr, "KHz"))
559 count *= 1000;
560 else if (!strcmp(unit_ptr, "MHz"))
561 count *= 1000000;
562 else
563 pr_err("Unexpected hz unit '%s'\n", unit_ptr);
564 break;
565 case DRM_PMU_UNIT_NS:
566 /* Only unit ns expected. */
567 break;
568 case DRM_PMU_UNIT_MAX:
569 default:
570 break;
571 }
572 return count;
573 }
574
read_drm_event(int fdinfo_dir_fd,const char * fd_name,const char * match,enum drm_pmu_unit unit)575 static uint64_t read_drm_event(int fdinfo_dir_fd, const char *fd_name,
576 const char *match, enum drm_pmu_unit unit)
577 {
578 char buf[640];
579 struct io io;
580 char *line = NULL;
581 size_t line_len;
582 uint64_t count = 0;
583
584 io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf));
585 if (io.fd == -1) {
586 /* Failed to open file, ignore. */
587 return 0;
588 }
589 while (io__getline(&io, &line, &line_len) > 0) {
590 size_t i = strlen(match);
591
592 if (strncmp(line, match, i))
593 continue;
594 if (line[i] != ':')
595 continue;
596 while (isblank(line[++i]))
597 ;
598 if (line[line_len - 1] == '\n')
599 line[line_len - 1] = '\0';
600 count = read_count_and_apply_unit(&line[i], unit);
601 break;
602 }
603 free(line);
604 close(io.fd);
605 return count;
606 }
607
608 struct read_drm_event_cb_args {
609 const char *match;
610 uint64_t count;
611 enum drm_pmu_unit unit;
612 };
613
read_drm_event_cb(void * vargs,int fdinfo_dir_fd,const char * fd_name)614 static int read_drm_event_cb(void *vargs, int fdinfo_dir_fd, const char *fd_name)
615 {
616 struct read_drm_event_cb_args *args = vargs;
617
618 args->count += read_drm_event(fdinfo_dir_fd, fd_name, args->match, args->unit);
619 return 0;
620 }
621
drm_pmu__read_system_wide(struct drm_pmu * drm,struct evsel * evsel)622 static uint64_t drm_pmu__read_system_wide(struct drm_pmu *drm, struct evsel *evsel)
623 {
624 struct read_drm_event_cb_args args = {
625 .count = 0,
626 .match = drm->events[evsel->core.attr.config].name,
627 .unit = drm->events[evsel->core.attr.config].unit,
628 };
629
630 for_each_drm_fdinfo(/*skip_all_duplicates=*/false, read_drm_event_cb, &args);
631 return args.count;
632 }
633
drm_pmu__read_for_pid(struct drm_pmu * drm,struct evsel * evsel,int pid)634 static uint64_t drm_pmu__read_for_pid(struct drm_pmu *drm, struct evsel *evsel, int pid)
635 {
636 struct read_drm_event_cb_args args = {
637 .count = 0,
638 .match = drm->events[evsel->core.attr.config].name,
639 .unit = drm->events[evsel->core.attr.config].unit,
640 };
641 struct minor_info minors = {
642 .minors = NULL,
643 .minors_num = 0,
644 .minors_len = 0,
645 };
646 int proc_dir = open(procfs__mountpoint(), O_DIRECTORY);
647 char pid_name[12];
648 int ret;
649
650 if (proc_dir < 0)
651 return 0;
652
653 snprintf(pid_name, sizeof(pid_name), "%d", pid);
654 ret = for_each_drm_fdinfo_in_dir(read_drm_event_cb, &args, proc_dir, pid_name, &minors);
655 free(minors.minors);
656 close(proc_dir);
657 return ret == 0 ? args.count : 0;
658 }
659
evsel__drm_pmu_read(struct evsel * evsel,int cpu_map_idx,int thread)660 int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread)
661 {
662 struct drm_pmu *drm = container_of(evsel->pmu, struct drm_pmu, pmu);
663 struct perf_counts_values *count, *old_count = NULL;
664 int pid = perf_thread_map__pid(evsel->core.threads, thread);
665 uint64_t counter;
666
667 if (pid != -1)
668 counter = drm_pmu__read_for_pid(drm, evsel, pid);
669 else
670 counter = drm_pmu__read_system_wide(drm, evsel);
671
672 if (evsel->prev_raw_counts)
673 old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread);
674
675 count = perf_counts(evsel->counts, cpu_map_idx, thread);
676 if (old_count) {
677 count->val = old_count->val + counter;
678 count->run = old_count->run + 1;
679 count->ena = old_count->ena + 1;
680 } else {
681 count->val = counter;
682 count->run++;
683 count->ena++;
684 }
685 return 0;
686 }
687