diff options
Diffstat (limited to 'tools')
52 files changed, 2604 insertions, 302 deletions
diff --git a/tools/Makefile b/tools/Makefile index 41067f304215..a9b02008443c 100644 --- a/tools/Makefile +++ b/tools/Makefile | |||
@@ -15,6 +15,7 @@ help: | |||
15 | @echo ' net - misc networking tools' | 15 | @echo ' net - misc networking tools' |
16 | @echo ' vm - misc vm tools' | 16 | @echo ' vm - misc vm tools' |
17 | @echo ' x86_energy_perf_policy - Intel energy policy tool' | 17 | @echo ' x86_energy_perf_policy - Intel energy policy tool' |
18 | @echo ' tmon - thermal monitoring and tuning tool' | ||
18 | @echo '' | 19 | @echo '' |
19 | @echo 'You can do:' | 20 | @echo 'You can do:' |
20 | @echo ' $$ make -C tools/ <tool>_install' | 21 | @echo ' $$ make -C tools/ <tool>_install' |
@@ -50,6 +51,9 @@ selftests: FORCE | |||
50 | turbostat x86_energy_perf_policy: FORCE | 51 | turbostat x86_energy_perf_policy: FORCE |
51 | $(call descend,power/x86/$@) | 52 | $(call descend,power/x86/$@) |
52 | 53 | ||
54 | tmon: FORCE | ||
55 | $(call descend,thermal/$@) | ||
56 | |||
53 | cpupower_install: | 57 | cpupower_install: |
54 | $(call descend,power/$(@:_install=),install) | 58 | $(call descend,power/$(@:_install=),install) |
55 | 59 | ||
@@ -62,9 +66,13 @@ selftests_install: | |||
62 | turbostat_install x86_energy_perf_policy_install: | 66 | turbostat_install x86_energy_perf_policy_install: |
63 | $(call descend,power/x86/$(@:_install=),install) | 67 | $(call descend,power/x86/$(@:_install=),install) |
64 | 68 | ||
69 | tmon_install: | ||
70 | $(call descend,thermal/$(@:_install=),install) | ||
71 | |||
65 | install: cgroup_install cpupower_install firewire_install lguest_install \ | 72 | install: cgroup_install cpupower_install firewire_install lguest_install \ |
66 | perf_install selftests_install turbostat_install usb_install \ | 73 | perf_install selftests_install turbostat_install usb_install \ |
67 | virtio_install vm_install net_install x86_energy_perf_policy_install | 74 | virtio_install vm_install net_install x86_energy_perf_policy_install \ |
75 | tmon | ||
68 | 76 | ||
69 | cpupower_clean: | 77 | cpupower_clean: |
70 | $(call descend,power/cpupower,clean) | 78 | $(call descend,power/cpupower,clean) |
@@ -84,8 +92,11 @@ selftests_clean: | |||
84 | turbostat_clean x86_energy_perf_policy_clean: | 92 | turbostat_clean x86_energy_perf_policy_clean: |
85 | $(call descend,power/x86/$(@:_clean=),clean) | 93 | $(call descend,power/x86/$(@:_clean=),clean) |
86 | 94 | ||
95 | tmon_clean: | ||
96 | $(call descend,thermal/tmon,clean) | ||
97 | |||
87 | clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \ | 98 | clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \ |
88 | selftests_clean turbostat_clean usb_clean virtio_clean \ | 99 | selftests_clean turbostat_clean usb_clean virtio_clean \ |
89 | vm_clean net_clean x86_energy_perf_policy_clean | 100 | vm_clean net_clean x86_energy_perf_policy_clean tmon_clean |
90 | 101 | ||
91 | .PHONY: FORCE | 102 | .PHONY: FORCE |
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 8f450adaa9c2..0362d575de7d 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
@@ -3435,6 +3435,19 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg | |||
3435 | goto out_warning_op; | 3435 | goto out_warning_op; |
3436 | } | 3436 | } |
3437 | break; | 3437 | break; |
3438 | case PRINT_DYNAMIC_ARRAY: | ||
3439 | /* Without [], we pass the address to the dynamic data */ | ||
3440 | offset = pevent_read_number(pevent, | ||
3441 | data + arg->dynarray.field->offset, | ||
3442 | arg->dynarray.field->size); | ||
3443 | /* | ||
3444 | * The actual length of the dynamic array is stored | ||
3445 | * in the top half of the field, and the offset | ||
3446 | * is in the bottom half of the 32 bit field. | ||
3447 | */ | ||
3448 | offset &= 0xffff; | ||
3449 | val = (unsigned long long)(data + offset); | ||
3450 | break; | ||
3438 | default: /* not sure what to do there */ | 3451 | default: /* not sure what to do there */ |
3439 | return 0; | 3452 | return 0; |
3440 | } | 3453 | } |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 052f7c4dc00c..43b42c4f4a91 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -201,6 +201,12 @@ abort events and some memory events in precise mode on modern Intel CPUs. | |||
201 | --transaction:: | 201 | --transaction:: |
202 | Record transaction flags for transaction related events. | 202 | Record transaction flags for transaction related events. |
203 | 203 | ||
204 | --force-per-cpu:: | ||
205 | Force the use of per-cpu mmaps. By default, when tasks are specified (i.e. -p, | ||
206 | -t or -u options) per-thread mmaps are created. This option overrides that and | ||
207 | forces per-cpu mmaps. A side-effect of that is that inheritance is | ||
208 | automatically enabled. Add the -i option also to disable inheritance. | ||
209 | |||
204 | SEE ALSO | 210 | SEE ALSO |
205 | -------- | 211 | -------- |
206 | linkperf:perf-stat[1], linkperf:perf-list[1] | 212 | linkperf:perf-stat[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 7b0497f95a75..fae38d9a44a4 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -93,9 +93,15 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. | |||
93 | --comm:: | 93 | --comm:: |
94 | Show process COMM right beside its ID, on by default, disable with --no-comm. | 94 | Show process COMM right beside its ID, on by default, disable with --no-comm. |
95 | 95 | ||
96 | -s:: | ||
96 | --summary:: | 97 | --summary:: |
97 | Show a summary of syscalls by thread with min, max, and average times (in | 98 | Show only a summary of syscalls by thread with min, max, and average times |
98 | msec) and relative stddev. | 99 | (in msec) and relative stddev. |
100 | |||
101 | -S:: | ||
102 | --with-summary:: | ||
103 | Show all syscalls followed by a summary by thread with min, max, and | ||
104 | average times (in msec) and relative stddev. | ||
99 | 105 | ||
100 | --tool_stats:: | 106 | --tool_stats:: |
101 | Show tool stats such as number of times fd->pathname was discovered thru | 107 | Show tool stats such as number of times fd->pathname was discovered thru |
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index cd9f92078aba..f8bf5f244d77 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -1510,13 +1510,13 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
1510 | /* | 1510 | /* |
1511 | * target related setups | 1511 | * target related setups |
1512 | */ | 1512 | */ |
1513 | err = perf_target__validate(&kvm->opts.target); | 1513 | err = target__validate(&kvm->opts.target); |
1514 | if (err) { | 1514 | if (err) { |
1515 | perf_target__strerror(&kvm->opts.target, err, errbuf, BUFSIZ); | 1515 | target__strerror(&kvm->opts.target, err, errbuf, BUFSIZ); |
1516 | ui__warning("%s", errbuf); | 1516 | ui__warning("%s", errbuf); |
1517 | } | 1517 | } |
1518 | 1518 | ||
1519 | if (perf_target__none(&kvm->opts.target)) | 1519 | if (target__none(&kvm->opts.target)) |
1520 | kvm->opts.target.system_wide = true; | 1520 | kvm->opts.target.system_wide = true; |
1521 | 1521 | ||
1522 | 1522 | ||
@@ -1544,18 +1544,8 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
1544 | } | 1544 | } |
1545 | kvm->session->evlist = kvm->evlist; | 1545 | kvm->session->evlist = kvm->evlist; |
1546 | perf_session__set_id_hdr_size(kvm->session); | 1546 | perf_session__set_id_hdr_size(kvm->session); |
1547 | 1547 | machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target, | |
1548 | 1548 | kvm->evlist->threads, false); | |
1549 | if (perf_target__has_task(&kvm->opts.target)) | ||
1550 | perf_event__synthesize_thread_map(&kvm->tool, | ||
1551 | kvm->evlist->threads, | ||
1552 | perf_event__process, | ||
1553 | &kvm->session->machines.host); | ||
1554 | else | ||
1555 | perf_event__synthesize_threads(&kvm->tool, perf_event__process, | ||
1556 | &kvm->session->machines.host); | ||
1557 | |||
1558 | |||
1559 | err = kvm_live_open_events(kvm); | 1549 | err = kvm_live_open_events(kvm); |
1560 | if (err) | 1550 | if (err) |
1561 | goto out; | 1551 | goto out; |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 89acc17cf2a0..6ea9e85bdc00 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -325,6 +325,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
325 | opt_set_filter), | 325 | opt_set_filter), |
326 | OPT_CALLBACK('x', "exec", NULL, "executable|path", | 326 | OPT_CALLBACK('x', "exec", NULL, "executable|path", |
327 | "target executable name or path", opt_set_target), | 327 | "target executable name or path", opt_set_target), |
328 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, | ||
329 | "Disable symbol demangling"), | ||
328 | OPT_END() | 330 | OPT_END() |
329 | }; | 331 | }; |
330 | int ret; | 332 | int ret; |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 15280b5e5574..7c8020a32784 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -76,12 +76,12 @@ struct perf_record { | |||
76 | long samples; | 76 | long samples; |
77 | }; | 77 | }; |
78 | 78 | ||
79 | static int write_output(struct perf_record *rec, void *buf, size_t size) | 79 | static int do_write_output(struct perf_record *rec, void *buf, size_t size) |
80 | { | 80 | { |
81 | struct perf_data_file *file = &rec->file; | 81 | struct perf_data_file *file = &rec->file; |
82 | 82 | ||
83 | while (size) { | 83 | while (size) { |
84 | int ret = write(file->fd, buf, size); | 84 | ssize_t ret = write(file->fd, buf, size); |
85 | 85 | ||
86 | if (ret < 0) { | 86 | if (ret < 0) { |
87 | pr_err("failed to write perf data, error: %m\n"); | 87 | pr_err("failed to write perf data, error: %m\n"); |
@@ -97,6 +97,11 @@ static int write_output(struct perf_record *rec, void *buf, size_t size) | |||
97 | return 0; | 97 | return 0; |
98 | } | 98 | } |
99 | 99 | ||
100 | static int write_output(struct perf_record *rec, void *buf, size_t size) | ||
101 | { | ||
102 | return do_write_output(rec, buf, size); | ||
103 | } | ||
104 | |||
100 | static int process_synthesized_event(struct perf_tool *tool, | 105 | static int process_synthesized_event(struct perf_tool *tool, |
101 | union perf_event *event, | 106 | union perf_event *event, |
102 | struct perf_sample *sample __maybe_unused, | 107 | struct perf_sample *sample __maybe_unused, |
@@ -480,16 +485,8 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
480 | perf_event__synthesize_guest_os, tool); | 485 | perf_event__synthesize_guest_os, tool); |
481 | } | 486 | } |
482 | 487 | ||
483 | if (perf_target__has_task(&opts->target)) | 488 | err = __machine__synthesize_threads(machine, tool, &opts->target, evsel_list->threads, |
484 | err = perf_event__synthesize_thread_map(tool, evsel_list->threads, | 489 | process_synthesized_event, opts->sample_address); |
485 | process_synthesized_event, | ||
486 | machine); | ||
487 | else if (perf_target__has_cpu(&opts->target)) | ||
488 | err = perf_event__synthesize_threads(tool, process_synthesized_event, | ||
489 | machine); | ||
490 | else /* command specified */ | ||
491 | err = 0; | ||
492 | |||
493 | if (err != 0) | 490 | if (err != 0) |
494 | goto out_delete_session; | 491 | goto out_delete_session; |
495 | 492 | ||
@@ -509,7 +506,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
509 | * (apart from group members) have enable_on_exec=1 set, | 506 | * (apart from group members) have enable_on_exec=1 set, |
510 | * so don't spoil it by prematurely enabling them. | 507 | * so don't spoil it by prematurely enabling them. |
511 | */ | 508 | */ |
512 | if (!perf_target__none(&opts->target)) | 509 | if (!target__none(&opts->target)) |
513 | perf_evlist__enable(evsel_list); | 510 | perf_evlist__enable(evsel_list); |
514 | 511 | ||
515 | /* | 512 | /* |
@@ -538,7 +535,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
538 | * die with the process and we wait for that. Thus no need to | 535 | * die with the process and we wait for that. Thus no need to |
539 | * disable events in this case. | 536 | * disable events in this case. |
540 | */ | 537 | */ |
541 | if (done && !disabled && !perf_target__none(&opts->target)) { | 538 | if (done && !disabled && !target__none(&opts->target)) { |
542 | perf_evlist__disable(evsel_list); | 539 | perf_evlist__disable(evsel_list); |
543 | disabled = true; | 540 | disabled = true; |
544 | } | 541 | } |
@@ -891,6 +888,8 @@ const struct option record_options[] = { | |||
891 | "sample by weight (on special events only)"), | 888 | "sample by weight (on special events only)"), |
892 | OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction, | 889 | OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction, |
893 | "sample transaction flags (special events only)"), | 890 | "sample transaction flags (special events only)"), |
891 | OPT_BOOLEAN(0, "force-per-cpu", &record.opts.target.force_per_cpu, | ||
892 | "force the use of per-cpu mmaps"), | ||
894 | OPT_END() | 893 | OPT_END() |
895 | }; | 894 | }; |
896 | 895 | ||
@@ -909,7 +908,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
909 | 908 | ||
910 | argc = parse_options(argc, argv, record_options, record_usage, | 909 | argc = parse_options(argc, argv, record_options, record_usage, |
911 | PARSE_OPT_STOP_AT_NON_OPTION); | 910 | PARSE_OPT_STOP_AT_NON_OPTION); |
912 | if (!argc && perf_target__none(&rec->opts.target)) | 911 | if (!argc && target__none(&rec->opts.target)) |
913 | usage_with_options(record_usage, record_options); | 912 | usage_with_options(record_usage, record_options); |
914 | 913 | ||
915 | if (nr_cgroups && !rec->opts.target.system_wide) { | 914 | if (nr_cgroups && !rec->opts.target.system_wide) { |
@@ -939,17 +938,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
939 | goto out_symbol_exit; | 938 | goto out_symbol_exit; |
940 | } | 939 | } |
941 | 940 | ||
942 | err = perf_target__validate(&rec->opts.target); | 941 | err = target__validate(&rec->opts.target); |
943 | if (err) { | 942 | if (err) { |
944 | perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); | 943 | target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); |
945 | ui__warning("%s", errbuf); | 944 | ui__warning("%s", errbuf); |
946 | } | 945 | } |
947 | 946 | ||
948 | err = perf_target__parse_uid(&rec->opts.target); | 947 | err = target__parse_uid(&rec->opts.target); |
949 | if (err) { | 948 | if (err) { |
950 | int saved_errno = errno; | 949 | int saved_errno = errno; |
951 | 950 | ||
952 | perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); | 951 | target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); |
953 | ui__error("%s", errbuf); | 952 | ui__error("%s", errbuf); |
954 | 953 | ||
955 | err = -saved_errno; | 954 | err = -saved_errno; |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 0fc1c941a73c..ee0d565f83e3 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -108,7 +108,7 @@ enum { | |||
108 | 108 | ||
109 | static struct perf_evlist *evsel_list; | 109 | static struct perf_evlist *evsel_list; |
110 | 110 | ||
111 | static struct perf_target target = { | 111 | static struct target target = { |
112 | .uid = UINT_MAX, | 112 | .uid = UINT_MAX, |
113 | }; | 113 | }; |
114 | 114 | ||
@@ -294,11 +294,10 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
294 | 294 | ||
295 | attr->inherit = !no_inherit; | 295 | attr->inherit = !no_inherit; |
296 | 296 | ||
297 | if (perf_target__has_cpu(&target)) | 297 | if (target__has_cpu(&target)) |
298 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); | 298 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); |
299 | 299 | ||
300 | if (!perf_target__has_task(&target) && | 300 | if (!target__has_task(&target) && perf_evsel__is_group_leader(evsel)) { |
301 | perf_evsel__is_group_leader(evsel)) { | ||
302 | attr->disabled = 1; | 301 | attr->disabled = 1; |
303 | if (!initial_delay) | 302 | if (!initial_delay) |
304 | attr->enable_on_exec = 1; | 303 | attr->enable_on_exec = 1; |
@@ -1236,7 +1235,7 @@ static void print_stat(int argc, const char **argv) | |||
1236 | fprintf(output, "\'system wide"); | 1235 | fprintf(output, "\'system wide"); |
1237 | else if (target.cpu_list) | 1236 | else if (target.cpu_list) |
1238 | fprintf(output, "\'CPU(s) %s", target.cpu_list); | 1237 | fprintf(output, "\'CPU(s) %s", target.cpu_list); |
1239 | else if (!perf_target__has_task(&target)) { | 1238 | else if (!target__has_task(&target)) { |
1240 | fprintf(output, "\'%s", argv[0]); | 1239 | fprintf(output, "\'%s", argv[0]); |
1241 | for (i = 1; i < argc; i++) | 1240 | for (i = 1; i < argc; i++) |
1242 | fprintf(output, " %s", argv[i]); | 1241 | fprintf(output, " %s", argv[i]); |
@@ -1667,7 +1666,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1667 | } else if (big_num_opt == 0) /* User passed --no-big-num */ | 1666 | } else if (big_num_opt == 0) /* User passed --no-big-num */ |
1668 | big_num = false; | 1667 | big_num = false; |
1669 | 1668 | ||
1670 | if (!argc && perf_target__none(&target)) | 1669 | if (!argc && target__none(&target)) |
1671 | usage_with_options(stat_usage, options); | 1670 | usage_with_options(stat_usage, options); |
1672 | 1671 | ||
1673 | if (run_count < 0) { | 1672 | if (run_count < 0) { |
@@ -1680,8 +1679,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1680 | } | 1679 | } |
1681 | 1680 | ||
1682 | /* no_aggr, cgroup are for system-wide only */ | 1681 | /* no_aggr, cgroup are for system-wide only */ |
1683 | if ((aggr_mode != AGGR_GLOBAL || nr_cgroups) | 1682 | if ((aggr_mode != AGGR_GLOBAL || nr_cgroups) && |
1684 | && !perf_target__has_cpu(&target)) { | 1683 | !target__has_cpu(&target)) { |
1685 | fprintf(stderr, "both cgroup and no-aggregation " | 1684 | fprintf(stderr, "both cgroup and no-aggregation " |
1686 | "modes only available in system-wide mode\n"); | 1685 | "modes only available in system-wide mode\n"); |
1687 | 1686 | ||
@@ -1694,14 +1693,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1694 | if (add_default_attributes()) | 1693 | if (add_default_attributes()) |
1695 | goto out; | 1694 | goto out; |
1696 | 1695 | ||
1697 | perf_target__validate(&target); | 1696 | target__validate(&target); |
1698 | 1697 | ||
1699 | if (perf_evlist__create_maps(evsel_list, &target) < 0) { | 1698 | if (perf_evlist__create_maps(evsel_list, &target) < 0) { |
1700 | if (perf_target__has_task(&target)) { | 1699 | if (target__has_task(&target)) { |
1701 | pr_err("Problems finding threads of monitor\n"); | 1700 | pr_err("Problems finding threads of monitor\n"); |
1702 | parse_options_usage(stat_usage, options, "p", 1); | 1701 | parse_options_usage(stat_usage, options, "p", 1); |
1703 | parse_options_usage(NULL, options, "t", 1); | 1702 | parse_options_usage(NULL, options, "t", 1); |
1704 | } else if (perf_target__has_cpu(&target)) { | 1703 | } else if (target__has_cpu(&target)) { |
1705 | perror("failed to parse CPUs map"); | 1704 | perror("failed to parse CPUs map"); |
1706 | parse_options_usage(stat_usage, options, "C", 1); | 1705 | parse_options_usage(stat_usage, options, "C", 1); |
1707 | parse_options_usage(NULL, options, "a", 1); | 1706 | parse_options_usage(NULL, options, "a", 1); |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 9acca8856ccb..71e6402729a8 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -950,14 +950,8 @@ static int __cmd_top(struct perf_top *top) | |||
950 | if (ret) | 950 | if (ret) |
951 | goto out_delete; | 951 | goto out_delete; |
952 | 952 | ||
953 | if (perf_target__has_task(&opts->target)) | 953 | machine__synthesize_threads(&top->session->machines.host, &opts->target, |
954 | perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, | 954 | top->evlist->threads, false); |
955 | perf_event__process, | ||
956 | &top->session->machines.host); | ||
957 | else | ||
958 | perf_event__synthesize_threads(&top->tool, perf_event__process, | ||
959 | &top->session->machines.host); | ||
960 | |||
961 | ret = perf_top__start_counters(top); | 955 | ret = perf_top__start_counters(top); |
962 | if (ret) | 956 | if (ret) |
963 | goto out_delete; | 957 | goto out_delete; |
@@ -973,7 +967,7 @@ static int __cmd_top(struct perf_top *top) | |||
973 | * XXX 'top' still doesn't start workloads like record, trace, but should, | 967 | * XXX 'top' still doesn't start workloads like record, trace, but should, |
974 | * so leave the check here. | 968 | * so leave the check here. |
975 | */ | 969 | */ |
976 | if (!perf_target__none(&opts->target)) | 970 | if (!target__none(&opts->target)) |
977 | perf_evlist__enable(top->evlist); | 971 | perf_evlist__enable(top->evlist); |
978 | 972 | ||
979 | /* Wait for a minimal set of events before starting the snapshot */ | 973 | /* Wait for a minimal set of events before starting the snapshot */ |
@@ -1059,7 +1053,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1059 | .sym_pcnt_filter = 5, | 1053 | .sym_pcnt_filter = 5, |
1060 | }; | 1054 | }; |
1061 | struct perf_record_opts *opts = &top.record_opts; | 1055 | struct perf_record_opts *opts = &top.record_opts; |
1062 | struct perf_target *target = &opts->target; | 1056 | struct target *target = &opts->target; |
1063 | const struct option options[] = { | 1057 | const struct option options[] = { |
1064 | OPT_CALLBACK('e', "event", &top.evlist, "event", | 1058 | OPT_CALLBACK('e', "event", &top.evlist, "event", |
1065 | "event selector. use 'perf list' to list available events", | 1059 | "event selector. use 'perf list' to list available events", |
@@ -1175,24 +1169,24 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1175 | 1169 | ||
1176 | setup_browser(false); | 1170 | setup_browser(false); |
1177 | 1171 | ||
1178 | status = perf_target__validate(target); | 1172 | status = target__validate(target); |
1179 | if (status) { | 1173 | if (status) { |
1180 | perf_target__strerror(target, status, errbuf, BUFSIZ); | 1174 | target__strerror(target, status, errbuf, BUFSIZ); |
1181 | ui__warning("%s", errbuf); | 1175 | ui__warning("%s\n", errbuf); |
1182 | } | 1176 | } |
1183 | 1177 | ||
1184 | status = perf_target__parse_uid(target); | 1178 | status = target__parse_uid(target); |
1185 | if (status) { | 1179 | if (status) { |
1186 | int saved_errno = errno; | 1180 | int saved_errno = errno; |
1187 | 1181 | ||
1188 | perf_target__strerror(target, status, errbuf, BUFSIZ); | 1182 | target__strerror(target, status, errbuf, BUFSIZ); |
1189 | ui__error("%s", errbuf); | 1183 | ui__error("%s\n", errbuf); |
1190 | 1184 | ||
1191 | status = -saved_errno; | 1185 | status = -saved_errno; |
1192 | goto out_delete_evlist; | 1186 | goto out_delete_evlist; |
1193 | } | 1187 | } |
1194 | 1188 | ||
1195 | if (perf_target__none(target)) | 1189 | if (target__none(target)) |
1196 | target->system_wide = true; | 1190 | target->system_wide = true; |
1197 | 1191 | ||
1198 | if (perf_evlist__create_maps(top.evlist, target) < 0) | 1192 | if (perf_evlist__create_maps(top.evlist, target) < 0) |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 329b7832b5da..8be17fc462ba 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -149,21 +149,32 @@ static void perf_evsel__delete_priv(struct perf_evsel *evsel) | |||
149 | perf_evsel__delete(evsel); | 149 | perf_evsel__delete(evsel); |
150 | } | 150 | } |
151 | 151 | ||
152 | static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, | 152 | static int perf_evsel__init_syscall_tp(struct perf_evsel *evsel, void *handler) |
153 | void *handler, int idx) | ||
154 | { | 153 | { |
155 | struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction, idx); | 154 | evsel->priv = malloc(sizeof(struct syscall_tp)); |
156 | 155 | if (evsel->priv != NULL) { | |
157 | if (evsel) { | ||
158 | evsel->priv = malloc(sizeof(struct syscall_tp)); | ||
159 | |||
160 | if (evsel->priv == NULL) | ||
161 | goto out_delete; | ||
162 | |||
163 | if (perf_evsel__init_sc_tp_uint_field(evsel, id)) | 156 | if (perf_evsel__init_sc_tp_uint_field(evsel, id)) |
164 | goto out_delete; | 157 | goto out_delete; |
165 | 158 | ||
166 | evsel->handler = handler; | 159 | evsel->handler = handler; |
160 | return 0; | ||
161 | } | ||
162 | |||
163 | return -ENOMEM; | ||
164 | |||
165 | out_delete: | ||
166 | free(evsel->priv); | ||
167 | evsel->priv = NULL; | ||
168 | return -ENOENT; | ||
169 | } | ||
170 | |||
171 | static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void *handler) | ||
172 | { | ||
173 | struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction); | ||
174 | |||
175 | if (evsel) { | ||
176 | if (perf_evsel__init_syscall_tp(evsel, handler)) | ||
177 | goto out_delete; | ||
167 | } | 178 | } |
168 | 179 | ||
169 | return evsel; | 180 | return evsel; |
@@ -186,17 +197,16 @@ static int perf_evlist__add_syscall_newtp(struct perf_evlist *evlist, | |||
186 | void *sys_exit_handler) | 197 | void *sys_exit_handler) |
187 | { | 198 | { |
188 | int ret = -1; | 199 | int ret = -1; |
189 | int idx = evlist->nr_entries; | ||
190 | struct perf_evsel *sys_enter, *sys_exit; | 200 | struct perf_evsel *sys_enter, *sys_exit; |
191 | 201 | ||
192 | sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler, idx++); | 202 | sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler); |
193 | if (sys_enter == NULL) | 203 | if (sys_enter == NULL) |
194 | goto out; | 204 | goto out; |
195 | 205 | ||
196 | if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args)) | 206 | if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args)) |
197 | goto out_delete_sys_enter; | 207 | goto out_delete_sys_enter; |
198 | 208 | ||
199 | sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler, idx++); | 209 | sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler); |
200 | if (sys_exit == NULL) | 210 | if (sys_exit == NULL) |
201 | goto out_delete_sys_enter; | 211 | goto out_delete_sys_enter; |
202 | 212 | ||
@@ -953,7 +963,8 @@ static struct syscall_fmt { | |||
953 | { .name = "mmap", .hexret = true, | 963 | { .name = "mmap", .hexret = true, |
954 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | 964 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ |
955 | [2] = SCA_MMAP_PROT, /* prot */ | 965 | [2] = SCA_MMAP_PROT, /* prot */ |
956 | [3] = SCA_MMAP_FLAGS, /* flags */ }, }, | 966 | [3] = SCA_MMAP_FLAGS, /* flags */ |
967 | [4] = SCA_FD, /* fd */ }, }, | ||
957 | { .name = "mprotect", .errmsg = true, | 968 | { .name = "mprotect", .errmsg = true, |
958 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ | 969 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ |
959 | [2] = SCA_MMAP_PROT, /* prot */ }, }, | 970 | [2] = SCA_MMAP_PROT, /* prot */ }, }, |
@@ -1157,6 +1168,7 @@ struct trace { | |||
1157 | bool sched; | 1168 | bool sched; |
1158 | bool multiple_threads; | 1169 | bool multiple_threads; |
1159 | bool summary; | 1170 | bool summary; |
1171 | bool summary_only; | ||
1160 | bool show_comm; | 1172 | bool show_comm; |
1161 | bool show_tool_stats; | 1173 | bool show_tool_stats; |
1162 | double duration_filter; | 1174 | double duration_filter; |
@@ -1342,15 +1354,8 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | |||
1342 | if (trace->host == NULL) | 1354 | if (trace->host == NULL) |
1343 | return -ENOMEM; | 1355 | return -ENOMEM; |
1344 | 1356 | ||
1345 | if (perf_target__has_task(&trace->opts.target)) { | 1357 | err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target, |
1346 | err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads, | 1358 | evlist->threads, trace__tool_process, false); |
1347 | trace__tool_process, | ||
1348 | trace->host); | ||
1349 | } else { | ||
1350 | err = perf_event__synthesize_threads(&trace->tool, trace__tool_process, | ||
1351 | trace->host); | ||
1352 | } | ||
1353 | |||
1354 | if (err) | 1359 | if (err) |
1355 | symbol__exit(); | 1360 | symbol__exit(); |
1356 | 1361 | ||
@@ -1607,7 +1612,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1607 | args, trace, thread); | 1612 | args, trace, thread); |
1608 | 1613 | ||
1609 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | 1614 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { |
1610 | if (!trace->duration_filter) { | 1615 | if (!trace->duration_filter && !trace->summary_only) { |
1611 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); | 1616 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); |
1612 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); | 1617 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); |
1613 | } | 1618 | } |
@@ -1660,6 +1665,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1660 | } else if (trace->duration_filter) | 1665 | } else if (trace->duration_filter) |
1661 | goto out; | 1666 | goto out; |
1662 | 1667 | ||
1668 | if (trace->summary_only) | ||
1669 | goto out; | ||
1670 | |||
1663 | trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); | 1671 | trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); |
1664 | 1672 | ||
1665 | if (ttrace->entry_pending) { | 1673 | if (ttrace->entry_pending) { |
@@ -1762,16 +1770,6 @@ static int trace__process_sample(struct perf_tool *tool, | |||
1762 | return err; | 1770 | return err; |
1763 | } | 1771 | } |
1764 | 1772 | ||
1765 | static bool | ||
1766 | perf_session__has_tp(struct perf_session *session, const char *name) | ||
1767 | { | ||
1768 | struct perf_evsel *evsel; | ||
1769 | |||
1770 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, name); | ||
1771 | |||
1772 | return evsel != NULL; | ||
1773 | } | ||
1774 | |||
1775 | static int parse_target_str(struct trace *trace) | 1773 | static int parse_target_str(struct trace *trace) |
1776 | { | 1774 | { |
1777 | if (trace->opts.target.pid) { | 1775 | if (trace->opts.target.pid) { |
@@ -1824,8 +1822,7 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); | |||
1824 | 1822 | ||
1825 | static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) | 1823 | static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) |
1826 | { | 1824 | { |
1827 | struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname", | 1825 | struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname"); |
1828 | evlist->nr_entries); | ||
1829 | if (evsel == NULL) | 1826 | if (evsel == NULL) |
1830 | return; | 1827 | return; |
1831 | 1828 | ||
@@ -2009,8 +2006,6 @@ out_error: | |||
2009 | static int trace__replay(struct trace *trace) | 2006 | static int trace__replay(struct trace *trace) |
2010 | { | 2007 | { |
2011 | const struct perf_evsel_str_handler handlers[] = { | 2008 | const struct perf_evsel_str_handler handlers[] = { |
2012 | { "raw_syscalls:sys_enter", trace__sys_enter, }, | ||
2013 | { "raw_syscalls:sys_exit", trace__sys_exit, }, | ||
2014 | { "probe:vfs_getname", trace__vfs_getname, }, | 2009 | { "probe:vfs_getname", trace__vfs_getname, }, |
2015 | }; | 2010 | }; |
2016 | struct perf_data_file file = { | 2011 | struct perf_data_file file = { |
@@ -2018,6 +2013,7 @@ static int trace__replay(struct trace *trace) | |||
2018 | .mode = PERF_DATA_MODE_READ, | 2013 | .mode = PERF_DATA_MODE_READ, |
2019 | }; | 2014 | }; |
2020 | struct perf_session *session; | 2015 | struct perf_session *session; |
2016 | struct perf_evsel *evsel; | ||
2021 | int err = -1; | 2017 | int err = -1; |
2022 | 2018 | ||
2023 | trace->tool.sample = trace__process_sample; | 2019 | trace->tool.sample = trace__process_sample; |
@@ -2049,13 +2045,29 @@ static int trace__replay(struct trace *trace) | |||
2049 | if (err) | 2045 | if (err) |
2050 | goto out; | 2046 | goto out; |
2051 | 2047 | ||
2052 | if (!perf_session__has_tp(session, "raw_syscalls:sys_enter")) { | 2048 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, |
2053 | pr_err("Data file does not have raw_syscalls:sys_enter events\n"); | 2049 | "raw_syscalls:sys_enter"); |
2050 | if (evsel == NULL) { | ||
2051 | pr_err("Data file does not have raw_syscalls:sys_enter event\n"); | ||
2054 | goto out; | 2052 | goto out; |
2055 | } | 2053 | } |
2056 | 2054 | ||
2057 | if (!perf_session__has_tp(session, "raw_syscalls:sys_exit")) { | 2055 | if (perf_evsel__init_syscall_tp(evsel, trace__sys_enter) < 0 || |
2058 | pr_err("Data file does not have raw_syscalls:sys_exit events\n"); | 2056 | perf_evsel__init_sc_tp_ptr_field(evsel, args)) { |
2057 | pr_err("Error during initialize raw_syscalls:sys_enter event\n"); | ||
2058 | goto out; | ||
2059 | } | ||
2060 | |||
2061 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, | ||
2062 | "raw_syscalls:sys_exit"); | ||
2063 | if (evsel == NULL) { | ||
2064 | pr_err("Data file does not have raw_syscalls:sys_exit event\n"); | ||
2065 | goto out; | ||
2066 | } | ||
2067 | |||
2068 | if (perf_evsel__init_syscall_tp(evsel, trace__sys_exit) < 0 || | ||
2069 | perf_evsel__init_sc_tp_uint_field(evsel, ret)) { | ||
2070 | pr_err("Error during initialize raw_syscalls:sys_exit event\n"); | ||
2059 | goto out; | 2071 | goto out; |
2060 | } | 2072 | } |
2061 | 2073 | ||
@@ -2082,12 +2094,7 @@ static size_t trace__fprintf_threads_header(FILE *fp) | |||
2082 | { | 2094 | { |
2083 | size_t printed; | 2095 | size_t printed; |
2084 | 2096 | ||
2085 | printed = fprintf(fp, "\n _____________________________________________________________________________\n"); | 2097 | printed = fprintf(fp, "\n Summary of events:\n\n"); |
2086 | printed += fprintf(fp, " __) Summary of events (__\n\n"); | ||
2087 | printed += fprintf(fp, " [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); | ||
2088 | printed += fprintf(fp, " syscall count min max avg stddev\n"); | ||
2089 | printed += fprintf(fp, " msec msec msec %%\n"); | ||
2090 | printed += fprintf(fp, " _____________________________________________________________________________\n\n"); | ||
2091 | 2098 | ||
2092 | return printed; | 2099 | return printed; |
2093 | } | 2100 | } |
@@ -2105,6 +2112,10 @@ static size_t thread__dump_stats(struct thread_trace *ttrace, | |||
2105 | 2112 | ||
2106 | printed += fprintf(fp, "\n"); | 2113 | printed += fprintf(fp, "\n"); |
2107 | 2114 | ||
2115 | printed += fprintf(fp, " syscall calls min avg max stddev\n"); | ||
2116 | printed += fprintf(fp, " (msec) (msec) (msec) (%%)\n"); | ||
2117 | printed += fprintf(fp, " --------------- -------- --------- --------- --------- ------\n"); | ||
2118 | |||
2108 | /* each int_node is a syscall */ | 2119 | /* each int_node is a syscall */ |
2109 | while (inode) { | 2120 | while (inode) { |
2110 | stats = inode->priv; | 2121 | stats = inode->priv; |
@@ -2119,10 +2130,10 @@ static size_t thread__dump_stats(struct thread_trace *ttrace, | |||
2119 | avg /= NSEC_PER_MSEC; | 2130 | avg /= NSEC_PER_MSEC; |
2120 | 2131 | ||
2121 | sc = &trace->syscalls.table[inode->i]; | 2132 | sc = &trace->syscalls.table[inode->i]; |
2122 | printed += fprintf(fp, "%24s %14s : ", "", sc->name); | 2133 | printed += fprintf(fp, " %-15s", sc->name); |
2123 | printed += fprintf(fp, "%5" PRIu64 " %8.3f %8.3f", | 2134 | printed += fprintf(fp, " %8" PRIu64 " %9.3f %9.3f", |
2124 | n, min, max); | 2135 | n, min, avg); |
2125 | printed += fprintf(fp, " %8.3f %6.2f\n", avg, pct); | 2136 | printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct); |
2126 | } | 2137 | } |
2127 | 2138 | ||
2128 | inode = intlist__next(inode); | 2139 | inode = intlist__next(inode); |
@@ -2163,10 +2174,10 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) | |||
2163 | else if (ratio > 5.0) | 2174 | else if (ratio > 5.0) |
2164 | color = PERF_COLOR_YELLOW; | 2175 | color = PERF_COLOR_YELLOW; |
2165 | 2176 | ||
2166 | printed += color_fprintf(fp, color, "%20s", thread__comm_str(thread)); | 2177 | printed += color_fprintf(fp, color, " %s (%d), ", thread__comm_str(thread), thread->tid); |
2167 | printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); | 2178 | printed += fprintf(fp, "%lu events, ", ttrace->nr_events); |
2168 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); | 2179 | printed += color_fprintf(fp, color, "%.1f%%", ratio); |
2169 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); | 2180 | printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms); |
2170 | printed += thread__dump_stats(ttrace, trace, fp); | 2181 | printed += thread__dump_stats(ttrace, trace, fp); |
2171 | 2182 | ||
2172 | data->printed += printed; | 2183 | data->printed += printed; |
@@ -2275,8 +2286,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2275 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), | 2286 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), |
2276 | OPT_BOOLEAN('T', "time", &trace.full_time, | 2287 | OPT_BOOLEAN('T', "time", &trace.full_time, |
2277 | "Show full timestamp, not time relative to first start"), | 2288 | "Show full timestamp, not time relative to first start"), |
2278 | OPT_BOOLEAN(0, "summary", &trace.summary, | 2289 | OPT_BOOLEAN('s', "summary", &trace.summary_only, |
2279 | "Show syscall summary with statistics"), | 2290 | "Show only syscall summary with statistics"), |
2291 | OPT_BOOLEAN('S', "with-summary", &trace.summary, | ||
2292 | "Show all syscalls and summary with statistics"), | ||
2280 | OPT_END() | 2293 | OPT_END() |
2281 | }; | 2294 | }; |
2282 | int err; | 2295 | int err; |
@@ -2287,6 +2300,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2287 | 2300 | ||
2288 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 2301 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); |
2289 | 2302 | ||
2303 | /* summary_only implies summary option, but don't overwrite summary if set */ | ||
2304 | if (trace.summary_only) | ||
2305 | trace.summary = trace.summary_only; | ||
2306 | |||
2290 | if (output_name != NULL) { | 2307 | if (output_name != NULL) { |
2291 | err = trace__open_output(&trace, output_name); | 2308 | err = trace__open_output(&trace, output_name); |
2292 | if (err < 0) { | 2309 | if (err < 0) { |
@@ -2310,21 +2327,21 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2310 | } | 2327 | } |
2311 | } | 2328 | } |
2312 | 2329 | ||
2313 | err = perf_target__validate(&trace.opts.target); | 2330 | err = target__validate(&trace.opts.target); |
2314 | if (err) { | 2331 | if (err) { |
2315 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 2332 | target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
2316 | fprintf(trace.output, "%s", bf); | 2333 | fprintf(trace.output, "%s", bf); |
2317 | goto out_close; | 2334 | goto out_close; |
2318 | } | 2335 | } |
2319 | 2336 | ||
2320 | err = perf_target__parse_uid(&trace.opts.target); | 2337 | err = target__parse_uid(&trace.opts.target); |
2321 | if (err) { | 2338 | if (err) { |
2322 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 2339 | target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
2323 | fprintf(trace.output, "%s", bf); | 2340 | fprintf(trace.output, "%s", bf); |
2324 | goto out_close; | 2341 | goto out_close; |
2325 | } | 2342 | } |
2326 | 2343 | ||
2327 | if (!argc && perf_target__none(&trace.opts.target)) | 2344 | if (!argc && target__none(&trace.opts.target)) |
2328 | trace.opts.target.system_wide = true; | 2345 | trace.opts.target.system_wide = true; |
2329 | 2346 | ||
2330 | if (input_name) | 2347 | if (input_name) |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index f5905f2b197d..f7d11a811c74 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -142,7 +142,8 @@ CORE_FEATURE_TESTS = \ | |||
142 | libunwind \ | 142 | libunwind \ |
143 | on-exit \ | 143 | on-exit \ |
144 | stackprotector \ | 144 | stackprotector \ |
145 | stackprotector-all | 145 | stackprotector-all \ |
146 | timerfd | ||
146 | 147 | ||
147 | # | 148 | # |
148 | # So here we detect whether test-all was rebuilt, to be able | 149 | # So here we detect whether test-all was rebuilt, to be able |
@@ -328,8 +329,14 @@ ifndef NO_LIBUNWIND | |||
328 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 1.1); | 329 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 1.1); |
329 | NO_LIBUNWIND := 1 | 330 | NO_LIBUNWIND := 1 |
330 | else | 331 | else |
331 | ifneq ($(feature-libunwind-debug-frame), 1) | 332 | ifeq ($(ARCH),arm) |
332 | msg := $(warning No debug_frame support found in libunwind); | 333 | $(call feature_check,libunwind-debug-frame) |
334 | ifneq ($(feature-libunwind-debug-frame), 1) | ||
335 | msg := $(warning No debug_frame support found in libunwind); | ||
336 | CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME | ||
337 | endif | ||
338 | else | ||
339 | # non-ARM has no dwarf_find_debug_frame() function: | ||
333 | CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME | 340 | CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME |
334 | endif | 341 | endif |
335 | endif | 342 | endif |
@@ -405,7 +412,6 @@ else | |||
405 | endif | 412 | endif |
406 | endif | 413 | endif |
407 | 414 | ||
408 | $(call feature_check,timerfd) | ||
409 | ifeq ($(feature-timerfd), 1) | 415 | ifeq ($(feature-timerfd), 1) |
410 | CFLAGS += -DHAVE_TIMERFD_SUPPORT | 416 | CFLAGS += -DHAVE_TIMERFD_SUPPORT |
411 | else | 417 | else |
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index e8e195f49a4e..87e790017c69 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile | |||
@@ -76,6 +76,9 @@ test-libnuma: | |||
76 | test-libunwind: | 76 | test-libunwind: |
77 | $(BUILD) $(LIBUNWIND_LIBS) -lelf | 77 | $(BUILD) $(LIBUNWIND_LIBS) -lelf |
78 | 78 | ||
79 | test-libunwind-debug-frame: | ||
80 | $(BUILD) $(LIBUNWIND_LIBS) -lelf | ||
81 | |||
79 | test-libaudit: | 82 | test-libaudit: |
80 | $(BUILD) -laudit | 83 | $(BUILD) -laudit |
81 | 84 | ||
diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index 799865b60772..59e7a705e146 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c | |||
@@ -49,10 +49,6 @@ | |||
49 | # include "test-libunwind.c" | 49 | # include "test-libunwind.c" |
50 | #undef main | 50 | #undef main |
51 | 51 | ||
52 | #define main main_test_libunwind_debug_frame | ||
53 | # include "test-libunwind-debug-frame.c" | ||
54 | #undef main | ||
55 | |||
56 | #define main main_test_libaudit | 52 | #define main main_test_libaudit |
57 | # include "test-libaudit.c" | 53 | # include "test-libaudit.c" |
58 | #undef main | 54 | #undef main |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 6a587e84fdfe..b079304bd53d 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -248,7 +248,7 @@ enum perf_call_graph_mode { | |||
248 | }; | 248 | }; |
249 | 249 | ||
250 | struct perf_record_opts { | 250 | struct perf_record_opts { |
251 | struct perf_target target; | 251 | struct target target; |
252 | int call_graph; | 252 | int call_graph; |
253 | bool group; | 253 | bool group; |
254 | bool inherit_stat; | 254 | bool inherit_stat; |
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 49ccc3b2995e..85d4919dd623 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c | |||
@@ -275,8 +275,19 @@ static int process_event(struct machine *machine, struct perf_evlist *evlist, | |||
275 | if (event->header.type == PERF_RECORD_SAMPLE) | 275 | if (event->header.type == PERF_RECORD_SAMPLE) |
276 | return process_sample_event(machine, evlist, event, state); | 276 | return process_sample_event(machine, evlist, event, state); |
277 | 277 | ||
278 | if (event->header.type < PERF_RECORD_MAX) | 278 | if (event->header.type == PERF_RECORD_THROTTLE || |
279 | return machine__process_event(machine, event, NULL); | 279 | event->header.type == PERF_RECORD_UNTHROTTLE) |
280 | return 0; | ||
281 | |||
282 | if (event->header.type < PERF_RECORD_MAX) { | ||
283 | int ret; | ||
284 | |||
285 | ret = machine__process_event(machine, event, NULL); | ||
286 | if (ret < 0) | ||
287 | pr_debug("machine__process_event failed, event type %u\n", | ||
288 | event->header.type); | ||
289 | return ret; | ||
290 | } | ||
280 | 291 | ||
281 | return 0; | 292 | return 0; |
282 | } | 293 | } |
@@ -441,7 +452,7 @@ static int do_test_code_reading(bool try_kcore) | |||
441 | } | 452 | } |
442 | 453 | ||
443 | ret = perf_event__synthesize_thread_map(NULL, threads, | 454 | ret = perf_event__synthesize_thread_map(NULL, threads, |
444 | perf_event__process, machine); | 455 | perf_event__process, machine, false); |
445 | if (ret < 0) { | 456 | if (ret < 0) { |
446 | pr_debug("perf_event__synthesize_thread_map failed\n"); | 457 | pr_debug("perf_event__synthesize_thread_map failed\n"); |
447 | goto out_err; | 458 | goto out_err; |
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c index 9b98c1554833..4774f7fbb758 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c | |||
@@ -32,7 +32,7 @@ static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, | |||
32 | 32 | ||
33 | int test__perf_evsel__tp_sched_test(void) | 33 | int test__perf_evsel__tp_sched_test(void) |
34 | { | 34 | { |
35 | struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0); | 35 | struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch"); |
36 | int ret = 0; | 36 | int ret = 0; |
37 | 37 | ||
38 | if (evsel == NULL) { | 38 | if (evsel == NULL) { |
@@ -63,7 +63,7 @@ int test__perf_evsel__tp_sched_test(void) | |||
63 | 63 | ||
64 | perf_evsel__delete(evsel); | 64 | perf_evsel__delete(evsel); |
65 | 65 | ||
66 | evsel = perf_evsel__newtp("sched", "sched_wakeup", 0); | 66 | evsel = perf_evsel__newtp("sched", "sched_wakeup"); |
67 | 67 | ||
68 | if (perf_evsel__test_field(evsel, "comm", 16, true)) | 68 | if (perf_evsel__test_field(evsel, "comm", 16, true)) |
69 | ret = -1; | 69 | ret = -1; |
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index a7232c204eb9..d64ab79c6d35 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c | |||
@@ -65,7 +65,7 @@ int test__basic_mmap(void) | |||
65 | char name[64]; | 65 | char name[64]; |
66 | 66 | ||
67 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | 67 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); |
68 | evsels[i] = perf_evsel__newtp("syscalls", name, i); | 68 | evsels[i] = perf_evsel__newtp("syscalls", name); |
69 | if (evsels[i] == NULL) { | 69 | if (evsels[i] == NULL) { |
70 | pr_debug("perf_evsel__new\n"); | 70 | pr_debug("perf_evsel__new\n"); |
71 | goto out_free_evlist; | 71 | goto out_free_evlist; |
diff --git a/tools/perf/tests/open-syscall-all-cpus.c b/tools/perf/tests/open-syscall-all-cpus.c index b0657a9ccda6..5fecdbd2f5f7 100644 --- a/tools/perf/tests/open-syscall-all-cpus.c +++ b/tools/perf/tests/open-syscall-all-cpus.c | |||
@@ -26,7 +26,7 @@ int test__open_syscall_event_on_all_cpus(void) | |||
26 | 26 | ||
27 | CPU_ZERO(&cpu_set); | 27 | CPU_ZERO(&cpu_set); |
28 | 28 | ||
29 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | 29 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open"); |
30 | if (evsel == NULL) { | 30 | if (evsel == NULL) { |
31 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | 31 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); |
32 | goto out_thread_map_delete; | 32 | goto out_thread_map_delete; |
diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index 524b221b829b..41cc0badb74b 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c | |||
@@ -27,7 +27,7 @@ int test__syscall_open_tp_fields(void) | |||
27 | goto out; | 27 | goto out; |
28 | } | 28 | } |
29 | 29 | ||
30 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | 30 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open"); |
31 | if (evsel == NULL) { | 31 | if (evsel == NULL) { |
32 | pr_debug("%s: perf_evsel__newtp\n", __func__); | 32 | pr_debug("%s: perf_evsel__newtp\n", __func__); |
33 | goto out_delete_evlist; | 33 | goto out_delete_evlist; |
diff --git a/tools/perf/tests/open-syscall.c b/tools/perf/tests/open-syscall.c index befc0671f95d..c1dc7d25f38c 100644 --- a/tools/perf/tests/open-syscall.c +++ b/tools/perf/tests/open-syscall.c | |||
@@ -15,7 +15,7 @@ int test__open_syscall_event(void) | |||
15 | return -1; | 15 | return -1; |
16 | } | 16 | } |
17 | 17 | ||
18 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | 18 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open"); |
19 | if (evsel == NULL) { | 19 | if (evsel == NULL) { |
20 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | 20 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); |
21 | goto out_thread_map_delete; | 21 | goto out_thread_map_delete; |
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index ef671cd41bb3..3cbd10496087 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -441,9 +441,8 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist) | |||
441 | 441 | ||
442 | static int test__checkevent_pmu_events(struct perf_evlist *evlist) | 442 | static int test__checkevent_pmu_events(struct perf_evlist *evlist) |
443 | { | 443 | { |
444 | struct perf_evsel *evsel; | 444 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
445 | 445 | ||
446 | evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
447 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 446 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
448 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | 447 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); |
449 | TEST_ASSERT_VAL("wrong exclude_user", | 448 | TEST_ASSERT_VAL("wrong exclude_user", |
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index 6e2b44ec0749..6664a7cd828c 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c | |||
@@ -9,7 +9,7 @@ | |||
9 | #include "util/cpumap.h" | 9 | #include "util/cpumap.h" |
10 | #include "util/thread_map.h" | 10 | #include "util/thread_map.h" |
11 | 11 | ||
12 | #define NR_LOOPS 1000000 | 12 | #define NR_LOOPS 10000000 |
13 | 13 | ||
14 | /* | 14 | /* |
15 | * This test will open software clock events (cpu-clock, task-clock) | 15 | * This test will open software clock events (cpu-clock, task-clock) |
@@ -34,7 +34,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | |||
34 | .freq = 1, | 34 | .freq = 1, |
35 | }; | 35 | }; |
36 | 36 | ||
37 | attr.sample_freq = 10000; | 37 | attr.sample_freq = 500; |
38 | 38 | ||
39 | evlist = perf_evlist__new(); | 39 | evlist = perf_evlist__new(); |
40 | if (evlist == NULL) { | 40 | if (evlist == NULL) { |
@@ -42,7 +42,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | |||
42 | return -1; | 42 | return -1; |
43 | } | 43 | } |
44 | 44 | ||
45 | evsel = perf_evsel__new(&attr, 0); | 45 | evsel = perf_evsel__new(&attr); |
46 | if (evsel == NULL) { | 46 | if (evsel == NULL) { |
47 | pr_debug("perf_evsel__new\n"); | 47 | pr_debug("perf_evsel__new\n"); |
48 | goto out_free_evlist; | 48 | goto out_free_evlist; |
@@ -57,7 +57,14 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | |||
57 | goto out_delete_maps; | 57 | goto out_delete_maps; |
58 | } | 58 | } |
59 | 59 | ||
60 | perf_evlist__open(evlist); | 60 | if (perf_evlist__open(evlist)) { |
61 | const char *knob = "/proc/sys/kernel/perf_event_max_sample_rate"; | ||
62 | |||
63 | err = -errno; | ||
64 | pr_debug("Couldn't open evlist: %s\nHint: check %s, using %" PRIu64 " in this test.\n", | ||
65 | strerror(errno), knob, (u64)attr.sample_freq); | ||
66 | goto out_delete_maps; | ||
67 | } | ||
61 | 68 | ||
62 | err = perf_evlist__mmap(evlist, 128, true); | 69 | err = perf_evlist__mmap(evlist, 128, true); |
63 | if (err < 0) { | 70 | if (err < 0) { |
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index c33d95f9559a..d09ab579119e 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c | |||
@@ -28,7 +28,7 @@ int test__task_exit(void) | |||
28 | union perf_event *event; | 28 | union perf_event *event; |
29 | struct perf_evsel *evsel; | 29 | struct perf_evsel *evsel; |
30 | struct perf_evlist *evlist; | 30 | struct perf_evlist *evlist; |
31 | struct perf_target target = { | 31 | struct target target = { |
32 | .uid = UINT_MAX, | 32 | .uid = UINT_MAX, |
33 | .uses_mmap = true, | 33 | .uses_mmap = true, |
34 | }; | 34 | }; |
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index bbc782e364b0..cbaa7af45513 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c | |||
@@ -569,7 +569,7 @@ void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence) | |||
569 | browser->top = browser->top + browser->top_idx + offset; | 569 | browser->top = browser->top + browser->top_idx + offset; |
570 | break; | 570 | break; |
571 | case SEEK_END: | 571 | case SEEK_END: |
572 | browser->top = browser->top + browser->nr_entries + offset; | 572 | browser->top = browser->top + browser->nr_entries - 1 + offset; |
573 | break; | 573 | break; |
574 | default: | 574 | default: |
575 | return; | 575 | return; |
@@ -680,7 +680,7 @@ static void __ui_browser__line_arrow_down(struct ui_browser *browser, | |||
680 | if (end >= browser->top_idx + browser->height) | 680 | if (end >= browser->top_idx + browser->height) |
681 | end_row = browser->height - 1; | 681 | end_row = browser->height - 1; |
682 | else | 682 | else |
683 | end_row = end - browser->top_idx;; | 683 | end_row = end - browser->top_idx; |
684 | 684 | ||
685 | ui_browser__gotorc(browser, row, column); | 685 | ui_browser__gotorc(browser, row, column); |
686 | SLsmg_draw_vline(end_row - row + 1); | 686 | SLsmg_draw_vline(end_row - row + 1); |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 16848bb4c418..a440e03cd8c2 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -1847,15 +1847,15 @@ browse_hists: | |||
1847 | switch (key) { | 1847 | switch (key) { |
1848 | case K_TAB: | 1848 | case K_TAB: |
1849 | if (pos->node.next == &evlist->entries) | 1849 | if (pos->node.next == &evlist->entries) |
1850 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | 1850 | pos = perf_evlist__first(evlist); |
1851 | else | 1851 | else |
1852 | pos = list_entry(pos->node.next, struct perf_evsel, node); | 1852 | pos = perf_evsel__next(pos); |
1853 | goto browse_hists; | 1853 | goto browse_hists; |
1854 | case K_UNTAB: | 1854 | case K_UNTAB: |
1855 | if (pos->node.prev == &evlist->entries) | 1855 | if (pos->node.prev == &evlist->entries) |
1856 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | 1856 | pos = perf_evlist__last(evlist); |
1857 | else | 1857 | else |
1858 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | 1858 | pos = perf_evsel__prev(pos); |
1859 | goto browse_hists; | 1859 | goto browse_hists; |
1860 | case K_ESC: | 1860 | case K_ESC: |
1861 | if (!ui_browser__dialog_yesno(&menu->b, | 1861 | if (!ui_browser__dialog_yesno(&menu->b, |
@@ -1943,8 +1943,7 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | |||
1943 | 1943 | ||
1944 | single_entry: | 1944 | single_entry: |
1945 | if (nr_entries == 1) { | 1945 | if (nr_entries == 1) { |
1946 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1946 | struct perf_evsel *first = perf_evlist__first(evlist); |
1947 | struct perf_evsel, node); | ||
1948 | const char *ev_name = perf_evsel__name(first); | 1947 | const char *ev_name = perf_evsel__name(first); |
1949 | 1948 | ||
1950 | return perf_evsel__hists_browse(first, nr_entries, help, | 1949 | return perf_evsel__hists_browse(first, nr_entries, help, |
diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c index 3e2d936d7443..c61d14b101e0 100644 --- a/tools/perf/ui/tui/progress.c +++ b/tools/perf/ui/tui/progress.c | |||
@@ -18,13 +18,14 @@ static void tui_progress__update(struct ui_progress *p) | |||
18 | if (p->total == 0) | 18 | if (p->total == 0) |
19 | return; | 19 | return; |
20 | 20 | ||
21 | ui__refresh_dimensions(true); | 21 | ui__refresh_dimensions(false); |
22 | pthread_mutex_lock(&ui__lock); | 22 | pthread_mutex_lock(&ui__lock); |
23 | y = SLtt_Screen_Rows / 2 - 2; | 23 | y = SLtt_Screen_Rows / 2 - 2; |
24 | SLsmg_set_color(0); | 24 | SLsmg_set_color(0); |
25 | SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); | 25 | SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); |
26 | SLsmg_gotorc(y++, 1); | 26 | SLsmg_gotorc(y++, 1); |
27 | SLsmg_write_string((char *)p->title); | 27 | SLsmg_write_string((char *)p->title); |
28 | SLsmg_fill_region(y, 1, 1, SLtt_Screen_Cols - 2, ' '); | ||
28 | SLsmg_set_color(HE_COLORSET_SELECTED); | 29 | SLsmg_set_color(HE_COLORSET_SELECTED); |
29 | bar = ((SLtt_Screen_Cols - 2) * p->curr) / p->total; | 30 | bar = ((SLtt_Screen_Cols - 2) * p->curr) / p->total; |
30 | SLsmg_fill_region(y, 1, 1, bar, ' '); | 31 | SLsmg_fill_region(y, 1, 1, bar, ' '); |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index ec9ae1114ed4..bb788c109fe6 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -170,7 +170,8 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
170 | union perf_event *event, | 170 | union perf_event *event, |
171 | pid_t pid, pid_t tgid, | 171 | pid_t pid, pid_t tgid, |
172 | perf_event__handler_t process, | 172 | perf_event__handler_t process, |
173 | struct machine *machine) | 173 | struct machine *machine, |
174 | bool mmap_data) | ||
174 | { | 175 | { |
175 | char filename[PATH_MAX]; | 176 | char filename[PATH_MAX]; |
176 | FILE *fp; | 177 | FILE *fp; |
@@ -188,10 +189,6 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
188 | } | 189 | } |
189 | 190 | ||
190 | event->header.type = PERF_RECORD_MMAP; | 191 | event->header.type = PERF_RECORD_MMAP; |
191 | /* | ||
192 | * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c | ||
193 | */ | ||
194 | event->header.misc = PERF_RECORD_MISC_USER; | ||
195 | 192 | ||
196 | while (1) { | 193 | while (1) { |
197 | char bf[BUFSIZ]; | 194 | char bf[BUFSIZ]; |
@@ -212,12 +209,22 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
212 | &event->mmap.start, &event->mmap.len, prot, | 209 | &event->mmap.start, &event->mmap.len, prot, |
213 | &event->mmap.pgoff, | 210 | &event->mmap.pgoff, |
214 | execname); | 211 | execname); |
215 | 212 | /* | |
216 | if (n != 5) | 213 | * Anon maps don't have the execname. |
214 | */ | ||
215 | if (n < 4) | ||
217 | continue; | 216 | continue; |
217 | /* | ||
218 | * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c | ||
219 | */ | ||
220 | event->header.misc = PERF_RECORD_MISC_USER; | ||
218 | 221 | ||
219 | if (prot[2] != 'x') | 222 | if (prot[2] != 'x') { |
220 | continue; | 223 | if (!mmap_data || prot[0] != 'r') |
224 | continue; | ||
225 | |||
226 | event->header.misc |= PERF_RECORD_MISC_MMAP_DATA; | ||
227 | } | ||
221 | 228 | ||
222 | if (!strcmp(execname, "")) | 229 | if (!strcmp(execname, "")) |
223 | strcpy(execname, anonstr); | 230 | strcpy(execname, anonstr); |
@@ -304,20 +311,21 @@ static int __event__synthesize_thread(union perf_event *comm_event, | |||
304 | pid_t pid, int full, | 311 | pid_t pid, int full, |
305 | perf_event__handler_t process, | 312 | perf_event__handler_t process, |
306 | struct perf_tool *tool, | 313 | struct perf_tool *tool, |
307 | struct machine *machine) | 314 | struct machine *machine, bool mmap_data) |
308 | { | 315 | { |
309 | pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, full, | 316 | pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, full, |
310 | process, machine); | 317 | process, machine); |
311 | if (tgid == -1) | 318 | if (tgid == -1) |
312 | return -1; | 319 | return -1; |
313 | return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, | 320 | return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, |
314 | process, machine); | 321 | process, machine, mmap_data); |
315 | } | 322 | } |
316 | 323 | ||
317 | int perf_event__synthesize_thread_map(struct perf_tool *tool, | 324 | int perf_event__synthesize_thread_map(struct perf_tool *tool, |
318 | struct thread_map *threads, | 325 | struct thread_map *threads, |
319 | perf_event__handler_t process, | 326 | perf_event__handler_t process, |
320 | struct machine *machine) | 327 | struct machine *machine, |
328 | bool mmap_data) | ||
321 | { | 329 | { |
322 | union perf_event *comm_event, *mmap_event; | 330 | union perf_event *comm_event, *mmap_event; |
323 | int err = -1, thread, j; | 331 | int err = -1, thread, j; |
@@ -334,7 +342,8 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
334 | for (thread = 0; thread < threads->nr; ++thread) { | 342 | for (thread = 0; thread < threads->nr; ++thread) { |
335 | if (__event__synthesize_thread(comm_event, mmap_event, | 343 | if (__event__synthesize_thread(comm_event, mmap_event, |
336 | threads->map[thread], 0, | 344 | threads->map[thread], 0, |
337 | process, tool, machine)) { | 345 | process, tool, machine, |
346 | mmap_data)) { | ||
338 | err = -1; | 347 | err = -1; |
339 | break; | 348 | break; |
340 | } | 349 | } |
@@ -356,10 +365,10 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
356 | 365 | ||
357 | /* if not, generate events for it */ | 366 | /* if not, generate events for it */ |
358 | if (need_leader && | 367 | if (need_leader && |
359 | __event__synthesize_thread(comm_event, | 368 | __event__synthesize_thread(comm_event, mmap_event, |
360 | mmap_event, | 369 | comm_event->comm.pid, 0, |
361 | comm_event->comm.pid, 0, | 370 | process, tool, machine, |
362 | process, tool, machine)) { | 371 | mmap_data)) { |
363 | err = -1; | 372 | err = -1; |
364 | break; | 373 | break; |
365 | } | 374 | } |
@@ -374,7 +383,7 @@ out: | |||
374 | 383 | ||
375 | int perf_event__synthesize_threads(struct perf_tool *tool, | 384 | int perf_event__synthesize_threads(struct perf_tool *tool, |
376 | perf_event__handler_t process, | 385 | perf_event__handler_t process, |
377 | struct machine *machine) | 386 | struct machine *machine, bool mmap_data) |
378 | { | 387 | { |
379 | DIR *proc; | 388 | DIR *proc; |
380 | struct dirent dirent, *next; | 389 | struct dirent dirent, *next; |
@@ -404,7 +413,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
404 | * one thread couldn't be synthesized. | 413 | * one thread couldn't be synthesized. |
405 | */ | 414 | */ |
406 | __event__synthesize_thread(comm_event, mmap_event, pid, 1, | 415 | __event__synthesize_thread(comm_event, mmap_event, pid, 1, |
407 | process, tool, machine); | 416 | process, tool, machine, mmap_data); |
408 | } | 417 | } |
409 | 418 | ||
410 | err = 0; | 419 | err = 0; |
@@ -528,19 +537,22 @@ int perf_event__process_lost(struct perf_tool *tool __maybe_unused, | |||
528 | 537 | ||
529 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | 538 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) |
530 | { | 539 | { |
531 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", | 540 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n", |
532 | event->mmap.pid, event->mmap.tid, event->mmap.start, | 541 | event->mmap.pid, event->mmap.tid, event->mmap.start, |
533 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); | 542 | event->mmap.len, event->mmap.pgoff, |
543 | (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', | ||
544 | event->mmap.filename); | ||
534 | } | 545 | } |
535 | 546 | ||
536 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) | 547 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) |
537 | { | 548 | { |
538 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 | 549 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 |
539 | " %02x:%02x %"PRIu64" %"PRIu64"]: %s\n", | 550 | " %02x:%02x %"PRIu64" %"PRIu64"]: %c %s\n", |
540 | event->mmap2.pid, event->mmap2.tid, event->mmap2.start, | 551 | event->mmap2.pid, event->mmap2.tid, event->mmap2.start, |
541 | event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, | 552 | event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, |
542 | event->mmap2.min, event->mmap2.ino, | 553 | event->mmap2.min, event->mmap2.ino, |
543 | event->mmap2.ino_generation, | 554 | event->mmap2.ino_generation, |
555 | (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', | ||
544 | event->mmap2.filename); | 556 | event->mmap2.filename); |
545 | } | 557 | } |
546 | 558 | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index f8d70f3003ab..30fec9901e44 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -208,10 +208,10 @@ typedef int (*perf_event__handler_t)(struct perf_tool *tool, | |||
208 | int perf_event__synthesize_thread_map(struct perf_tool *tool, | 208 | int perf_event__synthesize_thread_map(struct perf_tool *tool, |
209 | struct thread_map *threads, | 209 | struct thread_map *threads, |
210 | perf_event__handler_t process, | 210 | perf_event__handler_t process, |
211 | struct machine *machine); | 211 | struct machine *machine, bool mmap_data); |
212 | int perf_event__synthesize_threads(struct perf_tool *tool, | 212 | int perf_event__synthesize_threads(struct perf_tool *tool, |
213 | perf_event__handler_t process, | 213 | perf_event__handler_t process, |
214 | struct machine *machine); | 214 | struct machine *machine, bool mmap_data); |
215 | int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, | 215 | int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, |
216 | perf_event__handler_t process, | 216 | perf_event__handler_t process, |
217 | struct machine *machine, | 217 | struct machine *machine, |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index b939221efd8d..bbc746aa5716 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -117,6 +117,8 @@ void perf_evlist__delete(struct perf_evlist *evlist) | |||
117 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | 117 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) |
118 | { | 118 | { |
119 | list_add_tail(&entry->node, &evlist->entries); | 119 | list_add_tail(&entry->node, &evlist->entries); |
120 | entry->idx = evlist->nr_entries; | ||
121 | |||
120 | if (!evlist->nr_entries++) | 122 | if (!evlist->nr_entries++) |
121 | perf_evlist__set_id_pos(evlist); | 123 | perf_evlist__set_id_pos(evlist); |
122 | } | 124 | } |
@@ -165,7 +167,7 @@ int perf_evlist__add_default(struct perf_evlist *evlist) | |||
165 | 167 | ||
166 | event_attr_init(&attr); | 168 | event_attr_init(&attr); |
167 | 169 | ||
168 | evsel = perf_evsel__new(&attr, 0); | 170 | evsel = perf_evsel__new(&attr); |
169 | if (evsel == NULL) | 171 | if (evsel == NULL) |
170 | goto error; | 172 | goto error; |
171 | 173 | ||
@@ -190,7 +192,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist, | |||
190 | size_t i; | 192 | size_t i; |
191 | 193 | ||
192 | for (i = 0; i < nr_attrs; i++) { | 194 | for (i = 0; i < nr_attrs; i++) { |
193 | evsel = perf_evsel__new(attrs + i, evlist->nr_entries + i); | 195 | evsel = perf_evsel__new_idx(attrs + i, evlist->nr_entries + i); |
194 | if (evsel == NULL) | 196 | if (evsel == NULL) |
195 | goto out_delete_partial_list; | 197 | goto out_delete_partial_list; |
196 | list_add_tail(&evsel->node, &head); | 198 | list_add_tail(&evsel->node, &head); |
@@ -249,9 +251,8 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | |||
249 | int perf_evlist__add_newtp(struct perf_evlist *evlist, | 251 | int perf_evlist__add_newtp(struct perf_evlist *evlist, |
250 | const char *sys, const char *name, void *handler) | 252 | const char *sys, const char *name, void *handler) |
251 | { | 253 | { |
252 | struct perf_evsel *evsel; | 254 | struct perf_evsel *evsel = perf_evsel__newtp(sys, name); |
253 | 255 | ||
254 | evsel = perf_evsel__newtp(sys, name, evlist->nr_entries); | ||
255 | if (evsel == NULL) | 256 | if (evsel == NULL) |
256 | return -1; | 257 | return -1; |
257 | 258 | ||
@@ -704,12 +705,10 @@ static size_t perf_evlist__mmap_size(unsigned long pages) | |||
704 | return (pages + 1) * page_size; | 705 | return (pages + 1) * page_size; |
705 | } | 706 | } |
706 | 707 | ||
707 | int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, | 708 | static long parse_pages_arg(const char *str, unsigned long min, |
708 | int unset __maybe_unused) | 709 | unsigned long max) |
709 | { | 710 | { |
710 | unsigned int *mmap_pages = opt->value; | ||
711 | unsigned long pages, val; | 711 | unsigned long pages, val; |
712 | size_t size; | ||
713 | static struct parse_tag tags[] = { | 712 | static struct parse_tag tags[] = { |
714 | { .tag = 'B', .mult = 1 }, | 713 | { .tag = 'B', .mult = 1 }, |
715 | { .tag = 'K', .mult = 1 << 10 }, | 714 | { .tag = 'K', .mult = 1 << 10 }, |
@@ -718,33 +717,49 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, | |||
718 | { .tag = 0 }, | 717 | { .tag = 0 }, |
719 | }; | 718 | }; |
720 | 719 | ||
720 | if (str == NULL) | ||
721 | return -EINVAL; | ||
722 | |||
721 | val = parse_tag_value(str, tags); | 723 | val = parse_tag_value(str, tags); |
722 | if (val != (unsigned long) -1) { | 724 | if (val != (unsigned long) -1) { |
723 | /* we got file size value */ | 725 | /* we got file size value */ |
724 | pages = PERF_ALIGN(val, page_size) / page_size; | 726 | pages = PERF_ALIGN(val, page_size) / page_size; |
725 | if (pages < (1UL << 31) && !is_power_of_2(pages)) { | ||
726 | pages = next_pow2(pages); | ||
727 | pr_info("rounding mmap pages size to %lu (%lu pages)\n", | ||
728 | pages * page_size, pages); | ||
729 | } | ||
730 | } else { | 727 | } else { |
731 | /* we got pages count value */ | 728 | /* we got pages count value */ |
732 | char *eptr; | 729 | char *eptr; |
733 | pages = strtoul(str, &eptr, 10); | 730 | pages = strtoul(str, &eptr, 10); |
734 | if (*eptr != '\0') { | 731 | if (*eptr != '\0') |
735 | pr_err("failed to parse --mmap_pages/-m value\n"); | 732 | return -EINVAL; |
736 | return -1; | ||
737 | } | ||
738 | } | 733 | } |
739 | 734 | ||
740 | if (pages > UINT_MAX || pages > SIZE_MAX / page_size) { | 735 | if ((pages == 0) && (min == 0)) { |
741 | pr_err("--mmap_pages/-m value too big\n"); | 736 | /* leave number of pages at 0 */ |
742 | return -1; | 737 | } else if (pages < (1UL << 31) && !is_power_of_2(pages)) { |
738 | /* round pages up to next power of 2 */ | ||
739 | pages = next_pow2(pages); | ||
740 | pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", | ||
741 | pages * page_size, pages); | ||
743 | } | 742 | } |
744 | 743 | ||
745 | size = perf_evlist__mmap_size(pages); | 744 | if (pages > max) |
746 | if (!size) { | 745 | return -EINVAL; |
747 | pr_err("--mmap_pages/-m value must be a power of two."); | 746 | |
747 | return pages; | ||
748 | } | ||
749 | |||
750 | int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, | ||
751 | int unset __maybe_unused) | ||
752 | { | ||
753 | unsigned int *mmap_pages = opt->value; | ||
754 | unsigned long max = UINT_MAX; | ||
755 | long pages; | ||
756 | |||
757 | if (max < SIZE_MAX / page_size) | ||
758 | max = SIZE_MAX / page_size; | ||
759 | |||
760 | pages = parse_pages_arg(str, 1, max); | ||
761 | if (pages < 0) { | ||
762 | pr_err("Invalid argument for --mmap_pages/-m\n"); | ||
748 | return -1; | 763 | return -1; |
749 | } | 764 | } |
750 | 765 | ||
@@ -796,8 +811,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
796 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); | 811 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); |
797 | } | 812 | } |
798 | 813 | ||
799 | int perf_evlist__create_maps(struct perf_evlist *evlist, | 814 | int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) |
800 | struct perf_target *target) | ||
801 | { | 815 | { |
802 | evlist->threads = thread_map__new_str(target->pid, target->tid, | 816 | evlist->threads = thread_map__new_str(target->pid, target->tid, |
803 | target->uid); | 817 | target->uid); |
@@ -805,9 +819,11 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, | |||
805 | if (evlist->threads == NULL) | 819 | if (evlist->threads == NULL) |
806 | return -1; | 820 | return -1; |
807 | 821 | ||
808 | if (perf_target__has_task(target)) | 822 | if (target->force_per_cpu) |
823 | evlist->cpus = cpu_map__new(target->cpu_list); | ||
824 | else if (target__has_task(target)) | ||
809 | evlist->cpus = cpu_map__dummy_new(); | 825 | evlist->cpus = cpu_map__dummy_new(); |
810 | else if (!perf_target__has_cpu(target) && !target->uses_mmap) | 826 | else if (!target__has_cpu(target) && !target->uses_mmap) |
811 | evlist->cpus = cpu_map__dummy_new(); | 827 | evlist->cpus = cpu_map__dummy_new(); |
812 | else | 828 | else |
813 | evlist->cpus = cpu_map__new(target->cpu_list); | 829 | evlist->cpus = cpu_map__new(target->cpu_list); |
@@ -1016,8 +1032,7 @@ out_err: | |||
1016 | return err; | 1032 | return err; |
1017 | } | 1033 | } |
1018 | 1034 | ||
1019 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | 1035 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target, |
1020 | struct perf_target *target, | ||
1021 | const char *argv[], bool pipe_output, | 1036 | const char *argv[], bool pipe_output, |
1022 | bool want_signal) | 1037 | bool want_signal) |
1023 | { | 1038 | { |
@@ -1069,7 +1084,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
1069 | exit(-1); | 1084 | exit(-1); |
1070 | } | 1085 | } |
1071 | 1086 | ||
1072 | if (perf_target__none(target)) | 1087 | if (target__none(target)) |
1073 | evlist->threads->map[0] = evlist->workload.pid; | 1088 | evlist->threads->map[0] = evlist->workload.pid; |
1074 | 1089 | ||
1075 | close(child_ready_pipe[1]); | 1090 | close(child_ready_pipe[1]); |
@@ -1135,7 +1150,7 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) | |||
1135 | perf_evsel__name(evsel)); | 1150 | perf_evsel__name(evsel)); |
1136 | } | 1151 | } |
1137 | 1152 | ||
1138 | return printed + fprintf(fp, "\n");; | 1153 | return printed + fprintf(fp, "\n"); |
1139 | } | 1154 | } |
1140 | 1155 | ||
1141 | int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, | 1156 | int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index ecaa582f40e2..649d6ea98a84 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -102,7 +102,7 @@ void perf_evlist__config(struct perf_evlist *evlist, | |||
102 | int perf_record_opts__config(struct perf_record_opts *opts); | 102 | int perf_record_opts__config(struct perf_record_opts *opts); |
103 | 103 | ||
104 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | 104 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, |
105 | struct perf_target *target, | 105 | struct target *target, |
106 | const char *argv[], bool pipe_output, | 106 | const char *argv[], bool pipe_output, |
107 | bool want_signal); | 107 | bool want_signal); |
108 | int perf_evlist__start_workload(struct perf_evlist *evlist); | 108 | int perf_evlist__start_workload(struct perf_evlist *evlist); |
@@ -134,8 +134,7 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | |||
134 | evlist->threads = threads; | 134 | evlist->threads = threads; |
135 | } | 135 | } |
136 | 136 | ||
137 | int perf_evlist__create_maps(struct perf_evlist *evlist, | 137 | int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target); |
138 | struct perf_target *target); | ||
139 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | 138 | void perf_evlist__delete_maps(struct perf_evlist *evlist); |
140 | int perf_evlist__apply_filters(struct perf_evlist *evlist); | 139 | int perf_evlist__apply_filters(struct perf_evlist *evlist); |
141 | 140 | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 5280820ed389..46dd4c2a41ce 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -168,7 +168,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
168 | perf_evsel__calc_id_pos(evsel); | 168 | perf_evsel__calc_id_pos(evsel); |
169 | } | 169 | } |
170 | 170 | ||
171 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 171 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) |
172 | { | 172 | { |
173 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 173 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
174 | 174 | ||
@@ -219,7 +219,7 @@ out: | |||
219 | return format; | 219 | return format; |
220 | } | 220 | } |
221 | 221 | ||
222 | struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx) | 222 | struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) |
223 | { | 223 | { |
224 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 224 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
225 | 225 | ||
@@ -645,7 +645,7 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
645 | } | 645 | } |
646 | } | 646 | } |
647 | 647 | ||
648 | if (perf_target__has_cpu(&opts->target)) | 648 | if (target__has_cpu(&opts->target) || opts->target.force_per_cpu) |
649 | perf_evsel__set_sample_bit(evsel, CPU); | 649 | perf_evsel__set_sample_bit(evsel, CPU); |
650 | 650 | ||
651 | if (opts->period) | 651 | if (opts->period) |
@@ -653,7 +653,7 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
653 | 653 | ||
654 | if (!perf_missing_features.sample_id_all && | 654 | if (!perf_missing_features.sample_id_all && |
655 | (opts->sample_time || !opts->no_inherit || | 655 | (opts->sample_time || !opts->no_inherit || |
656 | perf_target__has_cpu(&opts->target))) | 656 | target__has_cpu(&opts->target) || opts->target.force_per_cpu)) |
657 | perf_evsel__set_sample_bit(evsel, TIME); | 657 | perf_evsel__set_sample_bit(evsel, TIME); |
658 | 658 | ||
659 | if (opts->raw_samples) { | 659 | if (opts->raw_samples) { |
@@ -696,7 +696,7 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
696 | * Setting enable_on_exec for independent events and | 696 | * Setting enable_on_exec for independent events and |
697 | * group leaders for traced executed by perf. | 697 | * group leaders for traced executed by perf. |
698 | */ | 698 | */ |
699 | if (perf_target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) | 699 | if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) |
700 | attr->enable_on_exec = 1; | 700 | attr->enable_on_exec = 1; |
701 | } | 701 | } |
702 | 702 | ||
@@ -2006,8 +2006,7 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | |||
2006 | return false; | 2006 | return false; |
2007 | } | 2007 | } |
2008 | 2008 | ||
2009 | int perf_evsel__open_strerror(struct perf_evsel *evsel, | 2009 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, |
2010 | struct perf_target *target, | ||
2011 | int err, char *msg, size_t size) | 2010 | int err, char *msg, size_t size) |
2012 | { | 2011 | { |
2013 | switch (err) { | 2012 | switch (err) { |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 64ec8e1a7a28..1ea7c92e6e33 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -96,8 +96,19 @@ struct thread_map; | |||
96 | struct perf_evlist; | 96 | struct perf_evlist; |
97 | struct perf_record_opts; | 97 | struct perf_record_opts; |
98 | 98 | ||
99 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); | 99 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); |
100 | struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx); | 100 | |
101 | static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) | ||
102 | { | ||
103 | return perf_evsel__new_idx(attr, 0); | ||
104 | } | ||
105 | |||
106 | struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx); | ||
107 | |||
108 | static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name) | ||
109 | { | ||
110 | return perf_evsel__newtp_idx(sys, name, 0); | ||
111 | } | ||
101 | 112 | ||
102 | struct event_format *event_format__new(const char *sys, const char *name); | 113 | struct event_format *event_format__new(const char *sys, const char *name); |
103 | 114 | ||
@@ -268,6 +279,11 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel) | |||
268 | return list_entry(evsel->node.next, struct perf_evsel, node); | 279 | return list_entry(evsel->node.next, struct perf_evsel, node); |
269 | } | 280 | } |
270 | 281 | ||
282 | static inline struct perf_evsel *perf_evsel__prev(struct perf_evsel *evsel) | ||
283 | { | ||
284 | return list_entry(evsel->node.prev, struct perf_evsel, node); | ||
285 | } | ||
286 | |||
271 | /** | 287 | /** |
272 | * perf_evsel__is_group_leader - Return whether given evsel is a leader event | 288 | * perf_evsel__is_group_leader - Return whether given evsel is a leader event |
273 | * | 289 | * |
@@ -307,8 +323,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, | |||
307 | 323 | ||
308 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | 324 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, |
309 | char *msg, size_t msgsize); | 325 | char *msg, size_t msgsize); |
310 | int perf_evsel__open_strerror(struct perf_evsel *evsel, | 326 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, |
311 | struct perf_target *target, | ||
312 | int err, char *msg, size_t size); | 327 | int err, char *msg, size_t size); |
313 | 328 | ||
314 | static inline int perf_evsel__group_idx(struct perf_evsel *evsel) | 329 | static inline int perf_evsel__group_idx(struct perf_evsel *evsel) |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 26d9520a0c1b..369c03648f88 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -2797,7 +2797,7 @@ int perf_session__read_header(struct perf_session *session) | |||
2797 | perf_event__attr_swap(&f_attr.attr); | 2797 | perf_event__attr_swap(&f_attr.attr); |
2798 | 2798 | ||
2799 | tmp = lseek(fd, 0, SEEK_CUR); | 2799 | tmp = lseek(fd, 0, SEEK_CUR); |
2800 | evsel = perf_evsel__new(&f_attr.attr, i); | 2800 | evsel = perf_evsel__new(&f_attr.attr); |
2801 | 2801 | ||
2802 | if (evsel == NULL) | 2802 | if (evsel == NULL) |
2803 | goto out_delete_evlist; | 2803 | goto out_delete_evlist; |
@@ -2916,7 +2916,7 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, | |||
2916 | return -ENOMEM; | 2916 | return -ENOMEM; |
2917 | } | 2917 | } |
2918 | 2918 | ||
2919 | evsel = perf_evsel__new(&event->attr.attr, evlist->nr_entries); | 2919 | evsel = perf_evsel__new(&event->attr.attr); |
2920 | if (evsel == NULL) | 2920 | if (evsel == NULL) |
2921 | return -ENOMEM; | 2921 | return -ENOMEM; |
2922 | 2922 | ||
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index ce034c183a7e..84cdb072ac83 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1368,7 +1368,7 @@ int machine__resolve_callchain(struct machine *machine, | |||
1368 | 1368 | ||
1369 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | 1369 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, |
1370 | thread, evsel->attr.sample_regs_user, | 1370 | thread, evsel->attr.sample_regs_user, |
1371 | sample); | 1371 | sample, max_stack); |
1372 | 1372 | ||
1373 | } | 1373 | } |
1374 | 1374 | ||
@@ -1394,3 +1394,15 @@ int machine__for_each_thread(struct machine *machine, | |||
1394 | } | 1394 | } |
1395 | return rc; | 1395 | return rc; |
1396 | } | 1396 | } |
1397 | |||
1398 | int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, | ||
1399 | struct target *target, struct thread_map *threads, | ||
1400 | perf_event__handler_t process, bool data_mmap) | ||
1401 | { | ||
1402 | if (target__has_task(target)) | ||
1403 | return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap); | ||
1404 | else if (target__has_cpu(target)) | ||
1405 | return perf_event__synthesize_threads(tool, process, machine, data_mmap); | ||
1406 | /* command specified */ | ||
1407 | return 0; | ||
1408 | } | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 2389ba81fafe..477133015440 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
5 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
6 | #include "map.h" | 6 | #include "map.h" |
7 | #include "event.h" | ||
7 | 8 | ||
8 | struct addr_location; | 9 | struct addr_location; |
9 | struct branch_stack; | 10 | struct branch_stack; |
@@ -178,4 +179,15 @@ int machine__for_each_thread(struct machine *machine, | |||
178 | int (*fn)(struct thread *thread, void *p), | 179 | int (*fn)(struct thread *thread, void *p), |
179 | void *priv); | 180 | void *priv); |
180 | 181 | ||
182 | int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, | ||
183 | struct target *target, struct thread_map *threads, | ||
184 | perf_event__handler_t process, bool data_mmap); | ||
185 | static inline | ||
186 | int machine__synthesize_threads(struct machine *machine, struct target *target, | ||
187 | struct thread_map *threads, bool data_mmap) | ||
188 | { | ||
189 | return __machine__synthesize_threads(machine, NULL, target, threads, | ||
190 | perf_event__process, data_mmap); | ||
191 | } | ||
192 | |||
181 | #endif /* __PERF_MACHINE_H */ | 193 | #endif /* __PERF_MACHINE_H */ |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c90e55cf7e82..6de6f89c2a61 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -277,7 +277,7 @@ static int __add_event(struct list_head *list, int *idx, | |||
277 | 277 | ||
278 | event_attr_init(attr); | 278 | event_attr_init(attr); |
279 | 279 | ||
280 | evsel = perf_evsel__new(attr, (*idx)++); | 280 | evsel = perf_evsel__new_idx(attr, (*idx)++); |
281 | if (!evsel) | 281 | if (!evsel) |
282 | return -ENOMEM; | 282 | return -ENOMEM; |
283 | 283 | ||
@@ -378,7 +378,7 @@ static int add_tracepoint(struct list_head *list, int *idx, | |||
378 | { | 378 | { |
379 | struct perf_evsel *evsel; | 379 | struct perf_evsel *evsel; |
380 | 380 | ||
381 | evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++); | 381 | evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++); |
382 | if (!evsel) | 382 | if (!evsel) |
383 | return -ENOMEM; | 383 | return -ENOMEM; |
384 | 384 | ||
@@ -1097,7 +1097,7 @@ static bool is_event_supported(u8 type, unsigned config) | |||
1097 | .threads = { 0 }, | 1097 | .threads = { 0 }, |
1098 | }; | 1098 | }; |
1099 | 1099 | ||
1100 | evsel = perf_evsel__new(&attr, 0); | 1100 | evsel = perf_evsel__new(&attr); |
1101 | if (evsel) { | 1101 | if (evsel) { |
1102 | ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; | 1102 | ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; |
1103 | perf_evsel__delete(evsel); | 1103 | perf_evsel__delete(evsel); |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 3c1b75c8b9a6..8b0bb1f4494a 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -1137,6 +1137,8 @@ static void sort_entry__setup_elide(struct sort_entry *se, | |||
1137 | 1137 | ||
1138 | void sort__setup_elide(FILE *output) | 1138 | void sort__setup_elide(FILE *output) |
1139 | { | 1139 | { |
1140 | struct sort_entry *se; | ||
1141 | |||
1140 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | 1142 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, |
1141 | "dso", output); | 1143 | "dso", output); |
1142 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, | 1144 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, |
@@ -1172,4 +1174,15 @@ void sort__setup_elide(FILE *output) | |||
1172 | "snoop", output); | 1174 | "snoop", output); |
1173 | } | 1175 | } |
1174 | 1176 | ||
1177 | /* | ||
1178 | * It makes no sense to elide all of sort entries. | ||
1179 | * Just revert them to show up again. | ||
1180 | */ | ||
1181 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
1182 | if (!se->elide) | ||
1183 | return; | ||
1184 | } | ||
1185 | |||
1186 | list_for_each_entry(se, &hist_entry__sort_list, list) | ||
1187 | se->elide = false; | ||
1175 | } | 1188 | } |
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 065528b7563e..3c778a07b7cc 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c | |||
@@ -13,9 +13,9 @@ | |||
13 | #include <string.h> | 13 | #include <string.h> |
14 | 14 | ||
15 | 15 | ||
16 | enum perf_target_errno perf_target__validate(struct perf_target *target) | 16 | enum target_errno target__validate(struct target *target) |
17 | { | 17 | { |
18 | enum perf_target_errno ret = PERF_ERRNO_TARGET__SUCCESS; | 18 | enum target_errno ret = TARGET_ERRNO__SUCCESS; |
19 | 19 | ||
20 | if (target->pid) | 20 | if (target->pid) |
21 | target->tid = target->pid; | 21 | target->tid = target->pid; |
@@ -23,42 +23,42 @@ enum perf_target_errno perf_target__validate(struct perf_target *target) | |||
23 | /* CPU and PID are mutually exclusive */ | 23 | /* CPU and PID are mutually exclusive */ |
24 | if (target->tid && target->cpu_list) { | 24 | if (target->tid && target->cpu_list) { |
25 | target->cpu_list = NULL; | 25 | target->cpu_list = NULL; |
26 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 26 | if (ret == TARGET_ERRNO__SUCCESS) |
27 | ret = PERF_ERRNO_TARGET__PID_OVERRIDE_CPU; | 27 | ret = TARGET_ERRNO__PID_OVERRIDE_CPU; |
28 | } | 28 | } |
29 | 29 | ||
30 | /* UID and PID are mutually exclusive */ | 30 | /* UID and PID are mutually exclusive */ |
31 | if (target->tid && target->uid_str) { | 31 | if (target->tid && target->uid_str) { |
32 | target->uid_str = NULL; | 32 | target->uid_str = NULL; |
33 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 33 | if (ret == TARGET_ERRNO__SUCCESS) |
34 | ret = PERF_ERRNO_TARGET__PID_OVERRIDE_UID; | 34 | ret = TARGET_ERRNO__PID_OVERRIDE_UID; |
35 | } | 35 | } |
36 | 36 | ||
37 | /* UID and CPU are mutually exclusive */ | 37 | /* UID and CPU are mutually exclusive */ |
38 | if (target->uid_str && target->cpu_list) { | 38 | if (target->uid_str && target->cpu_list) { |
39 | target->cpu_list = NULL; | 39 | target->cpu_list = NULL; |
40 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 40 | if (ret == TARGET_ERRNO__SUCCESS) |
41 | ret = PERF_ERRNO_TARGET__UID_OVERRIDE_CPU; | 41 | ret = TARGET_ERRNO__UID_OVERRIDE_CPU; |
42 | } | 42 | } |
43 | 43 | ||
44 | /* PID and SYSTEM are mutually exclusive */ | 44 | /* PID and SYSTEM are mutually exclusive */ |
45 | if (target->tid && target->system_wide) { | 45 | if (target->tid && target->system_wide) { |
46 | target->system_wide = false; | 46 | target->system_wide = false; |
47 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 47 | if (ret == TARGET_ERRNO__SUCCESS) |
48 | ret = PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM; | 48 | ret = TARGET_ERRNO__PID_OVERRIDE_SYSTEM; |
49 | } | 49 | } |
50 | 50 | ||
51 | /* UID and SYSTEM are mutually exclusive */ | 51 | /* UID and SYSTEM are mutually exclusive */ |
52 | if (target->uid_str && target->system_wide) { | 52 | if (target->uid_str && target->system_wide) { |
53 | target->system_wide = false; | 53 | target->system_wide = false; |
54 | if (ret == PERF_ERRNO_TARGET__SUCCESS) | 54 | if (ret == TARGET_ERRNO__SUCCESS) |
55 | ret = PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM; | 55 | ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM; |
56 | } | 56 | } |
57 | 57 | ||
58 | return ret; | 58 | return ret; |
59 | } | 59 | } |
60 | 60 | ||
61 | enum perf_target_errno perf_target__parse_uid(struct perf_target *target) | 61 | enum target_errno target__parse_uid(struct target *target) |
62 | { | 62 | { |
63 | struct passwd pwd, *result; | 63 | struct passwd pwd, *result; |
64 | char buf[1024]; | 64 | char buf[1024]; |
@@ -66,7 +66,7 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target) | |||
66 | 66 | ||
67 | target->uid = UINT_MAX; | 67 | target->uid = UINT_MAX; |
68 | if (str == NULL) | 68 | if (str == NULL) |
69 | return PERF_ERRNO_TARGET__SUCCESS; | 69 | return TARGET_ERRNO__SUCCESS; |
70 | 70 | ||
71 | /* Try user name first */ | 71 | /* Try user name first */ |
72 | getpwnam_r(str, &pwd, buf, sizeof(buf), &result); | 72 | getpwnam_r(str, &pwd, buf, sizeof(buf), &result); |
@@ -79,22 +79,22 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target) | |||
79 | int uid = strtol(str, &endptr, 10); | 79 | int uid = strtol(str, &endptr, 10); |
80 | 80 | ||
81 | if (*endptr != '\0') | 81 | if (*endptr != '\0') |
82 | return PERF_ERRNO_TARGET__INVALID_UID; | 82 | return TARGET_ERRNO__INVALID_UID; |
83 | 83 | ||
84 | getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); | 84 | getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); |
85 | 85 | ||
86 | if (result == NULL) | 86 | if (result == NULL) |
87 | return PERF_ERRNO_TARGET__USER_NOT_FOUND; | 87 | return TARGET_ERRNO__USER_NOT_FOUND; |
88 | } | 88 | } |
89 | 89 | ||
90 | target->uid = result->pw_uid; | 90 | target->uid = result->pw_uid; |
91 | return PERF_ERRNO_TARGET__SUCCESS; | 91 | return TARGET_ERRNO__SUCCESS; |
92 | } | 92 | } |
93 | 93 | ||
94 | /* | 94 | /* |
95 | * This must have a same ordering as the enum perf_target_errno. | 95 | * This must have a same ordering as the enum target_errno. |
96 | */ | 96 | */ |
97 | static const char *perf_target__error_str[] = { | 97 | static const char *target__error_str[] = { |
98 | "PID/TID switch overriding CPU", | 98 | "PID/TID switch overriding CPU", |
99 | "PID/TID switch overriding UID", | 99 | "PID/TID switch overriding UID", |
100 | "UID switch overriding CPU", | 100 | "UID switch overriding CPU", |
@@ -104,7 +104,7 @@ static const char *perf_target__error_str[] = { | |||
104 | "Problems obtaining information for user %s", | 104 | "Problems obtaining information for user %s", |
105 | }; | 105 | }; |
106 | 106 | ||
107 | int perf_target__strerror(struct perf_target *target, int errnum, | 107 | int target__strerror(struct target *target, int errnum, |
108 | char *buf, size_t buflen) | 108 | char *buf, size_t buflen) |
109 | { | 109 | { |
110 | int idx; | 110 | int idx; |
@@ -124,21 +124,19 @@ int perf_target__strerror(struct perf_target *target, int errnum, | |||
124 | return 0; | 124 | return 0; |
125 | } | 125 | } |
126 | 126 | ||
127 | if (errnum < __PERF_ERRNO_TARGET__START || | 127 | if (errnum < __TARGET_ERRNO__START || errnum >= __TARGET_ERRNO__END) |
128 | errnum >= __PERF_ERRNO_TARGET__END) | ||
129 | return -1; | 128 | return -1; |
130 | 129 | ||
131 | idx = errnum - __PERF_ERRNO_TARGET__START; | 130 | idx = errnum - __TARGET_ERRNO__START; |
132 | msg = perf_target__error_str[idx]; | 131 | msg = target__error_str[idx]; |
133 | 132 | ||
134 | switch (errnum) { | 133 | switch (errnum) { |
135 | case PERF_ERRNO_TARGET__PID_OVERRIDE_CPU | 134 | case TARGET_ERRNO__PID_OVERRIDE_CPU ... TARGET_ERRNO__UID_OVERRIDE_SYSTEM: |
136 | ... PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM: | ||
137 | snprintf(buf, buflen, "%s", msg); | 135 | snprintf(buf, buflen, "%s", msg); |
138 | break; | 136 | break; |
139 | 137 | ||
140 | case PERF_ERRNO_TARGET__INVALID_UID: | 138 | case TARGET_ERRNO__INVALID_UID: |
141 | case PERF_ERRNO_TARGET__USER_NOT_FOUND: | 139 | case TARGET_ERRNO__USER_NOT_FOUND: |
142 | snprintf(buf, buflen, msg, target->uid_str); | 140 | snprintf(buf, buflen, msg, target->uid_str); |
143 | break; | 141 | break; |
144 | 142 | ||
diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h index a4be8575fda5..2d0c50690892 100644 --- a/tools/perf/util/target.h +++ b/tools/perf/util/target.h | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
6 | 6 | ||
7 | struct perf_target { | 7 | struct target { |
8 | const char *pid; | 8 | const char *pid; |
9 | const char *tid; | 9 | const char *tid; |
10 | const char *cpu_list; | 10 | const char *cpu_list; |
@@ -12,10 +12,11 @@ struct perf_target { | |||
12 | uid_t uid; | 12 | uid_t uid; |
13 | bool system_wide; | 13 | bool system_wide; |
14 | bool uses_mmap; | 14 | bool uses_mmap; |
15 | bool force_per_cpu; | ||
15 | }; | 16 | }; |
16 | 17 | ||
17 | enum perf_target_errno { | 18 | enum target_errno { |
18 | PERF_ERRNO_TARGET__SUCCESS = 0, | 19 | TARGET_ERRNO__SUCCESS = 0, |
19 | 20 | ||
20 | /* | 21 | /* |
21 | * Choose an arbitrary negative big number not to clash with standard | 22 | * Choose an arbitrary negative big number not to clash with standard |
@@ -24,42 +25,40 @@ enum perf_target_errno { | |||
24 | * | 25 | * |
25 | * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html | 26 | * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html |
26 | */ | 27 | */ |
27 | __PERF_ERRNO_TARGET__START = -10000, | 28 | __TARGET_ERRNO__START = -10000, |
28 | 29 | ||
30 | /* for target__validate() */ | ||
31 | TARGET_ERRNO__PID_OVERRIDE_CPU = __TARGET_ERRNO__START, | ||
32 | TARGET_ERRNO__PID_OVERRIDE_UID, | ||
33 | TARGET_ERRNO__UID_OVERRIDE_CPU, | ||
34 | TARGET_ERRNO__PID_OVERRIDE_SYSTEM, | ||
35 | TARGET_ERRNO__UID_OVERRIDE_SYSTEM, | ||
29 | 36 | ||
30 | /* for perf_target__validate() */ | 37 | /* for target__parse_uid() */ |
31 | PERF_ERRNO_TARGET__PID_OVERRIDE_CPU = __PERF_ERRNO_TARGET__START, | 38 | TARGET_ERRNO__INVALID_UID, |
32 | PERF_ERRNO_TARGET__PID_OVERRIDE_UID, | 39 | TARGET_ERRNO__USER_NOT_FOUND, |
33 | PERF_ERRNO_TARGET__UID_OVERRIDE_CPU, | ||
34 | PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM, | ||
35 | PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM, | ||
36 | 40 | ||
37 | /* for perf_target__parse_uid() */ | 41 | __TARGET_ERRNO__END, |
38 | PERF_ERRNO_TARGET__INVALID_UID, | ||
39 | PERF_ERRNO_TARGET__USER_NOT_FOUND, | ||
40 | |||
41 | __PERF_ERRNO_TARGET__END, | ||
42 | }; | 42 | }; |
43 | 43 | ||
44 | enum perf_target_errno perf_target__validate(struct perf_target *target); | 44 | enum target_errno target__validate(struct target *target); |
45 | enum perf_target_errno perf_target__parse_uid(struct perf_target *target); | 45 | enum target_errno target__parse_uid(struct target *target); |
46 | 46 | ||
47 | int perf_target__strerror(struct perf_target *target, int errnum, char *buf, | 47 | int target__strerror(struct target *target, int errnum, char *buf, size_t buflen); |
48 | size_t buflen); | ||
49 | 48 | ||
50 | static inline bool perf_target__has_task(struct perf_target *target) | 49 | static inline bool target__has_task(struct target *target) |
51 | { | 50 | { |
52 | return target->tid || target->pid || target->uid_str; | 51 | return target->tid || target->pid || target->uid_str; |
53 | } | 52 | } |
54 | 53 | ||
55 | static inline bool perf_target__has_cpu(struct perf_target *target) | 54 | static inline bool target__has_cpu(struct target *target) |
56 | { | 55 | { |
57 | return target->system_wide || target->cpu_list; | 56 | return target->system_wide || target->cpu_list; |
58 | } | 57 | } |
59 | 58 | ||
60 | static inline bool perf_target__none(struct perf_target *target) | 59 | static inline bool target__none(struct target *target) |
61 | { | 60 | { |
62 | return !perf_target__has_task(target) && !perf_target__has_cpu(target); | 61 | return !target__has_task(target) && !target__has_cpu(target); |
63 | } | 62 | } |
64 | 63 | ||
65 | #endif /* _PERF_TARGET_H */ | 64 | #endif /* _PERF_TARGET_H */ |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index f857b51b6bde..ce793c7dd23c 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -27,7 +27,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
27 | float ksamples_per_sec; | 27 | float ksamples_per_sec; |
28 | float esamples_percent; | 28 | float esamples_percent; |
29 | struct perf_record_opts *opts = &top->record_opts; | 29 | struct perf_record_opts *opts = &top->record_opts; |
30 | struct perf_target *target = &opts->target; | 30 | struct target *target = &opts->target; |
31 | size_t ret = 0; | 31 | size_t ret = 0; |
32 | 32 | ||
33 | if (top->samples) { | 33 | if (top->samples) { |
diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c index 5390d0b8862a..0efd5393de85 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind.c | |||
@@ -559,7 +559,7 @@ static unw_accessors_t accessors = { | |||
559 | }; | 559 | }; |
560 | 560 | ||
561 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | 561 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, |
562 | void *arg) | 562 | void *arg, int max_stack) |
563 | { | 563 | { |
564 | unw_addr_space_t addr_space; | 564 | unw_addr_space_t addr_space; |
565 | unw_cursor_t c; | 565 | unw_cursor_t c; |
@@ -575,7 +575,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
575 | if (ret) | 575 | if (ret) |
576 | display_error(ret); | 576 | display_error(ret); |
577 | 577 | ||
578 | while (!ret && (unw_step(&c) > 0)) { | 578 | while (!ret && (unw_step(&c) > 0) && max_stack--) { |
579 | unw_word_t ip; | 579 | unw_word_t ip; |
580 | 580 | ||
581 | unw_get_reg(&c, UNW_REG_IP, &ip); | 581 | unw_get_reg(&c, UNW_REG_IP, &ip); |
@@ -588,7 +588,8 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
588 | 588 | ||
589 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 589 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
590 | struct machine *machine, struct thread *thread, | 590 | struct machine *machine, struct thread *thread, |
591 | u64 sample_uregs, struct perf_sample *data) | 591 | u64 sample_uregs, struct perf_sample *data, |
592 | int max_stack) | ||
592 | { | 593 | { |
593 | unw_word_t ip; | 594 | unw_word_t ip; |
594 | struct unwind_info ui = { | 595 | struct unwind_info ui = { |
@@ -610,5 +611,5 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
610 | if (ret) | 611 | if (ret) |
611 | return -ENOMEM; | 612 | return -ENOMEM; |
612 | 613 | ||
613 | return get_entries(&ui, cb, arg); | 614 | return get_entries(&ui, cb, arg, max_stack); |
614 | } | 615 | } |
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index ec0c71a2ca2e..d5966f49e22c 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h | |||
@@ -18,7 +18,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
18 | struct machine *machine, | 18 | struct machine *machine, |
19 | struct thread *thread, | 19 | struct thread *thread, |
20 | u64 sample_uregs, | 20 | u64 sample_uregs, |
21 | struct perf_sample *data); | 21 | struct perf_sample *data, int max_stack); |
22 | int unwind__arch_reg_id(int regnum); | 22 | int unwind__arch_reg_id(int regnum); |
23 | #else | 23 | #else |
24 | static inline int | 24 | static inline int |
@@ -27,7 +27,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, | |||
27 | struct machine *machine __maybe_unused, | 27 | struct machine *machine __maybe_unused, |
28 | struct thread *thread __maybe_unused, | 28 | struct thread *thread __maybe_unused, |
29 | u64 sample_uregs __maybe_unused, | 29 | u64 sample_uregs __maybe_unused, |
30 | struct perf_sample *data __maybe_unused) | 30 | struct perf_sample *data __maybe_unused, |
31 | int max_stack __maybe_unused) | ||
31 | { | 32 | { |
32 | return 0; | 33 | return 0; |
33 | } | 34 | } |
diff --git a/tools/thermal/tmon/Makefile b/tools/thermal/tmon/Makefile new file mode 100644 index 000000000000..447321104ec0 --- /dev/null +++ b/tools/thermal/tmon/Makefile | |||
@@ -0,0 +1,47 @@ | |||
1 | VERSION = 1.0 | ||
2 | |||
3 | BINDIR=usr/bin | ||
4 | WARNFLAGS=-Wall -Wshadow -W -Wformat -Wimplicit-function-declaration -Wimplicit-int | ||
5 | CFLAGS= -O1 ${WARNFLAGS} -fstack-protector | ||
6 | CC=gcc | ||
7 | |||
8 | CFLAGS+=-D VERSION=\"$(VERSION)\" | ||
9 | LDFLAGS+= | ||
10 | TARGET=tmon | ||
11 | |||
12 | INSTALL_PROGRAM=install -m 755 -p | ||
13 | DEL_FILE=rm -f | ||
14 | |||
15 | INSTALL_CONFIGFILE=install -m 644 -p | ||
16 | CONFIG_FILE= | ||
17 | CONFIG_PATH= | ||
18 | |||
19 | |||
20 | OBJS = tmon.o tui.o sysfs.o pid.o | ||
21 | OBJS += | ||
22 | |||
23 | tmon: $(OBJS) Makefile tmon.h | ||
24 | $(CC) ${CFLAGS} $(LDFLAGS) $(OBJS) -o $(TARGET) -lm -lpanel -lncursesw -lpthread | ||
25 | |||
26 | valgrind: tmon | ||
27 | sudo valgrind -v --track-origins=yes --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes ./$(TARGET) 1> /dev/null | ||
28 | |||
29 | install: | ||
30 | - mkdir -p $(INSTALL_ROOT)/$(BINDIR) | ||
31 | - $(INSTALL_PROGRAM) "$(TARGET)" "$(INSTALL_ROOT)/$(BINDIR)/$(TARGET)" | ||
32 | - mkdir -p $(INSTALL_ROOT)/$(CONFIG_PATH) | ||
33 | - $(INSTALL_CONFIGFILE) "$(CONFIG_FILE)" "$(INSTALL_ROOT)/$(CONFIG_PATH)" | ||
34 | |||
35 | uninstall: | ||
36 | $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/$(TARGET)" | ||
37 | $(CONFIG_FILE) "$(CONFIG_PATH)" | ||
38 | |||
39 | |||
40 | clean: | ||
41 | find . -name "*.o" | xargs $(DEL_FILE) | ||
42 | rm -f $(TARGET) | ||
43 | |||
44 | dist: | ||
45 | git tag v$(VERSION) | ||
46 | git archive --format=tar --prefix="$(TARGET)-$(VERSION)/" v$(VERSION) | \ | ||
47 | gzip > $(TARGET)-$(VERSION).tar.gz | ||
diff --git a/tools/thermal/tmon/README b/tools/thermal/tmon/README new file mode 100644 index 000000000000..457949897a8e --- /dev/null +++ b/tools/thermal/tmon/README | |||
@@ -0,0 +1,50 @@ | |||
1 | TMON - A Monitoring and Testing Tool for Linux kernel thermal subsystem | ||
2 | |||
3 | Why TMON? | ||
4 | ========== | ||
5 | Increasingly, Linux is running on thermally constrained devices. The simple | ||
6 | thermal relationship between processor and fan has become past for modern | ||
7 | computers. | ||
8 | |||
9 | As hardware vendors cope with the thermal constraints on their products, more | ||
10 | and more sensors are added, new cooling capabilities are introduced. The | ||
11 | complexity of the thermal relationship can grow exponentially among cooling | ||
12 | devices, zones, sensors, and trip points. They can also change dynamically. | ||
13 | |||
14 | To expose such relationship to the userspace, Linux generic thermal layer | ||
15 | introduced sysfs entry at /sys/class/thermal with a matrix of symbolic | ||
16 | links, trip point bindings, and device instances. To traverse such | ||
17 | matrix by hand is not a trivial task. Testing is also difficult in that | ||
18 | thermal conditions are often exception cases that hard to reach in | ||
19 | normal operations. | ||
20 | |||
21 | TMON is conceived as a tool to help visualize, tune, and test the | ||
22 | complex thermal subsystem. | ||
23 | |||
24 | Files | ||
25 | ===== | ||
26 | tmon.c : main function for set up and configurations. | ||
27 | tui.c : handles ncurses based user interface | ||
28 | sysfs.c : access to the generic thermal sysfs | ||
29 | pid.c : a proportional-integral-derivative (PID) controller | ||
30 | that can be used for thermal relationship training. | ||
31 | |||
32 | Requirements | ||
33 | ============ | ||
34 | Depends on ncurses | ||
35 | |||
36 | Build | ||
37 | ========= | ||
38 | $ make | ||
39 | $ sudo ./tmon -h | ||
40 | Usage: tmon [OPTION...] | ||
41 | -c, --control cooling device in control | ||
42 | -d, --daemon run as daemon, no TUI | ||
43 | -l, --log log data to /var/tmp/tmon.log | ||
44 | -h, --help show this help message | ||
45 | -t, --time-interval set time interval for sampling | ||
46 | -v, --version show version | ||
47 | -g, --debug debug message in syslog | ||
48 | |||
49 | 1. For monitoring only: | ||
50 | $ sudo ./tmon | ||
diff --git a/tools/thermal/tmon/pid.c b/tools/thermal/tmon/pid.c new file mode 100644 index 000000000000..fd7e9e9d6f4a --- /dev/null +++ b/tools/thermal/tmon/pid.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * pid.c PID controller for testing cooling devices | ||
3 | * | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2012 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License version | ||
10 | * 2 or later as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * Author Name Jacob Pan <jacob.jun.pan@linux.intel.com> | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <unistd.h> | ||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <stdint.h> | ||
26 | #include <sys/types.h> | ||
27 | #include <dirent.h> | ||
28 | #include <libintl.h> | ||
29 | #include <ctype.h> | ||
30 | #include <assert.h> | ||
31 | #include <time.h> | ||
32 | #include <limits.h> | ||
33 | #include <math.h> | ||
34 | #include <sys/stat.h> | ||
35 | #include <syslog.h> | ||
36 | |||
37 | #include "tmon.h" | ||
38 | |||
39 | /************************************************************************** | ||
40 | * PID (Proportional-Integral-Derivative) controller is commonly used in | ||
41 | * linear control system, consider the the process. | ||
42 | * G(s) = U(s)/E(s) | ||
43 | * kp = proportional gain | ||
44 | * ki = integral gain | ||
45 | * kd = derivative gain | ||
46 | * Ts | ||
47 | * We use type C Alan Bradley equation which takes set point off the | ||
48 | * output dependency in P and D term. | ||
49 | * | ||
50 | * y[k] = y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k] | ||
51 | * - 2*x[k-1]+x[k-2])/Ts | ||
52 | * | ||
53 | * | ||
54 | ***********************************************************************/ | ||
55 | struct pid_params p_param; | ||
56 | /* cached data from previous loop */ | ||
57 | static double xk_1, xk_2; /* input temperature x[k-#] */ | ||
58 | |||
59 | /* | ||
60 | * TODO: make PID parameters tuned automatically, | ||
61 | * 1. use CPU burn to produce open loop unit step response | ||
62 | * 2. calculate PID based on Ziegler-Nichols rule | ||
63 | * | ||
64 | * add a flag for tuning PID | ||
65 | */ | ||
66 | int init_thermal_controller(void) | ||
67 | { | ||
68 | int ret = 0; | ||
69 | |||
70 | /* init pid params */ | ||
71 | p_param.ts = ticktime; | ||
72 | /* TODO: get it from TUI tuning tab */ | ||
73 | p_param.kp = .36; | ||
74 | p_param.ki = 5.0; | ||
75 | p_param.kd = 0.19; | ||
76 | |||
77 | p_param.t_target = target_temp_user; | ||
78 | |||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | void controller_reset(void) | ||
83 | { | ||
84 | /* TODO: relax control data when not over thermal limit */ | ||
85 | syslog(LOG_DEBUG, "TC inactive, relax p-state\n"); | ||
86 | p_param.y_k = 0.0; | ||
87 | xk_1 = 0.0; | ||
88 | xk_2 = 0.0; | ||
89 | set_ctrl_state(0); | ||
90 | } | ||
91 | |||
92 | /* To be called at time interval Ts. Type C PID controller. | ||
93 | * y[k] = y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k] | ||
94 | * - 2*x[k-1]+x[k-2])/Ts | ||
95 | * TODO: add low pass filter for D term | ||
96 | */ | ||
97 | #define GUARD_BAND (2) | ||
98 | void controller_handler(const double xk, double *yk) | ||
99 | { | ||
100 | double ek; | ||
101 | double p_term, i_term, d_term; | ||
102 | |||
103 | ek = p_param.t_target - xk; /* error */ | ||
104 | if (ek >= 3.0) { | ||
105 | syslog(LOG_DEBUG, "PID: %3.1f Below set point %3.1f, stop\n", | ||
106 | xk, p_param.t_target); | ||
107 | controller_reset(); | ||
108 | *yk = 0.0; | ||
109 | return; | ||
110 | } | ||
111 | /* compute intermediate PID terms */ | ||
112 | p_term = -p_param.kp * (xk - xk_1); | ||
113 | i_term = p_param.kp * p_param.ki * p_param.ts * ek; | ||
114 | d_term = -p_param.kp * p_param.kd * (xk - 2 * xk_1 + xk_2) / p_param.ts; | ||
115 | /* compute output */ | ||
116 | *yk += p_term + i_term + d_term; | ||
117 | /* update sample data */ | ||
118 | xk_1 = xk; | ||
119 | xk_2 = xk_1; | ||
120 | |||
121 | /* clamp output adjustment range */ | ||
122 | if (*yk < -LIMIT_HIGH) | ||
123 | *yk = -LIMIT_HIGH; | ||
124 | else if (*yk > -LIMIT_LOW) | ||
125 | *yk = -LIMIT_LOW; | ||
126 | |||
127 | p_param.y_k = *yk; | ||
128 | |||
129 | set_ctrl_state(lround(fabs(p_param.y_k))); | ||
130 | |||
131 | } | ||
diff --git a/tools/thermal/tmon/sysfs.c b/tools/thermal/tmon/sysfs.c new file mode 100644 index 000000000000..dfe454855cd2 --- /dev/null +++ b/tools/thermal/tmon/sysfs.c | |||
@@ -0,0 +1,596 @@ | |||
1 | /* | ||
2 | * sysfs.c sysfs ABI access functions for TMON program | ||
3 | * | ||
4 | * Copyright (C) 2013 Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License version | ||
8 | * 2 or later as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Author: Jacob Pan <jacob.jun.pan@linux.intel.com> | ||
16 | * | ||
17 | */ | ||
18 | #include <unistd.h> | ||
19 | #include <stdio.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <string.h> | ||
22 | #include <stdint.h> | ||
23 | #include <dirent.h> | ||
24 | #include <libintl.h> | ||
25 | #include <ctype.h> | ||
26 | #include <time.h> | ||
27 | #include <syslog.h> | ||
28 | #include <sys/time.h> | ||
29 | #include <errno.h> | ||
30 | |||
31 | #include "tmon.h" | ||
32 | |||
33 | struct tmon_platform_data ptdata; | ||
34 | const char *trip_type_name[] = { | ||
35 | "critical", | ||
36 | "hot", | ||
37 | "passive", | ||
38 | "active", | ||
39 | }; | ||
40 | |||
41 | int sysfs_set_ulong(char *path, char *filename, unsigned long val) | ||
42 | { | ||
43 | FILE *fd; | ||
44 | int ret = -1; | ||
45 | char filepath[256]; | ||
46 | |||
47 | snprintf(filepath, 256, "%s/%s", path, filename); | ||
48 | |||
49 | fd = fopen(filepath, "w"); | ||
50 | if (!fd) { | ||
51 | syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath); | ||
52 | return ret; | ||
53 | } | ||
54 | ret = fprintf(fd, "%lu", val); | ||
55 | fclose(fd); | ||
56 | |||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | /* history of thermal data, used for control algo */ | ||
61 | #define NR_THERMAL_RECORDS 3 | ||
62 | struct thermal_data_record trec[NR_THERMAL_RECORDS]; | ||
63 | int cur_thermal_record; /* index to the trec array */ | ||
64 | |||
65 | static int sysfs_get_ulong(char *path, char *filename, unsigned long *p_ulong) | ||
66 | { | ||
67 | FILE *fd; | ||
68 | int ret = -1; | ||
69 | char filepath[256]; | ||
70 | |||
71 | snprintf(filepath, 256, "%s/%s", path, filename); | ||
72 | |||
73 | fd = fopen(filepath, "r"); | ||
74 | if (!fd) { | ||
75 | syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath); | ||
76 | return ret; | ||
77 | } | ||
78 | ret = fscanf(fd, "%lu", p_ulong); | ||
79 | fclose(fd); | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int sysfs_get_string(char *path, char *filename, char *str) | ||
85 | { | ||
86 | FILE *fd; | ||
87 | int ret = -1; | ||
88 | char filepath[256]; | ||
89 | |||
90 | snprintf(filepath, 256, "%s/%s", path, filename); | ||
91 | |||
92 | fd = fopen(filepath, "r"); | ||
93 | if (!fd) { | ||
94 | syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath); | ||
95 | return ret; | ||
96 | } | ||
97 | ret = fscanf(fd, "%256s", str); | ||
98 | fclose(fd); | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | /* get states of the cooling device instance */ | ||
104 | static int probe_cdev(struct cdev_info *cdi, char *path) | ||
105 | { | ||
106 | sysfs_get_string(path, "type", cdi->type); | ||
107 | sysfs_get_ulong(path, "max_state", &cdi->max_state); | ||
108 | sysfs_get_ulong(path, "cur_state", &cdi->cur_state); | ||
109 | |||
110 | syslog(LOG_INFO, "%s: %s: type %s, max %lu, curr %lu inst %d\n", | ||
111 | __func__, path, | ||
112 | cdi->type, cdi->max_state, cdi->cur_state, cdi->instance); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static int str_to_trip_type(char *name) | ||
118 | { | ||
119 | int i; | ||
120 | |||
121 | for (i = 0; i < NR_THERMAL_TRIP_TYPE; i++) { | ||
122 | if (!strcmp(name, trip_type_name[i])) | ||
123 | return i; | ||
124 | } | ||
125 | |||
126 | return -ENOENT; | ||
127 | } | ||
128 | |||
129 | /* scan and fill in trip point info for a thermal zone and trip point id */ | ||
130 | static int get_trip_point_data(char *tz_path, int tzid, int tpid) | ||
131 | { | ||
132 | char filename[256]; | ||
133 | char temp_str[256]; | ||
134 | int trip_type; | ||
135 | |||
136 | if (tpid >= MAX_NR_TRIP) | ||
137 | return -EINVAL; | ||
138 | /* check trip point type */ | ||
139 | snprintf(filename, sizeof(filename), "trip_point_%d_type", tpid); | ||
140 | sysfs_get_string(tz_path, filename, temp_str); | ||
141 | trip_type = str_to_trip_type(temp_str); | ||
142 | if (trip_type < 0) { | ||
143 | syslog(LOG_ERR, "%s:%s no matching type\n", __func__, temp_str); | ||
144 | return -ENOENT; | ||
145 | } | ||
146 | ptdata.tzi[tzid].tp[tpid].type = trip_type; | ||
147 | syslog(LOG_INFO, "%s:tz:%d tp:%d:type:%s type id %d\n", __func__, tzid, | ||
148 | tpid, temp_str, trip_type); | ||
149 | |||
150 | /* TODO: check attribute */ | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | /* return instance id for file format such as trip_point_4_temp */ | ||
156 | static int get_instance_id(char *name, int pos, int skip) | ||
157 | { | ||
158 | char *ch; | ||
159 | int i = 0; | ||
160 | |||
161 | ch = strtok(name, "_"); | ||
162 | while (ch != NULL) { | ||
163 | ++i; | ||
164 | syslog(LOG_INFO, "%s:%s:%s:%d", __func__, name, ch, i); | ||
165 | ch = strtok(NULL, "_"); | ||
166 | if (pos == i) | ||
167 | return atol(ch + skip); | ||
168 | } | ||
169 | |||
170 | return -1; | ||
171 | } | ||
172 | |||
173 | /* Find trip point info of a thermal zone */ | ||
174 | static int find_tzone_tp(char *tz_name, char *d_name, struct tz_info *tzi, | ||
175 | int tz_id) | ||
176 | { | ||
177 | int tp_id; | ||
178 | unsigned long temp_ulong; | ||
179 | |||
180 | if (strstr(d_name, "trip_point") && | ||
181 | strstr(d_name, "temp")) { | ||
182 | /* check if trip point temp is non-zero | ||
183 | * ignore 0/invalid trip points | ||
184 | */ | ||
185 | sysfs_get_ulong(tz_name, d_name, &temp_ulong); | ||
186 | if (temp_ulong < MAX_TEMP_KC) { | ||
187 | tzi->nr_trip_pts++; | ||
188 | /* found a valid trip point */ | ||
189 | tp_id = get_instance_id(d_name, 2, 0); | ||
190 | syslog(LOG_DEBUG, "tzone %s trip %d temp %lu tpnode %s", | ||
191 | tz_name, tp_id, temp_ulong, d_name); | ||
192 | if (tp_id < 0 || tp_id >= MAX_NR_TRIP) { | ||
193 | syslog(LOG_ERR, "Failed to find TP inst %s\n", | ||
194 | d_name); | ||
195 | return -1; | ||
196 | } | ||
197 | get_trip_point_data(tz_name, tz_id, tp_id); | ||
198 | tzi->tp[tp_id].temp = temp_ulong; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | /* check cooling devices for binding info. */ | ||
206 | static int find_tzone_cdev(struct dirent *nl, char *tz_name, | ||
207 | struct tz_info *tzi, int tz_id, int cid) | ||
208 | { | ||
209 | unsigned long trip_instance = 0; | ||
210 | char cdev_name_linked[256]; | ||
211 | char cdev_name[256]; | ||
212 | char cdev_trip_name[256]; | ||
213 | int cdev_id; | ||
214 | |||
215 | if (nl->d_type == DT_LNK) { | ||
216 | syslog(LOG_DEBUG, "TZ%d: cdev: %s cid %d\n", tz_id, nl->d_name, | ||
217 | cid); | ||
218 | tzi->nr_cdev++; | ||
219 | if (tzi->nr_cdev > ptdata.nr_cooling_dev) { | ||
220 | syslog(LOG_ERR, "Err: Too many cdev? %d\n", | ||
221 | tzi->nr_cdev); | ||
222 | return -EINVAL; | ||
223 | } | ||
224 | /* find the link to real cooling device record binding */ | ||
225 | snprintf(cdev_name, 256, "%s/%s", tz_name, nl->d_name); | ||
226 | memset(cdev_name_linked, 0, sizeof(cdev_name_linked)); | ||
227 | if (readlink(cdev_name, cdev_name_linked, | ||
228 | sizeof(cdev_name_linked) - 1) != -1) { | ||
229 | cdev_id = get_instance_id(cdev_name_linked, 1, | ||
230 | sizeof("device") - 1); | ||
231 | syslog(LOG_DEBUG, "cdev %s linked to %s : %d\n", | ||
232 | cdev_name, cdev_name_linked, cdev_id); | ||
233 | tzi->cdev_binding |= (1 << cdev_id); | ||
234 | |||
235 | /* find the trip point in which the cdev is binded to | ||
236 | * in this tzone | ||
237 | */ | ||
238 | snprintf(cdev_trip_name, 256, "%s%s", nl->d_name, | ||
239 | "_trip_point"); | ||
240 | sysfs_get_ulong(tz_name, cdev_trip_name, | ||
241 | &trip_instance); | ||
242 | /* validate trip point range, e.g. trip could return -1 | ||
243 | * when passive is enabled | ||
244 | */ | ||
245 | if (trip_instance > MAX_NR_TRIP) | ||
246 | trip_instance = 0; | ||
247 | tzi->trip_binding[cdev_id] |= 1 << trip_instance; | ||
248 | syslog(LOG_DEBUG, "cdev %s -> trip:%lu: 0x%lx %d\n", | ||
249 | cdev_name, trip_instance, | ||
250 | tzi->trip_binding[cdev_id], | ||
251 | cdev_id); | ||
252 | |||
253 | |||
254 | } | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | return -ENODEV; | ||
259 | } | ||
260 | |||
261 | |||
262 | |||
263 | /***************************************************************************** | ||
264 | * Before calling scan_tzones, thermal sysfs must be probed to determine | ||
265 | * the number of thermal zones and cooling devices. | ||
266 | * We loop through each thermal zone and fill in tz_info struct, i.e. | ||
267 | * ptdata.tzi[] | ||
268 | root@jacob-chiefriver:~# tree -d /sys/class/thermal/thermal_zone0 | ||
269 | /sys/class/thermal/thermal_zone0 | ||
270 | |-- cdev0 -> ../cooling_device4 | ||
271 | |-- cdev1 -> ../cooling_device3 | ||
272 | |-- cdev10 -> ../cooling_device7 | ||
273 | |-- cdev11 -> ../cooling_device6 | ||
274 | |-- cdev12 -> ../cooling_device5 | ||
275 | |-- cdev2 -> ../cooling_device2 | ||
276 | |-- cdev3 -> ../cooling_device1 | ||
277 | |-- cdev4 -> ../cooling_device0 | ||
278 | |-- cdev5 -> ../cooling_device12 | ||
279 | |-- cdev6 -> ../cooling_device11 | ||
280 | |-- cdev7 -> ../cooling_device10 | ||
281 | |-- cdev8 -> ../cooling_device9 | ||
282 | |-- cdev9 -> ../cooling_device8 | ||
283 | |-- device -> ../../../LNXSYSTM:00/device:62/LNXTHERM:00 | ||
284 | |-- power | ||
285 | `-- subsystem -> ../../../../class/thermal | ||
286 | *****************************************************************************/ | ||
287 | static int scan_tzones(void) | ||
288 | { | ||
289 | DIR *dir; | ||
290 | struct dirent **namelist; | ||
291 | char tz_name[256]; | ||
292 | int i, j, n, k = 0; | ||
293 | |||
294 | if (!ptdata.nr_tz_sensor) | ||
295 | return -1; | ||
296 | |||
297 | for (i = 0; i <= ptdata.max_tz_instance; i++) { | ||
298 | memset(tz_name, 0, sizeof(tz_name)); | ||
299 | snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE, i); | ||
300 | |||
301 | dir = opendir(tz_name); | ||
302 | if (!dir) { | ||
303 | syslog(LOG_INFO, "Thermal zone %s skipped\n", tz_name); | ||
304 | continue; | ||
305 | } | ||
306 | /* keep track of valid tzones */ | ||
307 | n = scandir(tz_name, &namelist, 0, alphasort); | ||
308 | if (n < 0) | ||
309 | syslog(LOG_ERR, "scandir failed in %s", tz_name); | ||
310 | else { | ||
311 | sysfs_get_string(tz_name, "type", ptdata.tzi[k].type); | ||
312 | ptdata.tzi[k].instance = i; | ||
313 | /* detect trip points and cdev attached to this tzone */ | ||
314 | j = 0; /* index for cdev */ | ||
315 | ptdata.tzi[k].nr_cdev = 0; | ||
316 | ptdata.tzi[k].nr_trip_pts = 0; | ||
317 | while (n--) { | ||
318 | char *temp_str; | ||
319 | |||
320 | if (find_tzone_tp(tz_name, namelist[n]->d_name, | ||
321 | &ptdata.tzi[k], k)) | ||
322 | break; | ||
323 | temp_str = strstr(namelist[n]->d_name, "cdev"); | ||
324 | if (!temp_str) { | ||
325 | free(namelist[n]); | ||
326 | continue; | ||
327 | } | ||
328 | if (!find_tzone_cdev(namelist[n], tz_name, | ||
329 | &ptdata.tzi[k], i, j)) | ||
330 | j++; /* increment cdev index */ | ||
331 | free(namelist[n]); | ||
332 | } | ||
333 | free(namelist); | ||
334 | } | ||
335 | /*TODO: reverse trip points */ | ||
336 | closedir(dir); | ||
337 | syslog(LOG_INFO, "TZ %d has %d cdev\n", i, | ||
338 | ptdata.tzi[k].nr_cdev); | ||
339 | k++; | ||
340 | } | ||
341 | |||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int scan_cdevs(void) | ||
346 | { | ||
347 | DIR *dir; | ||
348 | struct dirent **namelist; | ||
349 | char cdev_name[256]; | ||
350 | int i, n, k = 0; | ||
351 | |||
352 | if (!ptdata.nr_cooling_dev) { | ||
353 | fprintf(stderr, "No cooling devices found\n"); | ||
354 | return 0; | ||
355 | } | ||
356 | for (i = 0; i <= ptdata.max_cdev_instance; i++) { | ||
357 | memset(cdev_name, 0, sizeof(cdev_name)); | ||
358 | snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV, i); | ||
359 | |||
360 | dir = opendir(cdev_name); | ||
361 | if (!dir) { | ||
362 | syslog(LOG_INFO, "Cooling dev %s skipped\n", cdev_name); | ||
363 | /* there is a gap in cooling device id, check again | ||
364 | * for the same index. | ||
365 | */ | ||
366 | continue; | ||
367 | } | ||
368 | |||
369 | n = scandir(cdev_name, &namelist, 0, alphasort); | ||
370 | if (n < 0) | ||
371 | syslog(LOG_ERR, "scandir failed in %s", cdev_name); | ||
372 | else { | ||
373 | sysfs_get_string(cdev_name, "type", ptdata.cdi[k].type); | ||
374 | ptdata.cdi[k].instance = i; | ||
375 | if (strstr(ptdata.cdi[k].type, ctrl_cdev)) { | ||
376 | ptdata.cdi[k].flag |= CDEV_FLAG_IN_CONTROL; | ||
377 | syslog(LOG_DEBUG, "control cdev id %d\n", i); | ||
378 | } | ||
379 | while (n--) | ||
380 | free(namelist[n]); | ||
381 | free(namelist); | ||
382 | } | ||
383 | closedir(dir); | ||
384 | k++; | ||
385 | } | ||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | |||
390 | int probe_thermal_sysfs(void) | ||
391 | { | ||
392 | DIR *dir; | ||
393 | struct dirent **namelist; | ||
394 | int n; | ||
395 | |||
396 | dir = opendir(THERMAL_SYSFS); | ||
397 | if (!dir) { | ||
398 | fprintf(stderr, "\nNo thermal sysfs, exit\n"); | ||
399 | return -1; | ||
400 | } | ||
401 | n = scandir(THERMAL_SYSFS, &namelist, 0, alphasort); | ||
402 | if (n < 0) | ||
403 | syslog(LOG_ERR, "scandir failed in thermal sysfs"); | ||
404 | else { | ||
405 | /* detect number of thermal zones and cooling devices */ | ||
406 | while (n--) { | ||
407 | int inst; | ||
408 | |||
409 | if (strstr(namelist[n]->d_name, CDEV)) { | ||
410 | inst = get_instance_id(namelist[n]->d_name, 1, | ||
411 | sizeof("device") - 1); | ||
412 | /* keep track of the max cooling device since | ||
413 | * there may be gaps. | ||
414 | */ | ||
415 | if (inst > ptdata.max_cdev_instance) | ||
416 | ptdata.max_cdev_instance = inst; | ||
417 | |||
418 | syslog(LOG_DEBUG, "found cdev: %s %d %d\n", | ||
419 | namelist[n]->d_name, | ||
420 | ptdata.nr_cooling_dev, | ||
421 | ptdata.max_cdev_instance); | ||
422 | ptdata.nr_cooling_dev++; | ||
423 | } else if (strstr(namelist[n]->d_name, TZONE)) { | ||
424 | inst = get_instance_id(namelist[n]->d_name, 1, | ||
425 | sizeof("zone") - 1); | ||
426 | if (inst > ptdata.max_tz_instance) | ||
427 | ptdata.max_tz_instance = inst; | ||
428 | |||
429 | syslog(LOG_DEBUG, "found tzone: %s %d %d\n", | ||
430 | namelist[n]->d_name, | ||
431 | ptdata.nr_tz_sensor, | ||
432 | ptdata.max_tz_instance); | ||
433 | ptdata.nr_tz_sensor++; | ||
434 | } | ||
435 | free(namelist[n]); | ||
436 | } | ||
437 | free(namelist); | ||
438 | } | ||
439 | syslog(LOG_INFO, "found %d tzone(s), %d cdev(s), target zone %d\n", | ||
440 | ptdata.nr_tz_sensor, ptdata.nr_cooling_dev, | ||
441 | target_thermal_zone); | ||
442 | closedir(dir); | ||
443 | |||
444 | if (!ptdata.nr_tz_sensor) { | ||
445 | fprintf(stderr, "\nNo thermal zones found, exit\n\n"); | ||
446 | return -1; | ||
447 | } | ||
448 | |||
449 | ptdata.tzi = calloc(sizeof(struct tz_info), ptdata.max_tz_instance+1); | ||
450 | if (!ptdata.tzi) { | ||
451 | fprintf(stderr, "Err: allocate tz_info\n"); | ||
452 | return -1; | ||
453 | } | ||
454 | |||
455 | /* we still show thermal zone information if there is no cdev */ | ||
456 | if (ptdata.nr_cooling_dev) { | ||
457 | ptdata.cdi = calloc(sizeof(struct cdev_info), | ||
458 | ptdata.max_cdev_instance + 1); | ||
459 | if (!ptdata.cdi) { | ||
460 | free(ptdata.tzi); | ||
461 | fprintf(stderr, "Err: allocate cdev_info\n"); | ||
462 | return -1; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | /* now probe tzones */ | ||
467 | if (scan_tzones()) | ||
468 | return -1; | ||
469 | if (scan_cdevs()) | ||
470 | return -1; | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | /* convert sysfs zone instance to zone array index */ | ||
475 | int zone_instance_to_index(int zone_inst) | ||
476 | { | ||
477 | int i; | ||
478 | |||
479 | for (i = 0; i < ptdata.nr_tz_sensor; i++) | ||
480 | if (ptdata.tzi[i].instance == zone_inst) | ||
481 | return i; | ||
482 | return -ENOENT; | ||
483 | } | ||
484 | |||
485 | /* read temperature of all thermal zones */ | ||
486 | int update_thermal_data() | ||
487 | { | ||
488 | int i; | ||
489 | char tz_name[256]; | ||
490 | static unsigned long samples; | ||
491 | |||
492 | if (!ptdata.nr_tz_sensor) { | ||
493 | syslog(LOG_ERR, "No thermal zones found!\n"); | ||
494 | return -1; | ||
495 | } | ||
496 | |||
497 | /* circular buffer for keeping historic data */ | ||
498 | if (cur_thermal_record >= NR_THERMAL_RECORDS) | ||
499 | cur_thermal_record = 0; | ||
500 | gettimeofday(&trec[cur_thermal_record].tv, NULL); | ||
501 | if (tmon_log) { | ||
502 | fprintf(tmon_log, "%lu ", ++samples); | ||
503 | fprintf(tmon_log, "%3.1f ", p_param.t_target); | ||
504 | } | ||
505 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { | ||
506 | memset(tz_name, 0, sizeof(tz_name)); | ||
507 | snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE, | ||
508 | ptdata.tzi[i].instance); | ||
509 | sysfs_get_ulong(tz_name, "temp", | ||
510 | &trec[cur_thermal_record].temp[i]); | ||
511 | if (tmon_log) | ||
512 | fprintf(tmon_log, "%lu ", | ||
513 | trec[cur_thermal_record].temp[i]/1000); | ||
514 | } | ||
515 | for (i = 0; i < ptdata.nr_cooling_dev; i++) { | ||
516 | char cdev_name[256]; | ||
517 | unsigned long val; | ||
518 | |||
519 | snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV, | ||
520 | ptdata.cdi[i].instance); | ||
521 | probe_cdev(&ptdata.cdi[i], cdev_name); | ||
522 | val = ptdata.cdi[i].cur_state; | ||
523 | if (val > 1000000) | ||
524 | val = 0; | ||
525 | if (tmon_log) | ||
526 | fprintf(tmon_log, "%lu ", val); | ||
527 | } | ||
528 | |||
529 | if (tmon_log) { | ||
530 | fprintf(tmon_log, "\n"); | ||
531 | fflush(tmon_log); | ||
532 | } | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | void set_ctrl_state(unsigned long state) | ||
538 | { | ||
539 | char ctrl_cdev_path[256]; | ||
540 | int i; | ||
541 | unsigned long cdev_state; | ||
542 | |||
543 | if (no_control) | ||
544 | return; | ||
545 | /* set all ctrl cdev to the same state */ | ||
546 | for (i = 0; i < ptdata.nr_cooling_dev; i++) { | ||
547 | if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) { | ||
548 | if (ptdata.cdi[i].max_state < 10) { | ||
549 | strcpy(ctrl_cdev, "None."); | ||
550 | return; | ||
551 | } | ||
552 | /* scale to percentage of max_state */ | ||
553 | cdev_state = state * ptdata.cdi[i].max_state/100; | ||
554 | syslog(LOG_DEBUG, | ||
555 | "ctrl cdev %d set state %lu scaled to %lu\n", | ||
556 | ptdata.cdi[i].instance, state, cdev_state); | ||
557 | snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS, | ||
558 | CDEV, ptdata.cdi[i].instance); | ||
559 | syslog(LOG_DEBUG, "ctrl cdev path %s", ctrl_cdev_path); | ||
560 | sysfs_set_ulong(ctrl_cdev_path, "cur_state", | ||
561 | cdev_state); | ||
562 | } | ||
563 | } | ||
564 | } | ||
565 | |||
566 | void get_ctrl_state(unsigned long *state) | ||
567 | { | ||
568 | char ctrl_cdev_path[256]; | ||
569 | int ctrl_cdev_id = -1; | ||
570 | int i; | ||
571 | |||
572 | /* TODO: take average of all ctrl types. also consider change based on | ||
573 | * uevent. Take the first reading for now. | ||
574 | */ | ||
575 | for (i = 0; i < ptdata.nr_cooling_dev; i++) { | ||
576 | if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) { | ||
577 | ctrl_cdev_id = ptdata.cdi[i].instance; | ||
578 | syslog(LOG_INFO, "ctrl cdev %d get state\n", | ||
579 | ptdata.cdi[i].instance); | ||
580 | break; | ||
581 | } | ||
582 | } | ||
583 | if (ctrl_cdev_id == -1) { | ||
584 | *state = 0; | ||
585 | return; | ||
586 | } | ||
587 | snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS, | ||
588 | CDEV, ctrl_cdev_id); | ||
589 | sysfs_get_ulong(ctrl_cdev_path, "cur_state", state); | ||
590 | } | ||
591 | |||
592 | void free_thermal_data(void) | ||
593 | { | ||
594 | free(ptdata.tzi); | ||
595 | free(ptdata.cdi); | ||
596 | } | ||
diff --git a/tools/thermal/tmon/tmon.8 b/tools/thermal/tmon/tmon.8 new file mode 100644 index 000000000000..0be727cb9892 --- /dev/null +++ b/tools/thermal/tmon/tmon.8 | |||
@@ -0,0 +1,142 @@ | |||
1 | .TH TMON 8 | ||
2 | .SH NAME | ||
3 | \fBtmon\fP - A monitoring and testing tool for Linux kernel thermal subsystem | ||
4 | |||
5 | .SH SYNOPSIS | ||
6 | .ft B | ||
7 | .B tmon | ||
8 | .RB [ Options ] | ||
9 | .br | ||
10 | .SH DESCRIPTION | ||
11 | \fBtmon \fP can be used to visualize thermal relationship and | ||
12 | real-time thermal data; tune | ||
13 | and test cooling devices and sensors; collect thermal data for offline | ||
14 | analysis and plot. \fBtmon\fP must be run as root in order to control device | ||
15 | states via sysfs. | ||
16 | .PP | ||
17 | \fBFunctions\fP | ||
18 | .PP | ||
19 | .nf | ||
20 | 1. Thermal relationships: | ||
21 | - show thermal zone information | ||
22 | - show cooling device information | ||
23 | - show trip point binding within each thermal zone | ||
24 | - show trip point and cooling device instance bindings | ||
25 | .PP | ||
26 | 2. Real time data display | ||
27 | - show temperature of all thermal zones w.r.t. its trip points and types | ||
28 | - show states of all cooling devices | ||
29 | .PP | ||
30 | 3. Thermal relationship learning and device tuning | ||
31 | - with a built-in Proportional Integral Derivative (\fBPID\fP) | ||
32 | controller, user can pair a cooling device to a thermal sensor for | ||
33 | testing the effectiveness and learn about the thermal distance between the two | ||
34 | - allow manual control of cooling device states and target temperature | ||
35 | .PP | ||
36 | 4. Data logging in /var/tmp/tmon.log | ||
37 | - contains thermal configuration data, i.e. cooling device, thermal | ||
38 | zones, and trip points. Can be used for data collection in remote | ||
39 | debugging. | ||
40 | - log real-time thermal data into space separated format that can be | ||
41 | directly consumed by plotting tools such as Rscript. | ||
42 | |||
43 | .SS Options | ||
44 | .PP | ||
45 | The \fB-c --control\fP option sets a cooling device type to control temperature | ||
46 | of a thermal zone | ||
47 | .PP | ||
48 | The \fB-d --daemon\fP option runs \fBtmon \fP as daemon without user interface | ||
49 | .PP | ||
50 | The \fB-g --debug\fP option allow debug messages to be stored in syslog | ||
51 | .PP | ||
52 | The \fB-h --help\fP option shows help message | ||
53 | .PP | ||
54 | The \fB-l --log\fP option write data to /var/tmp/tmon.log | ||
55 | .PP | ||
56 | The \fB-t --time-interval\fP option sets the polling interval in seconds | ||
57 | .PP | ||
58 | The \fB-v --version\fP option shows the version of \fBtmon \fP | ||
59 | .PP | ||
60 | The \fB-z --zone\fP option sets the target therma zone instance to be controlled | ||
61 | .PP | ||
62 | |||
63 | .SH FIELD DESCRIPTIONS | ||
64 | .nf | ||
65 | .PP | ||
66 | \fBP \fP passive cooling trip point type | ||
67 | \fBA \fP active cooling trip point type (fan) | ||
68 | \fBC \fP critical trip point type | ||
69 | \fBA \fP hot trip point type | ||
70 | \fBkp \fP proportional gain of \fBPID\fP controller | ||
71 | \fBki \fP integral gain of \fBPID\fP controller | ||
72 | \fBkd \fP derivative gain of \fBPID\fP controller | ||
73 | |||
74 | .SH REQUIREMENT | ||
75 | Build depends on ncurses | ||
76 | .PP | ||
77 | Runtime depends on window size large enough to show the number of | ||
78 | devices found on the system. | ||
79 | |||
80 | .PP | ||
81 | |||
82 | .SH INTERACTIVE COMMANDS | ||
83 | .pp | ||
84 | .nf | ||
85 | \fBCtrl-C, q/Q\fP stops \fBtmon\fP | ||
86 | \fBTAB\fP shows tuning pop up panel, choose a letter to modify | ||
87 | |||
88 | .SH EXAMPLES | ||
89 | Without any parameters, tmon is in monitoring only mode and refresh | ||
90 | screen every 1 second. | ||
91 | .PP | ||
92 | 1. For monitoring only: | ||
93 | .nf | ||
94 | $ sudo ./tmon | ||
95 | |||
96 | 2. Use Processor cooling device to control thermal zone 0 at default 65C. | ||
97 | $ sudo ./tmon -c Processor -z 0 | ||
98 | |||
99 | 3. Use intel_powerclamp(idle injection) cooling device to control thermal zone 1 | ||
100 | $ sudo ./tmon -c intel_powerclamp -z 1 | ||
101 | |||
102 | 4. Turn on debug and collect data log at /var/tmp/tmon.log | ||
103 | $ sudo ./tmon -g -l | ||
104 | |||
105 | For example, the log below shows PID controller was adjusting current states | ||
106 | for all cooling devices with "Processor" type such that thermal zone 0 | ||
107 | can stay below 65 dC. | ||
108 | |||
109 | #---------- THERMAL DATA LOG STARTED ----------- | ||
110 | Samples TargetTemp acpitz0 acpitz1 Fan0 Fan1 Fan2 Fan3 Fan4 Fan5 | ||
111 | Fan6 Fan7 Fan8 Fan9 Processor10 Processor11 Processor12 Processor13 | ||
112 | LCD14 intel_powerclamp15 1 65.0 65 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 2 | ||
113 | 65.0 66 65 0 0 0 0 0 0 0 0 0 0 4 4 4 4 6 0 3 65.0 60 54 0 0 0 0 0 0 0 0 | ||
114 | 0 0 4 4 4 4 6 0 4 65.0 53 53 0 0 0 0 0 0 0 0 0 0 4 4 4 4 6 0 | ||
115 | 5 65.0 52 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 | ||
116 | 6 65.0 53 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 | ||
117 | 7 65.0 68 70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 | ||
118 | 8 65.0 68 68 0 0 0 0 0 0 0 0 0 0 5 5 5 5 6 0 | ||
119 | 9 65.0 68 68 0 0 0 0 0 0 0 0 0 0 6 6 6 6 6 0 | ||
120 | 10 65.0 67 67 0 0 0 0 0 0 0 0 0 0 7 7 7 7 6 0 | ||
121 | 11 65.0 67 67 0 0 0 0 0 0 0 0 0 0 8 8 8 8 6 0 | ||
122 | 12 65.0 67 67 0 0 0 0 0 0 0 0 0 0 8 8 8 8 6 0 | ||
123 | 13 65.0 67 67 0 0 0 0 0 0 0 0 0 0 9 9 9 9 6 0 | ||
124 | 14 65.0 66 66 0 0 0 0 0 0 0 0 0 0 10 10 10 10 6 0 | ||
125 | 15 65.0 66 67 0 0 0 0 0 0 0 0 0 0 10 10 10 10 6 0 | ||
126 | 16 65.0 66 66 0 0 0 0 0 0 0 0 0 0 11 11 11 11 6 0 | ||
127 | 17 65.0 66 66 0 0 0 0 0 0 0 0 0 0 11 11 11 11 6 0 | ||
128 | 18 65.0 64 61 0 0 0 0 0 0 0 0 0 0 11 11 11 11 6 0 | ||
129 | 19 65.0 60 59 0 0 0 0 0 0 0 0 0 0 12 12 12 12 6 0 | ||
130 | |||
131 | Data can be read directly into an array by an example R-script below: | ||
132 | |||
133 | #!/usr/bin/Rscript | ||
134 | tdata <- read.table("/var/tmp/tmon.log", header=T, comment.char="#") | ||
135 | attach(tdata) | ||
136 | jpeg("tmon.jpg") | ||
137 | X11() | ||
138 | g_range <- range(0, intel_powerclamp15, TargetTemp, acpitz0) | ||
139 | plot( Samples, intel_powerclamp15, col="blue", ylim=g_range, axes=FALSE, ann=FALSE) | ||
140 | par(new=TRUE) | ||
141 | lines(TargetTemp, type="o", pch=22, lty=2, col="red") | ||
142 | dev.off() | ||
diff --git a/tools/thermal/tmon/tmon.c b/tools/thermal/tmon/tmon.c new file mode 100644 index 000000000000..b30f531173e4 --- /dev/null +++ b/tools/thermal/tmon/tmon.c | |||
@@ -0,0 +1,352 @@ | |||
1 | /* | ||
2 | * tmon.c Thermal Monitor (TMON) main function and entry point | ||
3 | * | ||
4 | * Copyright (C) 2012 Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License version | ||
8 | * 2 or later as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Author: Jacob Pan <jacob.jun.pan@linux.intel.com> | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include <getopt.h> | ||
20 | #include <unistd.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <ncurses.h> | ||
27 | #include <ctype.h> | ||
28 | #include <time.h> | ||
29 | #include <signal.h> | ||
30 | #include <limits.h> | ||
31 | #include <sys/time.h> | ||
32 | #include <pthread.h> | ||
33 | #include <math.h> | ||
34 | #include <stdarg.h> | ||
35 | #include <syslog.h> | ||
36 | |||
37 | #include "tmon.h" | ||
38 | |||
39 | unsigned long ticktime = 1; /* seconds */ | ||
40 | unsigned long no_control = 1; /* monitoring only or use cooling device for | ||
41 | * temperature control. | ||
42 | */ | ||
43 | double time_elapsed = 0.0; | ||
44 | unsigned long target_temp_user = 65; /* can be select by tui later */ | ||
45 | int dialogue_on; | ||
46 | int tmon_exit; | ||
47 | static short daemon_mode; | ||
48 | static int logging; /* for recording thermal data to a file */ | ||
49 | static int debug_on; | ||
50 | FILE *tmon_log; | ||
51 | /*cooling device used for the PID controller */ | ||
52 | char ctrl_cdev[CDEV_NAME_SIZE] = "None"; | ||
53 | int target_thermal_zone; /* user selected target zone instance */ | ||
54 | static void start_daemon_mode(void); | ||
55 | |||
56 | pthread_t event_tid; | ||
57 | pthread_mutex_t input_lock; | ||
58 | void usage() | ||
59 | { | ||
60 | printf("Usage: tmon [OPTION...]\n"); | ||
61 | printf(" -c, --control cooling device in control\n"); | ||
62 | printf(" -d, --daemon run as daemon, no TUI\n"); | ||
63 | printf(" -g, --debug debug message in syslog\n"); | ||
64 | printf(" -h, --help show this help message\n"); | ||
65 | printf(" -l, --log log data to /var/tmp/tmon.log\n"); | ||
66 | printf(" -t, --time-interval sampling time interval, > 1 sec.\n"); | ||
67 | printf(" -v, --version show version\n"); | ||
68 | printf(" -z, --zone target thermal zone id\n"); | ||
69 | |||
70 | exit(0); | ||
71 | } | ||
72 | |||
73 | void version() | ||
74 | { | ||
75 | printf("TMON version %s\n", VERSION); | ||
76 | exit(EXIT_SUCCESS); | ||
77 | } | ||
78 | |||
79 | static void tmon_cleanup(void) | ||
80 | { | ||
81 | |||
82 | syslog(LOG_INFO, "TMON exit cleanup\n"); | ||
83 | fflush(stdout); | ||
84 | refresh(); | ||
85 | if (tmon_log) | ||
86 | fclose(tmon_log); | ||
87 | if (event_tid) { | ||
88 | pthread_mutex_lock(&input_lock); | ||
89 | pthread_cancel(event_tid); | ||
90 | pthread_mutex_unlock(&input_lock); | ||
91 | pthread_mutex_destroy(&input_lock); | ||
92 | } | ||
93 | closelog(); | ||
94 | /* relax control knobs, undo throttling */ | ||
95 | set_ctrl_state(0); | ||
96 | |||
97 | keypad(stdscr, FALSE); | ||
98 | echo(); | ||
99 | nocbreak(); | ||
100 | close_windows(); | ||
101 | endwin(); | ||
102 | free_thermal_data(); | ||
103 | |||
104 | exit(1); | ||
105 | } | ||
106 | |||
107 | |||
108 | static void tmon_sig_handler(int sig) | ||
109 | { | ||
110 | syslog(LOG_INFO, "TMON caught signal %d\n", sig); | ||
111 | refresh(); | ||
112 | switch (sig) { | ||
113 | case SIGTERM: | ||
114 | printf("sigterm, exit and clean up\n"); | ||
115 | fflush(stdout); | ||
116 | break; | ||
117 | case SIGKILL: | ||
118 | printf("sigkill, exit and clean up\n"); | ||
119 | fflush(stdout); | ||
120 | break; | ||
121 | case SIGINT: | ||
122 | printf("ctrl-c, exit and clean up\n"); | ||
123 | fflush(stdout); | ||
124 | break; | ||
125 | default: | ||
126 | break; | ||
127 | } | ||
128 | tmon_exit = true; | ||
129 | } | ||
130 | |||
131 | |||
132 | static void start_syslog(void) | ||
133 | { | ||
134 | if (debug_on) | ||
135 | setlogmask(LOG_UPTO(LOG_DEBUG)); | ||
136 | else | ||
137 | setlogmask(LOG_UPTO(LOG_ERR)); | ||
138 | openlog("tmon.log", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0); | ||
139 | syslog(LOG_NOTICE, "TMON started by User %d", getuid()); | ||
140 | } | ||
141 | |||
142 | static void prepare_logging(void) | ||
143 | { | ||
144 | int i; | ||
145 | |||
146 | if (!logging) | ||
147 | return; | ||
148 | /* open local data log file */ | ||
149 | tmon_log = fopen(TMON_LOG_FILE, "w+"); | ||
150 | if (!tmon_log) { | ||
151 | syslog(LOG_ERR, "failed to open log file %s\n", TMON_LOG_FILE); | ||
152 | return; | ||
153 | } | ||
154 | |||
155 | fprintf(tmon_log, "#----------- THERMAL SYSTEM CONFIG -------------\n"); | ||
156 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { | ||
157 | char binding_str[33]; /* size of long + 1 */ | ||
158 | int j; | ||
159 | |||
160 | memset(binding_str, 0, sizeof(binding_str)); | ||
161 | for (j = 0; j < 32; j++) | ||
162 | binding_str[j] = (ptdata.tzi[i].cdev_binding & 1<<j) ? | ||
163 | '1' : '0'; | ||
164 | |||
165 | fprintf(tmon_log, "#thermal zone %s%02d cdevs binding: %32s\n", | ||
166 | ptdata.tzi[i].type, | ||
167 | ptdata.tzi[i].instance, | ||
168 | binding_str); | ||
169 | for (j = 0; j < ptdata.tzi[i].nr_trip_pts; j++) { | ||
170 | fprintf(tmon_log, "#\tTP%02d type:%s, temp:%lu\n", j, | ||
171 | trip_type_name[ptdata.tzi[i].tp[j].type], | ||
172 | ptdata.tzi[i].tp[j].temp); | ||
173 | } | ||
174 | |||
175 | } | ||
176 | |||
177 | for (i = 0; i < ptdata.nr_cooling_dev; i++) | ||
178 | fprintf(tmon_log, "#cooling devices%02d: %s\n", | ||
179 | i, ptdata.cdi[i].type); | ||
180 | |||
181 | fprintf(tmon_log, "#---------- THERMAL DATA LOG STARTED -----------\n"); | ||
182 | fprintf(tmon_log, "Samples TargetTemp "); | ||
183 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { | ||
184 | fprintf(tmon_log, "%s%d ", ptdata.tzi[i].type, | ||
185 | ptdata.tzi[i].instance); | ||
186 | } | ||
187 | for (i = 0; i < ptdata.nr_cooling_dev; i++) | ||
188 | fprintf(tmon_log, "%s%d ", ptdata.cdi[i].type, | ||
189 | ptdata.cdi[i].instance); | ||
190 | |||
191 | fprintf(tmon_log, "\n"); | ||
192 | } | ||
193 | |||
194 | static struct option opts[] = { | ||
195 | { "control", 1, NULL, 'c' }, | ||
196 | { "daemon", 0, NULL, 'd' }, | ||
197 | { "time-interval", 1, NULL, 't' }, | ||
198 | { "log", 0, NULL, 'l' }, | ||
199 | { "help", 0, NULL, 'h' }, | ||
200 | { "version", 0, NULL, 'v' }, | ||
201 | { "debug", 0, NULL, 'g' }, | ||
202 | { 0, 0, NULL, 0 } | ||
203 | }; | ||
204 | |||
205 | |||
206 | int main(int argc, char **argv) | ||
207 | { | ||
208 | int err = 0; | ||
209 | int id2 = 0, c; | ||
210 | double yk = 0.0; /* controller output */ | ||
211 | int target_tz_index; | ||
212 | |||
213 | if (geteuid() != 0) { | ||
214 | printf("TMON needs to be run as root\n"); | ||
215 | exit(EXIT_FAILURE); | ||
216 | } | ||
217 | |||
218 | while ((c = getopt_long(argc, argv, "c:dlht:vgz:", opts, &id2)) != -1) { | ||
219 | switch (c) { | ||
220 | case 'c': | ||
221 | no_control = 0; | ||
222 | strncpy(ctrl_cdev, optarg, CDEV_NAME_SIZE); | ||
223 | break; | ||
224 | case 'd': | ||
225 | start_daemon_mode(); | ||
226 | printf("Run TMON in daemon mode\n"); | ||
227 | break; | ||
228 | case 't': | ||
229 | ticktime = strtod(optarg, NULL); | ||
230 | if (ticktime < 1) | ||
231 | ticktime = 1; | ||
232 | break; | ||
233 | case 'l': | ||
234 | printf("Logging data to /var/tmp/tmon.log\n"); | ||
235 | logging = 1; | ||
236 | break; | ||
237 | case 'h': | ||
238 | usage(); | ||
239 | break; | ||
240 | case 'v': | ||
241 | version(); | ||
242 | break; | ||
243 | case 'g': | ||
244 | debug_on = 1; | ||
245 | break; | ||
246 | case 'z': | ||
247 | target_thermal_zone = strtod(optarg, NULL); | ||
248 | break; | ||
249 | default: | ||
250 | break; | ||
251 | } | ||
252 | } | ||
253 | if (pthread_mutex_init(&input_lock, NULL) != 0) { | ||
254 | fprintf(stderr, "\n mutex init failed, exit\n"); | ||
255 | return 1; | ||
256 | } | ||
257 | start_syslog(); | ||
258 | if (signal(SIGINT, tmon_sig_handler) == SIG_ERR) | ||
259 | syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); | ||
260 | if (signal(SIGTERM, tmon_sig_handler) == SIG_ERR) | ||
261 | syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); | ||
262 | |||
263 | if (probe_thermal_sysfs()) { | ||
264 | pthread_mutex_destroy(&input_lock); | ||
265 | closelog(); | ||
266 | return -1; | ||
267 | } | ||
268 | initialize_curses(); | ||
269 | setup_windows(); | ||
270 | signal(SIGWINCH, resize_handler); | ||
271 | show_title_bar(); | ||
272 | show_sensors_w(); | ||
273 | show_cooling_device(); | ||
274 | update_thermal_data(); | ||
275 | show_data_w(); | ||
276 | prepare_logging(); | ||
277 | init_thermal_controller(); | ||
278 | |||
279 | nodelay(stdscr, TRUE); | ||
280 | err = pthread_create(&event_tid, NULL, &handle_tui_events, NULL); | ||
281 | if (err != 0) { | ||
282 | printf("\ncan't create thread :[%s]", strerror(err)); | ||
283 | tmon_cleanup(); | ||
284 | exit(EXIT_FAILURE); | ||
285 | } | ||
286 | |||
287 | /* validate range of user selected target zone, default to the first | ||
288 | * instance if out of range | ||
289 | */ | ||
290 | target_tz_index = zone_instance_to_index(target_thermal_zone); | ||
291 | if (target_tz_index < 0) { | ||
292 | target_thermal_zone = ptdata.tzi[0].instance; | ||
293 | syslog(LOG_ERR, "target zone is not found, default to %d\n", | ||
294 | target_thermal_zone); | ||
295 | } | ||
296 | while (1) { | ||
297 | sleep(ticktime); | ||
298 | show_title_bar(); | ||
299 | show_sensors_w(); | ||
300 | update_thermal_data(); | ||
301 | if (!dialogue_on) { | ||
302 | show_data_w(); | ||
303 | show_cooling_device(); | ||
304 | } | ||
305 | cur_thermal_record++; | ||
306 | time_elapsed += ticktime; | ||
307 | controller_handler(trec[0].temp[target_tz_index] / 1000, | ||
308 | &yk); | ||
309 | trec[0].pid_out_pct = yk; | ||
310 | if (!dialogue_on) | ||
311 | show_control_w(); | ||
312 | if (tmon_exit) | ||
313 | break; | ||
314 | } | ||
315 | tmon_cleanup(); | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static void start_daemon_mode() | ||
320 | { | ||
321 | daemon_mode = 1; | ||
322 | /* fork */ | ||
323 | pid_t sid, pid = fork(); | ||
324 | if (pid < 0) { | ||
325 | exit(EXIT_FAILURE); | ||
326 | } else if (pid > 0) | ||
327 | /* kill parent */ | ||
328 | exit(EXIT_SUCCESS); | ||
329 | |||
330 | /* disable TUI, it may not be necessary, but saves some resource */ | ||
331 | disable_tui(); | ||
332 | |||
333 | /* change the file mode mask */ | ||
334 | umask(0); | ||
335 | |||
336 | /* new SID for the daemon process */ | ||
337 | sid = setsid(); | ||
338 | if (sid < 0) | ||
339 | exit(EXIT_FAILURE); | ||
340 | |||
341 | /* change working directory */ | ||
342 | if ((chdir("/")) < 0) | ||
343 | exit(EXIT_FAILURE); | ||
344 | |||
345 | |||
346 | sleep(10); | ||
347 | |||
348 | close(STDIN_FILENO); | ||
349 | close(STDOUT_FILENO); | ||
350 | close(STDERR_FILENO); | ||
351 | |||
352 | } | ||
diff --git a/tools/thermal/tmon/tmon.h b/tools/thermal/tmon/tmon.h new file mode 100644 index 000000000000..9e3c49c547ac --- /dev/null +++ b/tools/thermal/tmon/tmon.h | |||
@@ -0,0 +1,204 @@ | |||
1 | /* | ||
2 | * tmon.h contains data structures and constants used by TMON | ||
3 | * | ||
4 | * Copyright (C) 2012 Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License version | ||
8 | * 2 or later as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Author Name Jacob Pan <jacob.jun.pan@linux.intel.com> | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #ifndef TMON_H | ||
20 | #define TMON_H | ||
21 | |||
22 | #define MAX_DISP_TEMP 125 | ||
23 | #define MAX_CTRL_TEMP 105 | ||
24 | #define MIN_CTRL_TEMP 40 | ||
25 | #define MAX_NR_TZONE 16 | ||
26 | #define MAX_NR_CDEV 32 | ||
27 | #define MAX_NR_TRIP 16 | ||
28 | #define MAX_NR_CDEV_TRIP 12 /* number of cooling devices that can bind | ||
29 | * to a thermal zone trip. | ||
30 | */ | ||
31 | #define MAX_TEMP_KC 140000 | ||
32 | /* starting char position to draw sensor data, such as tz names | ||
33 | * trip point list, etc. | ||
34 | */ | ||
35 | #define DATA_LEFT_ALIGN 10 | ||
36 | #define NR_LINES_TZDATA 1 | ||
37 | #define TMON_LOG_FILE "/var/tmp/tmon.log" | ||
38 | |||
39 | extern unsigned long ticktime; | ||
40 | extern double time_elapsed; | ||
41 | extern unsigned long target_temp_user; | ||
42 | extern int dialogue_on; | ||
43 | extern char ctrl_cdev[]; | ||
44 | extern pthread_mutex_t input_lock; | ||
45 | extern int tmon_exit; | ||
46 | extern int target_thermal_zone; | ||
47 | /* use fixed size record to simplify data processing and transfer | ||
48 | * TBD: more info to be added, e.g. programmable trip point data. | ||
49 | */ | ||
50 | struct thermal_data_record { | ||
51 | struct timeval tv; | ||
52 | unsigned long temp[MAX_NR_TZONE]; | ||
53 | double pid_out_pct; | ||
54 | }; | ||
55 | |||
56 | struct cdev_info { | ||
57 | char type[64]; | ||
58 | int instance; | ||
59 | unsigned long max_state; | ||
60 | unsigned long cur_state; | ||
61 | unsigned long flag; | ||
62 | }; | ||
63 | |||
64 | enum trip_type { | ||
65 | THERMAL_TRIP_CRITICAL, | ||
66 | THERMAL_TRIP_HOT, | ||
67 | THERMAL_TRIP_PASSIVE, | ||
68 | THERMAL_TRIP_ACTIVE, | ||
69 | NR_THERMAL_TRIP_TYPE, | ||
70 | }; | ||
71 | |||
72 | struct trip_point { | ||
73 | enum trip_type type; | ||
74 | unsigned long temp; | ||
75 | unsigned long hysteresis; | ||
76 | int attribute; /* programmability etc. */ | ||
77 | }; | ||
78 | |||
79 | /* thermal zone configuration information, binding with cooling devices could | ||
80 | * change at runtime. | ||
81 | */ | ||
82 | struct tz_info { | ||
83 | char type[256]; /* e.g. acpitz */ | ||
84 | int instance; | ||
85 | int passive; /* active zone has passive node to force passive mode */ | ||
86 | int nr_cdev; /* number of cooling device binded */ | ||
87 | int nr_trip_pts; | ||
88 | struct trip_point tp[MAX_NR_TRIP]; | ||
89 | unsigned long cdev_binding; /* bitmap for attached cdevs */ | ||
90 | /* cdev bind trip points, allow one cdev bind to multiple trips */ | ||
91 | unsigned long trip_binding[MAX_NR_CDEV]; | ||
92 | }; | ||
93 | |||
94 | struct tmon_platform_data { | ||
95 | int nr_tz_sensor; | ||
96 | int nr_cooling_dev; | ||
97 | /* keep track of instance ids since there might be gaps */ | ||
98 | int max_tz_instance; | ||
99 | int max_cdev_instance; | ||
100 | struct tz_info *tzi; | ||
101 | struct cdev_info *cdi; | ||
102 | }; | ||
103 | |||
104 | struct control_ops { | ||
105 | void (*set_ratio)(unsigned long ratio); | ||
106 | unsigned long (*get_ratio)(unsigned long ratio); | ||
107 | |||
108 | }; | ||
109 | |||
110 | enum cdev_types { | ||
111 | CDEV_TYPE_PROC, | ||
112 | CDEV_TYPE_FAN, | ||
113 | CDEV_TYPE_MEM, | ||
114 | CDEV_TYPE_NR, | ||
115 | }; | ||
116 | |||
117 | /* REVISIT: the idea is to group sensors if possible, e.g. on intel mid | ||
118 | * we have "skin0", "skin1", "sys", "msicdie" | ||
119 | * on DPTF enabled systems, we might have PCH, TSKN, TAMB, etc. | ||
120 | */ | ||
121 | enum tzone_types { | ||
122 | TZONE_TYPE_ACPI, | ||
123 | TZONE_TYPE_PCH, | ||
124 | TZONE_TYPE_NR, | ||
125 | }; | ||
126 | |||
127 | /* limit the output of PID controller adjustment */ | ||
128 | #define LIMIT_HIGH (95) | ||
129 | #define LIMIT_LOW (2) | ||
130 | |||
131 | struct pid_params { | ||
132 | double kp; /* Controller gain from Dialog Box */ | ||
133 | double ki; /* Time-constant for I action from Dialog Box */ | ||
134 | double kd; /* Time-constant for D action from Dialog Box */ | ||
135 | double ts; | ||
136 | double k_lpf; | ||
137 | |||
138 | double t_target; | ||
139 | double y_k; | ||
140 | }; | ||
141 | |||
142 | extern int init_thermal_controller(void); | ||
143 | extern void controller_handler(const double xk, double *yk); | ||
144 | |||
145 | extern struct tmon_platform_data ptdata; | ||
146 | extern struct pid_params p_param; | ||
147 | |||
148 | extern FILE *tmon_log; | ||
149 | extern int cur_thermal_record; /* index to the trec array */ | ||
150 | extern struct thermal_data_record trec[]; | ||
151 | extern const char *trip_type_name[]; | ||
152 | extern unsigned long no_control; | ||
153 | |||
154 | extern void initialize_curses(void); | ||
155 | extern void show_controller_stats(char *line); | ||
156 | extern void show_title_bar(void); | ||
157 | extern void setup_windows(void); | ||
158 | extern void disable_tui(void); | ||
159 | extern void show_sensors_w(void); | ||
160 | extern void show_data_w(void); | ||
161 | extern void write_status_bar(int x, char *line); | ||
162 | extern void show_control_w(); | ||
163 | |||
164 | extern void show_cooling_device(void); | ||
165 | extern void show_dialogue(void); | ||
166 | extern int update_thermal_data(void); | ||
167 | |||
168 | extern int probe_thermal_sysfs(void); | ||
169 | extern void free_thermal_data(void); | ||
170 | extern void resize_handler(int sig); | ||
171 | extern void set_ctrl_state(unsigned long state); | ||
172 | extern void get_ctrl_state(unsigned long *state); | ||
173 | extern void *handle_tui_events(void *arg); | ||
174 | extern int sysfs_set_ulong(char *path, char *filename, unsigned long val); | ||
175 | extern int zone_instance_to_index(int zone_inst); | ||
176 | extern void close_windows(void); | ||
177 | |||
178 | #define PT_COLOR_DEFAULT 1 | ||
179 | #define PT_COLOR_HEADER_BAR 2 | ||
180 | #define PT_COLOR_ERROR 3 | ||
181 | #define PT_COLOR_RED 4 | ||
182 | #define PT_COLOR_YELLOW 5 | ||
183 | #define PT_COLOR_GREEN 6 | ||
184 | #define PT_COLOR_BRIGHT 7 | ||
185 | #define PT_COLOR_BLUE 8 | ||
186 | |||
187 | /* each thermal zone uses 12 chars, 8 for name, 2 for instance, 2 space | ||
188 | * also used to list trip points in forms of AAAC, which represents | ||
189 | * A: Active | ||
190 | * C: Critical | ||
191 | */ | ||
192 | #define TZONE_RECORD_SIZE 12 | ||
193 | #define TZ_LEFT_ALIGN 32 | ||
194 | #define CDEV_NAME_SIZE 20 | ||
195 | #define CDEV_FLAG_IN_CONTROL (1 << 0) | ||
196 | |||
197 | /* dialogue box starts */ | ||
198 | #define DIAG_X 48 | ||
199 | #define DIAG_Y 8 | ||
200 | #define THERMAL_SYSFS "/sys/class/thermal" | ||
201 | #define CDEV "cooling_device" | ||
202 | #define TZONE "thermal_zone" | ||
203 | #define TDATA_LEFT 16 | ||
204 | #endif /* TMON_H */ | ||
diff --git a/tools/thermal/tmon/tui.c b/tools/thermal/tmon/tui.c new file mode 100644 index 000000000000..89f8ef0e15c8 --- /dev/null +++ b/tools/thermal/tmon/tui.c | |||
@@ -0,0 +1,638 @@ | |||
1 | /* | ||
2 | * tui.c ncurses text user interface for TMON program | ||
3 | * | ||
4 | * Copyright (C) 2013 Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License version | ||
8 | * 2 or later as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Author: Jacob Pan <jacob.jun.pan@linux.intel.com> | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include <unistd.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <string.h> | ||
23 | #include <stdint.h> | ||
24 | #include <ncurses.h> | ||
25 | #include <time.h> | ||
26 | #include <syslog.h> | ||
27 | #include <panel.h> | ||
28 | #include <pthread.h> | ||
29 | #include <signal.h> | ||
30 | |||
31 | #include "tmon.h" | ||
32 | |||
33 | static PANEL *data_panel; | ||
34 | static PANEL *dialogue_panel; | ||
35 | static PANEL *top; | ||
36 | |||
37 | static WINDOW *title_bar_window; | ||
38 | static WINDOW *tz_sensor_window; | ||
39 | static WINDOW *cooling_device_window; | ||
40 | static WINDOW *control_window; | ||
41 | static WINDOW *status_bar_window; | ||
42 | static WINDOW *thermal_data_window; | ||
43 | static WINDOW *dialogue_window; | ||
44 | |||
45 | char status_bar_slots[10][40]; | ||
46 | static void draw_hbar(WINDOW *win, int y, int start, int len, | ||
47 | unsigned long pattern, bool end); | ||
48 | |||
49 | static int maxx, maxy; | ||
50 | static int maxwidth = 200; | ||
51 | |||
52 | #define TITLE_BAR_HIGHT 1 | ||
53 | #define SENSOR_WIN_HIGHT 4 /* one row for tz name, one for trip points */ | ||
54 | |||
55 | |||
56 | /* daemon mode flag (set by startup parameter -d) */ | ||
57 | static int tui_disabled; | ||
58 | |||
59 | static void close_panel(PANEL *p) | ||
60 | { | ||
61 | if (p) { | ||
62 | del_panel(p); | ||
63 | p = NULL; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | static void close_window(WINDOW *win) | ||
68 | { | ||
69 | if (win) { | ||
70 | delwin(win); | ||
71 | win = NULL; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | void close_windows(void) | ||
76 | { | ||
77 | if (tui_disabled) | ||
78 | return; | ||
79 | /* must delete panels before their attached windows */ | ||
80 | if (dialogue_window) | ||
81 | close_panel(dialogue_panel); | ||
82 | if (cooling_device_window) | ||
83 | close_panel(data_panel); | ||
84 | |||
85 | close_window(title_bar_window); | ||
86 | close_window(tz_sensor_window); | ||
87 | close_window(status_bar_window); | ||
88 | close_window(cooling_device_window); | ||
89 | close_window(control_window); | ||
90 | close_window(thermal_data_window); | ||
91 | close_window(dialogue_window); | ||
92 | |||
93 | } | ||
94 | |||
95 | void write_status_bar(int x, char *line) | ||
96 | { | ||
97 | mvwprintw(status_bar_window, 0, x, "%s", line); | ||
98 | wrefresh(status_bar_window); | ||
99 | } | ||
100 | |||
101 | void setup_windows(void) | ||
102 | { | ||
103 | int y_begin = 1; | ||
104 | |||
105 | if (tui_disabled) | ||
106 | return; | ||
107 | |||
108 | getmaxyx(stdscr, maxy, maxx); | ||
109 | resizeterm(maxy, maxx); | ||
110 | |||
111 | title_bar_window = subwin(stdscr, TITLE_BAR_HIGHT, maxx, 0, 0); | ||
112 | y_begin += TITLE_BAR_HIGHT; | ||
113 | |||
114 | tz_sensor_window = subwin(stdscr, SENSOR_WIN_HIGHT, maxx, y_begin, 0); | ||
115 | y_begin += SENSOR_WIN_HIGHT; | ||
116 | |||
117 | cooling_device_window = subwin(stdscr, ptdata.nr_cooling_dev + 3, maxx, | ||
118 | y_begin, 0); | ||
119 | y_begin += ptdata.nr_cooling_dev + 3; /* 2 lines for border */ | ||
120 | /* two lines to show borders, one line per tz show trip point position | ||
121 | * and value. | ||
122 | * dialogue window is a pop-up, when needed it lays on top of cdev win | ||
123 | */ | ||
124 | |||
125 | dialogue_window = subwin(stdscr, ptdata.nr_cooling_dev+5, maxx-50, | ||
126 | DIAG_Y, DIAG_X); | ||
127 | |||
128 | thermal_data_window = subwin(stdscr, ptdata.nr_tz_sensor * | ||
129 | NR_LINES_TZDATA + 3, maxx, y_begin, 0); | ||
130 | y_begin += ptdata.nr_tz_sensor * NR_LINES_TZDATA + 3; | ||
131 | control_window = subwin(stdscr, 4, maxx, y_begin, 0); | ||
132 | |||
133 | scrollok(cooling_device_window, TRUE); | ||
134 | maxwidth = maxx - 18; | ||
135 | status_bar_window = subwin(stdscr, 1, maxx, maxy-1, 0); | ||
136 | |||
137 | strcpy(status_bar_slots[0], " Ctrl-c - Quit "); | ||
138 | strcpy(status_bar_slots[1], " TAB - Tuning "); | ||
139 | wmove(status_bar_window, 1, 30); | ||
140 | |||
141 | /* prepare panels for dialogue, if panel already created then we must | ||
142 | * be doing resizing, so just replace windows with new ones, old ones | ||
143 | * should have been deleted by close_window | ||
144 | */ | ||
145 | data_panel = new_panel(cooling_device_window); | ||
146 | if (!data_panel) | ||
147 | syslog(LOG_DEBUG, "No data panel\n"); | ||
148 | else { | ||
149 | if (dialogue_window) { | ||
150 | dialogue_panel = new_panel(dialogue_window); | ||
151 | if (!dialogue_panel) | ||
152 | syslog(LOG_DEBUG, "No dialogue panel\n"); | ||
153 | else { | ||
154 | /* Set up the user pointer to the next panel*/ | ||
155 | set_panel_userptr(data_panel, dialogue_panel); | ||
156 | set_panel_userptr(dialogue_panel, data_panel); | ||
157 | top = data_panel; | ||
158 | } | ||
159 | } else | ||
160 | syslog(LOG_INFO, "no dialogue win, term too small\n"); | ||
161 | } | ||
162 | doupdate(); | ||
163 | werase(stdscr); | ||
164 | refresh(); | ||
165 | } | ||
166 | |||
167 | void resize_handler(int sig) | ||
168 | { | ||
169 | /* start over when term gets resized, but first we clean up */ | ||
170 | close_windows(); | ||
171 | endwin(); | ||
172 | refresh(); | ||
173 | clear(); | ||
174 | getmaxyx(stdscr, maxy, maxx); /* get the new screen size */ | ||
175 | setup_windows(); | ||
176 | /* rate limit */ | ||
177 | sleep(1); | ||
178 | syslog(LOG_DEBUG, "SIG %d, term resized to %d x %d\n", | ||
179 | sig, maxy, maxx); | ||
180 | signal(SIGWINCH, resize_handler); | ||
181 | } | ||
182 | |||
183 | const char cdev_title[] = " COOLING DEVICES "; | ||
184 | void show_cooling_device(void) | ||
185 | { | ||
186 | int i, j, x, y = 0; | ||
187 | |||
188 | if (tui_disabled || !cooling_device_window) | ||
189 | return; | ||
190 | |||
191 | werase(cooling_device_window); | ||
192 | wattron(cooling_device_window, A_BOLD); | ||
193 | mvwprintw(cooling_device_window, 1, 1, | ||
194 | "ID Cooling Dev Cur Max Thermal Zone Binding"); | ||
195 | wattroff(cooling_device_window, A_BOLD); | ||
196 | for (j = 0; j < ptdata.nr_cooling_dev; j++) { | ||
197 | /* draw cooling device list on the left in the order of | ||
198 | * cooling device instances. skip unused idr. | ||
199 | */ | ||
200 | mvwprintw(cooling_device_window, j + 2, 1, | ||
201 | "%02d %12.12s%6d %6d", | ||
202 | ptdata.cdi[j].instance, | ||
203 | ptdata.cdi[j].type, | ||
204 | ptdata.cdi[j].cur_state, | ||
205 | ptdata.cdi[j].max_state); | ||
206 | } | ||
207 | |||
208 | /* show cdev binding, y is the global cooling device instance */ | ||
209 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { | ||
210 | int tz_inst = ptdata.tzi[i].instance; | ||
211 | for (j = 0; j < ptdata.nr_cooling_dev; j++) { | ||
212 | int cdev_inst; | ||
213 | y = j; | ||
214 | x = tz_inst * TZONE_RECORD_SIZE + TZ_LEFT_ALIGN; | ||
215 | |||
216 | draw_hbar(cooling_device_window, y+2, x, | ||
217 | TZONE_RECORD_SIZE-1, ACS_VLINE, false); | ||
218 | |||
219 | /* draw a column of spaces to separate thermal zones */ | ||
220 | mvwprintw(cooling_device_window, y+2, x-1, " "); | ||
221 | if (ptdata.tzi[i].cdev_binding) { | ||
222 | cdev_inst = ptdata.cdi[j].instance; | ||
223 | unsigned long trip_binding = | ||
224 | ptdata.tzi[i].trip_binding[cdev_inst]; | ||
225 | int k = 0; /* per zone trip point id that | ||
226 | * binded to this cdev, one to | ||
227 | * many possible based on the | ||
228 | * binding bitmask. | ||
229 | */ | ||
230 | syslog(LOG_DEBUG, | ||
231 | "bind tz%d cdev%d tp%lx %d cdev%lx\n", | ||
232 | i, j, trip_binding, y, | ||
233 | ptdata.tzi[i].cdev_binding); | ||
234 | /* draw each trip binding for the cdev */ | ||
235 | while (trip_binding >>= 1) { | ||
236 | k++; | ||
237 | if (!(trip_binding & 1)) | ||
238 | continue; | ||
239 | /* draw '*' to show binding */ | ||
240 | mvwprintw(cooling_device_window, | ||
241 | y + 2, | ||
242 | x + ptdata.tzi[i].nr_trip_pts - | ||
243 | k - 1, "*"); | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | /* draw border after data so that border will not be messed up | ||
249 | * even there is not enough space for all the data to be shown | ||
250 | */ | ||
251 | wborder(cooling_device_window, 0, 0, 0, 0, 0, 0, 0, 0); | ||
252 | wattron(cooling_device_window, A_BOLD); | ||
253 | mvwprintw(cooling_device_window, 0, maxx/2 - sizeof(cdev_title), | ||
254 | cdev_title); | ||
255 | wattroff(cooling_device_window, A_BOLD); | ||
256 | |||
257 | wrefresh(cooling_device_window); | ||
258 | } | ||
259 | |||
260 | const char DIAG_TITLE[] = "[ TUNABLES ]"; | ||
261 | #define DIAG_DEV_ROWS 5 | ||
262 | void show_dialogue(void) | ||
263 | { | ||
264 | int j, x = 0, y = 0; | ||
265 | WINDOW *w = dialogue_window; | ||
266 | |||
267 | if (tui_disabled || !w) | ||
268 | return; | ||
269 | |||
270 | werase(w); | ||
271 | box(w, 0, 0); | ||
272 | mvwprintw(w, 0, maxx/4, DIAG_TITLE); | ||
273 | /* list all the available tunables */ | ||
274 | for (j = 0; j <= ptdata.nr_cooling_dev; j++) { | ||
275 | y = j % DIAG_DEV_ROWS; | ||
276 | if (y == 0 && j != 0) | ||
277 | x += 20; | ||
278 | if (j == ptdata.nr_cooling_dev) | ||
279 | /* save last choice for target temp */ | ||
280 | mvwprintw(w, y+1, x+1, "%C-%.12s", 'A'+j, "Set Temp"); | ||
281 | else | ||
282 | mvwprintw(w, y+1, x+1, "%C-%.10s-%2d", 'A'+j, | ||
283 | ptdata.cdi[j].type, ptdata.cdi[j].instance); | ||
284 | } | ||
285 | wattron(w, A_BOLD); | ||
286 | mvwprintw(w, DIAG_DEV_ROWS+1, 1, "Enter Choice [A-Z]?"); | ||
287 | wattroff(w, A_BOLD); | ||
288 | /* y size of dialogue win is nr cdev + 5, so print legend | ||
289 | * at the bottom line | ||
290 | */ | ||
291 | mvwprintw(w, ptdata.nr_cooling_dev+3, 1, | ||
292 | "Legend: A=Active, P=Passive, C=Critical"); | ||
293 | |||
294 | wrefresh(dialogue_window); | ||
295 | } | ||
296 | |||
297 | void write_dialogue_win(char *buf, int y, int x) | ||
298 | { | ||
299 | WINDOW *w = dialogue_window; | ||
300 | |||
301 | mvwprintw(w, y, x, "%s", buf); | ||
302 | } | ||
303 | |||
304 | const char control_title[] = " CONTROLS "; | ||
305 | void show_control_w(void) | ||
306 | { | ||
307 | unsigned long state; | ||
308 | |||
309 | get_ctrl_state(&state); | ||
310 | |||
311 | if (tui_disabled || !control_window) | ||
312 | return; | ||
313 | |||
314 | werase(control_window); | ||
315 | mvwprintw(control_window, 1, 1, | ||
316 | "PID gain: kp=%2.2f ki=%2.2f kd=%2.2f Output %2.2f", | ||
317 | p_param.kp, p_param.ki, p_param.kd, p_param.y_k); | ||
318 | |||
319 | mvwprintw(control_window, 2, 1, | ||
320 | "Target Temp: %2.1fC, Zone: %d, Control Device: %.12s", | ||
321 | p_param.t_target, target_thermal_zone, ctrl_cdev); | ||
322 | |||
323 | /* draw border last such that everything is within boundary */ | ||
324 | wborder(control_window, 0, 0, 0, 0, 0, 0, 0, 0); | ||
325 | wattron(control_window, A_BOLD); | ||
326 | mvwprintw(control_window, 0, maxx/2 - sizeof(control_title), | ||
327 | control_title); | ||
328 | wattroff(control_window, A_BOLD); | ||
329 | |||
330 | wrefresh(control_window); | ||
331 | } | ||
332 | |||
333 | void initialize_curses(void) | ||
334 | { | ||
335 | if (tui_disabled) | ||
336 | return; | ||
337 | |||
338 | initscr(); | ||
339 | start_color(); | ||
340 | keypad(stdscr, TRUE); /* enable keyboard mapping */ | ||
341 | nonl(); /* tell curses not to do NL->CR/NL on output */ | ||
342 | cbreak(); /* take input chars one at a time */ | ||
343 | noecho(); /* dont echo input */ | ||
344 | curs_set(0); /* turn off cursor */ | ||
345 | use_default_colors(); | ||
346 | |||
347 | init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK); | ||
348 | init_pair(PT_COLOR_HEADER_BAR, COLOR_BLACK, COLOR_WHITE); | ||
349 | init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED); | ||
350 | init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED); | ||
351 | init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW); | ||
352 | init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN); | ||
353 | init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE); | ||
354 | init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK); | ||
355 | |||
356 | } | ||
357 | |||
358 | void show_title_bar(void) | ||
359 | { | ||
360 | int i; | ||
361 | int x = 0; | ||
362 | |||
363 | if (tui_disabled || !title_bar_window) | ||
364 | return; | ||
365 | |||
366 | wattrset(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR)); | ||
367 | wbkgd(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR)); | ||
368 | werase(title_bar_window); | ||
369 | |||
370 | mvwprintw(title_bar_window, 0, 0, | ||
371 | " TMON v%s", VERSION); | ||
372 | |||
373 | wrefresh(title_bar_window); | ||
374 | |||
375 | werase(status_bar_window); | ||
376 | |||
377 | for (i = 0; i < 10; i++) { | ||
378 | if (strlen(status_bar_slots[i]) == 0) | ||
379 | continue; | ||
380 | wattron(status_bar_window, A_REVERSE); | ||
381 | mvwprintw(status_bar_window, 0, x, "%s", status_bar_slots[i]); | ||
382 | wattroff(status_bar_window, A_REVERSE); | ||
383 | x += strlen(status_bar_slots[i]) + 1; | ||
384 | } | ||
385 | wrefresh(status_bar_window); | ||
386 | } | ||
387 | |||
388 | static void handle_input_val(int ch) | ||
389 | { | ||
390 | char buf[32]; | ||
391 | int val; | ||
392 | char path[256]; | ||
393 | WINDOW *w = dialogue_window; | ||
394 | |||
395 | echo(); | ||
396 | keypad(w, TRUE); | ||
397 | wgetnstr(w, buf, 31); | ||
398 | val = atoi(buf); | ||
399 | |||
400 | if (ch == ptdata.nr_cooling_dev) { | ||
401 | snprintf(buf, 31, "Invalid Temp %d! %d-%d", val, | ||
402 | MIN_CTRL_TEMP, MAX_CTRL_TEMP); | ||
403 | if (val < MIN_CTRL_TEMP || val > MAX_CTRL_TEMP) | ||
404 | write_status_bar(40, buf); | ||
405 | else { | ||
406 | p_param.t_target = val; | ||
407 | snprintf(buf, 31, "Set New Target Temp %d", val); | ||
408 | write_status_bar(40, buf); | ||
409 | } | ||
410 | } else { | ||
411 | snprintf(path, 256, "%s/%s%d", THERMAL_SYSFS, | ||
412 | CDEV, ptdata.cdi[ch].instance); | ||
413 | sysfs_set_ulong(path, "cur_state", val); | ||
414 | } | ||
415 | noecho(); | ||
416 | dialogue_on = 0; | ||
417 | show_data_w(); | ||
418 | show_control_w(); | ||
419 | |||
420 | top = (PANEL *)panel_userptr(top); | ||
421 | top_panel(top); | ||
422 | } | ||
423 | |||
424 | static void handle_input_choice(int ch) | ||
425 | { | ||
426 | char buf[48]; | ||
427 | int base = 0; | ||
428 | int cdev_id = 0; | ||
429 | |||
430 | if ((ch >= 'A' && ch <= 'A' + ptdata.nr_cooling_dev) || | ||
431 | (ch >= 'a' && ch <= 'a' + ptdata.nr_cooling_dev)) { | ||
432 | base = (ch < 'a') ? 'A' : 'a'; | ||
433 | cdev_id = ch - base; | ||
434 | if (ptdata.nr_cooling_dev == cdev_id) | ||
435 | snprintf(buf, sizeof(buf), "New Target Temp:"); | ||
436 | else | ||
437 | snprintf(buf, sizeof(buf), "New Value for %.10s-%2d: ", | ||
438 | ptdata.cdi[cdev_id].type, | ||
439 | ptdata.cdi[cdev_id].instance); | ||
440 | write_dialogue_win(buf, DIAG_DEV_ROWS+2, 2); | ||
441 | handle_input_val(cdev_id); | ||
442 | } else { | ||
443 | snprintf(buf, sizeof(buf), "Invalid selection %d", ch); | ||
444 | write_dialogue_win(buf, 8, 2); | ||
445 | } | ||
446 | } | ||
447 | |||
448 | void *handle_tui_events(void *arg) | ||
449 | { | ||
450 | int ch; | ||
451 | |||
452 | keypad(cooling_device_window, TRUE); | ||
453 | while ((ch = wgetch(cooling_device_window)) != EOF) { | ||
454 | if (tmon_exit) | ||
455 | break; | ||
456 | /* when term size is too small, no dialogue panels are set. | ||
457 | * we need to filter out such cases. | ||
458 | */ | ||
459 | if (!data_panel || !dialogue_panel || | ||
460 | !cooling_device_window || | ||
461 | !dialogue_window) { | ||
462 | |||
463 | continue; | ||
464 | } | ||
465 | pthread_mutex_lock(&input_lock); | ||
466 | if (dialogue_on) { | ||
467 | handle_input_choice(ch); | ||
468 | /* top panel filter */ | ||
469 | if (ch == 'q' || ch == 'Q') | ||
470 | ch = 0; | ||
471 | } | ||
472 | switch (ch) { | ||
473 | case KEY_LEFT: | ||
474 | box(cooling_device_window, 10, 0); | ||
475 | break; | ||
476 | case 9: /* TAB */ | ||
477 | top = (PANEL *)panel_userptr(top); | ||
478 | top_panel(top); | ||
479 | if (top == dialogue_panel) { | ||
480 | dialogue_on = 1; | ||
481 | show_dialogue(); | ||
482 | } else { | ||
483 | dialogue_on = 0; | ||
484 | /* force refresh */ | ||
485 | show_data_w(); | ||
486 | show_control_w(); | ||
487 | } | ||
488 | break; | ||
489 | case 'q': | ||
490 | case 'Q': | ||
491 | tmon_exit = 1; | ||
492 | break; | ||
493 | } | ||
494 | update_panels(); | ||
495 | doupdate(); | ||
496 | pthread_mutex_unlock(&input_lock); | ||
497 | } | ||
498 | |||
499 | if (arg) | ||
500 | *(int *)arg = 0; /* make gcc happy */ | ||
501 | |||
502 | return NULL; | ||
503 | } | ||
504 | |||
505 | /* draw a horizontal bar in given pattern */ | ||
506 | static void draw_hbar(WINDOW *win, int y, int start, int len, unsigned long ptn, | ||
507 | bool end) | ||
508 | { | ||
509 | mvwaddch(win, y, start, ptn); | ||
510 | whline(win, ptn, len); | ||
511 | if (end) | ||
512 | mvwaddch(win, y, MAX_DISP_TEMP+TDATA_LEFT, ']'); | ||
513 | } | ||
514 | |||
515 | static char trip_type_to_char(int type) | ||
516 | { | ||
517 | switch (type) { | ||
518 | case THERMAL_TRIP_CRITICAL: return 'C'; | ||
519 | case THERMAL_TRIP_HOT: return 'H'; | ||
520 | case THERMAL_TRIP_PASSIVE: return 'P'; | ||
521 | case THERMAL_TRIP_ACTIVE: return 'A'; | ||
522 | default: | ||
523 | return '?'; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | /* fill a string with trip point type and value in one line | ||
528 | * e.g. P(56) C(106) | ||
529 | * maintain the distance one degree per char | ||
530 | */ | ||
531 | static void draw_tp_line(int tz, int y) | ||
532 | { | ||
533 | int j; | ||
534 | int x; | ||
535 | |||
536 | for (j = 0; j < ptdata.tzi[tz].nr_trip_pts; j++) { | ||
537 | x = ptdata.tzi[tz].tp[j].temp / 1000; | ||
538 | mvwprintw(thermal_data_window, y + 0, x + TDATA_LEFT, | ||
539 | "%c%d", trip_type_to_char(ptdata.tzi[tz].tp[j].type), | ||
540 | x); | ||
541 | syslog(LOG_INFO, "%s:tz %d tp %d temp = %lu\n", __func__, | ||
542 | tz, j, ptdata.tzi[tz].tp[j].temp); | ||
543 | } | ||
544 | } | ||
545 | |||
546 | const char data_win_title[] = " THERMAL DATA "; | ||
547 | void show_data_w(void) | ||
548 | { | ||
549 | int i; | ||
550 | |||
551 | |||
552 | if (tui_disabled || !thermal_data_window) | ||
553 | return; | ||
554 | |||
555 | werase(thermal_data_window); | ||
556 | wattron(thermal_data_window, A_BOLD); | ||
557 | mvwprintw(thermal_data_window, 0, maxx/2 - sizeof(data_win_title), | ||
558 | data_win_title); | ||
559 | wattroff(thermal_data_window, A_BOLD); | ||
560 | /* draw a line as ruler */ | ||
561 | for (i = 10; i < MAX_DISP_TEMP; i += 10) | ||
562 | mvwprintw(thermal_data_window, 1, i+TDATA_LEFT, "%2d", i); | ||
563 | |||
564 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { | ||
565 | int temp = trec[cur_thermal_record].temp[i] / 1000; | ||
566 | int y = 0; | ||
567 | |||
568 | y = i * NR_LINES_TZDATA + 2; | ||
569 | /* y at tz temp data line */ | ||
570 | mvwprintw(thermal_data_window, y, 1, "%6.6s%2d:[%3d][", | ||
571 | ptdata.tzi[i].type, | ||
572 | ptdata.tzi[i].instance, temp); | ||
573 | draw_hbar(thermal_data_window, y, TDATA_LEFT, temp, ACS_RARROW, | ||
574 | true); | ||
575 | draw_tp_line(i, y); | ||
576 | } | ||
577 | wborder(thermal_data_window, 0, 0, 0, 0, 0, 0, 0, 0); | ||
578 | wrefresh(thermal_data_window); | ||
579 | } | ||
580 | |||
581 | const char tz_title[] = "THERMAL ZONES(SENSORS)"; | ||
582 | |||
583 | void show_sensors_w(void) | ||
584 | { | ||
585 | int i, j; | ||
586 | char buffer[512]; | ||
587 | |||
588 | if (tui_disabled || !tz_sensor_window) | ||
589 | return; | ||
590 | |||
591 | werase(tz_sensor_window); | ||
592 | |||
593 | memset(buffer, 0, sizeof(buffer)); | ||
594 | wattron(tz_sensor_window, A_BOLD); | ||
595 | mvwprintw(tz_sensor_window, 1, 1, "Thermal Zones:"); | ||
596 | wattroff(tz_sensor_window, A_BOLD); | ||
597 | |||
598 | mvwprintw(tz_sensor_window, 1, TZ_LEFT_ALIGN, "%s", buffer); | ||
599 | /* fill trip points for each tzone */ | ||
600 | wattron(tz_sensor_window, A_BOLD); | ||
601 | mvwprintw(tz_sensor_window, 2, 1, "Trip Points:"); | ||
602 | wattroff(tz_sensor_window, A_BOLD); | ||
603 | |||
604 | /* draw trip point from low to high for each tz */ | ||
605 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { | ||
606 | int inst = ptdata.tzi[i].instance; | ||
607 | |||
608 | mvwprintw(tz_sensor_window, 1, | ||
609 | TZ_LEFT_ALIGN+TZONE_RECORD_SIZE * inst, "%.9s%02d", | ||
610 | ptdata.tzi[i].type, ptdata.tzi[i].instance); | ||
611 | for (j = ptdata.tzi[i].nr_trip_pts - 1; j >= 0; j--) { | ||
612 | /* loop through all trip points */ | ||
613 | char type; | ||
614 | int tp_pos; | ||
615 | /* reverse the order here since trips are sorted | ||
616 | * in ascending order in terms of temperature. | ||
617 | */ | ||
618 | tp_pos = ptdata.tzi[i].nr_trip_pts - j - 1; | ||
619 | |||
620 | type = trip_type_to_char(ptdata.tzi[i].tp[j].type); | ||
621 | mvwaddch(tz_sensor_window, 2, | ||
622 | inst * TZONE_RECORD_SIZE + TZ_LEFT_ALIGN + | ||
623 | tp_pos, type); | ||
624 | syslog(LOG_DEBUG, "draw tz %d tp %d ch:%c\n", | ||
625 | inst, j, type); | ||
626 | } | ||
627 | } | ||
628 | wborder(tz_sensor_window, 0, 0, 0, 0, 0, 0, 0, 0); | ||
629 | wattron(tz_sensor_window, A_BOLD); | ||
630 | mvwprintw(tz_sensor_window, 0, maxx/2 - sizeof(tz_title), tz_title); | ||
631 | wattroff(tz_sensor_window, A_BOLD); | ||
632 | wrefresh(tz_sensor_window); | ||
633 | } | ||
634 | |||
635 | void disable_tui(void) | ||
636 | { | ||
637 | tui_disabled = 1; | ||
638 | } | ||
diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c index da7a19558281..bdb71a26ae35 100644 --- a/tools/virtio/virtio_test.c +++ b/tools/virtio/virtio_test.c | |||
@@ -41,13 +41,14 @@ struct vdev_info { | |||
41 | struct vhost_memory *mem; | 41 | struct vhost_memory *mem; |
42 | }; | 42 | }; |
43 | 43 | ||
44 | void vq_notify(struct virtqueue *vq) | 44 | bool vq_notify(struct virtqueue *vq) |
45 | { | 45 | { |
46 | struct vq_info *info = vq->priv; | 46 | struct vq_info *info = vq->priv; |
47 | unsigned long long v = 1; | 47 | unsigned long long v = 1; |
48 | int r; | 48 | int r; |
49 | r = write(info->kick, &v, sizeof v); | 49 | r = write(info->kick, &v, sizeof v); |
50 | assert(r == sizeof v); | 50 | assert(r == sizeof v); |
51 | return true; | ||
51 | } | 52 | } |
52 | 53 | ||
53 | void vq_callback(struct virtqueue *vq) | 54 | void vq_callback(struct virtqueue *vq) |
@@ -171,7 +172,8 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq, | |||
171 | GFP_ATOMIC); | 172 | GFP_ATOMIC); |
172 | if (likely(r == 0)) { | 173 | if (likely(r == 0)) { |
173 | ++started; | 174 | ++started; |
174 | virtqueue_kick(vq->vq); | 175 | if (unlikely(!virtqueue_kick(vq->vq)) |
176 | r = -1; | ||
175 | } | 177 | } |
176 | } else | 178 | } else |
177 | r = -1; | 179 | r = -1; |
diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c index d053ea40c001..14a4f4cab5b9 100644 --- a/tools/virtio/vringh_test.c +++ b/tools/virtio/vringh_test.c | |||
@@ -22,7 +22,7 @@ static u64 user_addr_offset; | |||
22 | #define RINGSIZE 256 | 22 | #define RINGSIZE 256 |
23 | #define ALIGN 4096 | 23 | #define ALIGN 4096 |
24 | 24 | ||
25 | static void never_notify_host(struct virtqueue *vq) | 25 | static bool never_notify_host(struct virtqueue *vq) |
26 | { | 26 | { |
27 | abort(); | 27 | abort(); |
28 | } | 28 | } |
@@ -65,17 +65,22 @@ struct guest_virtio_device { | |||
65 | unsigned long notifies; | 65 | unsigned long notifies; |
66 | }; | 66 | }; |
67 | 67 | ||
68 | static void parallel_notify_host(struct virtqueue *vq) | 68 | static bool parallel_notify_host(struct virtqueue *vq) |
69 | { | 69 | { |
70 | int rc; | ||
70 | struct guest_virtio_device *gvdev; | 71 | struct guest_virtio_device *gvdev; |
71 | 72 | ||
72 | gvdev = container_of(vq->vdev, struct guest_virtio_device, vdev); | 73 | gvdev = container_of(vq->vdev, struct guest_virtio_device, vdev); |
73 | write(gvdev->to_host_fd, "", 1); | 74 | rc = write(gvdev->to_host_fd, "", 1); |
75 | if (rc < 0) | ||
76 | return false; | ||
74 | gvdev->notifies++; | 77 | gvdev->notifies++; |
78 | return true; | ||
75 | } | 79 | } |
76 | 80 | ||
77 | static void no_notify_host(struct virtqueue *vq) | 81 | static bool no_notify_host(struct virtqueue *vq) |
78 | { | 82 | { |
83 | return true; | ||
79 | } | 84 | } |
80 | 85 | ||
81 | #define NUM_XFERS (10000000) | 86 | #define NUM_XFERS (10000000) |