1 #include "builtin.h" 2 3 #include "util/util.h" 4 #include "util/cache.h" 5 #include "util/symbol.h" 6 #include "util/thread.h" 7 #include "util/header.h" 8 9 #include "util/parse-options.h" 10 11 #include "perf.h" 12 #include "util/debug.h" 13 14 #include "util/trace-event.h" 15 16 static char const *input_name = "perf.data"; 17 static int input; 18 static unsigned long page_size; 19 static unsigned long mmap_window = 32; 20 21 static unsigned long total = 0; 22 static unsigned long total_comm = 0; 23 24 static struct rb_root threads; 25 static struct thread *last_match; 26 27 static struct perf_header *header; 28 static u64 sample_type; 29 30 31 static int 32 process_comm_event(event_t *event, unsigned long offset, unsigned long head) 33 { 34 struct thread *thread; 35 36 thread = threads__findnew(event->comm.pid, &threads, &last_match); 37 38 dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n", 39 (void *)(offset + head), 40 (void *)(long)(event->header.size), 41 event->comm.comm, event->comm.pid); 42 43 if (thread == NULL || 44 thread__set_comm(thread, event->comm.comm)) { 45 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); 46 return -1; 47 } 48 total_comm++; 49 50 return 0; 51 } 52 53 static int 54 process_sample_event(event_t *event, unsigned long offset, unsigned long head) 55 { 56 char level; 57 int show = 0; 58 struct dso *dso = NULL; 59 struct thread *thread; 60 u64 ip = event->ip.ip; 61 u64 timestamp = -1; 62 u32 cpu = -1; 63 u64 period = 1; 64 void *more_data = event->ip.__more_data; 65 int cpumode; 66 67 thread = threads__findnew(event->ip.pid, &threads, &last_match); 68 69 if (sample_type & PERF_SAMPLE_TIME) { 70 timestamp = *(u64 *)more_data; 71 more_data += sizeof(u64); 72 } 73 74 if (sample_type & PERF_SAMPLE_CPU) { 75 cpu = *(u32 *)more_data; 76 more_data += sizeof(u32); 77 more_data += sizeof(u32); /* reserved */ 78 } 79 80 if (sample_type & PERF_SAMPLE_PERIOD) { 81 period = *(u64 *)more_data; 82 more_data += sizeof(u64); 83 } 84 85 dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", 86 (void *)(offset + head), 87 (void *)(long)(event->header.size), 88 event->header.misc, 89 event->ip.pid, event->ip.tid, 90 (void *)(long)ip, 91 (long long)period); 92 93 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); 94 95 if (thread == NULL) { 96 eprintf("problem processing %d event, skipping it.\n", 97 event->header.type); 98 return -1; 99 } 100 101 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 102 103 if (cpumode == PERF_RECORD_MISC_KERNEL) { 104 show = SHOW_KERNEL; 105 level = 'k'; 106 107 dso = kernel_dso; 108 109 dump_printf(" ...... dso: %s\n", dso->name); 110 111 } else if (cpumode == PERF_RECORD_MISC_USER) { 112 113 show = SHOW_USER; 114 level = '.'; 115 116 } else { 117 show = SHOW_HV; 118 level = 'H'; 119 120 dso = hypervisor_dso; 121 122 dump_printf(" ...... dso: [hypervisor]\n"); 123 } 124 125 if (sample_type & PERF_SAMPLE_RAW) { 126 struct { 127 u32 size; 128 char data[0]; 129 } *raw = more_data; 130 131 /* 132 * FIXME: better resolve from pid from the struct trace_entry 133 * field, although it should be the same than this perf 134 * event pid 135 */ 136 print_event(cpu, raw->data, raw->size, timestamp, thread->comm); 137 } 138 total += period; 139 140 return 0; 141 } 142 143 static int 144 process_event(event_t *event, unsigned long offset, unsigned long head) 145 { 146 trace_event(event); 147 148 switch (event->header.type) { 149 case PERF_RECORD_MMAP ... PERF_RECORD_LOST: 150 return 0; 151 152 case PERF_RECORD_COMM: 153 return process_comm_event(event, offset, head); 154 155 case PERF_RECORD_EXIT ... PERF_RECORD_READ: 156 return 0; 157 158 case PERF_RECORD_SAMPLE: 159 return process_sample_event(event, offset, head); 160 161 case PERF_RECORD_MAX: 162 default: 163 return -1; 164 } 165 166 return 0; 167 } 168 169 static int __cmd_trace(void) 170 { 171 int ret, rc = EXIT_FAILURE; 172 unsigned long offset = 0; 173 unsigned long head = 0; 174 struct stat perf_stat; 175 event_t *event; 176 uint32_t size; 177 char *buf; 178 179 trace_report(); 180 register_idle_thread(&threads, &last_match); 181 182 input = open(input_name, O_RDONLY); 183 if (input < 0) { 184 perror("failed to open file"); 185 exit(-1); 186 } 187 188 ret = fstat(input, &perf_stat); 189 if (ret < 0) { 190 perror("failed to stat file"); 191 exit(-1); 192 } 193 194 if (!perf_stat.st_size) { 195 fprintf(stderr, "zero-sized file, nothing to do!\n"); 196 exit(0); 197 } 198 header = perf_header__read(input); 199 head = header->data_offset; 200 sample_type = perf_header__sample_type(header); 201 202 if (!(sample_type & PERF_SAMPLE_RAW)) 203 die("No trace sample to read. Did you call perf record " 204 "without -R?"); 205 206 if (load_kernel() < 0) { 207 perror("failed to load kernel symbols"); 208 return EXIT_FAILURE; 209 } 210 211 remap: 212 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, 213 MAP_SHARED, input, offset); 214 if (buf == MAP_FAILED) { 215 perror("failed to mmap file"); 216 exit(-1); 217 } 218 219 more: 220 event = (event_t *)(buf + head); 221 222 size = event->header.size; 223 if (!size) 224 size = 8; 225 226 if (head + event->header.size >= page_size * mmap_window) { 227 unsigned long shift = page_size * (head / page_size); 228 int res; 229 230 res = munmap(buf, page_size * mmap_window); 231 assert(res == 0); 232 233 offset += shift; 234 head -= shift; 235 goto remap; 236 } 237 238 size = event->header.size; 239 240 241 if (!size || process_event(event, offset, head) < 0) { 242 243 /* 244 * assume we lost track of the stream, check alignment, and 245 * increment a single u64 in the hope to catch on again 'soon'. 246 */ 247 248 if (unlikely(head & 7)) 249 head &= ~7ULL; 250 251 size = 8; 252 } 253 254 head += size; 255 256 if (offset + head < (unsigned long)perf_stat.st_size) 257 goto more; 258 259 rc = EXIT_SUCCESS; 260 close(input); 261 262 return rc; 263 } 264 265 static const char * const annotate_usage[] = { 266 "perf trace [<options>] <command>", 267 NULL 268 }; 269 270 static const struct option options[] = { 271 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 272 "dump raw trace in ASCII"), 273 OPT_BOOLEAN('v', "verbose", &verbose, 274 "be more verbose (show symbol address, etc)"), 275 OPT_END() 276 }; 277 278 int cmd_trace(int argc, const char **argv, const char *prefix __used) 279 { 280 symbol__init(); 281 page_size = getpagesize(); 282 283 argc = parse_options(argc, argv, options, annotate_usage, 0); 284 if (argc) { 285 /* 286 * Special case: if there's an argument left then assume tha 287 * it's a symbol filter: 288 */ 289 if (argc > 1) 290 usage_with_options(annotate_usage, options); 291 } 292 293 294 setup_pager(); 295 296 return __cmd_trace(); 297 } 298