aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile15
-rw-r--r--tools/lib/traceevent/event-parse.c13
-rw-r--r--tools/perf/Documentation/perf-record.txt6
-rw-r--r--tools/perf/Documentation/perf-trace.txt10
-rw-r--r--tools/perf/builtin-kvm.c20
-rw-r--r--tools/perf/builtin-probe.c2
-rw-r--r--tools/perf/builtin-record.c37
-rw-r--r--tools/perf/builtin-stat.c21
-rw-r--r--tools/perf/builtin-top.c28
-rw-r--r--tools/perf/builtin-trace.c143
-rw-r--r--tools/perf/config/Makefile14
-rw-r--r--tools/perf/config/feature-checks/Makefile3
-rw-r--r--tools/perf/config/feature-checks/test-all.c4
-rw-r--r--tools/perf/perf.h2
-rw-r--r--tools/perf/tests/code-reading.c17
-rw-r--r--tools/perf/tests/evsel-tp-sched.c4
-rw-r--r--tools/perf/tests/mmap-basic.c2
-rw-r--r--tools/perf/tests/open-syscall-all-cpus.c2
-rw-r--r--tools/perf/tests/open-syscall-tp-fields.c2
-rw-r--r--tools/perf/tests/open-syscall.c2
-rw-r--r--tools/perf/tests/parse-events.c3
-rw-r--r--tools/perf/tests/sw-clock.c15
-rw-r--r--tools/perf/tests/task-exit.c2
-rw-r--r--tools/perf/ui/browser.c4
-rw-r--r--tools/perf/ui/browsers/hists.c11
-rw-r--r--tools/perf/ui/tui/progress.c3
-rw-r--r--tools/perf/util/event.c56
-rw-r--r--tools/perf/util/event.h4
-rw-r--r--tools/perf/util/evlist.c77
-rw-r--r--tools/perf/util/evlist.h5
-rw-r--r--tools/perf/util/evsel.c13
-rw-r--r--tools/perf/util/evsel.h23
-rw-r--r--tools/perf/util/header.c4
-rw-r--r--tools/perf/util/machine.c14
-rw-r--r--tools/perf/util/machine.h12
-rw-r--r--tools/perf/util/parse-events.c6
-rw-r--r--tools/perf/util/sort.c13
-rw-r--r--tools/perf/util/target.c54
-rw-r--r--tools/perf/util/target.h45
-rw-r--r--tools/perf/util/top.c2
-rw-r--r--tools/perf/util/unwind.c9
-rw-r--r--tools/perf/util/unwind.h5
-rw-r--r--tools/thermal/tmon/Makefile47
-rw-r--r--tools/thermal/tmon/README50
-rw-r--r--tools/thermal/tmon/pid.c131
-rw-r--r--tools/thermal/tmon/sysfs.c596
-rw-r--r--tools/thermal/tmon/tmon.8142
-rw-r--r--tools/thermal/tmon/tmon.c352
-rw-r--r--tools/thermal/tmon/tmon.h204
-rw-r--r--tools/thermal/tmon/tui.c638
-rw-r--r--tools/virtio/virtio_test.c6
-rw-r--r--tools/virtio/vringh_test.c13
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
50turbostat x86_energy_perf_policy: FORCE 51turbostat x86_energy_perf_policy: FORCE
51 $(call descend,power/x86/$@) 52 $(call descend,power/x86/$@)
52 53
54tmon: FORCE
55 $(call descend,thermal/$@)
56
53cpupower_install: 57cpupower_install:
54 $(call descend,power/$(@:_install=),install) 58 $(call descend,power/$(@:_install=),install)
55 59
@@ -62,9 +66,13 @@ selftests_install:
62turbostat_install x86_energy_perf_policy_install: 66turbostat_install x86_energy_perf_policy_install:
63 $(call descend,power/x86/$(@:_install=),install) 67 $(call descend,power/x86/$(@:_install=),install)
64 68
69tmon_install:
70 $(call descend,thermal/$(@:_install=),install)
71
65install: cgroup_install cpupower_install firewire_install lguest_install \ 72install: 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
69cpupower_clean: 77cpupower_clean:
70 $(call descend,power/cpupower,clean) 78 $(call descend,power/cpupower,clean)
@@ -84,8 +92,11 @@ selftests_clean:
84turbostat_clean x86_energy_perf_policy_clean: 92turbostat_clean x86_energy_perf_policy_clean:
85 $(call descend,power/x86/$(@:_clean=),clean) 93 $(call descend,power/x86/$(@:_clean=),clean)
86 94
95tmon_clean:
96 $(call descend,thermal/tmon,clean)
97
87clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \ 98clean: 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::
202Record transaction flags for transaction related events. 202Record transaction flags for transaction related events.
203 203
204--force-per-cpu::
205Force 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
207forces per-cpu mmaps. A side-effect of that is that inheritance is
208automatically enabled. Add the -i option also to disable inheritance.
209
204SEE ALSO 210SEE ALSO
205-------- 211--------
206linkperf:perf-stat[1], linkperf:perf-list[1] 212linkperf: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
79static int write_output(struct perf_record *rec, void *buf, size_t size) 79static 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
100static int write_output(struct perf_record *rec, void *buf, size_t size)
101{
102 return do_write_output(rec, buf, size);
103}
104
100static int process_synthesized_event(struct perf_tool *tool, 105static 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
109static struct perf_evlist *evsel_list; 109static struct perf_evlist *evsel_list;
110 110
111static struct perf_target target = { 111static 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
152static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, 152static 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
165out_delete:
166 free(evsel->priv);
167 evsel->priv = NULL;
168 return -ENOENT;
169}
170
171static 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
1765static bool
1766perf_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
1775static int parse_target_str(struct trace *trace) 1773static 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
1825static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) 1823static 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:
2009static int trace__replay(struct trace *trace) 2006static 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
406endif 413endif
407 414
408$(call feature_check,timerfd)
409ifeq ($(feature-timerfd), 1) 415ifeq ($(feature-timerfd), 1)
410 CFLAGS += -DHAVE_TIMERFD_SUPPORT 416 CFLAGS += -DHAVE_TIMERFD_SUPPORT
411else 417else
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:
76test-libunwind: 76test-libunwind:
77 $(BUILD) $(LIBUNWIND_LIBS) -lelf 77 $(BUILD) $(LIBUNWIND_LIBS) -lelf
78 78
79test-libunwind-debug-frame:
80 $(BUILD) $(LIBUNWIND_LIBS) -lelf
81
79test-libaudit: 82test-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
250struct perf_record_opts { 250struct 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
33int test__perf_evsel__tp_sched_test(void) 33int 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
442static int test__checkevent_pmu_events(struct perf_evlist *evlist) 442static 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
1944single_entry: 1944single_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
317int perf_event__synthesize_thread_map(struct perf_tool *tool, 324int 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
375int perf_event__synthesize_threads(struct perf_tool *tool, 384int 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
529size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) 538size_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
536size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) 547size_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,
208int perf_event__synthesize_thread_map(struct perf_tool *tool, 208int 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);
212int perf_event__synthesize_threads(struct perf_tool *tool, 212int 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);
215int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, 215int 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)
117void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) 117void 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,
249int perf_evlist__add_newtp(struct perf_evlist *evlist, 251int 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
707int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, 708static 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
750int 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
799int perf_evlist__create_maps(struct perf_evlist *evlist, 814int 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
1019int perf_evlist__prepare_workload(struct perf_evlist *evlist, 1035int 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
1141int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, 1156int 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,
102int perf_record_opts__config(struct perf_record_opts *opts); 102int perf_record_opts__config(struct perf_record_opts *opts);
103 103
104int perf_evlist__prepare_workload(struct perf_evlist *evlist, 104int 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);
108int perf_evlist__start_workload(struct perf_evlist *evlist); 108int 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
137int perf_evlist__create_maps(struct perf_evlist *evlist, 137int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target);
138 struct perf_target *target);
139void perf_evlist__delete_maps(struct perf_evlist *evlist); 138void perf_evlist__delete_maps(struct perf_evlist *evlist);
140int perf_evlist__apply_filters(struct perf_evlist *evlist); 139int 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
171struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) 171struct 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
222struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx) 222struct 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
2009int perf_evsel__open_strerror(struct perf_evsel *evsel, 2009int 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;
96struct perf_evlist; 96struct perf_evlist;
97struct perf_record_opts; 97struct perf_record_opts;
98 98
99struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); 99struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx);
100struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx); 100
101static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr)
102{
103 return perf_evsel__new_idx(attr, 0);
104}
105
106struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx);
107
108static 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
102struct event_format *event_format__new(const char *sys, const char *name); 113struct 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
282static 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
308bool perf_evsel__fallback(struct perf_evsel *evsel, int err, 324bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
309 char *msg, size_t msgsize); 325 char *msg, size_t msgsize);
310int perf_evsel__open_strerror(struct perf_evsel *evsel, 326int 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
314static inline int perf_evsel__group_idx(struct perf_evsel *evsel) 329static 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
1398int __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
8struct addr_location; 9struct addr_location;
9struct branch_stack; 10struct 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
182int __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);
185static inline
186int 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
1138void sort__setup_elide(FILE *output) 1138void 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
16enum perf_target_errno perf_target__validate(struct perf_target *target) 16enum 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
61enum perf_target_errno perf_target__parse_uid(struct perf_target *target) 61enum 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 */
97static const char *perf_target__error_str[] = { 97static 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
107int perf_target__strerror(struct perf_target *target, int errnum, 107int 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
7struct perf_target { 7struct 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
17enum perf_target_errno { 18enum 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
44enum perf_target_errno perf_target__validate(struct perf_target *target); 44enum target_errno target__validate(struct target *target);
45enum perf_target_errno perf_target__parse_uid(struct perf_target *target); 45enum target_errno target__parse_uid(struct target *target);
46 46
47int perf_target__strerror(struct perf_target *target, int errnum, char *buf, 47int target__strerror(struct target *target, int errnum, char *buf, size_t buflen);
48 size_t buflen);
49 48
50static inline bool perf_target__has_task(struct perf_target *target) 49static 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
55static inline bool perf_target__has_cpu(struct perf_target *target) 54static 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
60static inline bool perf_target__none(struct perf_target *target) 59static 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
561static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, 561static 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
589int unwind__get_entries(unwind_entry_cb_t cb, void *arg, 589int 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);
22int unwind__arch_reg_id(int regnum); 22int unwind__arch_reg_id(int regnum);
23#else 23#else
24static inline int 24static 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 @@
1VERSION = 1.0
2
3BINDIR=usr/bin
4WARNFLAGS=-Wall -Wshadow -W -Wformat -Wimplicit-function-declaration -Wimplicit-int
5CFLAGS= -O1 ${WARNFLAGS} -fstack-protector
6CC=gcc
7
8CFLAGS+=-D VERSION=\"$(VERSION)\"
9LDFLAGS+=
10TARGET=tmon
11
12INSTALL_PROGRAM=install -m 755 -p
13DEL_FILE=rm -f
14
15INSTALL_CONFIGFILE=install -m 644 -p
16CONFIG_FILE=
17CONFIG_PATH=
18
19
20OBJS = tmon.o tui.o sysfs.o pid.o
21OBJS +=
22
23tmon: $(OBJS) Makefile tmon.h
24 $(CC) ${CFLAGS} $(LDFLAGS) $(OBJS) -o $(TARGET) -lm -lpanel -lncursesw -lpthread
25
26valgrind: 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
29install:
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
35uninstall:
36 $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/$(TARGET)"
37 $(CONFIG_FILE) "$(CONFIG_PATH)"
38
39
40clean:
41 find . -name "*.o" | xargs $(DEL_FILE)
42 rm -f $(TARGET)
43
44dist:
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 @@
1TMON - A Monitoring and Testing Tool for Linux kernel thermal subsystem
2
3Why TMON?
4==========
5Increasingly, Linux is running on thermally constrained devices. The simple
6thermal relationship between processor and fan has become past for modern
7computers.
8
9As hardware vendors cope with the thermal constraints on their products, more
10and more sensors are added, new cooling capabilities are introduced. The
11complexity of the thermal relationship can grow exponentially among cooling
12devices, zones, sensors, and trip points. They can also change dynamically.
13
14To expose such relationship to the userspace, Linux generic thermal layer
15introduced sysfs entry at /sys/class/thermal with a matrix of symbolic
16links, trip point bindings, and device instances. To traverse such
17matrix by hand is not a trivial task. Testing is also difficult in that
18thermal conditions are often exception cases that hard to reach in
19normal operations.
20
21TMON is conceived as a tool to help visualize, tune, and test the
22complex thermal subsystem.
23
24Files
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
32Requirements
33============
34Depends on ncurses
35
36Build
37=========
38$ make
39$ sudo ./tmon -h
40Usage: 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
491. 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 ***********************************************************************/
55struct pid_params p_param;
56/* cached data from previous loop */
57static 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 */
66int 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
82void 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)
98void 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
33struct tmon_platform_data ptdata;
34const char *trip_type_name[] = {
35 "critical",
36 "hot",
37 "passive",
38 "active",
39};
40
41int 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
62struct thermal_data_record trec[NR_THERMAL_RECORDS];
63int cur_thermal_record; /* index to the trec array */
64
65static 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
84static 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 */
104static 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
117static 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 */
130static 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 */
156static 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 */
174static 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. */
206static 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[]
268root@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*****************************************************************************/
287static 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
345static 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
390int 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 */
475int 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 */
486int 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
537void 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
566void 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
592void 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
12real-time thermal data; tune
13and test cooling devices and sensors; collect thermal data for offline
14analysis and plot. \fBtmon\fP must be run as root in order to control device
15states via sysfs.
16.PP
17\fBFunctions\fP
18.PP
19.nf
201. 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
262. 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
303. Thermal relationship learning and device tuning
31- with a built-in Proportional Integral Derivative (\fBPID\fP)
32controller, user can pair a cooling device to a thermal sensor for
33testing the effectiveness and learn about the thermal distance between the two
34- allow manual control of cooling device states and target temperature
35.PP
364. 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
45The \fB-c --control\fP option sets a cooling device type to control temperature
46of a thermal zone
47.PP
48The \fB-d --daemon\fP option runs \fBtmon \fP as daemon without user interface
49.PP
50The \fB-g --debug\fP option allow debug messages to be stored in syslog
51.PP
52The \fB-h --help\fP option shows help message
53.PP
54The \fB-l --log\fP option write data to /var/tmp/tmon.log
55.PP
56The \fB-t --time-interval\fP option sets the polling interval in seconds
57.PP
58The \fB-v --version\fP option shows the version of \fBtmon \fP
59.PP
60The \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
75Build depends on ncurses
76.PP
77Runtime depends on window size large enough to show the number of
78devices 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
89Without any parameters, tmon is in monitoring only mode and refresh
90screen every 1 second.
91.PP
921. For monitoring only:
93.nf
94$ sudo ./tmon
95
962. Use Processor cooling device to control thermal zone 0 at default 65C.
97$ sudo ./tmon -c Processor -z 0
98
993. Use intel_powerclamp(idle injection) cooling device to control thermal zone 1
100$ sudo ./tmon -c intel_powerclamp -z 1
101
1024. Turn on debug and collect data log at /var/tmp/tmon.log
103$ sudo ./tmon -g -l
104
105For example, the log below shows PID controller was adjusting current states
106for all cooling devices with "Processor" type such that thermal zone 0
107can stay below 65 dC.
108
109#---------- THERMAL DATA LOG STARTED -----------
110Samples TargetTemp acpitz0 acpitz1 Fan0 Fan1 Fan2 Fan3 Fan4 Fan5
111Fan6 Fan7 Fan8 Fan9 Processor10 Processor11 Processor12 Processor13
112LCD14 intel_powerclamp15 1 65.0 65 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 2
11365.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
1140 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
1155 65.0 52 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0
1166 65.0 53 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0
1177 65.0 68 70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0
1188 65.0 68 68 0 0 0 0 0 0 0 0 0 0 5 5 5 5 6 0
1199 65.0 68 68 0 0 0 0 0 0 0 0 0 0 6 6 6 6 6 0
12010 65.0 67 67 0 0 0 0 0 0 0 0 0 0 7 7 7 7 6 0
12111 65.0 67 67 0 0 0 0 0 0 0 0 0 0 8 8 8 8 6 0
12212 65.0 67 67 0 0 0 0 0 0 0 0 0 0 8 8 8 8 6 0
12313 65.0 67 67 0 0 0 0 0 0 0 0 0 0 9 9 9 9 6 0
12414 65.0 66 66 0 0 0 0 0 0 0 0 0 0 10 10 10 10 6 0
12515 65.0 66 67 0 0 0 0 0 0 0 0 0 0 10 10 10 10 6 0
12616 65.0 66 66 0 0 0 0 0 0 0 0 0 0 11 11 11 11 6 0
12717 65.0 66 66 0 0 0 0 0 0 0 0 0 0 11 11 11 11 6 0
12818 65.0 64 61 0 0 0 0 0 0 0 0 0 0 11 11 11 11 6 0
12919 65.0 60 59 0 0 0 0 0 0 0 0 0 0 12 12 12 12 6 0
130
131Data can be read directly into an array by an example R-script below:
132
133#!/usr/bin/Rscript
134tdata <- read.table("/var/tmp/tmon.log", header=T, comment.char="#")
135attach(tdata)
136jpeg("tmon.jpg")
137X11()
138g_range <- range(0, intel_powerclamp15, TargetTemp, acpitz0)
139plot( Samples, intel_powerclamp15, col="blue", ylim=g_range, axes=FALSE, ann=FALSE)
140par(new=TRUE)
141lines(TargetTemp, type="o", pch=22, lty=2, col="red")
142dev.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
39unsigned long ticktime = 1; /* seconds */
40unsigned long no_control = 1; /* monitoring only or use cooling device for
41 * temperature control.
42 */
43double time_elapsed = 0.0;
44unsigned long target_temp_user = 65; /* can be select by tui later */
45int dialogue_on;
46int tmon_exit;
47static short daemon_mode;
48static int logging; /* for recording thermal data to a file */
49static int debug_on;
50FILE *tmon_log;
51/*cooling device used for the PID controller */
52char ctrl_cdev[CDEV_NAME_SIZE] = "None";
53int target_thermal_zone; /* user selected target zone instance */
54static void start_daemon_mode(void);
55
56pthread_t event_tid;
57pthread_mutex_t input_lock;
58void 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
73void version()
74{
75 printf("TMON version %s\n", VERSION);
76 exit(EXIT_SUCCESS);
77}
78
79static 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
108static 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
132static 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
142static 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
194static 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
206int 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
319static 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
39extern unsigned long ticktime;
40extern double time_elapsed;
41extern unsigned long target_temp_user;
42extern int dialogue_on;
43extern char ctrl_cdev[];
44extern pthread_mutex_t input_lock;
45extern int tmon_exit;
46extern 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*/
50struct thermal_data_record {
51 struct timeval tv;
52 unsigned long temp[MAX_NR_TZONE];
53 double pid_out_pct;
54};
55
56struct 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
64enum 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
72struct 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 */
82struct 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
94struct 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
104struct control_ops {
105 void (*set_ratio)(unsigned long ratio);
106 unsigned long (*get_ratio)(unsigned long ratio);
107
108};
109
110enum 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 */
121enum 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
131struct 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
142extern int init_thermal_controller(void);
143extern void controller_handler(const double xk, double *yk);
144
145extern struct tmon_platform_data ptdata;
146extern struct pid_params p_param;
147
148extern FILE *tmon_log;
149extern int cur_thermal_record; /* index to the trec array */
150extern struct thermal_data_record trec[];
151extern const char *trip_type_name[];
152extern unsigned long no_control;
153
154extern void initialize_curses(void);
155extern void show_controller_stats(char *line);
156extern void show_title_bar(void);
157extern void setup_windows(void);
158extern void disable_tui(void);
159extern void show_sensors_w(void);
160extern void show_data_w(void);
161extern void write_status_bar(int x, char *line);
162extern void show_control_w();
163
164extern void show_cooling_device(void);
165extern void show_dialogue(void);
166extern int update_thermal_data(void);
167
168extern int probe_thermal_sysfs(void);
169extern void free_thermal_data(void);
170extern void resize_handler(int sig);
171extern void set_ctrl_state(unsigned long state);
172extern void get_ctrl_state(unsigned long *state);
173extern void *handle_tui_events(void *arg);
174extern int sysfs_set_ulong(char *path, char *filename, unsigned long val);
175extern int zone_instance_to_index(int zone_inst);
176extern 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
33static PANEL *data_panel;
34static PANEL *dialogue_panel;
35static PANEL *top;
36
37static WINDOW *title_bar_window;
38static WINDOW *tz_sensor_window;
39static WINDOW *cooling_device_window;
40static WINDOW *control_window;
41static WINDOW *status_bar_window;
42static WINDOW *thermal_data_window;
43static WINDOW *dialogue_window;
44
45char status_bar_slots[10][40];
46static void draw_hbar(WINDOW *win, int y, int start, int len,
47 unsigned long pattern, bool end);
48
49static int maxx, maxy;
50static 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) */
57static int tui_disabled;
58
59static void close_panel(PANEL *p)
60{
61 if (p) {
62 del_panel(p);
63 p = NULL;
64 }
65}
66
67static void close_window(WINDOW *win)
68{
69 if (win) {
70 delwin(win);
71 win = NULL;
72 }
73}
74
75void 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
95void write_status_bar(int x, char *line)
96{
97 mvwprintw(status_bar_window, 0, x, "%s", line);
98 wrefresh(status_bar_window);
99}
100
101void 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
167void 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
183const char cdev_title[] = " COOLING DEVICES ";
184void 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
260const char DIAG_TITLE[] = "[ TUNABLES ]";
261#define DIAG_DEV_ROWS 5
262void 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
297void 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
304const char control_title[] = " CONTROLS ";
305void 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
333void 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
358void 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
388static 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
424static 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
448void *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 */
506static 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
515static 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 */
531static 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
546const char data_win_title[] = " THERMAL DATA ";
547void 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
581const char tz_title[] = "THERMAL ZONES(SENSORS)";
582
583void 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
635void 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
44void vq_notify(struct virtqueue *vq) 44bool 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
53void vq_callback(struct virtqueue *vq) 54void 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
25static void never_notify_host(struct virtqueue *vq) 25static 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
68static void parallel_notify_host(struct virtqueue *vq) 68static 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
77static void no_notify_host(struct virtqueue *vq) 81static 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)