diff options
-rw-r--r-- | tools/lib/traceevent/Makefile | 2 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-trace.txt | 16 | ||||
-rw-r--r-- | tools/perf/Makefile | 6 | ||||
-rw-r--r-- | tools/perf/builtin-kvm.c | 1 | ||||
-rw-r--r-- | tools/perf/builtin-trace.c | 379 | ||||
-rw-r--r-- | tools/perf/util/evlist.c | 3 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 93 | ||||
-rw-r--r-- | tools/perf/util/machine.c | 50 | ||||
-rw-r--r-- | tools/perf/util/python.c | 20 | ||||
-rw-r--r-- | tools/perf/util/thread.c | 3 | ||||
-rw-r--r-- | tools/perf/util/thread.h | 8 |
11 files changed, 488 insertions, 93 deletions
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index 0794acca46a3..ca6cb779876a 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile | |||
@@ -60,7 +60,7 @@ ifeq ($(BUILD_SRC),) | |||
60 | ifneq ($(BUILD_OUTPUT),) | 60 | ifneq ($(BUILD_OUTPUT),) |
61 | 61 | ||
62 | define build_output | 62 | define build_output |
63 | $(if $(VERBOSE:1=),@)$(MAKE) -C $(BUILD_OUTPUT) \ | 63 | $(if $(VERBOSE:1=),@)+$(MAKE) -C $(BUILD_OUTPUT) \ |
64 | BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 | 64 | BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 |
65 | endef | 65 | endef |
66 | 66 | ||
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 3b3552a8959e..fe19811faf90 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -23,29 +23,45 @@ analysis phases. | |||
23 | OPTIONS | 23 | OPTIONS |
24 | ------- | 24 | ------- |
25 | 25 | ||
26 | -a:: | ||
26 | --all-cpus:: | 27 | --all-cpus:: |
27 | System-wide collection from all CPUs. | 28 | System-wide collection from all CPUs. |
28 | 29 | ||
29 | -e:: | 30 | -e:: |
30 | --expr:: | 31 | --expr:: |
31 | List of events to show, currently only syscall names. | 32 | List of events to show, currently only syscall names. |
33 | Prefixing with ! shows all syscalls but the ones specified. You may | ||
34 | need to escape it. | ||
35 | |||
36 | -o:: | ||
37 | --output=:: | ||
38 | Output file name. | ||
32 | 39 | ||
33 | -p:: | 40 | -p:: |
34 | --pid=:: | 41 | --pid=:: |
35 | Record events on existing process ID (comma separated list). | 42 | Record events on existing process ID (comma separated list). |
36 | 43 | ||
44 | -t:: | ||
37 | --tid=:: | 45 | --tid=:: |
38 | Record events on existing thread ID (comma separated list). | 46 | Record events on existing thread ID (comma separated list). |
39 | 47 | ||
48 | -u:: | ||
40 | --uid=:: | 49 | --uid=:: |
41 | Record events in threads owned by uid. Name or number. | 50 | Record events in threads owned by uid. Name or number. |
42 | 51 | ||
52 | -v:: | ||
53 | --verbose=:: | ||
54 | Verbosity level. | ||
55 | |||
56 | -i:: | ||
43 | --no-inherit:: | 57 | --no-inherit:: |
44 | Child tasks do not inherit counters. | 58 | Child tasks do not inherit counters. |
45 | 59 | ||
60 | -m:: | ||
46 | --mmap-pages=:: | 61 | --mmap-pages=:: |
47 | Number of mmap data pages. Must be a power of two. | 62 | Number of mmap data pages. Must be a power of two. |
48 | 63 | ||
64 | -C:: | ||
49 | --cpu:: | 65 | --cpu:: |
50 | Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a | 66 | Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a |
51 | comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | 67 | comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index e0d3d9f96771..7d30a7ddcc88 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -766,17 +766,21 @@ check: $(OUTPUT)common-cmds.h | |||
766 | install-bin: all | 766 | install-bin: all |
767 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' | 767 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' |
768 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' | 768 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' |
769 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
770 | ifndef NO_LIBPERL | ||
769 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 771 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
770 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 772 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
771 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
772 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 773 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
773 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' | 774 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' |
774 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 775 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
776 | endif | ||
777 | ifndef NO_LIBPYTHON | ||
775 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' | 778 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' |
776 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 779 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
777 | $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' | 780 | $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' |
778 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' | 781 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' |
779 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 782 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
783 | endif | ||
780 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' | 784 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' |
781 | $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' | 785 | $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' |
782 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' | 786 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' |
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index fa2f3d79886a..6cd4de59be21 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -1305,7 +1305,6 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1305 | const char * const record_args[] = { | 1305 | const char * const record_args[] = { |
1306 | "record", | 1306 | "record", |
1307 | "-R", | 1307 | "-R", |
1308 | "-f", | ||
1309 | "-m", "1024", | 1308 | "-m", "1024", |
1310 | "-c", "1", | 1309 | "-c", "1", |
1311 | }; | 1310 | }; |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 120fdfb3d920..b72afc73f9a7 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include <traceevent/event-parse.h> | 1 | #include <traceevent/event-parse.h> |
2 | #include "builtin.h" | 2 | #include "builtin.h" |
3 | #include "util/color.h" | 3 | #include "util/color.h" |
4 | #include "util/debug.h" | ||
4 | #include "util/evlist.h" | 5 | #include "util/evlist.h" |
5 | #include "util/machine.h" | 6 | #include "util/machine.h" |
6 | #include "util/thread.h" | 7 | #include "util/thread.h" |
@@ -10,27 +11,166 @@ | |||
10 | 11 | ||
11 | #include <libaudit.h> | 12 | #include <libaudit.h> |
12 | #include <stdlib.h> | 13 | #include <stdlib.h> |
14 | #include <sys/mman.h> | ||
15 | |||
16 | static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, unsigned long arg) | ||
17 | { | ||
18 | return scnprintf(bf, size, "%#lx", arg); | ||
19 | } | ||
20 | |||
21 | #define SCA_HEX syscall_arg__scnprintf_hex | ||
22 | |||
23 | static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, unsigned long arg) | ||
24 | { | ||
25 | int printed = 0, prot = arg; | ||
26 | |||
27 | if (prot == PROT_NONE) | ||
28 | return scnprintf(bf, size, "NONE"); | ||
29 | #define P_MMAP_PROT(n) \ | ||
30 | if (prot & PROT_##n) { \ | ||
31 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
32 | prot &= ~PROT_##n; \ | ||
33 | } | ||
34 | |||
35 | P_MMAP_PROT(EXEC); | ||
36 | P_MMAP_PROT(READ); | ||
37 | P_MMAP_PROT(WRITE); | ||
38 | #ifdef PROT_SEM | ||
39 | P_MMAP_PROT(SEM); | ||
40 | #endif | ||
41 | P_MMAP_PROT(GROWSDOWN); | ||
42 | P_MMAP_PROT(GROWSUP); | ||
43 | #undef P_MMAP_PROT | ||
44 | |||
45 | if (prot) | ||
46 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", prot); | ||
47 | |||
48 | return printed; | ||
49 | } | ||
50 | |||
51 | #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot | ||
52 | |||
53 | static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, unsigned long arg) | ||
54 | { | ||
55 | int printed = 0, flags = arg; | ||
56 | |||
57 | #define P_MMAP_FLAG(n) \ | ||
58 | if (flags & MAP_##n) { \ | ||
59 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
60 | flags &= ~MAP_##n; \ | ||
61 | } | ||
62 | |||
63 | P_MMAP_FLAG(SHARED); | ||
64 | P_MMAP_FLAG(PRIVATE); | ||
65 | P_MMAP_FLAG(32BIT); | ||
66 | P_MMAP_FLAG(ANONYMOUS); | ||
67 | P_MMAP_FLAG(DENYWRITE); | ||
68 | P_MMAP_FLAG(EXECUTABLE); | ||
69 | P_MMAP_FLAG(FILE); | ||
70 | P_MMAP_FLAG(FIXED); | ||
71 | P_MMAP_FLAG(GROWSDOWN); | ||
72 | P_MMAP_FLAG(HUGETLB); | ||
73 | P_MMAP_FLAG(LOCKED); | ||
74 | P_MMAP_FLAG(NONBLOCK); | ||
75 | P_MMAP_FLAG(NORESERVE); | ||
76 | P_MMAP_FLAG(POPULATE); | ||
77 | P_MMAP_FLAG(STACK); | ||
78 | #ifdef MAP_UNINITIALIZED | ||
79 | P_MMAP_FLAG(UNINITIALIZED); | ||
80 | #endif | ||
81 | #undef P_MMAP_FLAG | ||
82 | |||
83 | if (flags) | ||
84 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); | ||
85 | |||
86 | return printed; | ||
87 | } | ||
88 | |||
89 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags | ||
90 | |||
91 | static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, unsigned long arg) | ||
92 | { | ||
93 | int behavior = arg; | ||
94 | |||
95 | switch (behavior) { | ||
96 | #define P_MADV_BHV(n) case MADV_##n: return scnprintf(bf, size, #n) | ||
97 | P_MADV_BHV(NORMAL); | ||
98 | P_MADV_BHV(RANDOM); | ||
99 | P_MADV_BHV(SEQUENTIAL); | ||
100 | P_MADV_BHV(WILLNEED); | ||
101 | P_MADV_BHV(DONTNEED); | ||
102 | P_MADV_BHV(REMOVE); | ||
103 | P_MADV_BHV(DONTFORK); | ||
104 | P_MADV_BHV(DOFORK); | ||
105 | P_MADV_BHV(HWPOISON); | ||
106 | #ifdef MADV_SOFT_OFFLINE | ||
107 | P_MADV_BHV(SOFT_OFFLINE); | ||
108 | #endif | ||
109 | P_MADV_BHV(MERGEABLE); | ||
110 | P_MADV_BHV(UNMERGEABLE); | ||
111 | P_MADV_BHV(HUGEPAGE); | ||
112 | P_MADV_BHV(NOHUGEPAGE); | ||
113 | #ifdef MADV_DONTDUMP | ||
114 | P_MADV_BHV(DONTDUMP); | ||
115 | #endif | ||
116 | #ifdef MADV_DODUMP | ||
117 | P_MADV_BHV(DODUMP); | ||
118 | #endif | ||
119 | #undef P_MADV_PHV | ||
120 | default: break; | ||
121 | } | ||
122 | |||
123 | return scnprintf(bf, size, "%#x", behavior); | ||
124 | } | ||
125 | |||
126 | #define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior | ||
13 | 127 | ||
14 | static struct syscall_fmt { | 128 | static struct syscall_fmt { |
15 | const char *name; | 129 | const char *name; |
16 | const char *alias; | 130 | const char *alias; |
131 | size_t (*arg_scnprintf[6])(char *bf, size_t size, unsigned long arg); | ||
17 | bool errmsg; | 132 | bool errmsg; |
18 | bool timeout; | 133 | bool timeout; |
134 | bool hexret; | ||
19 | } syscall_fmts[] = { | 135 | } syscall_fmts[] = { |
20 | { .name = "access", .errmsg = true, }, | 136 | { .name = "access", .errmsg = true, }, |
21 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, | 137 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, |
138 | { .name = "brk", .hexret = true, | ||
139 | .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, | ||
140 | { .name = "mmap", .hexret = true, }, | ||
22 | { .name = "connect", .errmsg = true, }, | 141 | { .name = "connect", .errmsg = true, }, |
23 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, | 142 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, |
24 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, | 143 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, |
25 | { .name = "futex", .errmsg = true, }, | 144 | { .name = "futex", .errmsg = true, }, |
145 | { .name = "ioctl", .errmsg = true, | ||
146 | .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, | ||
147 | { .name = "lstat", .errmsg = true, .alias = "newlstat", }, | ||
148 | { .name = "madvise", .errmsg = true, | ||
149 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ | ||
150 | [2] = SCA_MADV_BHV, /* behavior */ }, }, | ||
151 | { .name = "mmap", .hexret = true, | ||
152 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | ||
153 | [2] = SCA_MMAP_PROT, /* prot */ | ||
154 | [3] = SCA_MMAP_FLAGS, /* flags */ }, }, | ||
155 | { .name = "mprotect", .errmsg = true, | ||
156 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ | ||
157 | [2] = SCA_MMAP_PROT, /* prot */ }, }, | ||
158 | { .name = "mremap", .hexret = true, | ||
159 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | ||
160 | [4] = SCA_HEX, /* new_addr */ }, }, | ||
161 | { .name = "munmap", .errmsg = true, | ||
162 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, | ||
26 | { .name = "open", .errmsg = true, }, | 163 | { .name = "open", .errmsg = true, }, |
27 | { .name = "poll", .errmsg = true, .timeout = true, }, | 164 | { .name = "poll", .errmsg = true, .timeout = true, }, |
28 | { .name = "ppoll", .errmsg = true, .timeout = true, }, | 165 | { .name = "ppoll", .errmsg = true, .timeout = true, }, |
166 | { .name = "pread", .errmsg = true, .alias = "pread64", }, | ||
167 | { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, | ||
29 | { .name = "read", .errmsg = true, }, | 168 | { .name = "read", .errmsg = true, }, |
30 | { .name = "recvfrom", .errmsg = true, }, | 169 | { .name = "recvfrom", .errmsg = true, }, |
31 | { .name = "select", .errmsg = true, .timeout = true, }, | 170 | { .name = "select", .errmsg = true, .timeout = true, }, |
32 | { .name = "socket", .errmsg = true, }, | 171 | { .name = "socket", .errmsg = true, }, |
33 | { .name = "stat", .errmsg = true, .alias = "newstat", }, | 172 | { .name = "stat", .errmsg = true, .alias = "newstat", }, |
173 | { .name = "uname", .errmsg = true, .alias = "newuname", }, | ||
34 | }; | 174 | }; |
35 | 175 | ||
36 | static int syscall_fmt__cmp(const void *name, const void *fmtp) | 176 | static int syscall_fmt__cmp(const void *name, const void *fmtp) |
@@ -50,6 +190,7 @@ struct syscall { | |||
50 | const char *name; | 190 | const char *name; |
51 | bool filtered; | 191 | bool filtered; |
52 | struct syscall_fmt *fmt; | 192 | struct syscall_fmt *fmt; |
193 | size_t (**arg_scnprintf)(char *bf, size_t size, unsigned long arg); | ||
53 | }; | 194 | }; |
54 | 195 | ||
55 | static size_t fprintf_duration(unsigned long t, FILE *fp) | 196 | static size_t fprintf_duration(unsigned long t, FILE *fp) |
@@ -63,7 +204,7 @@ static size_t fprintf_duration(unsigned long t, FILE *fp) | |||
63 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); | 204 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); |
64 | else | 205 | else |
65 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); | 206 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); |
66 | return printed + fprintf(stdout, "): "); | 207 | return printed + fprintf(fp, "): "); |
67 | } | 208 | } |
68 | 209 | ||
69 | struct thread_trace { | 210 | struct thread_trace { |
@@ -80,7 +221,7 @@ static struct thread_trace *thread_trace__new(void) | |||
80 | return zalloc(sizeof(struct thread_trace)); | 221 | return zalloc(sizeof(struct thread_trace)); |
81 | } | 222 | } |
82 | 223 | ||
83 | static struct thread_trace *thread__trace(struct thread *thread) | 224 | static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) |
84 | { | 225 | { |
85 | struct thread_trace *ttrace; | 226 | struct thread_trace *ttrace; |
86 | 227 | ||
@@ -98,12 +239,13 @@ static struct thread_trace *thread__trace(struct thread *thread) | |||
98 | 239 | ||
99 | return ttrace; | 240 | return ttrace; |
100 | fail: | 241 | fail: |
101 | color_fprintf(stdout, PERF_COLOR_RED, | 242 | color_fprintf(fp, PERF_COLOR_RED, |
102 | "WARNING: not enough memory, dropping samples!\n"); | 243 | "WARNING: not enough memory, dropping samples!\n"); |
103 | return NULL; | 244 | return NULL; |
104 | } | 245 | } |
105 | 246 | ||
106 | struct trace { | 247 | struct trace { |
248 | struct perf_tool tool; | ||
107 | int audit_machine; | 249 | int audit_machine; |
108 | struct { | 250 | struct { |
109 | int max; | 251 | int max; |
@@ -112,8 +254,10 @@ struct trace { | |||
112 | struct perf_record_opts opts; | 254 | struct perf_record_opts opts; |
113 | struct machine host; | 255 | struct machine host; |
114 | u64 base_time; | 256 | u64 base_time; |
115 | struct strlist *ev_qualifier; | 257 | FILE *output; |
116 | unsigned long nr_events; | 258 | unsigned long nr_events; |
259 | struct strlist *ev_qualifier; | ||
260 | bool not_ev_qualifier; | ||
117 | bool sched; | 261 | bool sched; |
118 | bool multiple_threads; | 262 | bool multiple_threads; |
119 | double duration_filter; | 263 | double duration_filter; |
@@ -151,13 +295,14 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre | |||
151 | return printed; | 295 | return printed; |
152 | } | 296 | } |
153 | 297 | ||
154 | static int trace__process_event(struct machine *machine, union perf_event *event) | 298 | static int trace__process_event(struct trace *trace, struct machine *machine, |
299 | union perf_event *event) | ||
155 | { | 300 | { |
156 | int ret = 0; | 301 | int ret = 0; |
157 | 302 | ||
158 | switch (event->header.type) { | 303 | switch (event->header.type) { |
159 | case PERF_RECORD_LOST: | 304 | case PERF_RECORD_LOST: |
160 | color_fprintf(stdout, PERF_COLOR_RED, | 305 | color_fprintf(trace->output, PERF_COLOR_RED, |
161 | "LOST %" PRIu64 " events!\n", event->lost.lost); | 306 | "LOST %" PRIu64 " events!\n", event->lost.lost); |
162 | ret = machine__process_lost_event(machine, event); | 307 | ret = machine__process_lost_event(machine, event); |
163 | default: | 308 | default: |
@@ -168,12 +313,13 @@ static int trace__process_event(struct machine *machine, union perf_event *event | |||
168 | return ret; | 313 | return ret; |
169 | } | 314 | } |
170 | 315 | ||
171 | static int trace__tool_process(struct perf_tool *tool __maybe_unused, | 316 | static int trace__tool_process(struct perf_tool *tool, |
172 | union perf_event *event, | 317 | union perf_event *event, |
173 | struct perf_sample *sample __maybe_unused, | 318 | struct perf_sample *sample __maybe_unused, |
174 | struct machine *machine) | 319 | struct machine *machine) |
175 | { | 320 | { |
176 | return trace__process_event(machine, event); | 321 | struct trace *trace = container_of(tool, struct trace, tool); |
322 | return trace__process_event(trace, machine, event); | ||
177 | } | 323 | } |
178 | 324 | ||
179 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | 325 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) |
@@ -187,11 +333,11 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | |||
187 | machine__create_kernel_maps(&trace->host); | 333 | machine__create_kernel_maps(&trace->host); |
188 | 334 | ||
189 | if (perf_target__has_task(&trace->opts.target)) { | 335 | if (perf_target__has_task(&trace->opts.target)) { |
190 | err = perf_event__synthesize_thread_map(NULL, evlist->threads, | 336 | err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads, |
191 | trace__tool_process, | 337 | trace__tool_process, |
192 | &trace->host); | 338 | &trace->host); |
193 | } else { | 339 | } else { |
194 | err = perf_event__synthesize_threads(NULL, trace__tool_process, | 340 | err = perf_event__synthesize_threads(&trace->tool, trace__tool_process, |
195 | &trace->host); | 341 | &trace->host); |
196 | } | 342 | } |
197 | 343 | ||
@@ -201,6 +347,26 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | |||
201 | return err; | 347 | return err; |
202 | } | 348 | } |
203 | 349 | ||
350 | static int syscall__set_arg_fmts(struct syscall *sc) | ||
351 | { | ||
352 | struct format_field *field; | ||
353 | int idx = 0; | ||
354 | |||
355 | sc->arg_scnprintf = calloc(sc->tp_format->format.nr_fields - 1, sizeof(void *)); | ||
356 | if (sc->arg_scnprintf == NULL) | ||
357 | return -1; | ||
358 | |||
359 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | ||
360 | if (sc->fmt && sc->fmt->arg_scnprintf[idx]) | ||
361 | sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx]; | ||
362 | else if (field->flags & FIELD_IS_POINTER) | ||
363 | sc->arg_scnprintf[idx] = syscall_arg__scnprintf_hex; | ||
364 | ++idx; | ||
365 | } | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
204 | static int trace__read_syscall_info(struct trace *trace, int id) | 370 | static int trace__read_syscall_info(struct trace *trace, int id) |
205 | { | 371 | { |
206 | char tp_name[128]; | 372 | char tp_name[128]; |
@@ -230,13 +396,17 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
230 | sc = trace->syscalls.table + id; | 396 | sc = trace->syscalls.table + id; |
231 | sc->name = name; | 397 | sc->name = name; |
232 | 398 | ||
233 | if (trace->ev_qualifier && !strlist__find(trace->ev_qualifier, name)) { | 399 | if (trace->ev_qualifier) { |
234 | sc->filtered = true; | 400 | bool in = strlist__find(trace->ev_qualifier, name) != NULL; |
235 | /* | 401 | |
236 | * No need to do read tracepoint information since this will be | 402 | if (!(in ^ trace->not_ev_qualifier)) { |
237 | * filtered out. | 403 | sc->filtered = true; |
238 | */ | 404 | /* |
239 | return 0; | 405 | * No need to do read tracepoint information since this will be |
406 | * filtered out. | ||
407 | */ | ||
408 | return 0; | ||
409 | } | ||
240 | } | 410 | } |
241 | 411 | ||
242 | sc->fmt = syscall_fmt__find(sc->name); | 412 | sc->fmt = syscall_fmt__find(sc->name); |
@@ -249,7 +419,10 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
249 | sc->tp_format = event_format__new("syscalls", tp_name); | 419 | sc->tp_format = event_format__new("syscalls", tp_name); |
250 | } | 420 | } |
251 | 421 | ||
252 | return sc->tp_format != NULL ? 0 : -1; | 422 | if (sc->tp_format == NULL) |
423 | return -1; | ||
424 | |||
425 | return syscall__set_arg_fmts(sc); | ||
253 | } | 426 | } |
254 | 427 | ||
255 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | 428 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
@@ -263,8 +436,14 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | |||
263 | 436 | ||
264 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | 437 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { |
265 | printed += scnprintf(bf + printed, size - printed, | 438 | printed += scnprintf(bf + printed, size - printed, |
266 | "%s%s: %ld", printed ? ", " : "", | 439 | "%s%s: ", printed ? ", " : "", field->name); |
267 | field->name, args[i++]); | 440 | |
441 | if (sc->arg_scnprintf && sc->arg_scnprintf[i]) | ||
442 | printed += sc->arg_scnprintf[i](bf + printed, size - printed, args[i]); | ||
443 | else | ||
444 | printed += scnprintf(bf + printed, size - printed, | ||
445 | "%ld", args[i]); | ||
446 | ++i; | ||
268 | } | 447 | } |
269 | } else { | 448 | } else { |
270 | while (i < 6) { | 449 | while (i < 6) { |
@@ -288,7 +467,22 @@ static struct syscall *trace__syscall_info(struct trace *trace, | |||
288 | int id = perf_evsel__intval(evsel, sample, "id"); | 467 | int id = perf_evsel__intval(evsel, sample, "id"); |
289 | 468 | ||
290 | if (id < 0) { | 469 | if (id < 0) { |
291 | printf("Invalid syscall %d id, skipping...\n", id); | 470 | |
471 | /* | ||
472 | * XXX: Noticed on x86_64, reproduced as far back as 3.0.36, haven't tried | ||
473 | * before that, leaving at a higher verbosity level till that is | ||
474 | * explained. Reproduced with plain ftrace with: | ||
475 | * | ||
476 | * echo 1 > /t/events/raw_syscalls/sys_exit/enable | ||
477 | * grep "NR -1 " /t/trace_pipe | ||
478 | * | ||
479 | * After generating some load on the machine. | ||
480 | */ | ||
481 | if (verbose > 1) { | ||
482 | static u64 n; | ||
483 | fprintf(trace->output, "Invalid syscall %d id, skipping (%s, %" PRIu64 ") ...\n", | ||
484 | id, perf_evsel__name(evsel), ++n); | ||
485 | } | ||
292 | return NULL; | 486 | return NULL; |
293 | } | 487 | } |
294 | 488 | ||
@@ -302,10 +496,12 @@ static struct syscall *trace__syscall_info(struct trace *trace, | |||
302 | return &trace->syscalls.table[id]; | 496 | return &trace->syscalls.table[id]; |
303 | 497 | ||
304 | out_cant_read: | 498 | out_cant_read: |
305 | printf("Problems reading syscall %d", id); | 499 | if (verbose) { |
306 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) | 500 | fprintf(trace->output, "Problems reading syscall %d", id); |
307 | printf("(%s)", trace->syscalls.table[id].name); | 501 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) |
308 | puts(" information"); | 502 | fprintf(trace->output, "(%s)", trace->syscalls.table[id].name); |
503 | fputs(" information\n", trace->output); | ||
504 | } | ||
309 | return NULL; | 505 | return NULL; |
310 | } | 506 | } |
311 | 507 | ||
@@ -326,13 +522,13 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
326 | return 0; | 522 | return 0; |
327 | 523 | ||
328 | thread = machine__findnew_thread(&trace->host, sample->tid); | 524 | thread = machine__findnew_thread(&trace->host, sample->tid); |
329 | ttrace = thread__trace(thread); | 525 | ttrace = thread__trace(thread, trace->output); |
330 | if (ttrace == NULL) | 526 | if (ttrace == NULL) |
331 | return -1; | 527 | return -1; |
332 | 528 | ||
333 | args = perf_evsel__rawptr(evsel, sample, "args"); | 529 | args = perf_evsel__rawptr(evsel, sample, "args"); |
334 | if (args == NULL) { | 530 | if (args == NULL) { |
335 | printf("Problems reading syscall arguments\n"); | 531 | fprintf(trace->output, "Problems reading syscall arguments\n"); |
336 | return -1; | 532 | return -1; |
337 | } | 533 | } |
338 | 534 | ||
@@ -352,8 +548,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
352 | 548 | ||
353 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | 549 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { |
354 | if (!trace->duration_filter) { | 550 | if (!trace->duration_filter) { |
355 | trace__fprintf_entry_head(trace, thread, 1, sample->time, stdout); | 551 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); |
356 | printf("%-70s\n", ttrace->entry_str); | 552 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); |
357 | } | 553 | } |
358 | } else | 554 | } else |
359 | ttrace->entry_pending = true; | 555 | ttrace->entry_pending = true; |
@@ -377,7 +573,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
377 | return 0; | 573 | return 0; |
378 | 574 | ||
379 | thread = machine__findnew_thread(&trace->host, sample->tid); | 575 | thread = machine__findnew_thread(&trace->host, sample->tid); |
380 | ttrace = thread__trace(thread); | 576 | ttrace = thread__trace(thread, trace->output); |
381 | if (ttrace == NULL) | 577 | if (ttrace == NULL) |
382 | return -1; | 578 | return -1; |
383 | 579 | ||
@@ -394,28 +590,33 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
394 | } else if (trace->duration_filter) | 590 | } else if (trace->duration_filter) |
395 | goto out; | 591 | goto out; |
396 | 592 | ||
397 | trace__fprintf_entry_head(trace, thread, duration, sample->time, stdout); | 593 | trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); |
398 | 594 | ||
399 | if (ttrace->entry_pending) { | 595 | if (ttrace->entry_pending) { |
400 | printf("%-70s", ttrace->entry_str); | 596 | fprintf(trace->output, "%-70s", ttrace->entry_str); |
401 | } else { | 597 | } else { |
402 | printf(" ... ["); | 598 | fprintf(trace->output, " ... ["); |
403 | color_fprintf(stdout, PERF_COLOR_YELLOW, "continued"); | 599 | color_fprintf(trace->output, PERF_COLOR_YELLOW, "continued"); |
404 | printf("]: %s()", sc->name); | 600 | fprintf(trace->output, "]: %s()", sc->name); |
405 | } | 601 | } |
406 | 602 | ||
407 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { | 603 | if (sc->fmt == NULL) { |
604 | signed_print: | ||
605 | fprintf(trace->output, ") = %d", ret); | ||
606 | } else if (ret < 0 && sc->fmt->errmsg) { | ||
408 | char bf[256]; | 607 | char bf[256]; |
409 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 608 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
410 | *e = audit_errno_to_name(-ret); | 609 | *e = audit_errno_to_name(-ret); |
411 | 610 | ||
412 | printf(") = -1 %s %s", e, emsg); | 611 | fprintf(trace->output, ") = -1 %s %s", e, emsg); |
413 | } else if (ret == 0 && sc->fmt && sc->fmt->timeout) | 612 | } else if (ret == 0 && sc->fmt->timeout) |
414 | printf(") = 0 Timeout"); | 613 | fprintf(trace->output, ") = 0 Timeout"); |
614 | else if (sc->fmt->hexret) | ||
615 | fprintf(trace->output, ") = %#x", ret); | ||
415 | else | 616 | else |
416 | printf(") = %d", ret); | 617 | goto signed_print; |
417 | 618 | ||
418 | putchar('\n'); | 619 | fputc('\n', trace->output); |
419 | out: | 620 | out: |
420 | ttrace->entry_pending = false; | 621 | ttrace->entry_pending = false; |
421 | 622 | ||
@@ -428,7 +629,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs | |||
428 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | 629 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
429 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; | 630 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; |
430 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | 631 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); |
431 | struct thread_trace *ttrace = thread__trace(thread); | 632 | struct thread_trace *ttrace = thread__trace(thread, trace->output); |
432 | 633 | ||
433 | if (ttrace == NULL) | 634 | if (ttrace == NULL) |
434 | goto out_dump; | 635 | goto out_dump; |
@@ -438,7 +639,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs | |||
438 | return 0; | 639 | return 0; |
439 | 640 | ||
440 | out_dump: | 641 | out_dump: |
441 | printf("%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", | 642 | fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", |
442 | evsel->name, | 643 | evsel->name, |
443 | perf_evsel__strval(evsel, sample, "comm"), | 644 | perf_evsel__strval(evsel, sample, "comm"), |
444 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), | 645 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), |
@@ -456,32 +657,32 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
456 | const bool forks = argc > 0; | 657 | const bool forks = argc > 0; |
457 | 658 | ||
458 | if (evlist == NULL) { | 659 | if (evlist == NULL) { |
459 | printf("Not enough memory to run!\n"); | 660 | fprintf(trace->output, "Not enough memory to run!\n"); |
460 | goto out; | 661 | goto out; |
461 | } | 662 | } |
462 | 663 | ||
463 | if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || | 664 | if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || |
464 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { | 665 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { |
465 | printf("Couldn't read the raw_syscalls tracepoints information!\n"); | 666 | fprintf(trace->output, "Couldn't read the raw_syscalls tracepoints information!\n"); |
466 | goto out_delete_evlist; | 667 | goto out_delete_evlist; |
467 | } | 668 | } |
468 | 669 | ||
469 | if (trace->sched && | 670 | if (trace->sched && |
470 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | 671 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", |
471 | trace__sched_stat_runtime)) { | 672 | trace__sched_stat_runtime)) { |
472 | printf("Couldn't read the sched_stat_runtime tracepoint information!\n"); | 673 | fprintf(trace->output, "Couldn't read the sched_stat_runtime tracepoint information!\n"); |
473 | goto out_delete_evlist; | 674 | goto out_delete_evlist; |
474 | } | 675 | } |
475 | 676 | ||
476 | err = perf_evlist__create_maps(evlist, &trace->opts.target); | 677 | err = perf_evlist__create_maps(evlist, &trace->opts.target); |
477 | if (err < 0) { | 678 | if (err < 0) { |
478 | printf("Problems parsing the target to trace, check your options!\n"); | 679 | fprintf(trace->output, "Problems parsing the target to trace, check your options!\n"); |
479 | goto out_delete_evlist; | 680 | goto out_delete_evlist; |
480 | } | 681 | } |
481 | 682 | ||
482 | err = trace__symbols_init(trace, evlist); | 683 | err = trace__symbols_init(trace, evlist); |
483 | if (err < 0) { | 684 | if (err < 0) { |
484 | printf("Problems initializing symbol libraries!\n"); | 685 | fprintf(trace->output, "Problems initializing symbol libraries!\n"); |
485 | goto out_delete_maps; | 686 | goto out_delete_maps; |
486 | } | 687 | } |
487 | 688 | ||
@@ -494,20 +695,20 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
494 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, | 695 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, |
495 | argv, false, false); | 696 | argv, false, false); |
496 | if (err < 0) { | 697 | if (err < 0) { |
497 | printf("Couldn't run the workload!\n"); | 698 | fprintf(trace->output, "Couldn't run the workload!\n"); |
498 | goto out_delete_maps; | 699 | goto out_delete_maps; |
499 | } | 700 | } |
500 | } | 701 | } |
501 | 702 | ||
502 | err = perf_evlist__open(evlist); | 703 | err = perf_evlist__open(evlist); |
503 | if (err < 0) { | 704 | if (err < 0) { |
504 | printf("Couldn't create the events: %s\n", strerror(errno)); | 705 | fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno)); |
505 | goto out_delete_maps; | 706 | goto out_delete_maps; |
506 | } | 707 | } |
507 | 708 | ||
508 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 709 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
509 | if (err < 0) { | 710 | if (err < 0) { |
510 | printf("Couldn't mmap the events: %s\n", strerror(errno)); | 711 | fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno)); |
511 | goto out_close_evlist; | 712 | goto out_close_evlist; |
512 | } | 713 | } |
513 | 714 | ||
@@ -532,7 +733,7 @@ again: | |||
532 | 733 | ||
533 | err = perf_evlist__parse_sample(evlist, event, &sample); | 734 | err = perf_evlist__parse_sample(evlist, event, &sample); |
534 | if (err) { | 735 | if (err) { |
535 | printf("Can't parse sample, err = %d, skipping...\n", err); | 736 | fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err); |
536 | continue; | 737 | continue; |
537 | } | 738 | } |
538 | 739 | ||
@@ -540,18 +741,18 @@ again: | |||
540 | trace->base_time = sample.time; | 741 | trace->base_time = sample.time; |
541 | 742 | ||
542 | if (type != PERF_RECORD_SAMPLE) { | 743 | if (type != PERF_RECORD_SAMPLE) { |
543 | trace__process_event(&trace->host, event); | 744 | trace__process_event(trace, &trace->host, event); |
544 | continue; | 745 | continue; |
545 | } | 746 | } |
546 | 747 | ||
547 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 748 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
548 | if (evsel == NULL) { | 749 | if (evsel == NULL) { |
549 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); | 750 | fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); |
550 | continue; | 751 | continue; |
551 | } | 752 | } |
552 | 753 | ||
553 | if (sample.raw_data == NULL) { | 754 | if (sample.raw_data == NULL) { |
554 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 755 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
555 | perf_evsel__name(evsel), sample.tid, | 756 | perf_evsel__name(evsel), sample.tid, |
556 | sample.cpu, sample.raw_size); | 757 | sample.cpu, sample.raw_size); |
557 | continue; | 758 | continue; |
@@ -640,6 +841,23 @@ static int trace__set_duration(const struct option *opt, const char *str, | |||
640 | return 0; | 841 | return 0; |
641 | } | 842 | } |
642 | 843 | ||
844 | static int trace__open_output(struct trace *trace, const char *filename) | ||
845 | { | ||
846 | struct stat st; | ||
847 | |||
848 | if (!stat(filename, &st) && st.st_size) { | ||
849 | char oldname[PATH_MAX]; | ||
850 | |||
851 | scnprintf(oldname, sizeof(oldname), "%s.old", filename); | ||
852 | unlink(oldname); | ||
853 | rename(filename, oldname); | ||
854 | } | ||
855 | |||
856 | trace->output = fopen(filename, "w"); | ||
857 | |||
858 | return trace->output == NULL ? -errno : 0; | ||
859 | } | ||
860 | |||
643 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | 861 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) |
644 | { | 862 | { |
645 | const char * const trace_usage[] = { | 863 | const char * const trace_usage[] = { |
@@ -662,29 +880,33 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
662 | .no_delay = true, | 880 | .no_delay = true, |
663 | .mmap_pages = 1024, | 881 | .mmap_pages = 1024, |
664 | }, | 882 | }, |
883 | .output = stdout, | ||
665 | }; | 884 | }; |
885 | const char *output_name = NULL; | ||
666 | const char *ev_qualifier_str = NULL; | 886 | const char *ev_qualifier_str = NULL; |
667 | const struct option trace_options[] = { | 887 | const struct option trace_options[] = { |
668 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", | 888 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", |
669 | "list of events to trace"), | 889 | "list of events to trace"), |
890 | OPT_STRING('o', "output", &output_name, "file", "output file name"), | ||
670 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", | 891 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", |
671 | "trace events on existing process id"), | 892 | "trace events on existing process id"), |
672 | OPT_STRING(0, "tid", &trace.opts.target.tid, "tid", | 893 | OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", |
673 | "trace events on existing thread id"), | 894 | "trace events on existing thread id"), |
674 | OPT_BOOLEAN(0, "all-cpus", &trace.opts.target.system_wide, | 895 | OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide, |
675 | "system-wide collection from all CPUs"), | 896 | "system-wide collection from all CPUs"), |
676 | OPT_STRING(0, "cpu", &trace.opts.target.cpu_list, "cpu", | 897 | OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", |
677 | "list of cpus to monitor"), | 898 | "list of cpus to monitor"), |
678 | OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit, | 899 | OPT_BOOLEAN('i', "no-inherit", &trace.opts.no_inherit, |
679 | "child tasks do not inherit counters"), | 900 | "child tasks do not inherit counters"), |
680 | OPT_UINTEGER(0, "mmap-pages", &trace.opts.mmap_pages, | 901 | OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages, |
681 | "number of mmap data pages"), | 902 | "number of mmap data pages"), |
682 | OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", | 903 | OPT_STRING('u', "uid", &trace.opts.target.uid_str, "user", |
683 | "user to profile"), | 904 | "user to profile"), |
684 | OPT_CALLBACK(0, "duration", &trace, "float", | 905 | OPT_CALLBACK(0, "duration", &trace, "float", |
685 | "show only events with duration > N.M ms", | 906 | "show only events with duration > N.M ms", |
686 | trace__set_duration), | 907 | trace__set_duration), |
687 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), | 908 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), |
909 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), | ||
688 | OPT_END() | 910 | OPT_END() |
689 | }; | 911 | }; |
690 | int err; | 912 | int err; |
@@ -692,26 +914,41 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
692 | 914 | ||
693 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 915 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); |
694 | 916 | ||
917 | if (output_name != NULL) { | ||
918 | err = trace__open_output(&trace, output_name); | ||
919 | if (err < 0) { | ||
920 | perror("failed to create output file"); | ||
921 | goto out; | ||
922 | } | ||
923 | } | ||
924 | |||
695 | if (ev_qualifier_str != NULL) { | 925 | if (ev_qualifier_str != NULL) { |
696 | trace.ev_qualifier = strlist__new(true, ev_qualifier_str); | 926 | const char *s = ev_qualifier_str; |
927 | |||
928 | trace.not_ev_qualifier = *s == '!'; | ||
929 | if (trace.not_ev_qualifier) | ||
930 | ++s; | ||
931 | trace.ev_qualifier = strlist__new(true, s); | ||
697 | if (trace.ev_qualifier == NULL) { | 932 | if (trace.ev_qualifier == NULL) { |
698 | puts("Not enough memory to parse event qualifier"); | 933 | fputs("Not enough memory to parse event qualifier", |
699 | return -ENOMEM; | 934 | trace.output); |
935 | err = -ENOMEM; | ||
936 | goto out_close; | ||
700 | } | 937 | } |
701 | } | 938 | } |
702 | 939 | ||
703 | err = perf_target__validate(&trace.opts.target); | 940 | err = perf_target__validate(&trace.opts.target); |
704 | if (err) { | 941 | if (err) { |
705 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 942 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
706 | printf("%s", bf); | 943 | fprintf(trace.output, "%s", bf); |
707 | return err; | 944 | goto out_close; |
708 | } | 945 | } |
709 | 946 | ||
710 | err = perf_target__parse_uid(&trace.opts.target); | 947 | err = perf_target__parse_uid(&trace.opts.target); |
711 | if (err) { | 948 | if (err) { |
712 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 949 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
713 | printf("%s", bf); | 950 | fprintf(trace.output, "%s", bf); |
714 | return err; | 951 | goto out_close; |
715 | } | 952 | } |
716 | 953 | ||
717 | if (!argc && perf_target__none(&trace.opts.target)) | 954 | if (!argc && perf_target__none(&trace.opts.target)) |
@@ -720,7 +957,11 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
720 | err = trace__run(&trace, argc, argv); | 957 | err = trace__run(&trace, argc, argv); |
721 | 958 | ||
722 | if (trace.sched && !err) | 959 | if (trace.sched && !err) |
723 | trace__fprintf_thread_summary(&trace, stdout); | 960 | trace__fprintf_thread_summary(&trace, trace.output); |
724 | 961 | ||
962 | out_close: | ||
963 | if (output_name != NULL) | ||
964 | fclose(trace.output); | ||
965 | out: | ||
725 | return err; | 966 | return err; |
726 | } | 967 | } |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index c7d111f74553..1f5105ac5c85 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "target.h" | 14 | #include "target.h" |
15 | #include "evlist.h" | 15 | #include "evlist.h" |
16 | #include "evsel.h" | 16 | #include "evsel.h" |
17 | #include "debug.h" | ||
17 | #include <unistd.h> | 18 | #include <unistd.h> |
18 | 19 | ||
19 | #include "parse-events.h" | 20 | #include "parse-events.h" |
@@ -486,6 +487,7 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m | |||
486 | int nr_cpus = cpu_map__nr(evlist->cpus); | 487 | int nr_cpus = cpu_map__nr(evlist->cpus); |
487 | int nr_threads = thread_map__nr(evlist->threads); | 488 | int nr_threads = thread_map__nr(evlist->threads); |
488 | 489 | ||
490 | pr_debug2("perf event ring buffer mmapped per cpu\n"); | ||
489 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 491 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
490 | int output = -1; | 492 | int output = -1; |
491 | 493 | ||
@@ -524,6 +526,7 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in | |||
524 | int thread; | 526 | int thread; |
525 | int nr_threads = thread_map__nr(evlist->threads); | 527 | int nr_threads = thread_map__nr(evlist->threads); |
526 | 528 | ||
529 | pr_debug2("perf event ring buffer mmapped per thread\n"); | ||
527 | for (thread = 0; thread < nr_threads; thread++) { | 530 | for (thread = 0; thread < nr_threads; thread++) { |
528 | int output = -1; | 531 | int output = -1; |
529 | 532 | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 960394ea1e3a..47cbe1e58b73 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <traceevent/event-parse.h> | 13 | #include <traceevent/event-parse.h> |
14 | #include <linux/hw_breakpoint.h> | 14 | #include <linux/hw_breakpoint.h> |
15 | #include <linux/perf_event.h> | 15 | #include <linux/perf_event.h> |
16 | #include <sys/resource.h> | ||
16 | #include "asm/bug.h" | 17 | #include "asm/bug.h" |
17 | #include "evsel.h" | 18 | #include "evsel.h" |
18 | #include "evlist.h" | 19 | #include "evlist.h" |
@@ -21,6 +22,7 @@ | |||
21 | #include "thread_map.h" | 22 | #include "thread_map.h" |
22 | #include "target.h" | 23 | #include "target.h" |
23 | #include "perf_regs.h" | 24 | #include "perf_regs.h" |
25 | #include "debug.h" | ||
24 | 26 | ||
25 | static struct { | 27 | static struct { |
26 | bool sample_id_all; | 28 | bool sample_id_all; |
@@ -861,12 +863,72 @@ static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread) | |||
861 | return fd; | 863 | return fd; |
862 | } | 864 | } |
863 | 865 | ||
866 | #define __PRINT_ATTR(fmt, cast, field) \ | ||
867 | fprintf(fp, " %-19s "fmt"\n", #field, cast attr->field) | ||
868 | |||
869 | #define PRINT_ATTR_U32(field) __PRINT_ATTR("%u" , , field) | ||
870 | #define PRINT_ATTR_X32(field) __PRINT_ATTR("%#x", , field) | ||
871 | #define PRINT_ATTR_U64(field) __PRINT_ATTR("%" PRIu64, (uint64_t), field) | ||
872 | #define PRINT_ATTR_X64(field) __PRINT_ATTR("%#"PRIx64, (uint64_t), field) | ||
873 | |||
874 | #define PRINT_ATTR2N(name1, field1, name2, field2) \ | ||
875 | fprintf(fp, " %-19s %u %-19s %u\n", \ | ||
876 | name1, attr->field1, name2, attr->field2) | ||
877 | |||
878 | #define PRINT_ATTR2(field1, field2) \ | ||
879 | PRINT_ATTR2N(#field1, field1, #field2, field2) | ||
880 | |||
881 | static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | ||
882 | { | ||
883 | size_t ret = 0; | ||
884 | |||
885 | ret += fprintf(fp, "%.60s\n", graph_dotted_line); | ||
886 | ret += fprintf(fp, "perf_event_attr:\n"); | ||
887 | |||
888 | ret += PRINT_ATTR_U32(type); | ||
889 | ret += PRINT_ATTR_U32(size); | ||
890 | ret += PRINT_ATTR_X64(config); | ||
891 | ret += PRINT_ATTR_U64(sample_period); | ||
892 | ret += PRINT_ATTR_U64(sample_freq); | ||
893 | ret += PRINT_ATTR_X64(sample_type); | ||
894 | ret += PRINT_ATTR_X64(read_format); | ||
895 | |||
896 | ret += PRINT_ATTR2(disabled, inherit); | ||
897 | ret += PRINT_ATTR2(pinned, exclusive); | ||
898 | ret += PRINT_ATTR2(exclude_user, exclude_kernel); | ||
899 | ret += PRINT_ATTR2(exclude_hv, exclude_idle); | ||
900 | ret += PRINT_ATTR2(mmap, comm); | ||
901 | ret += PRINT_ATTR2(freq, inherit_stat); | ||
902 | ret += PRINT_ATTR2(enable_on_exec, task); | ||
903 | ret += PRINT_ATTR2(watermark, precise_ip); | ||
904 | ret += PRINT_ATTR2(mmap_data, sample_id_all); | ||
905 | ret += PRINT_ATTR2(exclude_host, exclude_guest); | ||
906 | ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, | ||
907 | "excl.callchain_user", exclude_callchain_user); | ||
908 | |||
909 | ret += PRINT_ATTR_U32(wakeup_events); | ||
910 | ret += PRINT_ATTR_U32(wakeup_watermark); | ||
911 | ret += PRINT_ATTR_X32(bp_type); | ||
912 | ret += PRINT_ATTR_X64(bp_addr); | ||
913 | ret += PRINT_ATTR_X64(config1); | ||
914 | ret += PRINT_ATTR_U64(bp_len); | ||
915 | ret += PRINT_ATTR_X64(config2); | ||
916 | ret += PRINT_ATTR_X64(branch_sample_type); | ||
917 | ret += PRINT_ATTR_X64(sample_regs_user); | ||
918 | ret += PRINT_ATTR_U32(sample_stack_user); | ||
919 | |||
920 | ret += fprintf(fp, "%.60s\n", graph_dotted_line); | ||
921 | |||
922 | return ret; | ||
923 | } | ||
924 | |||
864 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 925 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
865 | struct thread_map *threads) | 926 | struct thread_map *threads) |
866 | { | 927 | { |
867 | int cpu, thread; | 928 | int cpu, thread; |
868 | unsigned long flags = 0; | 929 | unsigned long flags = 0; |
869 | int pid = -1, err; | 930 | int pid = -1, err; |
931 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; | ||
870 | 932 | ||
871 | if (evsel->fd == NULL && | 933 | if (evsel->fd == NULL && |
872 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 934 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) |
@@ -884,6 +946,9 @@ retry_sample_id: | |||
884 | if (perf_missing_features.sample_id_all) | 946 | if (perf_missing_features.sample_id_all) |
885 | evsel->attr.sample_id_all = 0; | 947 | evsel->attr.sample_id_all = 0; |
886 | 948 | ||
949 | if (verbose >= 2) | ||
950 | perf_event_attr__fprintf(&evsel->attr, stderr); | ||
951 | |||
887 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 952 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
888 | 953 | ||
889 | for (thread = 0; thread < threads->nr; thread++) { | 954 | for (thread = 0; thread < threads->nr; thread++) { |
@@ -893,6 +958,9 @@ retry_sample_id: | |||
893 | pid = threads->map[thread]; | 958 | pid = threads->map[thread]; |
894 | 959 | ||
895 | group_fd = get_group_fd(evsel, cpu, thread); | 960 | group_fd = get_group_fd(evsel, cpu, thread); |
961 | retry_open: | ||
962 | pr_debug2("perf_event_open: pid %d cpu %d group_fd %d flags %#lx\n", | ||
963 | pid, cpus->map[cpu], group_fd, flags); | ||
896 | 964 | ||
897 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 965 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
898 | pid, | 966 | pid, |
@@ -902,12 +970,37 @@ retry_sample_id: | |||
902 | err = -errno; | 970 | err = -errno; |
903 | goto try_fallback; | 971 | goto try_fallback; |
904 | } | 972 | } |
973 | set_rlimit = NO_CHANGE; | ||
905 | } | 974 | } |
906 | } | 975 | } |
907 | 976 | ||
908 | return 0; | 977 | return 0; |
909 | 978 | ||
910 | try_fallback: | 979 | try_fallback: |
980 | /* | ||
981 | * perf stat needs between 5 and 22 fds per CPU. When we run out | ||
982 | * of them try to increase the limits. | ||
983 | */ | ||
984 | if (err == -EMFILE && set_rlimit < INCREASED_MAX) { | ||
985 | struct rlimit l; | ||
986 | int old_errno = errno; | ||
987 | |||
988 | if (getrlimit(RLIMIT_NOFILE, &l) == 0) { | ||
989 | if (set_rlimit == NO_CHANGE) | ||
990 | l.rlim_cur = l.rlim_max; | ||
991 | else { | ||
992 | l.rlim_cur = l.rlim_max + 1000; | ||
993 | l.rlim_max = l.rlim_cur; | ||
994 | } | ||
995 | if (setrlimit(RLIMIT_NOFILE, &l) == 0) { | ||
996 | set_rlimit++; | ||
997 | errno = old_errno; | ||
998 | goto retry_open; | ||
999 | } | ||
1000 | } | ||
1001 | errno = old_errno; | ||
1002 | } | ||
1003 | |||
911 | if (err != -EINVAL || cpu > 0 || thread > 0) | 1004 | if (err != -EINVAL || cpu > 0 || thread > 0) |
912 | goto out_close; | 1005 | goto out_close; |
913 | 1006 | ||
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 4514e7e9b659..59486c180626 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -253,7 +253,8 @@ void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size) | |||
253 | return; | 253 | return; |
254 | } | 254 | } |
255 | 255 | ||
256 | static struct thread *__machine__findnew_thread(struct machine *machine, pid_t tid, | 256 | static struct thread *__machine__findnew_thread(struct machine *machine, |
257 | pid_t pid, pid_t tid, | ||
257 | bool create) | 258 | bool create) |
258 | { | 259 | { |
259 | struct rb_node **p = &machine->threads.rb_node; | 260 | struct rb_node **p = &machine->threads.rb_node; |
@@ -265,8 +266,11 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t t | |||
265 | * so most of the time we dont have to look up | 266 | * so most of the time we dont have to look up |
266 | * the full rbtree: | 267 | * the full rbtree: |
267 | */ | 268 | */ |
268 | if (machine->last_match && machine->last_match->tid == tid) | 269 | if (machine->last_match && machine->last_match->tid == tid) { |
270 | if (pid && pid != machine->last_match->pid_) | ||
271 | machine->last_match->pid_ = pid; | ||
269 | return machine->last_match; | 272 | return machine->last_match; |
273 | } | ||
270 | 274 | ||
271 | while (*p != NULL) { | 275 | while (*p != NULL) { |
272 | parent = *p; | 276 | parent = *p; |
@@ -274,6 +278,8 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t t | |||
274 | 278 | ||
275 | if (th->tid == tid) { | 279 | if (th->tid == tid) { |
276 | machine->last_match = th; | 280 | machine->last_match = th; |
281 | if (pid && pid != th->pid_) | ||
282 | th->pid_ = pid; | ||
277 | return th; | 283 | return th; |
278 | } | 284 | } |
279 | 285 | ||
@@ -286,7 +292,7 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t t | |||
286 | if (!create) | 292 | if (!create) |
287 | return NULL; | 293 | return NULL; |
288 | 294 | ||
289 | th = thread__new(tid); | 295 | th = thread__new(pid, tid); |
290 | if (th != NULL) { | 296 | if (th != NULL) { |
291 | rb_link_node(&th->rb_node, parent, p); | 297 | rb_link_node(&th->rb_node, parent, p); |
292 | rb_insert_color(&th->rb_node, &machine->threads); | 298 | rb_insert_color(&th->rb_node, &machine->threads); |
@@ -298,12 +304,12 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t t | |||
298 | 304 | ||
299 | struct thread *machine__findnew_thread(struct machine *machine, pid_t tid) | 305 | struct thread *machine__findnew_thread(struct machine *machine, pid_t tid) |
300 | { | 306 | { |
301 | return __machine__findnew_thread(machine, tid, true); | 307 | return __machine__findnew_thread(machine, 0, tid, true); |
302 | } | 308 | } |
303 | 309 | ||
304 | struct thread *machine__find_thread(struct machine *machine, pid_t tid) | 310 | struct thread *machine__find_thread(struct machine *machine, pid_t tid) |
305 | { | 311 | { |
306 | return __machine__findnew_thread(machine, tid, false); | 312 | return __machine__findnew_thread(machine, 0, tid, false); |
307 | } | 313 | } |
308 | 314 | ||
309 | int machine__process_comm_event(struct machine *machine, union perf_event *event) | 315 | int machine__process_comm_event(struct machine *machine, union perf_event *event) |
@@ -1031,11 +1037,27 @@ out_problem: | |||
1031 | return 0; | 1037 | return 0; |
1032 | } | 1038 | } |
1033 | 1039 | ||
1040 | static void machine__remove_thread(struct machine *machine, struct thread *th) | ||
1041 | { | ||
1042 | machine->last_match = NULL; | ||
1043 | rb_erase(&th->rb_node, &machine->threads); | ||
1044 | /* | ||
1045 | * We may have references to this thread, for instance in some hist_entry | ||
1046 | * instances, so just move them to a separate list. | ||
1047 | */ | ||
1048 | list_add_tail(&th->node, &machine->dead_threads); | ||
1049 | } | ||
1050 | |||
1034 | int machine__process_fork_event(struct machine *machine, union perf_event *event) | 1051 | int machine__process_fork_event(struct machine *machine, union perf_event *event) |
1035 | { | 1052 | { |
1036 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | 1053 | struct thread *thread = machine__find_thread(machine, event->fork.tid); |
1037 | struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); | 1054 | struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); |
1038 | 1055 | ||
1056 | /* if a thread currently exists for the thread id remove it */ | ||
1057 | if (thread != NULL) | ||
1058 | machine__remove_thread(machine, thread); | ||
1059 | |||
1060 | thread = machine__findnew_thread(machine, event->fork.tid); | ||
1039 | if (dump_trace) | 1061 | if (dump_trace) |
1040 | perf_event__fprintf_task(event, stdout); | 1062 | perf_event__fprintf_task(event, stdout); |
1041 | 1063 | ||
@@ -1048,18 +1070,8 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
1048 | return 0; | 1070 | return 0; |
1049 | } | 1071 | } |
1050 | 1072 | ||
1051 | static void machine__remove_thread(struct machine *machine, struct thread *th) | 1073 | int machine__process_exit_event(struct machine *machine __maybe_unused, |
1052 | { | 1074 | union perf_event *event) |
1053 | machine->last_match = NULL; | ||
1054 | rb_erase(&th->rb_node, &machine->threads); | ||
1055 | /* | ||
1056 | * We may have references to this thread, for instance in some hist_entry | ||
1057 | * instances, so just move them to a separate list. | ||
1058 | */ | ||
1059 | list_add_tail(&th->node, &machine->dead_threads); | ||
1060 | } | ||
1061 | |||
1062 | int machine__process_exit_event(struct machine *machine, union perf_event *event) | ||
1063 | { | 1075 | { |
1064 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | 1076 | struct thread *thread = machine__find_thread(machine, event->fork.tid); |
1065 | 1077 | ||
@@ -1067,7 +1079,7 @@ int machine__process_exit_event(struct machine *machine, union perf_event *event | |||
1067 | perf_event__fprintf_task(event, stdout); | 1079 | perf_event__fprintf_task(event, stdout); |
1068 | 1080 | ||
1069 | if (thread != NULL) | 1081 | if (thread != NULL) |
1070 | machine__remove_thread(machine, thread); | 1082 | thread__exited(thread); |
1071 | 1083 | ||
1072 | return 0; | 1084 | return 0; |
1073 | } | 1085 | } |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 925e0c3e6d91..381f4fda9654 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -8,6 +8,26 @@ | |||
8 | #include "cpumap.h" | 8 | #include "cpumap.h" |
9 | #include "thread_map.h" | 9 | #include "thread_map.h" |
10 | 10 | ||
11 | /* | ||
12 | * Support debug printing even though util/debug.c is not linked. That means | ||
13 | * implementing 'verbose' and 'eprintf'. | ||
14 | */ | ||
15 | int verbose; | ||
16 | |||
17 | int eprintf(int level, const char *fmt, ...) | ||
18 | { | ||
19 | va_list args; | ||
20 | int ret = 0; | ||
21 | |||
22 | if (verbose >= level) { | ||
23 | va_start(args, fmt); | ||
24 | ret = vfprintf(stderr, fmt, args); | ||
25 | va_end(args); | ||
26 | } | ||
27 | |||
28 | return ret; | ||
29 | } | ||
30 | |||
11 | /* Define PyVarObject_HEAD_INIT for python 2.5 */ | 31 | /* Define PyVarObject_HEAD_INIT for python 2.5 */ |
12 | #ifndef PyVarObject_HEAD_INIT | 32 | #ifndef PyVarObject_HEAD_INIT |
13 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, | 33 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 6feeb88eb5b0..e3d4a550a703 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -7,12 +7,13 @@ | |||
7 | #include "util.h" | 7 | #include "util.h" |
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | 9 | ||
10 | struct thread *thread__new(pid_t tid) | 10 | struct thread *thread__new(pid_t pid, pid_t tid) |
11 | { | 11 | { |
12 | struct thread *self = zalloc(sizeof(*self)); | 12 | struct thread *self = zalloc(sizeof(*self)); |
13 | 13 | ||
14 | if (self != NULL) { | 14 | if (self != NULL) { |
15 | map_groups__init(&self->mg); | 15 | map_groups__init(&self->mg); |
16 | self->pid_ = pid; | ||
16 | self->tid = tid; | 17 | self->tid = tid; |
17 | self->ppid = -1; | 18 | self->ppid = -1; |
18 | self->comm = malloc(32); | 19 | self->comm = malloc(32); |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 13c62c909392..4ebbb40d46d4 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -12,10 +12,12 @@ struct thread { | |||
12 | struct list_head node; | 12 | struct list_head node; |
13 | }; | 13 | }; |
14 | struct map_groups mg; | 14 | struct map_groups mg; |
15 | pid_t pid_; /* Not all tools update this */ | ||
15 | pid_t tid; | 16 | pid_t tid; |
16 | pid_t ppid; | 17 | pid_t ppid; |
17 | char shortname[3]; | 18 | char shortname[3]; |
18 | bool comm_set; | 19 | bool comm_set; |
20 | bool dead; /* if set thread has exited */ | ||
19 | char *comm; | 21 | char *comm; |
20 | int comm_len; | 22 | int comm_len; |
21 | 23 | ||
@@ -24,8 +26,12 @@ struct thread { | |||
24 | 26 | ||
25 | struct machine; | 27 | struct machine; |
26 | 28 | ||
27 | struct thread *thread__new(pid_t tid); | 29 | struct thread *thread__new(pid_t pid, pid_t tid); |
28 | void thread__delete(struct thread *self); | 30 | void thread__delete(struct thread *self); |
31 | static inline void thread__exited(struct thread *thread) | ||
32 | { | ||
33 | thread->dead = true; | ||
34 | } | ||
29 | 35 | ||
30 | int thread__set_comm(struct thread *self, const char *comm); | 36 | int thread__set_comm(struct thread *self, const char *comm); |
31 | int thread__comm_len(struct thread *self); | 37 | int thread__comm_len(struct thread *self); |