diff options
| -rw-r--r-- | tools/perf/Documentation/perf-stat.txt | 4 | ||||
| -rw-r--r-- | tools/perf/builtin-report.c | 11 | ||||
| -rw-r--r-- | tools/perf/builtin-stat.c | 402 | ||||
| -rw-r--r-- | tools/perf/builtin-trace.c | 32 | ||||
| -rw-r--r-- | tools/perf/tests/Build | 1 | ||||
| -rw-r--r-- | tools/perf/tests/builtin-test.c | 4 | ||||
| -rw-r--r-- | tools/perf/tests/openat-syscall-all-cpus.c | 6 | ||||
| -rw-r--r-- | tools/perf/tests/openat-syscall.c | 4 | ||||
| -rw-r--r-- | tools/perf/tests/tests.h | 1 | ||||
| -rw-r--r-- | tools/perf/tests/thread-map.c | 38 | ||||
| -rw-r--r-- | tools/perf/util/evlist.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/evsel.c | 24 | ||||
| -rw-r--r-- | tools/perf/util/evsel.h | 28 | ||||
| -rw-r--r-- | tools/perf/util/python-ext-sources | 1 | ||||
| -rw-r--r-- | tools/perf/util/stat.c | 132 | ||||
| -rw-r--r-- | tools/perf/util/stat.h | 47 | ||||
| -rw-r--r-- | tools/perf/util/symbol.c | 5 | ||||
| -rw-r--r-- | tools/perf/util/thread_map.c | 76 | ||||
| -rw-r--r-- | tools/perf/util/thread_map.h | 8 |
19 files changed, 570 insertions, 255 deletions
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 04e150d83e7d..47469abdcc1c 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
| @@ -144,6 +144,10 @@ is a useful mode to detect imbalance between physical cores. To enable this mod | |||
| 144 | use --per-core in addition to -a. (system-wide). The output includes the | 144 | use --per-core in addition to -a. (system-wide). The output includes the |
| 145 | core number and the number of online logical processors on that physical processor. | 145 | core number and the number of online logical processors on that physical processor. |
| 146 | 146 | ||
| 147 | --per-thread:: | ||
| 148 | Aggregate counts per monitored threads, when monitoring threads (-t option) | ||
| 149 | or processes (-p option). | ||
| 150 | |||
| 147 | -D msecs:: | 151 | -D msecs:: |
| 148 | --delay msecs:: | 152 | --delay msecs:: |
| 149 | After starting the program, wait msecs before measuring. This is useful to | 153 | After starting the program, wait msecs before measuring. This is useful to |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 32626ea3e227..348bed4a2abf 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -742,6 +742,17 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 742 | 742 | ||
| 743 | argc = parse_options(argc, argv, options, report_usage, 0); | 743 | argc = parse_options(argc, argv, options, report_usage, 0); |
| 744 | 744 | ||
| 745 | if (symbol_conf.vmlinux_name && | ||
| 746 | access(symbol_conf.vmlinux_name, R_OK)) { | ||
| 747 | pr_err("Invalid file: %s\n", symbol_conf.vmlinux_name); | ||
| 748 | return -EINVAL; | ||
| 749 | } | ||
| 750 | if (symbol_conf.kallsyms_name && | ||
| 751 | access(symbol_conf.kallsyms_name, R_OK)) { | ||
| 752 | pr_err("Invalid file: %s\n", symbol_conf.kallsyms_name); | ||
| 753 | return -EINVAL; | ||
| 754 | } | ||
| 755 | |||
| 745 | if (report.use_stdio) | 756 | if (report.use_stdio) |
| 746 | use_browser = 0; | 757 | use_browser = 0; |
| 747 | else if (report.use_tui) | 758 | else if (report.use_tui) |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 3e1636cae76b..37e301a32f43 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -67,10 +67,7 @@ | |||
| 67 | #define CNTR_NOT_SUPPORTED "<not supported>" | 67 | #define CNTR_NOT_SUPPORTED "<not supported>" |
| 68 | #define CNTR_NOT_COUNTED "<not counted>" | 68 | #define CNTR_NOT_COUNTED "<not counted>" |
| 69 | 69 | ||
| 70 | static void print_stat(int argc, const char **argv); | 70 | static void print_counters(struct timespec *ts, int argc, const char **argv); |
| 71 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix); | ||
| 72 | static void print_counter(struct perf_evsel *counter, char *prefix); | ||
| 73 | static void print_aggr(char *prefix); | ||
| 74 | 71 | ||
| 75 | /* Default events used for perf stat -T */ | 72 | /* Default events used for perf stat -T */ |
| 76 | static const char *transaction_attrs = { | 73 | static const char *transaction_attrs = { |
| @@ -141,86 +138,9 @@ static inline void diff_timespec(struct timespec *r, struct timespec *a, | |||
| 141 | } | 138 | } |
| 142 | } | 139 | } |
| 143 | 140 | ||
| 144 | static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel) | 141 | static void perf_stat__reset_stats(void) |
| 145 | { | 142 | { |
| 146 | int i; | 143 | perf_evlist__reset_stats(evsel_list); |
| 147 | struct perf_stat *ps = evsel->priv; | ||
| 148 | |||
| 149 | for (i = 0; i < 3; i++) | ||
| 150 | init_stats(&ps->res_stats[i]); | ||
| 151 | |||
| 152 | perf_stat_evsel_id_init(evsel); | ||
| 153 | } | ||
| 154 | |||
| 155 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) | ||
| 156 | { | ||
| 157 | evsel->priv = zalloc(sizeof(struct perf_stat)); | ||
| 158 | if (evsel->priv == NULL) | ||
| 159 | return -ENOMEM; | ||
| 160 | perf_evsel__reset_stat_priv(evsel); | ||
| 161 | return 0; | ||
| 162 | } | ||
| 163 | |||
| 164 | static void perf_evsel__free_stat_priv(struct perf_evsel *evsel) | ||
| 165 | { | ||
| 166 | zfree(&evsel->priv); | ||
| 167 | } | ||
| 168 | |||
| 169 | static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel) | ||
| 170 | { | ||
| 171 | struct perf_counts *counts; | ||
| 172 | |||
| 173 | counts = perf_counts__new(perf_evsel__nr_cpus(evsel)); | ||
| 174 | if (counts) | ||
| 175 | evsel->prev_raw_counts = counts; | ||
| 176 | |||
| 177 | return counts ? 0 : -ENOMEM; | ||
| 178 | } | ||
| 179 | |||
| 180 | static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) | ||
| 181 | { | ||
| 182 | perf_counts__delete(evsel->prev_raw_counts); | ||
| 183 | evsel->prev_raw_counts = NULL; | ||
| 184 | } | ||
| 185 | |||
| 186 | static void perf_evlist__free_stats(struct perf_evlist *evlist) | ||
| 187 | { | ||
| 188 | struct perf_evsel *evsel; | ||
| 189 | |||
| 190 | evlist__for_each(evlist, evsel) { | ||
| 191 | perf_evsel__free_stat_priv(evsel); | ||
| 192 | perf_evsel__free_counts(evsel); | ||
| 193 | perf_evsel__free_prev_raw_counts(evsel); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | static int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw) | ||
| 198 | { | ||
| 199 | struct perf_evsel *evsel; | ||
| 200 | |||
| 201 | evlist__for_each(evlist, evsel) { | ||
| 202 | if (perf_evsel__alloc_stat_priv(evsel) < 0 || | ||
| 203 | perf_evsel__alloc_counts(evsel, perf_evsel__nr_cpus(evsel)) < 0 || | ||
| 204 | (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel) < 0)) | ||
| 205 | goto out_free; | ||
| 206 | } | ||
| 207 | |||
| 208 | return 0; | ||
| 209 | |||
| 210 | out_free: | ||
| 211 | perf_evlist__free_stats(evlist); | ||
| 212 | return -1; | ||
| 213 | } | ||
| 214 | |||
| 215 | static void perf_stat__reset_stats(struct perf_evlist *evlist) | ||
| 216 | { | ||
| 217 | struct perf_evsel *evsel; | ||
| 218 | |||
| 219 | evlist__for_each(evlist, evsel) { | ||
| 220 | perf_evsel__reset_stat_priv(evsel); | ||
| 221 | perf_evsel__reset_counts(evsel, perf_evsel__nr_cpus(evsel)); | ||
| 222 | } | ||
| 223 | |||
| 224 | perf_stat__reset_shadow_stats(); | 144 | perf_stat__reset_shadow_stats(); |
| 225 | } | 145 | } |
| 226 | 146 | ||
| @@ -294,8 +214,9 @@ static int check_per_pkg(struct perf_evsel *counter, int cpu, bool *skip) | |||
| 294 | return 0; | 214 | return 0; |
| 295 | } | 215 | } |
| 296 | 216 | ||
| 297 | static int read_cb(struct perf_evsel *evsel, int cpu, int thread __maybe_unused, | 217 | static int |
| 298 | struct perf_counts_values *count) | 218 | process_counter_values(struct perf_evsel *evsel, int cpu, int thread, |
| 219 | struct perf_counts_values *count) | ||
| 299 | { | 220 | { |
| 300 | struct perf_counts_values *aggr = &evsel->counts->aggr; | 221 | struct perf_counts_values *aggr = &evsel->counts->aggr; |
| 301 | static struct perf_counts_values zero; | 222 | static struct perf_counts_values zero; |
| @@ -310,13 +231,13 @@ static int read_cb(struct perf_evsel *evsel, int cpu, int thread __maybe_unused, | |||
| 310 | count = &zero; | 231 | count = &zero; |
| 311 | 232 | ||
| 312 | switch (aggr_mode) { | 233 | switch (aggr_mode) { |
| 234 | case AGGR_THREAD: | ||
| 313 | case AGGR_CORE: | 235 | case AGGR_CORE: |
| 314 | case AGGR_SOCKET: | 236 | case AGGR_SOCKET: |
| 315 | case AGGR_NONE: | 237 | case AGGR_NONE: |
| 316 | if (!evsel->snapshot) | 238 | if (!evsel->snapshot) |
| 317 | perf_evsel__compute_deltas(evsel, cpu, count); | 239 | perf_evsel__compute_deltas(evsel, cpu, thread, count); |
| 318 | perf_counts_values__scale(count, scale, NULL); | 240 | perf_counts_values__scale(count, scale, NULL); |
| 319 | evsel->counts->cpu[cpu] = *count; | ||
| 320 | if (aggr_mode == AGGR_NONE) | 241 | if (aggr_mode == AGGR_NONE) |
| 321 | perf_stat__update_shadow_stats(evsel, count->values, cpu); | 242 | perf_stat__update_shadow_stats(evsel, count->values, cpu); |
| 322 | break; | 243 | break; |
| @@ -333,26 +254,48 @@ static int read_cb(struct perf_evsel *evsel, int cpu, int thread __maybe_unused, | |||
| 333 | return 0; | 254 | return 0; |
| 334 | } | 255 | } |
| 335 | 256 | ||
| 336 | static int read_counter(struct perf_evsel *counter); | 257 | static int process_counter_maps(struct perf_evsel *counter) |
| 258 | { | ||
| 259 | int nthreads = thread_map__nr(counter->threads); | ||
| 260 | int ncpus = perf_evsel__nr_cpus(counter); | ||
| 261 | int cpu, thread; | ||
| 337 | 262 | ||
| 338 | /* | 263 | if (counter->system_wide) |
| 339 | * Read out the results of a single counter: | 264 | nthreads = 1; |
| 340 | * aggregate counts across CPUs in system-wide mode | 265 | |
| 341 | */ | 266 | for (thread = 0; thread < nthreads; thread++) { |
| 342 | static int read_counter_aggr(struct perf_evsel *counter) | 267 | for (cpu = 0; cpu < ncpus; cpu++) { |
| 268 | if (process_counter_values(counter, cpu, thread, | ||
| 269 | perf_counts(counter->counts, cpu, thread))) | ||
| 270 | return -1; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | return 0; | ||
| 275 | } | ||
| 276 | |||
| 277 | static int process_counter(struct perf_evsel *counter) | ||
| 343 | { | 278 | { |
| 344 | struct perf_counts_values *aggr = &counter->counts->aggr; | 279 | struct perf_counts_values *aggr = &counter->counts->aggr; |
| 345 | struct perf_stat *ps = counter->priv; | 280 | struct perf_stat *ps = counter->priv; |
| 346 | u64 *count = counter->counts->aggr.values; | 281 | u64 *count = counter->counts->aggr.values; |
| 347 | int i; | 282 | int i, ret; |
| 348 | 283 | ||
| 349 | aggr->val = aggr->ena = aggr->run = 0; | 284 | aggr->val = aggr->ena = aggr->run = 0; |
| 285 | init_stats(ps->res_stats); | ||
| 350 | 286 | ||
| 351 | if (read_counter(counter)) | 287 | if (counter->per_pkg) |
| 352 | return -1; | 288 | zero_per_pkg(counter); |
| 289 | |||
| 290 | ret = process_counter_maps(counter); | ||
| 291 | if (ret) | ||
| 292 | return ret; | ||
| 293 | |||
| 294 | if (aggr_mode != AGGR_GLOBAL) | ||
| 295 | return 0; | ||
| 353 | 296 | ||
| 354 | if (!counter->snapshot) | 297 | if (!counter->snapshot) |
| 355 | perf_evsel__compute_deltas(counter, -1, aggr); | 298 | perf_evsel__compute_deltas(counter, -1, -1, aggr); |
| 356 | perf_counts_values__scale(aggr, scale, &counter->counts->scaled); | 299 | perf_counts_values__scale(aggr, scale, &counter->counts->scaled); |
| 357 | 300 | ||
| 358 | for (i = 0; i < 3; i++) | 301 | for (i = 0; i < 3; i++) |
| @@ -387,12 +330,12 @@ static int read_counter(struct perf_evsel *counter) | |||
| 387 | if (counter->system_wide) | 330 | if (counter->system_wide) |
| 388 | nthreads = 1; | 331 | nthreads = 1; |
| 389 | 332 | ||
| 390 | if (counter->per_pkg) | ||
| 391 | zero_per_pkg(counter); | ||
| 392 | |||
| 393 | for (thread = 0; thread < nthreads; thread++) { | 333 | for (thread = 0; thread < nthreads; thread++) { |
| 394 | for (cpu = 0; cpu < ncpus; cpu++) { | 334 | for (cpu = 0; cpu < ncpus; cpu++) { |
| 395 | if (perf_evsel__read_cb(counter, cpu, thread, read_cb)) | 335 | struct perf_counts_values *count; |
| 336 | |||
| 337 | count = perf_counts(counter->counts, cpu, thread); | ||
| 338 | if (perf_evsel__read(counter, cpu, thread, count)) | ||
| 396 | return -1; | 339 | return -1; |
| 397 | } | 340 | } |
| 398 | } | 341 | } |
| @@ -400,68 +343,34 @@ static int read_counter(struct perf_evsel *counter) | |||
| 400 | return 0; | 343 | return 0; |
| 401 | } | 344 | } |
| 402 | 345 | ||
| 403 | static void print_interval(void) | 346 | static void read_counters(bool close) |
| 404 | { | 347 | { |
| 405 | static int num_print_interval; | ||
| 406 | struct perf_evsel *counter; | 348 | struct perf_evsel *counter; |
| 407 | struct perf_stat *ps; | ||
| 408 | struct timespec ts, rs; | ||
| 409 | char prefix[64]; | ||
| 410 | 349 | ||
| 411 | if (aggr_mode == AGGR_GLOBAL) { | 350 | evlist__for_each(evsel_list, counter) { |
| 412 | evlist__for_each(evsel_list, counter) { | 351 | if (read_counter(counter)) |
| 413 | ps = counter->priv; | 352 | pr_warning("failed to read counter %s\n", counter->name); |
| 414 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | ||
| 415 | read_counter_aggr(counter); | ||
| 416 | } | ||
| 417 | } else { | ||
| 418 | evlist__for_each(evsel_list, counter) { | ||
| 419 | ps = counter->priv; | ||
| 420 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | ||
| 421 | read_counter(counter); | ||
| 422 | } | ||
| 423 | } | ||
| 424 | 353 | ||
| 425 | clock_gettime(CLOCK_MONOTONIC, &ts); | 354 | if (process_counter(counter)) |
| 426 | diff_timespec(&rs, &ts, &ref_time); | 355 | pr_warning("failed to process counter %s\n", counter->name); |
| 427 | sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep); | ||
| 428 | 356 | ||
| 429 | if (num_print_interval == 0 && !csv_output) { | 357 | if (close) { |
| 430 | switch (aggr_mode) { | 358 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), |
| 431 | case AGGR_SOCKET: | 359 | thread_map__nr(evsel_list->threads)); |
| 432 | fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit"); | ||
| 433 | break; | ||
| 434 | case AGGR_CORE: | ||
| 435 | fprintf(output, "# time core cpus counts %*s events\n", unit_width, "unit"); | ||
| 436 | break; | ||
| 437 | case AGGR_NONE: | ||
| 438 | fprintf(output, "# time CPU counts %*s events\n", unit_width, "unit"); | ||
| 439 | break; | ||
| 440 | case AGGR_GLOBAL: | ||
| 441 | default: | ||
| 442 | fprintf(output, "# time counts %*s events\n", unit_width, "unit"); | ||
| 443 | } | 360 | } |
| 444 | } | 361 | } |
| 362 | } | ||
| 445 | 363 | ||
| 446 | if (++num_print_interval == 25) | 364 | static void process_interval(void) |
| 447 | num_print_interval = 0; | 365 | { |
| 366 | struct timespec ts, rs; | ||
| 448 | 367 | ||
| 449 | switch (aggr_mode) { | 368 | read_counters(false); |
| 450 | case AGGR_CORE: | ||
| 451 | case AGGR_SOCKET: | ||
| 452 | print_aggr(prefix); | ||
| 453 | break; | ||
| 454 | case AGGR_NONE: | ||
| 455 | evlist__for_each(evsel_list, counter) | ||
| 456 | print_counter(counter, prefix); | ||
| 457 | break; | ||
| 458 | case AGGR_GLOBAL: | ||
| 459 | default: | ||
| 460 | evlist__for_each(evsel_list, counter) | ||
| 461 | print_counter_aggr(counter, prefix); | ||
| 462 | } | ||
| 463 | 369 | ||
| 464 | fflush(output); | 370 | clock_gettime(CLOCK_MONOTONIC, &ts); |
| 371 | diff_timespec(&rs, &ts, &ref_time); | ||
| 372 | |||
| 373 | print_counters(&rs, 0, NULL); | ||
| 465 | } | 374 | } |
| 466 | 375 | ||
| 467 | static void handle_initial_delay(void) | 376 | static void handle_initial_delay(void) |
| @@ -576,7 +485,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
| 576 | if (interval) { | 485 | if (interval) { |
| 577 | while (!waitpid(child_pid, &status, WNOHANG)) { | 486 | while (!waitpid(child_pid, &status, WNOHANG)) { |
| 578 | nanosleep(&ts, NULL); | 487 | nanosleep(&ts, NULL); |
| 579 | print_interval(); | 488 | process_interval(); |
| 580 | } | 489 | } |
| 581 | } | 490 | } |
| 582 | wait(&status); | 491 | wait(&status); |
| @@ -594,7 +503,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
| 594 | while (!done) { | 503 | while (!done) { |
| 595 | nanosleep(&ts, NULL); | 504 | nanosleep(&ts, NULL); |
| 596 | if (interval) | 505 | if (interval) |
| 597 | print_interval(); | 506 | process_interval(); |
| 598 | } | 507 | } |
| 599 | } | 508 | } |
| 600 | 509 | ||
| @@ -602,18 +511,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
| 602 | 511 | ||
| 603 | update_stats(&walltime_nsecs_stats, t1 - t0); | 512 | update_stats(&walltime_nsecs_stats, t1 - t0); |
| 604 | 513 | ||
| 605 | if (aggr_mode == AGGR_GLOBAL) { | 514 | read_counters(true); |
| 606 | evlist__for_each(evsel_list, counter) { | ||
| 607 | read_counter_aggr(counter); | ||
| 608 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), | ||
| 609 | thread_map__nr(evsel_list->threads)); | ||
| 610 | } | ||
| 611 | } else { | ||
| 612 | evlist__for_each(evsel_list, counter) { | ||
| 613 | read_counter(counter); | ||
| 614 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1); | ||
| 615 | } | ||
| 616 | } | ||
| 617 | 515 | ||
| 618 | return WEXITSTATUS(status); | 516 | return WEXITSTATUS(status); |
| 619 | } | 517 | } |
| @@ -705,6 +603,14 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr) | |||
| 705 | csv_output ? 0 : -4, | 603 | csv_output ? 0 : -4, |
| 706 | perf_evsel__cpus(evsel)->map[id], csv_sep); | 604 | perf_evsel__cpus(evsel)->map[id], csv_sep); |
| 707 | break; | 605 | break; |
| 606 | case AGGR_THREAD: | ||
| 607 | fprintf(output, "%*s-%*d%s", | ||
| 608 | csv_output ? 0 : 16, | ||
| 609 | thread_map__comm(evsel->threads, id), | ||
| 610 | csv_output ? 0 : -8, | ||
| 611 | thread_map__pid(evsel->threads, id), | ||
| 612 | csv_sep); | ||
| 613 | break; | ||
| 708 | case AGGR_GLOBAL: | 614 | case AGGR_GLOBAL: |
| 709 | default: | 615 | default: |
| 710 | break; | 616 | break; |
| @@ -805,9 +711,9 @@ static void print_aggr(char *prefix) | |||
| 805 | s2 = aggr_get_id(evsel_list->cpus, cpu2); | 711 | s2 = aggr_get_id(evsel_list->cpus, cpu2); |
| 806 | if (s2 != id) | 712 | if (s2 != id) |
| 807 | continue; | 713 | continue; |
| 808 | val += counter->counts->cpu[cpu].val; | 714 | val += perf_counts(counter->counts, cpu, 0)->val; |
| 809 | ena += counter->counts->cpu[cpu].ena; | 715 | ena += perf_counts(counter->counts, cpu, 0)->ena; |
| 810 | run += counter->counts->cpu[cpu].run; | 716 | run += perf_counts(counter->counts, cpu, 0)->run; |
| 811 | nr++; | 717 | nr++; |
| 812 | } | 718 | } |
| 813 | if (prefix) | 719 | if (prefix) |
| @@ -853,6 +759,40 @@ static void print_aggr(char *prefix) | |||
| 853 | } | 759 | } |
| 854 | } | 760 | } |
| 855 | 761 | ||
| 762 | static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | ||
| 763 | { | ||
| 764 | int nthreads = thread_map__nr(counter->threads); | ||
| 765 | int ncpus = cpu_map__nr(counter->cpus); | ||
| 766 | int cpu, thread; | ||
| 767 | double uval; | ||
| 768 | |||
| 769 | for (thread = 0; thread < nthreads; thread++) { | ||
| 770 | u64 ena = 0, run = 0, val = 0; | ||
| 771 | |||
| 772 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
| 773 | val += perf_counts(counter->counts, cpu, thread)->val; | ||
| 774 | ena += perf_counts(counter->counts, cpu, thread)->ena; | ||
| 775 | run += perf_counts(counter->counts, cpu, thread)->run; | ||
| 776 | } | ||
| 777 | |||
| 778 | if (prefix) | ||
| 779 | fprintf(output, "%s", prefix); | ||
| 780 | |||
| 781 | uval = val * counter->scale; | ||
| 782 | |||
| 783 | if (nsec_counter(counter)) | ||
| 784 | nsec_printout(thread, 0, counter, uval); | ||
| 785 | else | ||
| 786 | abs_printout(thread, 0, counter, uval); | ||
| 787 | |||
| 788 | if (!csv_output) | ||
| 789 | print_noise(counter, 1.0); | ||
| 790 | |||
| 791 | print_running(run, ena); | ||
| 792 | fputc('\n', output); | ||
| 793 | } | ||
| 794 | } | ||
| 795 | |||
| 856 | /* | 796 | /* |
| 857 | * Print out the results of a single counter: | 797 | * Print out the results of a single counter: |
| 858 | * aggregated counts in system-wide mode | 798 | * aggregated counts in system-wide mode |
| @@ -915,9 +855,9 @@ static void print_counter(struct perf_evsel *counter, char *prefix) | |||
| 915 | int cpu; | 855 | int cpu; |
| 916 | 856 | ||
| 917 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | 857 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { |
| 918 | val = counter->counts->cpu[cpu].val; | 858 | val = perf_counts(counter->counts, cpu, 0)->val; |
| 919 | ena = counter->counts->cpu[cpu].ena; | 859 | ena = perf_counts(counter->counts, cpu, 0)->ena; |
| 920 | run = counter->counts->cpu[cpu].run; | 860 | run = perf_counts(counter->counts, cpu, 0)->run; |
| 921 | 861 | ||
| 922 | if (prefix) | 862 | if (prefix) |
| 923 | fprintf(output, "%s", prefix); | 863 | fprintf(output, "%s", prefix); |
| @@ -962,9 +902,38 @@ static void print_counter(struct perf_evsel *counter, char *prefix) | |||
| 962 | } | 902 | } |
| 963 | } | 903 | } |
| 964 | 904 | ||
| 965 | static void print_stat(int argc, const char **argv) | 905 | static void print_interval(char *prefix, struct timespec *ts) |
| 906 | { | ||
| 907 | static int num_print_interval; | ||
| 908 | |||
| 909 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); | ||
| 910 | |||
| 911 | if (num_print_interval == 0 && !csv_output) { | ||
| 912 | switch (aggr_mode) { | ||
| 913 | case AGGR_SOCKET: | ||
| 914 | fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit"); | ||
| 915 | break; | ||
| 916 | case AGGR_CORE: | ||
| 917 | fprintf(output, "# time core cpus counts %*s events\n", unit_width, "unit"); | ||
| 918 | break; | ||
| 919 | case AGGR_NONE: | ||
| 920 | fprintf(output, "# time CPU counts %*s events\n", unit_width, "unit"); | ||
| 921 | break; | ||
| 922 | case AGGR_THREAD: | ||
| 923 | fprintf(output, "# time comm-pid counts %*s events\n", unit_width, "unit"); | ||
| 924 | break; | ||
| 925 | case AGGR_GLOBAL: | ||
| 926 | default: | ||
| 927 | fprintf(output, "# time counts %*s events\n", unit_width, "unit"); | ||
| 928 | } | ||
| 929 | } | ||
| 930 | |||
| 931 | if (++num_print_interval == 25) | ||
| 932 | num_print_interval = 0; | ||
| 933 | } | ||
| 934 | |||
| 935 | static void print_header(int argc, const char **argv) | ||
| 966 | { | 936 | { |
| 967 | struct perf_evsel *counter; | ||
| 968 | int i; | 937 | int i; |
| 969 | 938 | ||
| 970 | fflush(stdout); | 939 | fflush(stdout); |
| @@ -990,36 +959,57 @@ static void print_stat(int argc, const char **argv) | |||
| 990 | fprintf(output, " (%d runs)", run_count); | 959 | fprintf(output, " (%d runs)", run_count); |
| 991 | fprintf(output, ":\n\n"); | 960 | fprintf(output, ":\n\n"); |
| 992 | } | 961 | } |
| 962 | } | ||
| 963 | |||
| 964 | static void print_footer(void) | ||
| 965 | { | ||
| 966 | if (!null_run) | ||
| 967 | fprintf(output, "\n"); | ||
| 968 | fprintf(output, " %17.9f seconds time elapsed", | ||
| 969 | avg_stats(&walltime_nsecs_stats)/1e9); | ||
| 970 | if (run_count > 1) { | ||
| 971 | fprintf(output, " "); | ||
| 972 | print_noise_pct(stddev_stats(&walltime_nsecs_stats), | ||
| 973 | avg_stats(&walltime_nsecs_stats)); | ||
| 974 | } | ||
| 975 | fprintf(output, "\n\n"); | ||
| 976 | } | ||
| 977 | |||
| 978 | static void print_counters(struct timespec *ts, int argc, const char **argv) | ||
| 979 | { | ||
| 980 | struct perf_evsel *counter; | ||
| 981 | char buf[64], *prefix = NULL; | ||
| 982 | |||
| 983 | if (interval) | ||
| 984 | print_interval(prefix = buf, ts); | ||
| 985 | else | ||
| 986 | print_header(argc, argv); | ||
| 993 | 987 | ||
| 994 | switch (aggr_mode) { | 988 | switch (aggr_mode) { |
| 995 | case AGGR_CORE: | 989 | case AGGR_CORE: |
| 996 | case AGGR_SOCKET: | 990 | case AGGR_SOCKET: |
| 997 | print_aggr(NULL); | 991 | print_aggr(prefix); |
| 992 | break; | ||
| 993 | case AGGR_THREAD: | ||
| 994 | evlist__for_each(evsel_list, counter) | ||
| 995 | print_aggr_thread(counter, prefix); | ||
| 998 | break; | 996 | break; |
| 999 | case AGGR_GLOBAL: | 997 | case AGGR_GLOBAL: |
| 1000 | evlist__for_each(evsel_list, counter) | 998 | evlist__for_each(evsel_list, counter) |
| 1001 | print_counter_aggr(counter, NULL); | 999 | print_counter_aggr(counter, prefix); |
| 1002 | break; | 1000 | break; |
| 1003 | case AGGR_NONE: | 1001 | case AGGR_NONE: |
| 1004 | evlist__for_each(evsel_list, counter) | 1002 | evlist__for_each(evsel_list, counter) |
| 1005 | print_counter(counter, NULL); | 1003 | print_counter(counter, prefix); |
| 1006 | break; | 1004 | break; |
| 1007 | default: | 1005 | default: |
| 1008 | break; | 1006 | break; |
| 1009 | } | 1007 | } |
| 1010 | 1008 | ||
| 1011 | if (!csv_output) { | 1009 | if (!interval && !csv_output) |
| 1012 | if (!null_run) | 1010 | print_footer(); |
| 1013 | fprintf(output, "\n"); | 1011 | |
| 1014 | fprintf(output, " %17.9f seconds time elapsed", | 1012 | fflush(output); |
| 1015 | avg_stats(&walltime_nsecs_stats)/1e9); | ||
| 1016 | if (run_count > 1) { | ||
| 1017 | fprintf(output, " "); | ||
| 1018 | print_noise_pct(stddev_stats(&walltime_nsecs_stats), | ||
| 1019 | avg_stats(&walltime_nsecs_stats)); | ||
| 1020 | } | ||
| 1021 | fprintf(output, "\n\n"); | ||
| 1022 | } | ||
| 1023 | } | 1013 | } |
| 1024 | 1014 | ||
| 1025 | static volatile int signr = -1; | 1015 | static volatile int signr = -1; |
| @@ -1091,6 +1081,7 @@ static int perf_stat_init_aggr_mode(void) | |||
| 1091 | break; | 1081 | break; |
| 1092 | case AGGR_NONE: | 1082 | case AGGR_NONE: |
| 1093 | case AGGR_GLOBAL: | 1083 | case AGGR_GLOBAL: |
| 1084 | case AGGR_THREAD: | ||
| 1094 | default: | 1085 | default: |
| 1095 | break; | 1086 | break; |
| 1096 | } | 1087 | } |
| @@ -1315,6 +1306,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1315 | "aggregate counts per processor socket", AGGR_SOCKET), | 1306 | "aggregate counts per processor socket", AGGR_SOCKET), |
| 1316 | OPT_SET_UINT(0, "per-core", &aggr_mode, | 1307 | OPT_SET_UINT(0, "per-core", &aggr_mode, |
| 1317 | "aggregate counts per physical processor core", AGGR_CORE), | 1308 | "aggregate counts per physical processor core", AGGR_CORE), |
| 1309 | OPT_SET_UINT(0, "per-thread", &aggr_mode, | ||
| 1310 | "aggregate counts per thread", AGGR_THREAD), | ||
| 1318 | OPT_UINTEGER('D', "delay", &initial_delay, | 1311 | OPT_UINTEGER('D', "delay", &initial_delay, |
| 1319 | "ms to wait before starting measurement after program start"), | 1312 | "ms to wait before starting measurement after program start"), |
| 1320 | OPT_END() | 1313 | OPT_END() |
| @@ -1406,8 +1399,19 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1406 | run_count = 1; | 1399 | run_count = 1; |
| 1407 | } | 1400 | } |
| 1408 | 1401 | ||
| 1409 | /* no_aggr, cgroup are for system-wide only */ | 1402 | if ((aggr_mode == AGGR_THREAD) && !target__has_task(&target)) { |
| 1410 | if ((aggr_mode != AGGR_GLOBAL || nr_cgroups) && | 1403 | fprintf(stderr, "The --per-thread option is only available " |
| 1404 | "when monitoring via -p -t options.\n"); | ||
| 1405 | parse_options_usage(NULL, options, "p", 1); | ||
| 1406 | parse_options_usage(NULL, options, "t", 1); | ||
| 1407 | goto out; | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | /* | ||
| 1411 | * no_aggr, cgroup are for system-wide only | ||
| 1412 | * --per-thread is aggregated per thread, we dont mix it with cpu mode | ||
| 1413 | */ | ||
| 1414 | if (((aggr_mode != AGGR_GLOBAL && aggr_mode != AGGR_THREAD) || nr_cgroups) && | ||
| 1411 | !target__has_cpu(&target)) { | 1415 | !target__has_cpu(&target)) { |
| 1412 | fprintf(stderr, "both cgroup and no-aggregation " | 1416 | fprintf(stderr, "both cgroup and no-aggregation " |
| 1413 | "modes only available in system-wide mode\n"); | 1417 | "modes only available in system-wide mode\n"); |
| @@ -1435,6 +1439,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1435 | } | 1439 | } |
| 1436 | goto out; | 1440 | goto out; |
| 1437 | } | 1441 | } |
| 1442 | |||
| 1443 | /* | ||
| 1444 | * Initialize thread_map with comm names, | ||
| 1445 | * so we could print it out on output. | ||
| 1446 | */ | ||
| 1447 | if (aggr_mode == AGGR_THREAD) | ||
| 1448 | thread_map__read_comms(evsel_list->threads); | ||
| 1449 | |||
| 1438 | if (interval && interval < 100) { | 1450 | if (interval && interval < 100) { |
| 1439 | pr_err("print interval must be >= 100ms\n"); | 1451 | pr_err("print interval must be >= 100ms\n"); |
| 1440 | parse_options_usage(stat_usage, options, "I", 1); | 1452 | parse_options_usage(stat_usage, options, "I", 1); |
| @@ -1468,13 +1480,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1468 | 1480 | ||
| 1469 | status = run_perf_stat(argc, argv); | 1481 | status = run_perf_stat(argc, argv); |
| 1470 | if (forever && status != -1) { | 1482 | if (forever && status != -1) { |
| 1471 | print_stat(argc, argv); | 1483 | print_counters(NULL, argc, argv); |
| 1472 | perf_stat__reset_stats(evsel_list); | 1484 | perf_stat__reset_stats(); |
| 1473 | } | 1485 | } |
| 1474 | } | 1486 | } |
| 1475 | 1487 | ||
| 1476 | if (!forever && status != -1 && !interval) | 1488 | if (!forever && status != -1 && !interval) |
| 1477 | print_stat(argc, argv); | 1489 | print_counters(NULL, argc, argv); |
| 1478 | 1490 | ||
| 1479 | perf_evlist__free_stats(evsel_list); | 1491 | perf_evlist__free_stats(evsel_list); |
| 1480 | out: | 1492 | out: |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 2bf2ca771ca5..39ad4d0ca884 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
| @@ -1617,6 +1617,34 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
| 1617 | return syscall__set_arg_fmts(sc); | 1617 | return syscall__set_arg_fmts(sc); |
| 1618 | } | 1618 | } |
| 1619 | 1619 | ||
| 1620 | static int trace__validate_ev_qualifier(struct trace *trace) | ||
| 1621 | { | ||
| 1622 | int err = 0; | ||
| 1623 | struct str_node *pos; | ||
| 1624 | |||
| 1625 | strlist__for_each(pos, trace->ev_qualifier) { | ||
| 1626 | const char *sc = pos->s; | ||
| 1627 | |||
| 1628 | if (audit_name_to_syscall(sc, trace->audit.machine) < 0) { | ||
| 1629 | if (err == 0) { | ||
| 1630 | fputs("Error:\tInvalid syscall ", trace->output); | ||
| 1631 | err = -EINVAL; | ||
| 1632 | } else { | ||
| 1633 | fputs(", ", trace->output); | ||
| 1634 | } | ||
| 1635 | |||
| 1636 | fputs(sc, trace->output); | ||
| 1637 | } | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | if (err < 0) { | ||
| 1641 | fputs("\nHint:\ttry 'perf list syscalls:sys_enter_*'" | ||
| 1642 | "\nHint:\tand: 'man syscalls'\n", trace->output); | ||
| 1643 | } | ||
| 1644 | |||
| 1645 | return err; | ||
| 1646 | } | ||
| 1647 | |||
| 1620 | /* | 1648 | /* |
| 1621 | * args is to be interpreted as a series of longs but we need to handle | 1649 | * args is to be interpreted as a series of longs but we need to handle |
| 1622 | * 8-byte unaligned accesses. args points to raw_data within the event | 1650 | * 8-byte unaligned accesses. args points to raw_data within the event |
| @@ -2862,6 +2890,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 2862 | err = -ENOMEM; | 2890 | err = -ENOMEM; |
| 2863 | goto out_close; | 2891 | goto out_close; |
| 2864 | } | 2892 | } |
| 2893 | |||
| 2894 | err = trace__validate_ev_qualifier(&trace); | ||
| 2895 | if (err) | ||
| 2896 | goto out_close; | ||
| 2865 | } | 2897 | } |
| 2866 | 2898 | ||
| 2867 | err = target__validate(&trace.opts.target); | 2899 | err = target__validate(&trace.opts.target); |
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index ee41e705b2eb..d20d6e6ab65b 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build | |||
| @@ -31,6 +31,7 @@ perf-y += code-reading.o | |||
| 31 | perf-y += sample-parsing.o | 31 | perf-y += sample-parsing.o |
| 32 | perf-y += parse-no-sample-id-all.o | 32 | perf-y += parse-no-sample-id-all.o |
| 33 | perf-y += kmod-path.o | 33 | perf-y += kmod-path.o |
| 34 | perf-y += thread-map.o | ||
| 34 | 35 | ||
| 35 | perf-$(CONFIG_X86) += perf-time-to-tsc.o | 36 | perf-$(CONFIG_X86) += perf-time-to-tsc.o |
| 36 | 37 | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 87b9961646e4..c1dde733c3a6 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
| @@ -171,6 +171,10 @@ static struct test { | |||
| 171 | .func = test__kmod_path__parse, | 171 | .func = test__kmod_path__parse, |
| 172 | }, | 172 | }, |
| 173 | { | 173 | { |
| 174 | .desc = "Test thread map", | ||
| 175 | .func = test__thread_map, | ||
| 176 | }, | ||
| 177 | { | ||
| 174 | .func = NULL, | 178 | .func = NULL, |
| 175 | }, | 179 | }, |
| 176 | }; | 180 | }; |
diff --git a/tools/perf/tests/openat-syscall-all-cpus.c b/tools/perf/tests/openat-syscall-all-cpus.c index b8d552b13950..a572f87e9c8d 100644 --- a/tools/perf/tests/openat-syscall-all-cpus.c +++ b/tools/perf/tests/openat-syscall-all-cpus.c | |||
| @@ -78,7 +78,7 @@ int test__openat_syscall_event_on_all_cpus(void) | |||
| 78 | * we use the auto allocation it will allocate just for 1 cpu, | 78 | * we use the auto allocation it will allocate just for 1 cpu, |
| 79 | * as we start by cpu 0. | 79 | * as we start by cpu 0. |
| 80 | */ | 80 | */ |
| 81 | if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) { | 81 | if (perf_evsel__alloc_counts(evsel, cpus->nr, 1) < 0) { |
| 82 | pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr); | 82 | pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr); |
| 83 | goto out_close_fd; | 83 | goto out_close_fd; |
| 84 | } | 84 | } |
| @@ -98,9 +98,9 @@ int test__openat_syscall_event_on_all_cpus(void) | |||
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | expected = nr_openat_calls + cpu; | 100 | expected = nr_openat_calls + cpu; |
| 101 | if (evsel->counts->cpu[cpu].val != expected) { | 101 | if (perf_counts(evsel->counts, cpu, 0)->val != expected) { |
| 102 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", | 102 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", |
| 103 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); | 103 | expected, cpus->map[cpu], perf_counts(evsel->counts, cpu, 0)->val); |
| 104 | err = -1; | 104 | err = -1; |
| 105 | } | 105 | } |
| 106 | } | 106 | } |
diff --git a/tools/perf/tests/openat-syscall.c b/tools/perf/tests/openat-syscall.c index bdfa1f446681..c9a37bc6b33a 100644 --- a/tools/perf/tests/openat-syscall.c +++ b/tools/perf/tests/openat-syscall.c | |||
| @@ -44,9 +44,9 @@ int test__openat_syscall_event(void) | |||
| 44 | goto out_close_fd; | 44 | goto out_close_fd; |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | if (evsel->counts->cpu[0].val != nr_openat_calls) { | 47 | if (perf_counts(evsel->counts, 0, 0)->val != nr_openat_calls) { |
| 48 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n", | 48 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n", |
| 49 | nr_openat_calls, evsel->counts->cpu[0].val); | 49 | nr_openat_calls, perf_counts(evsel->counts, 0, 0)->val); |
| 50 | goto out_close_fd; | 50 | goto out_close_fd; |
| 51 | } | 51 | } |
| 52 | 52 | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 8e5038b48ba8..ebb47d96bc0b 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
| @@ -61,6 +61,7 @@ int test__switch_tracking(void); | |||
| 61 | int test__fdarray__filter(void); | 61 | int test__fdarray__filter(void); |
| 62 | int test__fdarray__add(void); | 62 | int test__fdarray__add(void); |
| 63 | int test__kmod_path__parse(void); | 63 | int test__kmod_path__parse(void); |
| 64 | int test__thread_map(void); | ||
| 64 | 65 | ||
| 65 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__) | 66 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__) |
| 66 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 67 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c new file mode 100644 index 000000000000..5acf000939ea --- /dev/null +++ b/tools/perf/tests/thread-map.c | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | #include <sys/types.h> | ||
| 2 | #include <unistd.h> | ||
| 3 | #include "tests.h" | ||
| 4 | #include "thread_map.h" | ||
| 5 | #include "debug.h" | ||
| 6 | |||
| 7 | int test__thread_map(void) | ||
| 8 | { | ||
| 9 | struct thread_map *map; | ||
| 10 | |||
| 11 | /* test map on current pid */ | ||
| 12 | map = thread_map__new_by_pid(getpid()); | ||
| 13 | TEST_ASSERT_VAL("failed to alloc map", map); | ||
| 14 | |||
| 15 | thread_map__read_comms(map); | ||
| 16 | |||
| 17 | TEST_ASSERT_VAL("wrong nr", map->nr == 1); | ||
| 18 | TEST_ASSERT_VAL("wrong pid", | ||
| 19 | thread_map__pid(map, 0) == getpid()); | ||
| 20 | TEST_ASSERT_VAL("wrong comm", | ||
| 21 | thread_map__comm(map, 0) && | ||
| 22 | !strcmp(thread_map__comm(map, 0), "perf")); | ||
| 23 | thread_map__put(map); | ||
| 24 | |||
| 25 | /* test dummy pid */ | ||
| 26 | map = thread_map__new_dummy(); | ||
| 27 | TEST_ASSERT_VAL("failed to alloc map", map); | ||
| 28 | |||
| 29 | thread_map__read_comms(map); | ||
| 30 | |||
| 31 | TEST_ASSERT_VAL("wrong nr", map->nr == 1); | ||
| 32 | TEST_ASSERT_VAL("wrong pid", thread_map__pid(map, 0) == -1); | ||
| 33 | TEST_ASSERT_VAL("wrong comm", | ||
| 34 | thread_map__comm(map, 0) && | ||
| 35 | !strcmp(thread_map__comm(map, 0), "dummy")); | ||
| 36 | thread_map__put(map); | ||
| 37 | return 0; | ||
| 38 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index a8489b9d2812..037633c1da9d 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
| @@ -289,5 +289,4 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
| 289 | 289 | ||
| 290 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | 290 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, |
| 291 | struct perf_evsel *tracking_evsel); | 291 | struct perf_evsel *tracking_evsel); |
| 292 | |||
| 293 | #endif /* __PERF_EVLIST_H */ | 292 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1b2f480a3e82..2936b3080722 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -898,7 +898,7 @@ void perf_evsel__delete(struct perf_evsel *evsel) | |||
| 898 | free(evsel); | 898 | free(evsel); |
| 899 | } | 899 | } |
| 900 | 900 | ||
| 901 | void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, | 901 | void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, int thread, |
| 902 | struct perf_counts_values *count) | 902 | struct perf_counts_values *count) |
| 903 | { | 903 | { |
| 904 | struct perf_counts_values tmp; | 904 | struct perf_counts_values tmp; |
| @@ -910,8 +910,8 @@ void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, | |||
| 910 | tmp = evsel->prev_raw_counts->aggr; | 910 | tmp = evsel->prev_raw_counts->aggr; |
| 911 | evsel->prev_raw_counts->aggr = *count; | 911 | evsel->prev_raw_counts->aggr = *count; |
| 912 | } else { | 912 | } else { |
| 913 | tmp = evsel->prev_raw_counts->cpu[cpu]; | 913 | tmp = *perf_counts(evsel->prev_raw_counts, cpu, thread); |
| 914 | evsel->prev_raw_counts->cpu[cpu] = *count; | 914 | *perf_counts(evsel->prev_raw_counts, cpu, thread) = *count; |
| 915 | } | 915 | } |
| 916 | 916 | ||
| 917 | count->val = count->val - tmp.val; | 917 | count->val = count->val - tmp.val; |
| @@ -939,20 +939,18 @@ void perf_counts_values__scale(struct perf_counts_values *count, | |||
| 939 | *pscaled = scaled; | 939 | *pscaled = scaled; |
| 940 | } | 940 | } |
| 941 | 941 | ||
| 942 | int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread, | 942 | int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, |
| 943 | perf_evsel__read_cb_t cb) | 943 | struct perf_counts_values *count) |
| 944 | { | 944 | { |
| 945 | struct perf_counts_values count; | 945 | memset(count, 0, sizeof(*count)); |
| 946 | |||
| 947 | memset(&count, 0, sizeof(count)); | ||
| 948 | 946 | ||
| 949 | if (FD(evsel, cpu, thread) < 0) | 947 | if (FD(evsel, cpu, thread) < 0) |
| 950 | return -EINVAL; | 948 | return -EINVAL; |
| 951 | 949 | ||
| 952 | if (readn(FD(evsel, cpu, thread), &count, sizeof(count)) < 0) | 950 | if (readn(FD(evsel, cpu, thread), count, sizeof(*count)) < 0) |
| 953 | return -errno; | 951 | return -errno; |
| 954 | 952 | ||
| 955 | return cb(evsel, cpu, thread, &count); | 953 | return 0; |
| 956 | } | 954 | } |
| 957 | 955 | ||
| 958 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | 956 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, |
| @@ -964,15 +962,15 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | |||
| 964 | if (FD(evsel, cpu, thread) < 0) | 962 | if (FD(evsel, cpu, thread) < 0) |
| 965 | return -EINVAL; | 963 | return -EINVAL; |
| 966 | 964 | ||
| 967 | if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0) | 965 | if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1, thread + 1) < 0) |
| 968 | return -ENOMEM; | 966 | return -ENOMEM; |
| 969 | 967 | ||
| 970 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) | 968 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) |
| 971 | return -errno; | 969 | return -errno; |
| 972 | 970 | ||
| 973 | perf_evsel__compute_deltas(evsel, cpu, &count); | 971 | perf_evsel__compute_deltas(evsel, cpu, thread, &count); |
| 974 | perf_counts_values__scale(&count, scale, NULL); | 972 | perf_counts_values__scale(&count, scale, NULL); |
| 975 | evsel->counts->cpu[cpu] = count; | 973 | *perf_counts(evsel->counts, cpu, thread) = count; |
| 976 | return 0; | 974 | return 0; |
| 977 | } | 975 | } |
| 978 | 976 | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 4dbf32d94dfb..4a7ed5656cf0 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -9,23 +9,7 @@ | |||
| 9 | #include "xyarray.h" | 9 | #include "xyarray.h" |
| 10 | #include "symbol.h" | 10 | #include "symbol.h" |
| 11 | #include "cpumap.h" | 11 | #include "cpumap.h" |
| 12 | 12 | #include "stat.h" | |
| 13 | struct perf_counts_values { | ||
| 14 | union { | ||
| 15 | struct { | ||
| 16 | u64 val; | ||
| 17 | u64 ena; | ||
| 18 | u64 run; | ||
| 19 | }; | ||
| 20 | u64 values[3]; | ||
| 21 | }; | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct perf_counts { | ||
| 25 | s8 scaled; | ||
| 26 | struct perf_counts_values aggr; | ||
| 27 | struct perf_counts_values cpu[]; | ||
| 28 | }; | ||
| 29 | 13 | ||
| 30 | struct perf_evsel; | 14 | struct perf_evsel; |
| 31 | 15 | ||
| @@ -128,7 +112,7 @@ static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel) | |||
| 128 | void perf_counts_values__scale(struct perf_counts_values *count, | 112 | void perf_counts_values__scale(struct perf_counts_values *count, |
| 129 | bool scale, s8 *pscaled); | 113 | bool scale, s8 *pscaled); |
| 130 | 114 | ||
| 131 | void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, | 115 | void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, int thread, |
| 132 | struct perf_counts_values *count); | 116 | struct perf_counts_values *count); |
| 133 | 117 | ||
| 134 | int perf_evsel__object_config(size_t object_size, | 118 | int perf_evsel__object_config(size_t object_size, |
| @@ -245,12 +229,8 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1, | |||
| 245 | (a)->attr.type == (b)->attr.type && \ | 229 | (a)->attr.type == (b)->attr.type && \ |
| 246 | (a)->attr.config == (b)->attr.config) | 230 | (a)->attr.config == (b)->attr.config) |
| 247 | 231 | ||
| 248 | typedef int (perf_evsel__read_cb_t)(struct perf_evsel *evsel, | 232 | int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, |
| 249 | int cpu, int thread, | 233 | struct perf_counts_values *count); |
| 250 | struct perf_counts_values *count); | ||
| 251 | |||
| 252 | int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread, | ||
| 253 | perf_evsel__read_cb_t cb); | ||
| 254 | 234 | ||
| 255 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | 235 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, |
| 256 | int cpu, int thread, bool scale); | 236 | int cpu, int thread, bool scale); |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 5925fec90562..e23ded40c79e 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources | |||
| @@ -20,3 +20,4 @@ util/stat.c | |||
| 20 | util/strlist.c | 20 | util/strlist.c |
| 21 | util/trace-event.c | 21 | util/trace-event.c |
| 22 | ../../lib/rbtree.c | 22 | ../../lib/rbtree.c |
| 23 | util/string.c | ||
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 4014b709f956..f2a0d1521e26 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #include <math.h> | 1 | #include <math.h> |
| 2 | #include "stat.h" | 2 | #include "stat.h" |
| 3 | #include "evlist.h" | ||
| 3 | #include "evsel.h" | 4 | #include "evsel.h" |
| 5 | #include "thread_map.h" | ||
| 4 | 6 | ||
| 5 | void update_stats(struct stats *stats, u64 val) | 7 | void update_stats(struct stats *stats, u64 val) |
| 6 | { | 8 | { |
| @@ -95,33 +97,46 @@ void perf_stat_evsel_id_init(struct perf_evsel *evsel) | |||
| 95 | } | 97 | } |
| 96 | } | 98 | } |
| 97 | 99 | ||
| 98 | struct perf_counts *perf_counts__new(int ncpus) | 100 | struct perf_counts *perf_counts__new(int ncpus, int nthreads) |
| 99 | { | 101 | { |
| 100 | int size = sizeof(struct perf_counts) + | 102 | struct perf_counts *counts = zalloc(sizeof(*counts)); |
| 101 | ncpus * sizeof(struct perf_counts_values); | ||
| 102 | 103 | ||
| 103 | return zalloc(size); | 104 | if (counts) { |
| 105 | struct xyarray *values; | ||
| 106 | |||
| 107 | values = xyarray__new(ncpus, nthreads, sizeof(struct perf_counts_values)); | ||
| 108 | if (!values) { | ||
| 109 | free(counts); | ||
| 110 | return NULL; | ||
| 111 | } | ||
| 112 | |||
| 113 | counts->values = values; | ||
| 114 | } | ||
| 115 | |||
| 116 | return counts; | ||
| 104 | } | 117 | } |
| 105 | 118 | ||
| 106 | void perf_counts__delete(struct perf_counts *counts) | 119 | void perf_counts__delete(struct perf_counts *counts) |
| 107 | { | 120 | { |
| 108 | free(counts); | 121 | if (counts) { |
| 122 | xyarray__delete(counts->values); | ||
| 123 | free(counts); | ||
| 124 | } | ||
| 109 | } | 125 | } |
| 110 | 126 | ||
| 111 | static void perf_counts__reset(struct perf_counts *counts, int ncpus) | 127 | static void perf_counts__reset(struct perf_counts *counts) |
| 112 | { | 128 | { |
| 113 | memset(counts, 0, (sizeof(*counts) + | 129 | xyarray__reset(counts->values); |
| 114 | (ncpus * sizeof(struct perf_counts_values)))); | ||
| 115 | } | 130 | } |
| 116 | 131 | ||
| 117 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus) | 132 | void perf_evsel__reset_counts(struct perf_evsel *evsel) |
| 118 | { | 133 | { |
| 119 | perf_counts__reset(evsel->counts, ncpus); | 134 | perf_counts__reset(evsel->counts); |
| 120 | } | 135 | } |
| 121 | 136 | ||
| 122 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 137 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads) |
| 123 | { | 138 | { |
| 124 | evsel->counts = perf_counts__new(ncpus); | 139 | evsel->counts = perf_counts__new(ncpus, nthreads); |
| 125 | return evsel->counts != NULL ? 0 : -ENOMEM; | 140 | return evsel->counts != NULL ? 0 : -ENOMEM; |
| 126 | } | 141 | } |
| 127 | 142 | ||
| @@ -130,3 +145,96 @@ void perf_evsel__free_counts(struct perf_evsel *evsel) | |||
| 130 | perf_counts__delete(evsel->counts); | 145 | perf_counts__delete(evsel->counts); |
| 131 | evsel->counts = NULL; | 146 | evsel->counts = NULL; |
| 132 | } | 147 | } |
| 148 | |||
| 149 | void perf_evsel__reset_stat_priv(struct perf_evsel *evsel) | ||
| 150 | { | ||
| 151 | int i; | ||
| 152 | struct perf_stat *ps = evsel->priv; | ||
| 153 | |||
| 154 | for (i = 0; i < 3; i++) | ||
| 155 | init_stats(&ps->res_stats[i]); | ||
| 156 | |||
| 157 | perf_stat_evsel_id_init(evsel); | ||
| 158 | } | ||
| 159 | |||
| 160 | int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) | ||
| 161 | { | ||
| 162 | evsel->priv = zalloc(sizeof(struct perf_stat)); | ||
| 163 | if (evsel->priv == NULL) | ||
| 164 | return -ENOMEM; | ||
| 165 | perf_evsel__reset_stat_priv(evsel); | ||
| 166 | return 0; | ||
| 167 | } | ||
| 168 | |||
| 169 | void perf_evsel__free_stat_priv(struct perf_evsel *evsel) | ||
| 170 | { | ||
| 171 | zfree(&evsel->priv); | ||
| 172 | } | ||
| 173 | |||
| 174 | int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel, | ||
| 175 | int ncpus, int nthreads) | ||
| 176 | { | ||
| 177 | struct perf_counts *counts; | ||
| 178 | |||
| 179 | counts = perf_counts__new(ncpus, nthreads); | ||
| 180 | if (counts) | ||
| 181 | evsel->prev_raw_counts = counts; | ||
| 182 | |||
| 183 | return counts ? 0 : -ENOMEM; | ||
| 184 | } | ||
| 185 | |||
| 186 | void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) | ||
| 187 | { | ||
| 188 | perf_counts__delete(evsel->prev_raw_counts); | ||
| 189 | evsel->prev_raw_counts = NULL; | ||
| 190 | } | ||
| 191 | |||
| 192 | int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw) | ||
| 193 | { | ||
| 194 | int ncpus = perf_evsel__nr_cpus(evsel); | ||
| 195 | int nthreads = thread_map__nr(evsel->threads); | ||
| 196 | |||
| 197 | if (perf_evsel__alloc_stat_priv(evsel) < 0 || | ||
| 198 | perf_evsel__alloc_counts(evsel, ncpus, nthreads) < 0 || | ||
| 199 | (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel, ncpus, nthreads) < 0)) | ||
| 200 | return -ENOMEM; | ||
| 201 | |||
| 202 | return 0; | ||
| 203 | } | ||
| 204 | |||
| 205 | int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw) | ||
| 206 | { | ||
| 207 | struct perf_evsel *evsel; | ||
| 208 | |||
| 209 | evlist__for_each(evlist, evsel) { | ||
| 210 | if (perf_evsel__alloc_stats(evsel, alloc_raw)) | ||
| 211 | goto out_free; | ||
| 212 | } | ||
| 213 | |||
| 214 | return 0; | ||
| 215 | |||
| 216 | out_free: | ||
| 217 | perf_evlist__free_stats(evlist); | ||
| 218 | return -1; | ||
| 219 | } | ||
| 220 | |||
| 221 | void perf_evlist__free_stats(struct perf_evlist *evlist) | ||
| 222 | { | ||
| 223 | struct perf_evsel *evsel; | ||
| 224 | |||
| 225 | evlist__for_each(evlist, evsel) { | ||
| 226 | perf_evsel__free_stat_priv(evsel); | ||
| 227 | perf_evsel__free_counts(evsel); | ||
| 228 | perf_evsel__free_prev_raw_counts(evsel); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | void perf_evlist__reset_stats(struct perf_evlist *evlist) | ||
| 233 | { | ||
| 234 | struct perf_evsel *evsel; | ||
| 235 | |||
| 236 | evlist__for_each(evlist, evsel) { | ||
| 237 | perf_evsel__reset_stat_priv(evsel); | ||
| 238 | perf_evsel__reset_counts(evsel); | ||
| 239 | } | ||
| 240 | } | ||
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 093dc3cb28dd..1cfbe0a980ac 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
| 5 | #include <stdio.h> | 5 | #include <stdio.h> |
| 6 | #include "xyarray.h" | ||
| 6 | 7 | ||
| 7 | struct stats | 8 | struct stats |
| 8 | { | 9 | { |
| @@ -29,8 +30,32 @@ enum aggr_mode { | |||
| 29 | AGGR_GLOBAL, | 30 | AGGR_GLOBAL, |
| 30 | AGGR_SOCKET, | 31 | AGGR_SOCKET, |
| 31 | AGGR_CORE, | 32 | AGGR_CORE, |
| 33 | AGGR_THREAD, | ||
| 32 | }; | 34 | }; |
| 33 | 35 | ||
| 36 | struct perf_counts_values { | ||
| 37 | union { | ||
| 38 | struct { | ||
| 39 | u64 val; | ||
| 40 | u64 ena; | ||
| 41 | u64 run; | ||
| 42 | }; | ||
| 43 | u64 values[3]; | ||
| 44 | }; | ||
| 45 | }; | ||
| 46 | |||
| 47 | struct perf_counts { | ||
| 48 | s8 scaled; | ||
| 49 | struct perf_counts_values aggr; | ||
| 50 | struct xyarray *values; | ||
| 51 | }; | ||
| 52 | |||
| 53 | static inline struct perf_counts_values* | ||
| 54 | perf_counts(struct perf_counts *counts, int cpu, int thread) | ||
| 55 | { | ||
| 56 | return xyarray__entry(counts->values, cpu, thread); | ||
| 57 | } | ||
| 58 | |||
| 34 | void update_stats(struct stats *stats, u64 val); | 59 | void update_stats(struct stats *stats, u64 val); |
| 35 | double avg_stats(struct stats *stats); | 60 | double avg_stats(struct stats *stats); |
| 36 | double stddev_stats(struct stats *stats); | 61 | double stddev_stats(struct stats *stats); |
| @@ -46,6 +71,8 @@ static inline void init_stats(struct stats *stats) | |||
| 46 | } | 71 | } |
| 47 | 72 | ||
| 48 | struct perf_evsel; | 73 | struct perf_evsel; |
| 74 | struct perf_evlist; | ||
| 75 | |||
| 49 | bool __perf_evsel_stat__is(struct perf_evsel *evsel, | 76 | bool __perf_evsel_stat__is(struct perf_evsel *evsel, |
| 50 | enum perf_stat_evsel_id id); | 77 | enum perf_stat_evsel_id id); |
| 51 | 78 | ||
| @@ -62,10 +89,24 @@ void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count, | |||
| 62 | void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel, | 89 | void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel, |
| 63 | double avg, int cpu, enum aggr_mode aggr); | 90 | double avg, int cpu, enum aggr_mode aggr); |
| 64 | 91 | ||
| 65 | struct perf_counts *perf_counts__new(int ncpus); | 92 | struct perf_counts *perf_counts__new(int ncpus, int nthreads); |
| 66 | void perf_counts__delete(struct perf_counts *counts); | 93 | void perf_counts__delete(struct perf_counts *counts); |
| 67 | 94 | ||
| 68 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus); | 95 | void perf_evsel__reset_counts(struct perf_evsel *evsel); |
| 69 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 96 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads); |
| 70 | void perf_evsel__free_counts(struct perf_evsel *evsel); | 97 | void perf_evsel__free_counts(struct perf_evsel *evsel); |
| 98 | |||
| 99 | void perf_evsel__reset_stat_priv(struct perf_evsel *evsel); | ||
| 100 | int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel); | ||
| 101 | void perf_evsel__free_stat_priv(struct perf_evsel *evsel); | ||
| 102 | |||
| 103 | int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel, | ||
| 104 | int ncpus, int nthreads); | ||
| 105 | void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel); | ||
| 106 | |||
| 107 | int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw); | ||
| 108 | |||
| 109 | int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw); | ||
| 110 | void perf_evlist__free_stats(struct perf_evlist *evlist); | ||
| 111 | void perf_evlist__reset_stats(struct perf_evlist *evlist); | ||
| 71 | #endif | 112 | #endif |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 504f2d73b7ee..48b588c6951a 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -1132,8 +1132,11 @@ static int dso__load_kcore(struct dso *dso, struct map *map, | |||
| 1132 | INIT_LIST_HEAD(&md.maps); | 1132 | INIT_LIST_HEAD(&md.maps); |
| 1133 | 1133 | ||
| 1134 | fd = open(kcore_filename, O_RDONLY); | 1134 | fd = open(kcore_filename, O_RDONLY); |
| 1135 | if (fd < 0) | 1135 | if (fd < 0) { |
| 1136 | pr_err("%s requires CAP_SYS_RAWIO capability to access.\n", | ||
| 1137 | kcore_filename); | ||
| 1136 | return -EINVAL; | 1138 | return -EINVAL; |
| 1139 | } | ||
| 1137 | 1140 | ||
| 1138 | /* Read new maps into temporary lists */ | 1141 | /* Read new maps into temporary lists */ |
| 1139 | err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md, | 1142 | err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md, |
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 368cc58c6892..da7646d767fe 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c | |||
| @@ -8,9 +8,11 @@ | |||
| 8 | #include <unistd.h> | 8 | #include <unistd.h> |
| 9 | #include "strlist.h" | 9 | #include "strlist.h" |
| 10 | #include <string.h> | 10 | #include <string.h> |
| 11 | #include <api/fs/fs.h> | ||
| 11 | #include "asm/bug.h" | 12 | #include "asm/bug.h" |
| 12 | #include "thread_map.h" | 13 | #include "thread_map.h" |
| 13 | #include "util.h" | 14 | #include "util.h" |
| 15 | #include "debug.h" | ||
| 14 | 16 | ||
| 15 | /* Skip "." and ".." directories */ | 17 | /* Skip "." and ".." directories */ |
| 16 | static int filter(const struct dirent *dir) | 18 | static int filter(const struct dirent *dir) |
| @@ -21,11 +23,26 @@ static int filter(const struct dirent *dir) | |||
| 21 | return 1; | 23 | return 1; |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 26 | static void thread_map__reset(struct thread_map *map, int start, int nr) | ||
| 27 | { | ||
| 28 | size_t size = (nr - start) * sizeof(map->map[0]); | ||
| 29 | |||
| 30 | memset(&map->map[start], 0, size); | ||
| 31 | } | ||
| 32 | |||
| 24 | static struct thread_map *thread_map__realloc(struct thread_map *map, int nr) | 33 | static struct thread_map *thread_map__realloc(struct thread_map *map, int nr) |
| 25 | { | 34 | { |
| 26 | size_t size = sizeof(*map) + sizeof(map->map[0]) * nr; | 35 | size_t size = sizeof(*map) + sizeof(map->map[0]) * nr; |
| 36 | int start = map ? map->nr : 0; | ||
| 27 | 37 | ||
| 28 | return realloc(map, size); | 38 | map = realloc(map, size); |
| 39 | /* | ||
| 40 | * We only realloc to add more items, let's reset new items. | ||
| 41 | */ | ||
| 42 | if (map) | ||
| 43 | thread_map__reset(map, start, nr); | ||
| 44 | |||
| 45 | return map; | ||
| 29 | } | 46 | } |
| 30 | 47 | ||
| 31 | #define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr) | 48 | #define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr) |
| @@ -304,8 +321,12 @@ struct thread_map *thread_map__new_str(const char *pid, const char *tid, | |||
| 304 | static void thread_map__delete(struct thread_map *threads) | 321 | static void thread_map__delete(struct thread_map *threads) |
| 305 | { | 322 | { |
| 306 | if (threads) { | 323 | if (threads) { |
| 324 | int i; | ||
| 325 | |||
| 307 | WARN_ONCE(atomic_read(&threads->refcnt) != 0, | 326 | WARN_ONCE(atomic_read(&threads->refcnt) != 0, |
| 308 | "thread map refcnt unbalanced\n"); | 327 | "thread map refcnt unbalanced\n"); |
| 328 | for (i = 0; i < threads->nr; i++) | ||
| 329 | free(thread_map__comm(threads, i)); | ||
| 309 | free(threads); | 330 | free(threads); |
| 310 | } | 331 | } |
| 311 | } | 332 | } |
| @@ -333,3 +354,56 @@ size_t thread_map__fprintf(struct thread_map *threads, FILE *fp) | |||
| 333 | 354 | ||
| 334 | return printed + fprintf(fp, "\n"); | 355 | return printed + fprintf(fp, "\n"); |
| 335 | } | 356 | } |
| 357 | |||
| 358 | static int get_comm(char **comm, pid_t pid) | ||
| 359 | { | ||
| 360 | char *path; | ||
| 361 | size_t size; | ||
| 362 | int err; | ||
| 363 | |||
| 364 | if (asprintf(&path, "%s/%d/comm", procfs__mountpoint(), pid) == -1) | ||
| 365 | return -ENOMEM; | ||
| 366 | |||
| 367 | err = filename__read_str(path, comm, &size); | ||
| 368 | if (!err) { | ||
| 369 | /* | ||
| 370 | * We're reading 16 bytes, while filename__read_str | ||
| 371 | * allocates data per BUFSIZ bytes, so we can safely | ||
| 372 | * mark the end of the string. | ||
| 373 | */ | ||
| 374 | (*comm)[size] = 0; | ||
| 375 | rtrim(*comm); | ||
| 376 | } | ||
| 377 | |||
| 378 | free(path); | ||
| 379 | return err; | ||
| 380 | } | ||
| 381 | |||
| 382 | static void comm_init(struct thread_map *map, int i) | ||
| 383 | { | ||
| 384 | pid_t pid = thread_map__pid(map, i); | ||
| 385 | char *comm = NULL; | ||
| 386 | |||
| 387 | /* dummy pid comm initialization */ | ||
| 388 | if (pid == -1) { | ||
| 389 | map->map[i].comm = strdup("dummy"); | ||
| 390 | return; | ||
| 391 | } | ||
| 392 | |||
| 393 | /* | ||
| 394 | * The comm name is like extra bonus ;-), | ||
| 395 | * so just warn if we fail for any reason. | ||
| 396 | */ | ||
| 397 | if (get_comm(&comm, pid)) | ||
| 398 | pr_warning("Couldn't resolve comm name for pid %d\n", pid); | ||
| 399 | |||
| 400 | map->map[i].comm = comm; | ||
| 401 | } | ||
| 402 | |||
| 403 | void thread_map__read_comms(struct thread_map *threads) | ||
| 404 | { | ||
| 405 | int i; | ||
| 406 | |||
| 407 | for (i = 0; i < threads->nr; ++i) | ||
| 408 | comm_init(threads, i); | ||
| 409 | } | ||
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 6b0cd2dc006b..af679d8a50f8 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | struct thread_map_data { | 8 | struct thread_map_data { |
| 9 | pid_t pid; | 9 | pid_t pid; |
| 10 | char *comm; | ||
| 10 | }; | 11 | }; |
| 11 | 12 | ||
| 12 | struct thread_map { | 13 | struct thread_map { |
| @@ -44,4 +45,11 @@ thread_map__set_pid(struct thread_map *map, int thread, pid_t pid) | |||
| 44 | { | 45 | { |
| 45 | map->map[thread].pid = pid; | 46 | map->map[thread].pid = pid; |
| 46 | } | 47 | } |
| 48 | |||
| 49 | static inline char *thread_map__comm(struct thread_map *map, int thread) | ||
| 50 | { | ||
| 51 | return map->map[thread].comm; | ||
| 52 | } | ||
| 53 | |||
| 54 | void thread_map__read_comms(struct thread_map *threads); | ||
| 47 | #endif /* __PERF_THREAD_MAP_H */ | 55 | #endif /* __PERF_THREAD_MAP_H */ |
