diff options
Diffstat (limited to 'tools/perf')
| -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 | ||
