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