diff options
-rw-r--r-- | tools/perf/Documentation/perf-record.txt | 5 | ||||
-rw-r--r-- | tools/perf/builtin-record.c | 16 | ||||
-rw-r--r-- | tools/perf/builtin-top.c | 14 | ||||
-rw-r--r-- | tools/perf/util/event.c | 285 | ||||
-rw-r--r-- | tools/perf/util/event.h | 7 | ||||
-rw-r--r-- | tools/perf/util/header.c | 18 | ||||
-rw-r--r-- | tools/perf/util/header.h | 1 | ||||
-rw-r--r-- | tools/perf/util/session.c | 66 | ||||
-rw-r--r-- | tools/perf/util/session.h | 3 |
9 files changed, 315 insertions, 100 deletions
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 0ad1bc75ab49..52462ae26455 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -108,6 +108,11 @@ OPTIONS | |||
108 | --data:: | 108 | --data:: |
109 | Sample addresses. | 109 | Sample addresses. |
110 | 110 | ||
111 | -T:: | ||
112 | --timestamp:: | ||
113 | Sample timestamps. Use it with 'perf report -D' to see the timestamps, | ||
114 | for instance. | ||
115 | |||
111 | -n:: | 116 | -n:: |
112 | --no-samples:: | 117 | --no-samples:: |
113 | Don't sample. | 118 | Don't sample. |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b34de9291c27..699dd2149c4b 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -49,6 +49,7 @@ static const char *output_name = "perf.data"; | |||
49 | static int group = 0; | 49 | static int group = 0; |
50 | static int realtime_prio = 0; | 50 | static int realtime_prio = 0; |
51 | static bool raw_samples = false; | 51 | static bool raw_samples = false; |
52 | static bool sample_id_all_avail = true; | ||
52 | static bool system_wide = false; | 53 | static bool system_wide = false; |
53 | static pid_t target_pid = -1; | 54 | static pid_t target_pid = -1; |
54 | static pid_t target_tid = -1; | 55 | static pid_t target_tid = -1; |
@@ -61,6 +62,7 @@ static bool call_graph = false; | |||
61 | static bool inherit_stat = false; | 62 | static bool inherit_stat = false; |
62 | static bool no_samples = false; | 63 | static bool no_samples = false; |
63 | static bool sample_address = false; | 64 | static bool sample_address = false; |
65 | static bool sample_time = false; | ||
64 | static bool no_buildid = false; | 66 | static bool no_buildid = false; |
65 | static bool no_buildid_cache = false; | 67 | static bool no_buildid_cache = false; |
66 | 68 | ||
@@ -283,6 +285,9 @@ static void create_counter(int counter, int cpu) | |||
283 | if (system_wide) | 285 | if (system_wide) |
284 | attr->sample_type |= PERF_SAMPLE_CPU; | 286 | attr->sample_type |= PERF_SAMPLE_CPU; |
285 | 287 | ||
288 | if (sample_time) | ||
289 | attr->sample_type |= PERF_SAMPLE_TIME; | ||
290 | |||
286 | if (raw_samples) { | 291 | if (raw_samples) { |
287 | attr->sample_type |= PERF_SAMPLE_TIME; | 292 | attr->sample_type |= PERF_SAMPLE_TIME; |
288 | attr->sample_type |= PERF_SAMPLE_RAW; | 293 | attr->sample_type |= PERF_SAMPLE_RAW; |
@@ -299,6 +304,8 @@ static void create_counter(int counter, int cpu) | |||
299 | attr->disabled = 1; | 304 | attr->disabled = 1; |
300 | attr->enable_on_exec = 1; | 305 | attr->enable_on_exec = 1; |
301 | } | 306 | } |
307 | retry_sample_id: | ||
308 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
302 | 309 | ||
303 | for (thread_index = 0; thread_index < thread_num; thread_index++) { | 310 | for (thread_index = 0; thread_index < thread_num; thread_index++) { |
304 | try_again: | 311 | try_again: |
@@ -315,6 +322,12 @@ try_again: | |||
315 | else if (err == ENODEV && cpu_list) { | 322 | else if (err == ENODEV && cpu_list) { |
316 | die("No such device - did you specify" | 323 | die("No such device - did you specify" |
317 | " an out-of-range profile CPU?\n"); | 324 | " an out-of-range profile CPU?\n"); |
325 | } else if (err == EINVAL && sample_id_all_avail) { | ||
326 | /* | ||
327 | * Old kernel, no attr->sample_id_type_all field | ||
328 | */ | ||
329 | sample_id_all_avail = false; | ||
330 | goto retry_sample_id; | ||
318 | } | 331 | } |
319 | 332 | ||
320 | /* | 333 | /* |
@@ -661,6 +674,8 @@ static int __cmd_record(int argc, const char **argv) | |||
661 | 674 | ||
662 | post_processing_offset = lseek(output, 0, SEEK_CUR); | 675 | post_processing_offset = lseek(output, 0, SEEK_CUR); |
663 | 676 | ||
677 | perf_session__set_sample_id_all(session, sample_id_all_avail); | ||
678 | |||
664 | if (pipe_output) { | 679 | if (pipe_output) { |
665 | err = event__synthesize_attrs(&session->header, | 680 | err = event__synthesize_attrs(&session->header, |
666 | process_synthesized_event, | 681 | process_synthesized_event, |
@@ -841,6 +856,7 @@ const struct option record_options[] = { | |||
841 | "per thread counts"), | 856 | "per thread counts"), |
842 | OPT_BOOLEAN('d', "data", &sample_address, | 857 | OPT_BOOLEAN('d', "data", &sample_address, |
843 | "Sample addresses"), | 858 | "Sample addresses"), |
859 | OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"), | ||
844 | OPT_BOOLEAN('n', "no-samples", &no_samples, | 860 | OPT_BOOLEAN('n', "no-samples", &no_samples, |
845 | "don't sample"), | 861 | "don't sample"), |
846 | OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache, | 862 | OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache, |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5aa29e1e855a..0515ce9d3d3e 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -977,12 +977,12 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
977 | } | 977 | } |
978 | 978 | ||
979 | static void event__process_sample(const event_t *self, | 979 | static void event__process_sample(const event_t *self, |
980 | struct perf_session *session, int counter) | 980 | struct sample_data *sample, |
981 | struct perf_session *session, int counter) | ||
981 | { | 982 | { |
982 | u64 ip = self->ip.ip; | 983 | u64 ip = self->ip.ip; |
983 | struct sym_entry *syme; | 984 | struct sym_entry *syme; |
984 | struct addr_location al; | 985 | struct addr_location al; |
985 | struct sample_data data; | ||
986 | struct machine *machine; | 986 | struct machine *machine; |
987 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 987 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
988 | 988 | ||
@@ -1025,9 +1025,7 @@ static void event__process_sample(const event_t *self, | |||
1025 | if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) | 1025 | if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) |
1026 | exact_samples++; | 1026 | exact_samples++; |
1027 | 1027 | ||
1028 | event__parse_sample(self, session->sample_type, &data); | 1028 | if (event__preprocess_sample(self, session, &al, sample, |
1029 | |||
1030 | if (event__preprocess_sample(self, session, &al, &data, | ||
1031 | symbol_filter) < 0 || | 1029 | symbol_filter) < 0 || |
1032 | al.filtered) | 1030 | al.filtered) |
1033 | return; | 1031 | return; |
@@ -1107,6 +1105,7 @@ static void perf_session__mmap_read_counter(struct perf_session *self, | |||
1107 | unsigned int head = mmap_read_head(md); | 1105 | unsigned int head = mmap_read_head(md); |
1108 | unsigned int old = md->prev; | 1106 | unsigned int old = md->prev; |
1109 | unsigned char *data = md->base + page_size; | 1107 | unsigned char *data = md->base + page_size; |
1108 | struct sample_data sample; | ||
1110 | int diff; | 1109 | int diff; |
1111 | 1110 | ||
1112 | /* | 1111 | /* |
@@ -1154,10 +1153,11 @@ static void perf_session__mmap_read_counter(struct perf_session *self, | |||
1154 | event = &event_copy; | 1153 | event = &event_copy; |
1155 | } | 1154 | } |
1156 | 1155 | ||
1156 | event__parse_sample(event, self, &sample); | ||
1157 | if (event->header.type == PERF_RECORD_SAMPLE) | 1157 | if (event->header.type == PERF_RECORD_SAMPLE) |
1158 | event__process_sample(event, self, md->counter); | 1158 | event__process_sample(event, &sample, self, md->counter); |
1159 | else | 1159 | else |
1160 | event__process(event, NULL, self); | 1160 | event__process(event, &sample, self); |
1161 | old += size; | 1161 | old += size; |
1162 | } | 1162 | } |
1163 | 1163 | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 34510f441975..e4cdc1ebe0fb 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -33,11 +33,10 @@ static struct sample_data synth_sample = { | |||
33 | .period = 1, | 33 | .period = 1, |
34 | }; | 34 | }; |
35 | 35 | ||
36 | static pid_t event__synthesize_comm(pid_t pid, int full, | 36 | static pid_t event__synthesize_comm(event_t *event, pid_t pid, int full, |
37 | event__handler_t process, | 37 | event__handler_t process, |
38 | struct perf_session *session) | 38 | struct perf_session *session) |
39 | { | 39 | { |
40 | event_t ev; | ||
41 | char filename[PATH_MAX]; | 40 | char filename[PATH_MAX]; |
42 | char bf[BUFSIZ]; | 41 | char bf[BUFSIZ]; |
43 | FILE *fp; | 42 | FILE *fp; |
@@ -58,34 +57,39 @@ out_race: | |||
58 | return 0; | 57 | return 0; |
59 | } | 58 | } |
60 | 59 | ||
61 | memset(&ev.comm, 0, sizeof(ev.comm)); | 60 | memset(&event->comm, 0, sizeof(event->comm)); |
62 | while (!ev.comm.comm[0] || !ev.comm.pid) { | 61 | |
63 | if (fgets(bf, sizeof(bf), fp) == NULL) | 62 | while (!event->comm.comm[0] || !event->comm.pid) { |
64 | goto out_failure; | 63 | if (fgets(bf, sizeof(bf), fp) == NULL) { |
64 | pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); | ||
65 | goto out; | ||
66 | } | ||
65 | 67 | ||
66 | if (memcmp(bf, "Name:", 5) == 0) { | 68 | if (memcmp(bf, "Name:", 5) == 0) { |
67 | char *name = bf + 5; | 69 | char *name = bf + 5; |
68 | while (*name && isspace(*name)) | 70 | while (*name && isspace(*name)) |
69 | ++name; | 71 | ++name; |
70 | size = strlen(name) - 1; | 72 | size = strlen(name) - 1; |
71 | memcpy(ev.comm.comm, name, size++); | 73 | memcpy(event->comm.comm, name, size++); |
72 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | 74 | } else if (memcmp(bf, "Tgid:", 5) == 0) { |
73 | char *tgids = bf + 5; | 75 | char *tgids = bf + 5; |
74 | while (*tgids && isspace(*tgids)) | 76 | while (*tgids && isspace(*tgids)) |
75 | ++tgids; | 77 | ++tgids; |
76 | tgid = ev.comm.pid = atoi(tgids); | 78 | tgid = event->comm.pid = atoi(tgids); |
77 | } | 79 | } |
78 | } | 80 | } |
79 | 81 | ||
80 | ev.comm.header.type = PERF_RECORD_COMM; | 82 | event->comm.header.type = PERF_RECORD_COMM; |
81 | size = ALIGN(size, sizeof(u64)); | 83 | size = ALIGN(size, sizeof(u64)); |
82 | ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size); | 84 | memset(event->comm.comm + size, 0, session->id_hdr_size); |
83 | 85 | event->comm.header.size = (sizeof(event->comm) - | |
86 | (sizeof(event->comm.comm) - size) + | ||
87 | session->id_hdr_size); | ||
84 | if (!full) { | 88 | if (!full) { |
85 | ev.comm.tid = pid; | 89 | event->comm.tid = pid; |
86 | 90 | ||
87 | process(&ev, &synth_sample, session); | 91 | process(event, &synth_sample, session); |
88 | goto out_fclose; | 92 | goto out; |
89 | } | 93 | } |
90 | 94 | ||
91 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | 95 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); |
@@ -100,22 +104,19 @@ out_race: | |||
100 | if (*end) | 104 | if (*end) |
101 | continue; | 105 | continue; |
102 | 106 | ||
103 | ev.comm.tid = pid; | 107 | event->comm.tid = pid; |
104 | 108 | ||
105 | process(&ev, &synth_sample, session); | 109 | process(event, &synth_sample, session); |
106 | } | 110 | } |
107 | closedir(tasks); | ||
108 | 111 | ||
109 | out_fclose: | 112 | closedir(tasks); |
113 | out: | ||
110 | fclose(fp); | 114 | fclose(fp); |
111 | return tgid; | ||
112 | 115 | ||
113 | out_failure: | 116 | return tgid; |
114 | pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); | ||
115 | return -1; | ||
116 | } | 117 | } |
117 | 118 | ||
118 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | 119 | static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid, |
119 | event__handler_t process, | 120 | event__handler_t process, |
120 | struct perf_session *session) | 121 | struct perf_session *session) |
121 | { | 122 | { |
@@ -133,29 +134,25 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
133 | return -1; | 134 | return -1; |
134 | } | 135 | } |
135 | 136 | ||
137 | event->header.type = PERF_RECORD_MMAP; | ||
138 | /* | ||
139 | * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c | ||
140 | */ | ||
141 | event->header.misc = PERF_RECORD_MISC_USER; | ||
142 | |||
136 | while (1) { | 143 | while (1) { |
137 | char bf[BUFSIZ], *pbf = bf; | 144 | char bf[BUFSIZ], *pbf = bf; |
138 | event_t ev = { | ||
139 | .header = { | ||
140 | .type = PERF_RECORD_MMAP, | ||
141 | /* | ||
142 | * Just like the kernel, see __perf_event_mmap | ||
143 | * in kernel/perf_event.c | ||
144 | */ | ||
145 | .misc = PERF_RECORD_MISC_USER, | ||
146 | }, | ||
147 | }; | ||
148 | int n; | 145 | int n; |
149 | size_t size; | 146 | size_t size; |
150 | if (fgets(bf, sizeof(bf), fp) == NULL) | 147 | if (fgets(bf, sizeof(bf), fp) == NULL) |
151 | break; | 148 | break; |
152 | 149 | ||
153 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 150 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
154 | n = hex2u64(pbf, &ev.mmap.start); | 151 | n = hex2u64(pbf, &event->mmap.start); |
155 | if (n < 0) | 152 | if (n < 0) |
156 | continue; | 153 | continue; |
157 | pbf += n + 1; | 154 | pbf += n + 1; |
158 | n = hex2u64(pbf, &ev.mmap.len); | 155 | n = hex2u64(pbf, &event->mmap.len); |
159 | if (n < 0) | 156 | if (n < 0) |
160 | continue; | 157 | continue; |
161 | pbf += n + 3; | 158 | pbf += n + 3; |
@@ -170,19 +167,21 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
170 | continue; | 167 | continue; |
171 | 168 | ||
172 | pbf += 3; | 169 | pbf += 3; |
173 | n = hex2u64(pbf, &ev.mmap.pgoff); | 170 | n = hex2u64(pbf, &event->mmap.pgoff); |
174 | 171 | ||
175 | size = strlen(execname); | 172 | size = strlen(execname); |
176 | execname[size - 1] = '\0'; /* Remove \n */ | 173 | execname[size - 1] = '\0'; /* Remove \n */ |
177 | memcpy(ev.mmap.filename, execname, size); | 174 | memcpy(event->mmap.filename, execname, size); |
178 | size = ALIGN(size, sizeof(u64)); | 175 | size = ALIGN(size, sizeof(u64)); |
179 | ev.mmap.len -= ev.mmap.start; | 176 | event->mmap.len -= event->mmap.start; |
180 | ev.mmap.header.size = (sizeof(ev.mmap) - | 177 | event->mmap.header.size = (sizeof(event->mmap) - |
181 | (sizeof(ev.mmap.filename) - size)); | 178 | (sizeof(event->mmap.filename) - size)); |
182 | ev.mmap.pid = tgid; | 179 | memset(event->mmap.filename + size, 0, session->id_hdr_size); |
183 | ev.mmap.tid = pid; | 180 | event->mmap.header.size += session->id_hdr_size; |
184 | 181 | event->mmap.pid = tgid; | |
185 | process(&ev, &synth_sample, session); | 182 | event->mmap.tid = pid; |
183 | |||
184 | process(event, &synth_sample, session); | ||
186 | } | 185 | } |
187 | } | 186 | } |
188 | 187 | ||
@@ -196,20 +195,27 @@ int event__synthesize_modules(event__handler_t process, | |||
196 | { | 195 | { |
197 | struct rb_node *nd; | 196 | struct rb_node *nd; |
198 | struct map_groups *kmaps = &machine->kmaps; | 197 | struct map_groups *kmaps = &machine->kmaps; |
199 | u16 misc; | 198 | event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); |
199 | |||
200 | if (event == NULL) { | ||
201 | pr_debug("Not enough memory synthesizing mmap event " | ||
202 | "for kernel modules\n"); | ||
203 | return -1; | ||
204 | } | ||
205 | |||
206 | event->header.type = PERF_RECORD_MMAP; | ||
200 | 207 | ||
201 | /* | 208 | /* |
202 | * kernel uses 0 for user space maps, see kernel/perf_event.c | 209 | * kernel uses 0 for user space maps, see kernel/perf_event.c |
203 | * __perf_event_mmap | 210 | * __perf_event_mmap |
204 | */ | 211 | */ |
205 | if (machine__is_host(machine)) | 212 | if (machine__is_host(machine)) |
206 | misc = PERF_RECORD_MISC_KERNEL; | 213 | event->header.misc = PERF_RECORD_MISC_KERNEL; |
207 | else | 214 | else |
208 | misc = PERF_RECORD_MISC_GUEST_KERNEL; | 215 | event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; |
209 | 216 | ||
210 | for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); | 217 | for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); |
211 | nd; nd = rb_next(nd)) { | 218 | nd; nd = rb_next(nd)) { |
212 | event_t ev; | ||
213 | size_t size; | 219 | size_t size; |
214 | struct map *pos = rb_entry(nd, struct map, rb_node); | 220 | struct map *pos = rb_entry(nd, struct map, rb_node); |
215 | 221 | ||
@@ -217,39 +223,78 @@ int event__synthesize_modules(event__handler_t process, | |||
217 | continue; | 223 | continue; |
218 | 224 | ||
219 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); | 225 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); |
220 | memset(&ev, 0, sizeof(ev)); | 226 | event->mmap.header.type = PERF_RECORD_MMAP; |
221 | ev.mmap.header.misc = misc; | 227 | event->mmap.header.size = (sizeof(event->mmap) - |
222 | ev.mmap.header.type = PERF_RECORD_MMAP; | 228 | (sizeof(event->mmap.filename) - size)); |
223 | ev.mmap.header.size = (sizeof(ev.mmap) - | 229 | memset(event->mmap.filename + size, 0, session->id_hdr_size); |
224 | (sizeof(ev.mmap.filename) - size)); | 230 | event->mmap.header.size += session->id_hdr_size; |
225 | ev.mmap.start = pos->start; | 231 | event->mmap.start = pos->start; |
226 | ev.mmap.len = pos->end - pos->start; | 232 | event->mmap.len = pos->end - pos->start; |
227 | ev.mmap.pid = machine->pid; | 233 | event->mmap.pid = machine->pid; |
228 | 234 | ||
229 | memcpy(ev.mmap.filename, pos->dso->long_name, | 235 | memcpy(event->mmap.filename, pos->dso->long_name, |
230 | pos->dso->long_name_len + 1); | 236 | pos->dso->long_name_len + 1); |
231 | process(&ev, &synth_sample, session); | 237 | process(event, &synth_sample, session); |
232 | } | 238 | } |
233 | 239 | ||
240 | free(event); | ||
234 | return 0; | 241 | return 0; |
235 | } | 242 | } |
236 | 243 | ||
237 | int event__synthesize_thread(pid_t pid, event__handler_t process, | 244 | static int __event__synthesize_thread(event_t *comm_event, event_t *mmap_event, |
238 | struct perf_session *session) | 245 | pid_t pid, event__handler_t process, |
246 | struct perf_session *session) | ||
239 | { | 247 | { |
240 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); | 248 | pid_t tgid = event__synthesize_comm(comm_event, pid, 1, process, |
249 | session); | ||
241 | if (tgid == -1) | 250 | if (tgid == -1) |
242 | return -1; | 251 | return -1; |
243 | return event__synthesize_mmap_events(pid, tgid, process, session); | 252 | return event__synthesize_mmap_events(mmap_event, pid, tgid, |
253 | process, session); | ||
244 | } | 254 | } |
245 | 255 | ||
246 | void event__synthesize_threads(event__handler_t process, | 256 | int event__synthesize_thread(pid_t pid, event__handler_t process, |
247 | struct perf_session *session) | 257 | struct perf_session *session) |
258 | { | ||
259 | event_t *comm_event, *mmap_event; | ||
260 | int err = -1; | ||
261 | |||
262 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); | ||
263 | if (comm_event == NULL) | ||
264 | goto out; | ||
265 | |||
266 | mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size); | ||
267 | if (mmap_event == NULL) | ||
268 | goto out_free_comm; | ||
269 | |||
270 | err = __event__synthesize_thread(comm_event, mmap_event, pid, | ||
271 | process, session); | ||
272 | free(mmap_event); | ||
273 | out_free_comm: | ||
274 | free(comm_event); | ||
275 | out: | ||
276 | return err; | ||
277 | } | ||
278 | |||
279 | int event__synthesize_threads(event__handler_t process, | ||
280 | struct perf_session *session) | ||
248 | { | 281 | { |
249 | DIR *proc; | 282 | DIR *proc; |
250 | struct dirent dirent, *next; | 283 | struct dirent dirent, *next; |
284 | event_t *comm_event, *mmap_event; | ||
285 | int err = -1; | ||
286 | |||
287 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); | ||
288 | if (comm_event == NULL) | ||
289 | goto out; | ||
290 | |||
291 | mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size); | ||
292 | if (mmap_event == NULL) | ||
293 | goto out_free_comm; | ||
251 | 294 | ||
252 | proc = opendir("/proc"); | 295 | proc = opendir("/proc"); |
296 | if (proc == NULL) | ||
297 | goto out_free_mmap; | ||
253 | 298 | ||
254 | while (!readdir_r(proc, &dirent, &next) && next) { | 299 | while (!readdir_r(proc, &dirent, &next) && next) { |
255 | char *end; | 300 | char *end; |
@@ -258,10 +303,18 @@ void event__synthesize_threads(event__handler_t process, | |||
258 | if (*end) /* only interested in proper numerical dirents */ | 303 | if (*end) /* only interested in proper numerical dirents */ |
259 | continue; | 304 | continue; |
260 | 305 | ||
261 | event__synthesize_thread(pid, process, session); | 306 | __event__synthesize_thread(comm_event, mmap_event, pid, |
307 | process, session); | ||
262 | } | 308 | } |
263 | 309 | ||
264 | closedir(proc); | 310 | closedir(proc); |
311 | err = 0; | ||
312 | out_free_mmap: | ||
313 | free(mmap_event); | ||
314 | out_free_comm: | ||
315 | free(comm_event); | ||
316 | out: | ||
317 | return err; | ||
265 | } | 318 | } |
266 | 319 | ||
267 | struct process_symbol_args { | 320 | struct process_symbol_args { |
@@ -295,18 +348,20 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
295 | char path[PATH_MAX]; | 348 | char path[PATH_MAX]; |
296 | char name_buff[PATH_MAX]; | 349 | char name_buff[PATH_MAX]; |
297 | struct map *map; | 350 | struct map *map; |
298 | 351 | int err; | |
299 | event_t ev = { | ||
300 | .header = { | ||
301 | .type = PERF_RECORD_MMAP, | ||
302 | }, | ||
303 | }; | ||
304 | /* | 352 | /* |
305 | * We should get this from /sys/kernel/sections/.text, but till that is | 353 | * We should get this from /sys/kernel/sections/.text, but till that is |
306 | * available use this, and after it is use this as a fallback for older | 354 | * available use this, and after it is use this as a fallback for older |
307 | * kernels. | 355 | * kernels. |
308 | */ | 356 | */ |
309 | struct process_symbol_args args = { .name = symbol_name, }; | 357 | struct process_symbol_args args = { .name = symbol_name, }; |
358 | event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); | ||
359 | |||
360 | if (event == NULL) { | ||
361 | pr_debug("Not enough memory synthesizing mmap event " | ||
362 | "for kernel modules\n"); | ||
363 | return -1; | ||
364 | } | ||
310 | 365 | ||
311 | mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff)); | 366 | mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff)); |
312 | if (machine__is_host(machine)) { | 367 | if (machine__is_host(machine)) { |
@@ -314,10 +369,10 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
314 | * kernel uses PERF_RECORD_MISC_USER for user space maps, | 369 | * kernel uses PERF_RECORD_MISC_USER for user space maps, |
315 | * see kernel/perf_event.c __perf_event_mmap | 370 | * see kernel/perf_event.c __perf_event_mmap |
316 | */ | 371 | */ |
317 | ev.header.misc = PERF_RECORD_MISC_KERNEL; | 372 | event->header.misc = PERF_RECORD_MISC_KERNEL; |
318 | filename = "/proc/kallsyms"; | 373 | filename = "/proc/kallsyms"; |
319 | } else { | 374 | } else { |
320 | ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; | 375 | event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; |
321 | if (machine__is_default_guest(machine)) | 376 | if (machine__is_default_guest(machine)) |
322 | filename = (char *) symbol_conf.default_guest_kallsyms; | 377 | filename = (char *) symbol_conf.default_guest_kallsyms; |
323 | else { | 378 | else { |
@@ -330,17 +385,21 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
330 | return -ENOENT; | 385 | return -ENOENT; |
331 | 386 | ||
332 | map = machine->vmlinux_maps[MAP__FUNCTION]; | 387 | map = machine->vmlinux_maps[MAP__FUNCTION]; |
333 | size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), | 388 | size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), |
334 | "%s%s", mmap_name, symbol_name) + 1; | 389 | "%s%s", mmap_name, symbol_name) + 1; |
335 | size = ALIGN(size, sizeof(u64)); | 390 | size = ALIGN(size, sizeof(u64)); |
336 | ev.mmap.header.size = (sizeof(ev.mmap) - | 391 | event->mmap.header.type = PERF_RECORD_MMAP; |
337 | (sizeof(ev.mmap.filename) - size)); | 392 | event->mmap.header.size = (sizeof(event->mmap) - |
338 | ev.mmap.pgoff = args.start; | 393 | (sizeof(event->mmap.filename) - size) + session->id_hdr_size); |
339 | ev.mmap.start = map->start; | 394 | event->mmap.pgoff = args.start; |
340 | ev.mmap.len = map->end - ev.mmap.start; | 395 | event->mmap.start = map->start; |
341 | ev.mmap.pid = machine->pid; | 396 | event->mmap.len = map->end - event->mmap.start; |
342 | 397 | event->mmap.pid = machine->pid; | |
343 | return process(&ev, &synth_sample, session); | 398 | |
399 | err = process(event, &synth_sample, session); | ||
400 | free(event); | ||
401 | |||
402 | return err; | ||
344 | } | 403 | } |
345 | 404 | ||
346 | static void thread__comm_adjust(struct thread *self, struct hists *hists) | 405 | static void thread__comm_adjust(struct thread *self, struct hists *hists) |
@@ -756,9 +815,65 @@ out_filtered: | |||
756 | return 0; | 815 | return 0; |
757 | } | 816 | } |
758 | 817 | ||
759 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data) | 818 | static int event__parse_id_sample(const event_t *event, |
819 | struct perf_session *session, | ||
820 | struct sample_data *sample) | ||
760 | { | 821 | { |
761 | const u64 *array = event->sample.array; | 822 | const u64 *array; |
823 | u64 type; | ||
824 | |||
825 | sample->cpu = sample->pid = sample->tid = -1; | ||
826 | sample->stream_id = sample->id = sample->time = -1ULL; | ||
827 | |||
828 | if (!session->sample_id_all) | ||
829 | return 0; | ||
830 | |||
831 | array = event->sample.array; | ||
832 | array += ((event->header.size - | ||
833 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
834 | type = session->sample_type; | ||
835 | |||
836 | if (type & PERF_SAMPLE_CPU) { | ||
837 | u32 *p = (u32 *)array; | ||
838 | sample->cpu = *p; | ||
839 | array--; | ||
840 | } | ||
841 | |||
842 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
843 | sample->stream_id = *array; | ||
844 | array--; | ||
845 | } | ||
846 | |||
847 | if (type & PERF_SAMPLE_ID) { | ||
848 | sample->id = *array; | ||
849 | array--; | ||
850 | } | ||
851 | |||
852 | if (type & PERF_SAMPLE_TIME) { | ||
853 | sample->time = *array; | ||
854 | array--; | ||
855 | } | ||
856 | |||
857 | if (type & PERF_SAMPLE_TID) { | ||
858 | u32 *p = (u32 *)array; | ||
859 | sample->pid = p[0]; | ||
860 | sample->tid = p[1]; | ||
861 | } | ||
862 | |||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
867 | struct sample_data *data) | ||
868 | { | ||
869 | const u64 *array; | ||
870 | u64 type; | ||
871 | |||
872 | if (event->header.type != PERF_RECORD_SAMPLE) | ||
873 | return event__parse_id_sample(event, session, data); | ||
874 | |||
875 | array = event->sample.array; | ||
876 | type = session->sample_type; | ||
762 | 877 | ||
763 | if (type & PERF_SAMPLE_IP) { | 878 | if (type & PERF_SAMPLE_IP) { |
764 | data->ip = event->ip.ip; | 879 | data->ip = event->ip.ip; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 08c400b83d57..a95ab18575ce 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -142,8 +142,8 @@ typedef int (*event__handler_t)(event_t *event, struct sample_data *sample, | |||
142 | 142 | ||
143 | int event__synthesize_thread(pid_t pid, event__handler_t process, | 143 | int event__synthesize_thread(pid_t pid, event__handler_t process, |
144 | struct perf_session *session); | 144 | struct perf_session *session); |
145 | void event__synthesize_threads(event__handler_t process, | 145 | int event__synthesize_threads(event__handler_t process, |
146 | struct perf_session *session); | 146 | struct perf_session *session); |
147 | int event__synthesize_kernel_mmap(event__handler_t process, | 147 | int event__synthesize_kernel_mmap(event__handler_t process, |
148 | struct perf_session *session, | 148 | struct perf_session *session, |
149 | struct machine *machine, | 149 | struct machine *machine, |
@@ -168,7 +168,8 @@ struct addr_location; | |||
168 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 168 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
169 | struct addr_location *al, struct sample_data *data, | 169 | struct addr_location *al, struct sample_data *data, |
170 | symbol_filter_t filter); | 170 | symbol_filter_t filter); |
171 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data); | 171 | int event__parse_sample(const event_t *event, struct perf_session *session, |
172 | struct sample_data *sample); | ||
172 | 173 | ||
173 | extern const char *event__name[]; | 174 | extern const char *event__name[]; |
174 | 175 | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index fe652f3b0aa7..073f0e1c7123 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -946,6 +946,24 @@ u64 perf_header__sample_type(struct perf_header *header) | |||
946 | return type; | 946 | return type; |
947 | } | 947 | } |
948 | 948 | ||
949 | bool perf_header__sample_id_all(const struct perf_header *header) | ||
950 | { | ||
951 | bool value = false, first = true; | ||
952 | int i; | ||
953 | |||
954 | for (i = 0; i < header->attrs; i++) { | ||
955 | struct perf_header_attr *attr = header->attr[i]; | ||
956 | |||
957 | if (first) { | ||
958 | value = attr->attr.sample_id_all; | ||
959 | first = false; | ||
960 | } else if (value != attr->attr.sample_id_all) | ||
961 | die("non matching sample_id_all"); | ||
962 | } | ||
963 | |||
964 | return value; | ||
965 | } | ||
966 | |||
949 | struct perf_event_attr * | 967 | struct perf_event_attr * |
950 | perf_header__find_attr(u64 id, struct perf_header *header) | 968 | perf_header__find_attr(u64 id, struct perf_header *header) |
951 | { | 969 | { |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index ed550bffd655..6335965e1f93 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -81,6 +81,7 @@ void perf_header_attr__delete(struct perf_header_attr *self); | |||
81 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | 81 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); |
82 | 82 | ||
83 | u64 perf_header__sample_type(struct perf_header *header); | 83 | u64 perf_header__sample_type(struct perf_header *header); |
84 | bool perf_header__sample_id_all(const struct perf_header *header); | ||
84 | struct perf_event_attr * | 85 | struct perf_event_attr * |
85 | perf_header__find_attr(u64 id, struct perf_header *header); | 86 | perf_header__find_attr(u64 id, struct perf_header *header); |
86 | void perf_header__set_feat(struct perf_header *self, int feat); | 87 | void perf_header__set_feat(struct perf_header *self, int feat); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 08ec018966a8..5c756609104e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -65,9 +65,37 @@ out_close: | |||
65 | return -1; | 65 | return -1; |
66 | } | 66 | } |
67 | 67 | ||
68 | void perf_session__update_sample_type(struct perf_session *self) | 68 | static void perf_session__id_header_size(struct perf_session *session) |
69 | { | 69 | { |
70 | self->sample_type = perf_header__sample_type(&self->header); | 70 | struct sample_data *data; |
71 | u64 sample_type = session->sample_type; | ||
72 | u16 size = 0; | ||
73 | |||
74 | if (!session->sample_id_all) | ||
75 | goto out; | ||
76 | |||
77 | if (sample_type & PERF_SAMPLE_TID) | ||
78 | size += sizeof(data->tid) * 2; | ||
79 | |||
80 | if (sample_type & PERF_SAMPLE_TIME) | ||
81 | size += sizeof(data->time); | ||
82 | |||
83 | if (sample_type & PERF_SAMPLE_ID) | ||
84 | size += sizeof(data->id); | ||
85 | |||
86 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
87 | size += sizeof(data->stream_id); | ||
88 | |||
89 | if (sample_type & PERF_SAMPLE_CPU) | ||
90 | size += sizeof(data->cpu) * 2; | ||
91 | out: | ||
92 | session->id_hdr_size = size; | ||
93 | } | ||
94 | |||
95 | void perf_session__set_sample_id_all(struct perf_session *session, bool value) | ||
96 | { | ||
97 | session->sample_id_all = value; | ||
98 | perf_session__id_header_size(session); | ||
71 | } | 99 | } |
72 | 100 | ||
73 | void perf_session__set_sample_type(struct perf_session *session, u64 type) | 101 | void perf_session__set_sample_type(struct perf_session *session, u64 type) |
@@ -75,6 +103,13 @@ void perf_session__set_sample_type(struct perf_session *session, u64 type) | |||
75 | session->sample_type = type; | 103 | session->sample_type = type; |
76 | } | 104 | } |
77 | 105 | ||
106 | void perf_session__update_sample_type(struct perf_session *self) | ||
107 | { | ||
108 | self->sample_type = perf_header__sample_type(&self->header); | ||
109 | self->sample_id_all = perf_header__sample_id_all(&self->header); | ||
110 | perf_session__id_header_size(self); | ||
111 | } | ||
112 | |||
78 | int perf_session__create_kernel_maps(struct perf_session *self) | 113 | int perf_session__create_kernel_maps(struct perf_session *self) |
79 | { | 114 | { |
80 | int ret = machine__create_kernel_maps(&self->host_machine); | 115 | int ret = machine__create_kernel_maps(&self->host_machine); |
@@ -443,7 +478,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
443 | if (iter->timestamp > limit) | 478 | if (iter->timestamp > limit) |
444 | break; | 479 | break; |
445 | 480 | ||
446 | event__parse_sample(iter->event, s->sample_type, &sample); | 481 | event__parse_sample(iter->event, s, &sample); |
447 | ops->sample(iter->event, &sample, s); | 482 | ops->sample(iter->event, &sample, s); |
448 | 483 | ||
449 | os->last_flush = iter->timestamp; | 484 | os->last_flush = iter->timestamp; |
@@ -618,6 +653,23 @@ static void callchain__dump(struct sample_data *sample) | |||
618 | printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]); | 653 | printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]); |
619 | } | 654 | } |
620 | 655 | ||
656 | static void perf_session__print_tstamp(struct perf_session *session, | ||
657 | event_t *event, | ||
658 | struct sample_data *sample) | ||
659 | { | ||
660 | if (event->header.type != PERF_RECORD_SAMPLE && | ||
661 | !session->sample_id_all) { | ||
662 | fputs("-1 -1 ", stdout); | ||
663 | return; | ||
664 | } | ||
665 | |||
666 | if ((session->sample_type & PERF_SAMPLE_CPU)) | ||
667 | printf("%u ", sample->cpu); | ||
668 | |||
669 | if (session->sample_type & PERF_SAMPLE_TIME) | ||
670 | printf("%Lu ", sample->time); | ||
671 | } | ||
672 | |||
621 | static int perf_session__process_event(struct perf_session *self, | 673 | static int perf_session__process_event(struct perf_session *self, |
622 | event_t *event, | 674 | event_t *event, |
623 | struct perf_event_ops *ops, | 675 | struct perf_event_ops *ops, |
@@ -630,8 +682,12 @@ static int perf_session__process_event(struct perf_session *self, | |||
630 | if (self->header.needs_swap && event__swap_ops[event->header.type]) | 682 | if (self->header.needs_swap && event__swap_ops[event->header.type]) |
631 | event__swap_ops[event->header.type](event); | 683 | event__swap_ops[event->header.type](event); |
632 | 684 | ||
633 | if (event->header.type == PERF_RECORD_SAMPLE) | 685 | if (event->header.type >= PERF_RECORD_MMAP && |
634 | event__parse_sample(event, self->sample_type, &sample); | 686 | event->header.type <= PERF_RECORD_SAMPLE) { |
687 | event__parse_sample(event, self, &sample); | ||
688 | if (dump_trace) | ||
689 | perf_session__print_tstamp(self, event, &sample); | ||
690 | } | ||
635 | 691 | ||
636 | if (event->header.type < PERF_RECORD_HEADER_MAX) { | 692 | if (event->header.type < PERF_RECORD_HEADER_MAX) { |
637 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | 693 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 4578f86a6209..ac36f99f14af 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -46,6 +46,8 @@ struct perf_session { | |||
46 | int fd; | 46 | int fd; |
47 | bool fd_pipe; | 47 | bool fd_pipe; |
48 | bool repipe; | 48 | bool repipe; |
49 | bool sample_id_all; | ||
50 | u16 id_hdr_size; | ||
49 | int cwdlen; | 51 | int cwdlen; |
50 | char *cwd; | 52 | char *cwd; |
51 | struct ordered_samples ordered_samples; | 53 | struct ordered_samples ordered_samples; |
@@ -106,6 +108,7 @@ int perf_session__create_kernel_maps(struct perf_session *self); | |||
106 | 108 | ||
107 | int do_read(int fd, void *buf, size_t size); | 109 | int do_read(int fd, void *buf, size_t size); |
108 | void perf_session__update_sample_type(struct perf_session *self); | 110 | void perf_session__update_sample_type(struct perf_session *self); |
111 | void perf_session__set_sample_id_all(struct perf_session *session, bool value); | ||
109 | void perf_session__set_sample_type(struct perf_session *session, u64 type); | 112 | void perf_session__set_sample_type(struct perf_session *session, u64 type); |
110 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); | 113 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); |
111 | 114 | ||