diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
| -rw-r--r-- | tools/perf/util/annotate.c | 303 |
1 files changed, 275 insertions, 28 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 6b4146b40a20..8069dfb5ba77 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
| @@ -18,6 +18,17 @@ | |||
| 18 | 18 | ||
| 19 | const char *disassembler_style; | 19 | const char *disassembler_style; |
| 20 | 20 | ||
| 21 | static struct ins *ins__find(const char *name); | ||
| 22 | static int disasm_line__parse(char *line, char **namep, char **rawp); | ||
| 23 | |||
| 24 | static void ins__delete(struct ins_operands *ops) | ||
| 25 | { | ||
| 26 | free(ops->source.raw); | ||
| 27 | free(ops->source.name); | ||
| 28 | free(ops->target.raw); | ||
| 29 | free(ops->target.name); | ||
| 30 | } | ||
| 31 | |||
| 21 | static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, | 32 | static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, |
| 22 | struct ins_operands *ops) | 33 | struct ins_operands *ops) |
| 23 | { | 34 | { |
| @@ -56,6 +67,12 @@ static int call__parse(struct ins_operands *ops) | |||
| 56 | return ops->target.name == NULL ? -1 : 0; | 67 | return ops->target.name == NULL ? -1 : 0; |
| 57 | 68 | ||
| 58 | indirect_call: | 69 | indirect_call: |
| 70 | tok = strchr(endptr, '('); | ||
| 71 | if (tok != NULL) { | ||
| 72 | ops->target.addr = 0; | ||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 59 | tok = strchr(endptr, '*'); | 76 | tok = strchr(endptr, '*'); |
| 60 | if (tok == NULL) | 77 | if (tok == NULL) |
| 61 | return -1; | 78 | return -1; |
| @@ -70,6 +87,9 @@ static int call__scnprintf(struct ins *ins, char *bf, size_t size, | |||
| 70 | if (ops->target.name) | 87 | if (ops->target.name) |
| 71 | return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name); | 88 | return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name); |
| 72 | 89 | ||
| 90 | if (ops->target.addr == 0) | ||
| 91 | return ins__raw_scnprintf(ins, bf, size, ops); | ||
| 92 | |||
| 73 | return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr); | 93 | return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr); |
| 74 | } | 94 | } |
| 75 | 95 | ||
| @@ -113,6 +133,185 @@ bool ins__is_jump(const struct ins *ins) | |||
| 113 | return ins->ops == &jump_ops; | 133 | return ins->ops == &jump_ops; |
| 114 | } | 134 | } |
| 115 | 135 | ||
| 136 | static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) | ||
| 137 | { | ||
| 138 | char *endptr, *name, *t; | ||
| 139 | |||
| 140 | if (strstr(raw, "(%rip)") == NULL) | ||
| 141 | return 0; | ||
| 142 | |||
| 143 | *addrp = strtoull(comment, &endptr, 16); | ||
| 144 | name = strchr(endptr, '<'); | ||
| 145 | if (name == NULL) | ||
| 146 | return -1; | ||
| 147 | |||
| 148 | name++; | ||
| 149 | |||
| 150 | t = strchr(name, '>'); | ||
| 151 | if (t == NULL) | ||
| 152 | return 0; | ||
| 153 | |||
| 154 | *t = '\0'; | ||
| 155 | *namep = strdup(name); | ||
| 156 | *t = '>'; | ||
| 157 | |||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static int lock__parse(struct ins_operands *ops) | ||
| 162 | { | ||
| 163 | char *name; | ||
| 164 | |||
| 165 | ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); | ||
| 166 | if (ops->locked.ops == NULL) | ||
| 167 | return 0; | ||
| 168 | |||
| 169 | if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) | ||
| 170 | goto out_free_ops; | ||
| 171 | |||
| 172 | ops->locked.ins = ins__find(name); | ||
| 173 | if (ops->locked.ins == NULL) | ||
| 174 | goto out_free_ops; | ||
| 175 | |||
| 176 | if (!ops->locked.ins->ops) | ||
| 177 | return 0; | ||
| 178 | |||
| 179 | if (ops->locked.ins->ops->parse) | ||
| 180 | ops->locked.ins->ops->parse(ops->locked.ops); | ||
| 181 | |||
| 182 | return 0; | ||
| 183 | |||
| 184 | out_free_ops: | ||
| 185 | free(ops->locked.ops); | ||
| 186 | ops->locked.ops = NULL; | ||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | static int lock__scnprintf(struct ins *ins, char *bf, size_t size, | ||
| 191 | struct ins_operands *ops) | ||
| 192 | { | ||
| 193 | int printed; | ||
| 194 | |||
| 195 | if (ops->locked.ins == NULL) | ||
| 196 | return ins__raw_scnprintf(ins, bf, size, ops); | ||
| 197 | |||
| 198 | printed = scnprintf(bf, size, "%-6.6s ", ins->name); | ||
| 199 | return printed + ins__scnprintf(ops->locked.ins, bf + printed, | ||
| 200 | size - printed, ops->locked.ops); | ||
| 201 | } | ||
| 202 | |||
| 203 | static void lock__delete(struct ins_operands *ops) | ||
| 204 | { | ||
| 205 | free(ops->locked.ops); | ||
| 206 | free(ops->target.raw); | ||
| 207 | free(ops->target.name); | ||
| 208 | } | ||
| 209 | |||
| 210 | static struct ins_ops lock_ops = { | ||
| 211 | .free = lock__delete, | ||
| 212 | .parse = lock__parse, | ||
| 213 | .scnprintf = lock__scnprintf, | ||
| 214 | }; | ||
| 215 | |||
| 216 | static int mov__parse(struct ins_operands *ops) | ||
| 217 | { | ||
| 218 | char *s = strchr(ops->raw, ','), *target, *comment, prev; | ||
| 219 | |||
| 220 | if (s == NULL) | ||
| 221 | return -1; | ||
| 222 | |||
| 223 | *s = '\0'; | ||
| 224 | ops->source.raw = strdup(ops->raw); | ||
| 225 | *s = ','; | ||
| 226 | |||
| 227 | if (ops->source.raw == NULL) | ||
| 228 | return -1; | ||
| 229 | |||
| 230 | target = ++s; | ||
| 231 | |||
| 232 | while (s[0] != '\0' && !isspace(s[0])) | ||
| 233 | ++s; | ||
| 234 | prev = *s; | ||
| 235 | *s = '\0'; | ||
| 236 | |||
| 237 | ops->target.raw = strdup(target); | ||
| 238 | *s = prev; | ||
| 239 | |||
| 240 | if (ops->target.raw == NULL) | ||
| 241 | goto out_free_source; | ||
| 242 | |||
| 243 | comment = strchr(s, '#'); | ||
| 244 | if (comment == NULL) | ||
| 245 | return 0; | ||
| 246 | |||
| 247 | while (comment[0] != '\0' && isspace(comment[0])) | ||
| 248 | ++comment; | ||
| 249 | |||
| 250 | comment__symbol(ops->source.raw, comment, &ops->source.addr, &ops->source.name); | ||
| 251 | comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); | ||
| 252 | |||
| 253 | return 0; | ||
| 254 | |||
| 255 | out_free_source: | ||
| 256 | free(ops->source.raw); | ||
| 257 | ops->source.raw = NULL; | ||
| 258 | return -1; | ||
| 259 | } | ||
| 260 | |||
| 261 | static int mov__scnprintf(struct ins *ins, char *bf, size_t size, | ||
| 262 | struct ins_operands *ops) | ||
| 263 | { | ||
| 264 | return scnprintf(bf, size, "%-6.6s %s,%s", ins->name, | ||
| 265 | ops->source.name ?: ops->source.raw, | ||
| 266 | ops->target.name ?: ops->target.raw); | ||
| 267 | } | ||
| 268 | |||
| 269 | static struct ins_ops mov_ops = { | ||
| 270 | .parse = mov__parse, | ||
| 271 | .scnprintf = mov__scnprintf, | ||
| 272 | }; | ||
| 273 | |||
| 274 | static int dec__parse(struct ins_operands *ops) | ||
| 275 | { | ||
| 276 | char *target, *comment, *s, prev; | ||
| 277 | |||
| 278 | target = s = ops->raw; | ||
| 279 | |||
| 280 | while (s[0] != '\0' && !isspace(s[0])) | ||
| 281 | ++s; | ||
| 282 | prev = *s; | ||
| 283 | *s = '\0'; | ||
| 284 | |||
| 285 | ops->target.raw = strdup(target); | ||
| 286 | *s = prev; | ||
| 287 | |||
| 288 | if (ops->target.raw == NULL) | ||
| 289 | return -1; | ||
| 290 | |||
| 291 | comment = strchr(s, '#'); | ||
| 292 | if (comment == NULL) | ||
| 293 | return 0; | ||
| 294 | |||
| 295 | while (comment[0] != '\0' && isspace(comment[0])) | ||
| 296 | ++comment; | ||
| 297 | |||
| 298 | comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); | ||
| 299 | |||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | static int dec__scnprintf(struct ins *ins, char *bf, size_t size, | ||
| 304 | struct ins_operands *ops) | ||
| 305 | { | ||
| 306 | return scnprintf(bf, size, "%-6.6s %s", ins->name, | ||
| 307 | ops->target.name ?: ops->target.raw); | ||
| 308 | } | ||
| 309 | |||
| 310 | static struct ins_ops dec_ops = { | ||
| 311 | .parse = dec__parse, | ||
| 312 | .scnprintf = dec__scnprintf, | ||
| 313 | }; | ||
| 314 | |||
| 116 | static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size, | 315 | static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size, |
| 117 | struct ins_operands *ops __used) | 316 | struct ins_operands *ops __used) |
| 118 | { | 317 | { |
| @@ -127,8 +326,25 @@ static struct ins_ops nop_ops = { | |||
| 127 | * Must be sorted by name! | 326 | * Must be sorted by name! |
| 128 | */ | 327 | */ |
| 129 | static struct ins instructions[] = { | 328 | static struct ins instructions[] = { |
| 329 | { .name = "add", .ops = &mov_ops, }, | ||
| 330 | { .name = "addl", .ops = &mov_ops, }, | ||
| 331 | { .name = "addq", .ops = &mov_ops, }, | ||
| 332 | { .name = "addw", .ops = &mov_ops, }, | ||
| 333 | { .name = "and", .ops = &mov_ops, }, | ||
| 334 | { .name = "bts", .ops = &mov_ops, }, | ||
| 130 | { .name = "call", .ops = &call_ops, }, | 335 | { .name = "call", .ops = &call_ops, }, |
| 131 | { .name = "callq", .ops = &call_ops, }, | 336 | { .name = "callq", .ops = &call_ops, }, |
| 337 | { .name = "cmp", .ops = &mov_ops, }, | ||
| 338 | { .name = "cmpb", .ops = &mov_ops, }, | ||
| 339 | { .name = "cmpl", .ops = &mov_ops, }, | ||
| 340 | { .name = "cmpq", .ops = &mov_ops, }, | ||
| 341 | { .name = "cmpw", .ops = &mov_ops, }, | ||
| 342 | { .name = "cmpxch", .ops = &mov_ops, }, | ||
| 343 | { .name = "dec", .ops = &dec_ops, }, | ||
| 344 | { .name = "decl", .ops = &dec_ops, }, | ||
| 345 | { .name = "imul", .ops = &mov_ops, }, | ||
| 346 | { .name = "inc", .ops = &dec_ops, }, | ||
| 347 | { .name = "incl", .ops = &dec_ops, }, | ||
| 132 | { .name = "ja", .ops = &jump_ops, }, | 348 | { .name = "ja", .ops = &jump_ops, }, |
| 133 | { .name = "jae", .ops = &jump_ops, }, | 349 | { .name = "jae", .ops = &jump_ops, }, |
| 134 | { .name = "jb", .ops = &jump_ops, }, | 350 | { .name = "jb", .ops = &jump_ops, }, |
| @@ -164,9 +380,25 @@ static struct ins instructions[] = { | |||
| 164 | { .name = "jrcxz", .ops = &jump_ops, }, | 380 | { .name = "jrcxz", .ops = &jump_ops, }, |
| 165 | { .name = "js", .ops = &jump_ops, }, | 381 | { .name = "js", .ops = &jump_ops, }, |
| 166 | { .name = "jz", .ops = &jump_ops, }, | 382 | { .name = "jz", .ops = &jump_ops, }, |
| 383 | { .name = "lea", .ops = &mov_ops, }, | ||
| 384 | { .name = "lock", .ops = &lock_ops, }, | ||
| 385 | { .name = "mov", .ops = &mov_ops, }, | ||
| 386 | { .name = "movb", .ops = &mov_ops, }, | ||
| 387 | { .name = "movdqa",.ops = &mov_ops, }, | ||
| 388 | { .name = "movl", .ops = &mov_ops, }, | ||
| 389 | { .name = "movq", .ops = &mov_ops, }, | ||
| 390 | { .name = "movslq", .ops = &mov_ops, }, | ||
| 391 | { .name = "movzbl", .ops = &mov_ops, }, | ||
| 392 | { .name = "movzwl", .ops = &mov_ops, }, | ||
| 167 | { .name = "nop", .ops = &nop_ops, }, | 393 | { .name = "nop", .ops = &nop_ops, }, |
| 168 | { .name = "nopl", .ops = &nop_ops, }, | 394 | { .name = "nopl", .ops = &nop_ops, }, |
| 169 | { .name = "nopw", .ops = &nop_ops, }, | 395 | { .name = "nopw", .ops = &nop_ops, }, |
| 396 | { .name = "or", .ops = &mov_ops, }, | ||
| 397 | { .name = "orl", .ops = &mov_ops, }, | ||
| 398 | { .name = "test", .ops = &mov_ops, }, | ||
| 399 | { .name = "testb", .ops = &mov_ops, }, | ||
| 400 | { .name = "testl", .ops = &mov_ops, }, | ||
| 401 | { .name = "xadd", .ops = &mov_ops, }, | ||
| 170 | }; | 402 | }; |
| 171 | 403 | ||
| 172 | static int ins__cmp(const void *name, const void *insp) | 404 | static int ins__cmp(const void *name, const void *insp) |
| @@ -257,6 +489,44 @@ static void disasm_line__init_ins(struct disasm_line *dl) | |||
| 257 | dl->ins->ops->parse(&dl->ops); | 489 | dl->ins->ops->parse(&dl->ops); |
| 258 | } | 490 | } |
| 259 | 491 | ||
| 492 | static int disasm_line__parse(char *line, char **namep, char **rawp) | ||
| 493 | { | ||
| 494 | char *name = line, tmp; | ||
| 495 | |||
| 496 | while (isspace(name[0])) | ||
| 497 | ++name; | ||
| 498 | |||
| 499 | if (name[0] == '\0') | ||
| 500 | return -1; | ||
| 501 | |||
| 502 | *rawp = name + 1; | ||
| 503 | |||
| 504 | while ((*rawp)[0] != '\0' && !isspace((*rawp)[0])) | ||
| 505 | ++*rawp; | ||
| 506 | |||
| 507 | tmp = (*rawp)[0]; | ||
| 508 | (*rawp)[0] = '\0'; | ||
| 509 | *namep = strdup(name); | ||
| 510 | |||
| 511 | if (*namep == NULL) | ||
| 512 | goto out_free_name; | ||
| 513 | |||
| 514 | (*rawp)[0] = tmp; | ||
| 515 | |||
| 516 | if ((*rawp)[0] != '\0') { | ||
| 517 | (*rawp)++; | ||
| 518 | while (isspace((*rawp)[0])) | ||
| 519 | ++(*rawp); | ||
| 520 | } | ||
| 521 | |||
| 522 | return 0; | ||
| 523 | |||
| 524 | out_free_name: | ||
| 525 | free(*namep); | ||
| 526 | *namep = NULL; | ||
| 527 | return -1; | ||
| 528 | } | ||
| 529 | |||
| 260 | static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize) | 530 | static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize) |
| 261 | { | 531 | { |
| 262 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); | 532 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); |
| @@ -268,35 +538,9 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privs | |||
| 268 | goto out_delete; | 538 | goto out_delete; |
| 269 | 539 | ||
| 270 | if (offset != -1) { | 540 | if (offset != -1) { |
| 271 | char *name = dl->line, tmp; | 541 | if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0) |
| 272 | |||
| 273 | while (isspace(name[0])) | ||
| 274 | ++name; | ||
| 275 | |||
| 276 | if (name[0] == '\0') | ||
| 277 | goto out_delete; | ||
| 278 | |||
| 279 | dl->ops.raw = name + 1; | ||
| 280 | |||
| 281 | while (dl->ops.raw[0] != '\0' && | ||
| 282 | !isspace(dl->ops.raw[0])) | ||
| 283 | ++dl->ops.raw; | ||
| 284 | |||
| 285 | tmp = dl->ops.raw[0]; | ||
| 286 | dl->ops.raw[0] = '\0'; | ||
| 287 | dl->name = strdup(name); | ||
| 288 | |||
| 289 | if (dl->name == NULL) | ||
| 290 | goto out_free_line; | 542 | goto out_free_line; |
| 291 | 543 | ||
| 292 | dl->ops.raw[0] = tmp; | ||
| 293 | |||
| 294 | if (dl->ops.raw[0] != '\0') { | ||
| 295 | dl->ops.raw++; | ||
| 296 | while (isspace(dl->ops.raw[0])) | ||
| 297 | ++dl->ops.raw; | ||
| 298 | } | ||
| 299 | |||
| 300 | disasm_line__init_ins(dl); | 544 | disasm_line__init_ins(dl); |
| 301 | } | 545 | } |
| 302 | } | 546 | } |
| @@ -314,7 +558,10 @@ void disasm_line__free(struct disasm_line *dl) | |||
| 314 | { | 558 | { |
| 315 | free(dl->line); | 559 | free(dl->line); |
| 316 | free(dl->name); | 560 | free(dl->name); |
| 317 | free(dl->ops.target.name); | 561 | if (dl->ins && dl->ins->ops->free) |
| 562 | dl->ins->ops->free(&dl->ops); | ||
| 563 | else | ||
| 564 | ins__delete(&dl->ops); | ||
| 318 | free(dl); | 565 | free(dl); |
| 319 | } | 566 | } |
| 320 | 567 | ||
