1 // SPDX-License-Identifier: GPL-2.0-only 2 #include "cgroup.h" 3 #include "counts.h" 4 #include "evsel.h" 5 #include "pmu.h" 6 #include "print-events.h" 7 #include "time-utils.h" 8 #include "tool_pmu.h" 9 #include <api/io.h> 10 #include <internal/threadmap.h> 11 #include <perf/threadmap.h> 12 #include <fcntl.h> 13 #include <strings.h> 14 15 static const char *const tool_pmu__event_names[TOOL_PMU__EVENT_MAX] = { 16 NULL, 17 "duration_time", 18 "user_time", 19 "system_time", 20 }; 21 22 23 const char *perf_tool_event__to_str(enum tool_pmu_event ev) 24 { 25 if (ev > TOOL_PMU__EVENT_NONE && ev < TOOL_PMU__EVENT_MAX) 26 return tool_pmu__event_names[ev]; 27 28 return NULL; 29 } 30 31 enum tool_pmu_event perf_tool_event__from_str(const char *str) 32 { 33 int i; 34 35 perf_tool_event__for_each_event(i) { 36 if (!strcasecmp(str, tool_pmu__event_names[i])) 37 return i; 38 } 39 return TOOL_PMU__EVENT_NONE; 40 } 41 42 static int tool_pmu__config_term(struct perf_event_attr *attr, 43 struct parse_events_term *term, 44 struct parse_events_error *err) 45 { 46 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) { 47 enum tool_pmu_event ev = perf_tool_event__from_str(term->config); 48 49 if (ev == TOOL_PMU__EVENT_NONE) 50 goto err_out; 51 52 attr->config = ev; 53 return 0; 54 } 55 err_out: 56 if (err) { 57 char *err_str; 58 59 parse_events_error__handle(err, term->err_val, 60 asprintf(&err_str, 61 "unexpected tool event term (%s) %s", 62 parse_events__term_type_str(term->type_term), 63 term->config) < 0 64 ? strdup("unexpected tool event term") 65 : err_str, 66 NULL); 67 } 68 return -EINVAL; 69 } 70 71 int tool_pmu__config_terms(struct perf_event_attr *attr, 72 struct parse_events_terms *terms, 73 struct parse_events_error *err) 74 { 75 struct parse_events_term *term; 76 77 list_for_each_entry(term, &terms->terms, list) { 78 if (tool_pmu__config_term(attr, term, err)) 79 return -EINVAL; 80 } 81 82 return 0; 83 84 } 85 86 int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_callback cb) 87 { 88 struct pmu_event_info info = { 89 .pmu = pmu, 90 .event_type_desc = "Tool event", 91 }; 92 int i; 93 94 perf_tool_event__for_each_event(i) { 95 int ret; 96 97 info.name = perf_tool_event__to_str(i); 98 info.alias = NULL; 99 info.scale_unit = NULL; 100 info.desc = NULL; 101 info.long_desc = NULL; 102 info.encoding_desc = NULL; 103 info.topic = NULL; 104 info.pmu_name = pmu->name; 105 info.deprecated = false; 106 ret = cb(state, &info); 107 if (ret) 108 return ret; 109 } 110 return 0; 111 } 112 113 bool perf_pmu__is_tool(const struct perf_pmu *pmu) 114 { 115 return pmu && pmu->type == PERF_PMU_TYPE_TOOL; 116 } 117 118 bool evsel__is_tool(const struct evsel *evsel) 119 { 120 return perf_pmu__is_tool(evsel->pmu); 121 } 122 123 enum tool_pmu_event evsel__tool_event(const struct evsel *evsel) 124 { 125 if (!evsel__is_tool(evsel)) 126 return TOOL_PMU__EVENT_NONE; 127 128 return (enum tool_pmu_event)evsel->core.attr.config; 129 } 130 131 const char *evsel__tool_pmu_event_name(const struct evsel *evsel) 132 { 133 return perf_tool_event__to_str(evsel->core.attr.config); 134 } 135 136 static bool read_until_char(struct io *io, char e) 137 { 138 int c; 139 140 do { 141 c = io__get_char(io); 142 if (c == -1) 143 return false; 144 } while (c != e); 145 return true; 146 } 147 148 static int read_stat_field(int fd, struct perf_cpu cpu, int field, __u64 *val) 149 { 150 char buf[256]; 151 struct io io; 152 int i; 153 154 io__init(&io, fd, buf, sizeof(buf)); 155 156 /* Skip lines to relevant CPU. */ 157 for (i = -1; i < cpu.cpu; i++) { 158 if (!read_until_char(&io, '\n')) 159 return -EINVAL; 160 } 161 /* Skip to "cpu". */ 162 if (io__get_char(&io) != 'c') return -EINVAL; 163 if (io__get_char(&io) != 'p') return -EINVAL; 164 if (io__get_char(&io) != 'u') return -EINVAL; 165 166 /* Skip N of cpuN. */ 167 if (!read_until_char(&io, ' ')) 168 return -EINVAL; 169 170 i = 1; 171 while (true) { 172 if (io__get_dec(&io, val) != ' ') 173 break; 174 if (field == i) 175 return 0; 176 i++; 177 } 178 return -EINVAL; 179 } 180 181 static int read_pid_stat_field(int fd, int field, __u64 *val) 182 { 183 char buf[256]; 184 struct io io; 185 int c, i; 186 187 io__init(&io, fd, buf, sizeof(buf)); 188 if (io__get_dec(&io, val) != ' ') 189 return -EINVAL; 190 if (field == 1) 191 return 0; 192 193 /* Skip comm. */ 194 if (io__get_char(&io) != '(' || !read_until_char(&io, ')')) 195 return -EINVAL; 196 if (field == 2) 197 return -EINVAL; /* String can't be returned. */ 198 199 /* Skip state */ 200 if (io__get_char(&io) != ' ' || io__get_char(&io) == -1) 201 return -EINVAL; 202 if (field == 3) 203 return -EINVAL; /* String can't be returned. */ 204 205 /* Loop over numeric fields*/ 206 if (io__get_char(&io) != ' ') 207 return -EINVAL; 208 209 i = 4; 210 while (true) { 211 c = io__get_dec(&io, val); 212 if (c == -1) 213 return -EINVAL; 214 if (c == -2) { 215 /* Assume a -ve was read */ 216 c = io__get_dec(&io, val); 217 *val *= -1; 218 } 219 if (c != ' ') 220 return -EINVAL; 221 if (field == i) 222 return 0; 223 i++; 224 } 225 return -EINVAL; 226 } 227 228 int evsel__tool_pmu_prepare_open(struct evsel *evsel, 229 struct perf_cpu_map *cpus, 230 int nthreads) 231 { 232 if ((evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME || 233 evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME) && 234 !evsel->start_times) { 235 evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus), 236 nthreads, 237 sizeof(__u64)); 238 if (!evsel->start_times) 239 return -ENOMEM; 240 } 241 return 0; 242 } 243 244 #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y)) 245 246 int evsel__tool_pmu_open(struct evsel *evsel, 247 struct perf_thread_map *threads, 248 int start_cpu_map_idx, int end_cpu_map_idx) 249 { 250 enum tool_pmu_event ev = evsel__tool_event(evsel); 251 int pid = -1, idx = 0, thread = 0, nthreads, err = 0, old_errno; 252 253 if (ev == TOOL_PMU__EVENT_DURATION_TIME) { 254 if (evsel->core.attr.sample_period) /* no sampling */ 255 return -EINVAL; 256 evsel->start_time = rdclock(); 257 return 0; 258 } 259 260 if (evsel->cgrp) 261 pid = evsel->cgrp->fd; 262 263 nthreads = perf_thread_map__nr(threads); 264 for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { 265 for (thread = 0; thread < nthreads; thread++) { 266 if (thread >= nthreads) 267 break; 268 269 if (!evsel->cgrp && !evsel->core.system_wide) 270 pid = perf_thread_map__pid(threads, thread); 271 272 if (ev == TOOL_PMU__EVENT_USER_TIME || ev == TOOL_PMU__EVENT_SYSTEM_TIME) { 273 bool system = ev == TOOL_PMU__EVENT_SYSTEM_TIME; 274 __u64 *start_time = NULL; 275 int fd; 276 277 if (evsel->core.attr.sample_period) { 278 /* no sampling */ 279 err = -EINVAL; 280 goto out_close; 281 } 282 if (pid > -1) { 283 char buf[64]; 284 285 snprintf(buf, sizeof(buf), "/proc/%d/stat", pid); 286 fd = open(buf, O_RDONLY); 287 evsel->pid_stat = true; 288 } else { 289 fd = open("/proc/stat", O_RDONLY); 290 } 291 FD(evsel, idx, thread) = fd; 292 if (fd < 0) { 293 err = -errno; 294 goto out_close; 295 } 296 start_time = xyarray__entry(evsel->start_times, idx, thread); 297 if (pid > -1) { 298 err = read_pid_stat_field(fd, system ? 15 : 14, 299 start_time); 300 } else { 301 struct perf_cpu cpu; 302 303 cpu = perf_cpu_map__cpu(evsel->core.cpus, idx); 304 err = read_stat_field(fd, cpu, system ? 3 : 1, 305 start_time); 306 } 307 if (err) 308 goto out_close; 309 } 310 311 } 312 } 313 return 0; 314 out_close: 315 if (err) 316 threads->err_thread = thread; 317 318 old_errno = errno; 319 do { 320 while (--thread >= 0) { 321 if (FD(evsel, idx, thread) >= 0) 322 close(FD(evsel, idx, thread)); 323 FD(evsel, idx, thread) = -1; 324 } 325 thread = nthreads; 326 } while (--idx >= 0); 327 errno = old_errno; 328 return err; 329 } 330 331 int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread) 332 { 333 __u64 *start_time, cur_time, delta_start; 334 int fd, err = 0; 335 struct perf_counts_values *count; 336 bool adjust = false; 337 338 count = perf_counts(evsel->counts, cpu_map_idx, thread); 339 340 switch (evsel__tool_event(evsel)) { 341 case TOOL_PMU__EVENT_DURATION_TIME: 342 /* 343 * Pretend duration_time is only on the first CPU and thread, or 344 * else aggregation will scale duration_time by the number of 345 * CPUs/threads. 346 */ 347 start_time = &evsel->start_time; 348 if (cpu_map_idx == 0 && thread == 0) 349 cur_time = rdclock(); 350 else 351 cur_time = *start_time; 352 break; 353 case TOOL_PMU__EVENT_USER_TIME: 354 case TOOL_PMU__EVENT_SYSTEM_TIME: { 355 bool system = evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME; 356 357 start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread); 358 fd = FD(evsel, cpu_map_idx, thread); 359 lseek(fd, SEEK_SET, 0); 360 if (evsel->pid_stat) { 361 /* The event exists solely on 1 CPU. */ 362 if (cpu_map_idx == 0) 363 err = read_pid_stat_field(fd, system ? 15 : 14, &cur_time); 364 else 365 cur_time = 0; 366 } else { 367 /* The event is for all threads. */ 368 if (thread == 0) { 369 struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus, 370 cpu_map_idx); 371 372 err = read_stat_field(fd, cpu, system ? 3 : 1, &cur_time); 373 } else { 374 cur_time = 0; 375 } 376 } 377 adjust = true; 378 break; 379 } 380 case TOOL_PMU__EVENT_NONE: 381 case TOOL_PMU__EVENT_MAX: 382 default: 383 err = -EINVAL; 384 } 385 if (err) 386 return err; 387 388 delta_start = cur_time - *start_time; 389 if (adjust) { 390 __u64 ticks_per_sec = sysconf(_SC_CLK_TCK); 391 392 delta_start *= 1000000000 / ticks_per_sec; 393 } 394 count->val = delta_start; 395 count->ena = count->run = delta_start; 396 count->lost = 0; 397 return 0; 398 } 399 400 struct perf_pmu *perf_pmus__tool_pmu(void) 401 { 402 static struct perf_pmu tool = { 403 .name = "tool", 404 .type = PERF_PMU_TYPE_TOOL, 405 .aliases = LIST_HEAD_INIT(tool.aliases), 406 .caps = LIST_HEAD_INIT(tool.caps), 407 .format = LIST_HEAD_INIT(tool.format), 408 }; 409 410 return &tool; 411 } 412