1 #include "builtin.h" 2 #include "perf.h" 3 4 #include "util/evsel.h" 5 #include "util/util.h" 6 #include "util/cache.h" 7 #include "util/symbol.h" 8 #include "util/thread.h" 9 #include "util/header.h" 10 #include "util/session.h" 11 12 #include "util/parse-options.h" 13 #include "util/trace-event.h" 14 #include "util/debug.h" 15 #include "util/debugfs.h" 16 #include "util/tool.h" 17 #include "util/stat.h" 18 19 #include <sys/prctl.h> 20 21 #include <semaphore.h> 22 #include <pthread.h> 23 #include <math.h> 24 25 #include "../../arch/x86/include/asm/svm.h" 26 #include "../../arch/x86/include/asm/vmx.h" 27 #include "../../arch/x86/include/asm/kvm.h" 28 29 struct event_key { 30 #define INVALID_KEY (~0ULL) 31 u64 key; 32 int info; 33 }; 34 35 struct kvm_event_stats { 36 u64 time; 37 struct stats stats; 38 }; 39 40 struct kvm_event { 41 struct list_head hash_entry; 42 struct rb_node rb; 43 44 struct event_key key; 45 46 struct kvm_event_stats total; 47 48 #define DEFAULT_VCPU_NUM 8 49 int max_vcpu; 50 struct kvm_event_stats *vcpu; 51 }; 52 53 typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int); 54 55 struct kvm_event_key { 56 const char *name; 57 key_cmp_fun key; 58 }; 59 60 61 struct perf_kvm; 62 63 struct kvm_events_ops { 64 bool (*is_begin_event)(struct perf_evsel *evsel, 65 struct perf_sample *sample, 66 struct event_key *key); 67 bool (*is_end_event)(struct perf_evsel *evsel, 68 struct perf_sample *sample, struct event_key *key); 69 void (*decode_key)(struct perf_kvm *kvm, struct event_key *key, 70 char decode[20]); 71 const char *name; 72 }; 73 74 struct exit_reasons_table { 75 unsigned long exit_code; 76 const char *reason; 77 }; 78 79 #define EVENTS_BITS 12 80 #define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS) 81 82 struct perf_kvm { 83 struct perf_tool tool; 84 struct perf_session *session; 85 86 const char *file_name; 87 const char *report_event; 88 const char *sort_key; 89 int trace_vcpu; 90 91 struct exit_reasons_table *exit_reasons; 92 int exit_reasons_size; 93 const char *exit_reasons_isa; 94 95 struct kvm_events_ops *events_ops; 96 key_cmp_fun compare; 97 struct list_head kvm_events_cache[EVENTS_CACHE_SIZE]; 98 u64 total_time; 99 u64 total_count; 100 101 struct rb_root result; 102 }; 103 104 105 static void exit_event_get_key(struct perf_evsel *evsel, 106 struct perf_sample *sample, 107 struct event_key *key) 108 { 109 key->info = 0; 110 key->key = perf_evsel__intval(evsel, sample, "exit_reason"); 111 } 112 113 static bool kvm_exit_event(struct perf_evsel *evsel) 114 { 115 return !strcmp(evsel->name, "kvm:kvm_exit"); 116 } 117 118 static bool exit_event_begin(struct perf_evsel *evsel, 119 struct perf_sample *sample, struct event_key *key) 120 { 121 if (kvm_exit_event(evsel)) { 122 exit_event_get_key(evsel, sample, key); 123 return true; 124 } 125 126 return false; 127 } 128 129 static bool kvm_entry_event(struct perf_evsel *evsel) 130 { 131 return !strcmp(evsel->name, "kvm:kvm_entry"); 132 } 133 134 static bool exit_event_end(struct perf_evsel *evsel, 135 struct perf_sample *sample __maybe_unused, 136 struct event_key *key __maybe_unused) 137 { 138 return kvm_entry_event(evsel); 139 } 140 141 static struct exit_reasons_table vmx_exit_reasons[] = { 142 VMX_EXIT_REASONS 143 }; 144 145 static struct exit_reasons_table svm_exit_reasons[] = { 146 SVM_EXIT_REASONS 147 }; 148 149 static const char *get_exit_reason(struct perf_kvm *kvm, u64 exit_code) 150 { 151 int i = kvm->exit_reasons_size; 152 struct exit_reasons_table *tbl = kvm->exit_reasons; 153 154 while (i--) { 155 if (tbl->exit_code == exit_code) 156 return tbl->reason; 157 tbl++; 158 } 159 160 pr_err("unknown kvm exit code:%lld on %s\n", 161 (unsigned long long)exit_code, kvm->exit_reasons_isa); 162 return "UNKNOWN"; 163 } 164 165 static void exit_event_decode_key(struct perf_kvm *kvm, 166 struct event_key *key, 167 char decode[20]) 168 { 169 const char *exit_reason = get_exit_reason(kvm, key->key); 170 171 scnprintf(decode, 20, "%s", exit_reason); 172 } 173 174 static struct kvm_events_ops exit_events = { 175 .is_begin_event = exit_event_begin, 176 .is_end_event = exit_event_end, 177 .decode_key = exit_event_decode_key, 178 .name = "VM-EXIT" 179 }; 180 181 /* 182 * For the mmio events, we treat: 183 * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry 184 * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). 185 */ 186 static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample, 187 struct event_key *key) 188 { 189 key->key = perf_evsel__intval(evsel, sample, "gpa"); 190 key->info = perf_evsel__intval(evsel, sample, "type"); 191 } 192 193 #define KVM_TRACE_MMIO_READ_UNSATISFIED 0 194 #define KVM_TRACE_MMIO_READ 1 195 #define KVM_TRACE_MMIO_WRITE 2 196 197 static bool mmio_event_begin(struct perf_evsel *evsel, 198 struct perf_sample *sample, struct event_key *key) 199 { 200 /* MMIO read begin event in kernel. */ 201 if (kvm_exit_event(evsel)) 202 return true; 203 204 /* MMIO write begin event in kernel. */ 205 if (!strcmp(evsel->name, "kvm:kvm_mmio") && 206 perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { 207 mmio_event_get_key(evsel, sample, key); 208 return true; 209 } 210 211 return false; 212 } 213 214 static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, 215 struct event_key *key) 216 { 217 /* MMIO write end event in kernel. */ 218 if (kvm_entry_event(evsel)) 219 return true; 220 221 /* MMIO read end event in kernel.*/ 222 if (!strcmp(evsel->name, "kvm:kvm_mmio") && 223 perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { 224 mmio_event_get_key(evsel, sample, key); 225 return true; 226 } 227 228 return false; 229 } 230 231 static void mmio_event_decode_key(struct perf_kvm *kvm __maybe_unused, 232 struct event_key *key, 233 char decode[20]) 234 { 235 scnprintf(decode, 20, "%#lx:%s", (unsigned long)key->key, 236 key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); 237 } 238 239 static struct kvm_events_ops mmio_events = { 240 .is_begin_event = mmio_event_begin, 241 .is_end_event = mmio_event_end, 242 .decode_key = mmio_event_decode_key, 243 .name = "MMIO Access" 244 }; 245 246 /* The time of emulation pio access is from kvm_pio to kvm_entry. */ 247 static void ioport_event_get_key(struct perf_evsel *evsel, 248 struct perf_sample *sample, 249 struct event_key *key) 250 { 251 key->key = perf_evsel__intval(evsel, sample, "port"); 252 key->info = perf_evsel__intval(evsel, sample, "rw"); 253 } 254 255 static bool ioport_event_begin(struct perf_evsel *evsel, 256 struct perf_sample *sample, 257 struct event_key *key) 258 { 259 if (!strcmp(evsel->name, "kvm:kvm_pio")) { 260 ioport_event_get_key(evsel, sample, key); 261 return true; 262 } 263 264 return false; 265 } 266 267 static bool ioport_event_end(struct perf_evsel *evsel, 268 struct perf_sample *sample __maybe_unused, 269 struct event_key *key __maybe_unused) 270 { 271 return kvm_entry_event(evsel); 272 } 273 274 static void ioport_event_decode_key(struct perf_kvm *kvm __maybe_unused, 275 struct event_key *key, 276 char decode[20]) 277 { 278 scnprintf(decode, 20, "%#llx:%s", (unsigned long long)key->key, 279 key->info ? "POUT" : "PIN"); 280 } 281 282 static struct kvm_events_ops ioport_events = { 283 .is_begin_event = ioport_event_begin, 284 .is_end_event = ioport_event_end, 285 .decode_key = ioport_event_decode_key, 286 .name = "IO Port Access" 287 }; 288 289 static bool register_kvm_events_ops(struct perf_kvm *kvm) 290 { 291 bool ret = true; 292 293 if (!strcmp(kvm->report_event, "vmexit")) 294 kvm->events_ops = &exit_events; 295 else if (!strcmp(kvm->report_event, "mmio")) 296 kvm->events_ops = &mmio_events; 297 else if (!strcmp(kvm->report_event, "ioport")) 298 kvm->events_ops = &ioport_events; 299 else { 300 pr_err("Unknown report event:%s\n", kvm->report_event); 301 ret = false; 302 } 303 304 return ret; 305 } 306 307 struct vcpu_event_record { 308 int vcpu_id; 309 u64 start_time; 310 struct kvm_event *last_event; 311 }; 312 313 314 static void init_kvm_event_record(struct perf_kvm *kvm) 315 { 316 int i; 317 318 for (i = 0; i < (int)EVENTS_CACHE_SIZE; i++) 319 INIT_LIST_HEAD(&kvm->kvm_events_cache[i]); 320 } 321 322 static int kvm_events_hash_fn(u64 key) 323 { 324 return key & (EVENTS_CACHE_SIZE - 1); 325 } 326 327 static bool kvm_event_expand(struct kvm_event *event, int vcpu_id) 328 { 329 int old_max_vcpu = event->max_vcpu; 330 331 if (vcpu_id < event->max_vcpu) 332 return true; 333 334 while (event->max_vcpu <= vcpu_id) 335 event->max_vcpu += DEFAULT_VCPU_NUM; 336 337 event->vcpu = realloc(event->vcpu, 338 event->max_vcpu * sizeof(*event->vcpu)); 339 if (!event->vcpu) { 340 pr_err("Not enough memory\n"); 341 return false; 342 } 343 344 memset(event->vcpu + old_max_vcpu, 0, 345 (event->max_vcpu - old_max_vcpu) * sizeof(*event->vcpu)); 346 return true; 347 } 348 349 static struct kvm_event *kvm_alloc_init_event(struct event_key *key) 350 { 351 struct kvm_event *event; 352 353 event = zalloc(sizeof(*event)); 354 if (!event) { 355 pr_err("Not enough memory\n"); 356 return NULL; 357 } 358 359 event->key = *key; 360 return event; 361 } 362 363 static struct kvm_event *find_create_kvm_event(struct perf_kvm *kvm, 364 struct event_key *key) 365 { 366 struct kvm_event *event; 367 struct list_head *head; 368 369 BUG_ON(key->key == INVALID_KEY); 370 371 head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)]; 372 list_for_each_entry(event, head, hash_entry) 373 if (event->key.key == key->key && event->key.info == key->info) 374 return event; 375 376 event = kvm_alloc_init_event(key); 377 if (!event) 378 return NULL; 379 380 list_add(&event->hash_entry, head); 381 return event; 382 } 383 384 static bool handle_begin_event(struct perf_kvm *kvm, 385 struct vcpu_event_record *vcpu_record, 386 struct event_key *key, u64 timestamp) 387 { 388 struct kvm_event *event = NULL; 389 390 if (key->key != INVALID_KEY) 391 event = find_create_kvm_event(kvm, key); 392 393 vcpu_record->last_event = event; 394 vcpu_record->start_time = timestamp; 395 return true; 396 } 397 398 static void 399 kvm_update_event_stats(struct kvm_event_stats *kvm_stats, u64 time_diff) 400 { 401 kvm_stats->time += time_diff; 402 update_stats(&kvm_stats->stats, time_diff); 403 } 404 405 static double kvm_event_rel_stddev(int vcpu_id, struct kvm_event *event) 406 { 407 struct kvm_event_stats *kvm_stats = &event->total; 408 409 if (vcpu_id != -1) 410 kvm_stats = &event->vcpu[vcpu_id]; 411 412 return rel_stddev_stats(stddev_stats(&kvm_stats->stats), 413 avg_stats(&kvm_stats->stats)); 414 } 415 416 static bool update_kvm_event(struct kvm_event *event, int vcpu_id, 417 u64 time_diff) 418 { 419 kvm_update_event_stats(&event->total, time_diff); 420 421 if (!kvm_event_expand(event, vcpu_id)) 422 return false; 423 424 kvm_update_event_stats(&event->vcpu[vcpu_id], time_diff); 425 return true; 426 } 427 428 static bool handle_end_event(struct perf_kvm *kvm, 429 struct vcpu_event_record *vcpu_record, 430 struct event_key *key, 431 u64 timestamp) 432 { 433 struct kvm_event *event; 434 u64 time_begin, time_diff; 435 436 event = vcpu_record->last_event; 437 time_begin = vcpu_record->start_time; 438 439 /* The begin event is not caught. */ 440 if (!time_begin) 441 return true; 442 443 /* 444 * In some case, the 'begin event' only records the start timestamp, 445 * the actual event is recognized in the 'end event' (e.g. mmio-event). 446 */ 447 448 /* Both begin and end events did not get the key. */ 449 if (!event && key->key == INVALID_KEY) 450 return true; 451 452 if (!event) 453 event = find_create_kvm_event(kvm, key); 454 455 if (!event) 456 return false; 457 458 vcpu_record->last_event = NULL; 459 vcpu_record->start_time = 0; 460 461 BUG_ON(timestamp < time_begin); 462 463 time_diff = timestamp - time_begin; 464 return update_kvm_event(event, vcpu_record->vcpu_id, time_diff); 465 } 466 467 static 468 struct vcpu_event_record *per_vcpu_record(struct thread *thread, 469 struct perf_evsel *evsel, 470 struct perf_sample *sample) 471 { 472 /* Only kvm_entry records vcpu id. */ 473 if (!thread->priv && kvm_entry_event(evsel)) { 474 struct vcpu_event_record *vcpu_record; 475 476 vcpu_record = zalloc(sizeof(*vcpu_record)); 477 if (!vcpu_record) { 478 pr_err("%s: Not enough memory\n", __func__); 479 return NULL; 480 } 481 482 vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, "vcpu_id"); 483 thread->priv = vcpu_record; 484 } 485 486 return thread->priv; 487 } 488 489 static bool handle_kvm_event(struct perf_kvm *kvm, 490 struct thread *thread, 491 struct perf_evsel *evsel, 492 struct perf_sample *sample) 493 { 494 struct vcpu_event_record *vcpu_record; 495 struct event_key key = {.key = INVALID_KEY}; 496 497 vcpu_record = per_vcpu_record(thread, evsel, sample); 498 if (!vcpu_record) 499 return true; 500 501 if (kvm->events_ops->is_begin_event(evsel, sample, &key)) 502 return handle_begin_event(kvm, vcpu_record, &key, sample->time); 503 504 if (kvm->events_ops->is_end_event(evsel, sample, &key)) 505 return handle_end_event(kvm, vcpu_record, &key, sample->time); 506 507 return true; 508 } 509 510 #define GET_EVENT_KEY(func, field) \ 511 static u64 get_event_ ##func(struct kvm_event *event, int vcpu) \ 512 { \ 513 if (vcpu == -1) \ 514 return event->total.field; \ 515 \ 516 if (vcpu >= event->max_vcpu) \ 517 return 0; \ 518 \ 519 return event->vcpu[vcpu].field; \ 520 } 521 522 #define COMPARE_EVENT_KEY(func, field) \ 523 GET_EVENT_KEY(func, field) \ 524 static int compare_kvm_event_ ## func(struct kvm_event *one, \ 525 struct kvm_event *two, int vcpu)\ 526 { \ 527 return get_event_ ##func(one, vcpu) > \ 528 get_event_ ##func(two, vcpu); \ 529 } 530 531 GET_EVENT_KEY(time, time); 532 COMPARE_EVENT_KEY(count, stats.n); 533 COMPARE_EVENT_KEY(mean, stats.mean); 534 535 #define DEF_SORT_NAME_KEY(name, compare_key) \ 536 { #name, compare_kvm_event_ ## compare_key } 537 538 static struct kvm_event_key keys[] = { 539 DEF_SORT_NAME_KEY(sample, count), 540 DEF_SORT_NAME_KEY(time, mean), 541 { NULL, NULL } 542 }; 543 544 static bool select_key(struct perf_kvm *kvm) 545 { 546 int i; 547 548 for (i = 0; keys[i].name; i++) { 549 if (!strcmp(keys[i].name, kvm->sort_key)) { 550 kvm->compare = keys[i].key; 551 return true; 552 } 553 } 554 555 pr_err("Unknown compare key:%s\n", kvm->sort_key); 556 return false; 557 } 558 559 static void insert_to_result(struct rb_root *result, struct kvm_event *event, 560 key_cmp_fun bigger, int vcpu) 561 { 562 struct rb_node **rb = &result->rb_node; 563 struct rb_node *parent = NULL; 564 struct kvm_event *p; 565 566 while (*rb) { 567 p = container_of(*rb, struct kvm_event, rb); 568 parent = *rb; 569 570 if (bigger(event, p, vcpu)) 571 rb = &(*rb)->rb_left; 572 else 573 rb = &(*rb)->rb_right; 574 } 575 576 rb_link_node(&event->rb, parent, rb); 577 rb_insert_color(&event->rb, result); 578 } 579 580 static void update_total_count(struct perf_kvm *kvm, struct kvm_event *event) 581 { 582 int vcpu = kvm->trace_vcpu; 583 584 kvm->total_count += get_event_count(event, vcpu); 585 kvm->total_time += get_event_time(event, vcpu); 586 } 587 588 static bool event_is_valid(struct kvm_event *event, int vcpu) 589 { 590 return !!get_event_count(event, vcpu); 591 } 592 593 static void sort_result(struct perf_kvm *kvm) 594 { 595 unsigned int i; 596 int vcpu = kvm->trace_vcpu; 597 struct kvm_event *event; 598 599 for (i = 0; i < EVENTS_CACHE_SIZE; i++) 600 list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry) 601 if (event_is_valid(event, vcpu)) { 602 update_total_count(kvm, event); 603 insert_to_result(&kvm->result, event, 604 kvm->compare, vcpu); 605 } 606 } 607 608 /* returns left most element of result, and erase it */ 609 static struct kvm_event *pop_from_result(struct rb_root *result) 610 { 611 struct rb_node *node = rb_first(result); 612 613 if (!node) 614 return NULL; 615 616 rb_erase(node, result); 617 return container_of(node, struct kvm_event, rb); 618 } 619 620 static void print_vcpu_info(int vcpu) 621 { 622 pr_info("Analyze events for "); 623 624 if (vcpu == -1) 625 pr_info("all VCPUs:\n\n"); 626 else 627 pr_info("VCPU %d:\n\n", vcpu); 628 } 629 630 static void print_result(struct perf_kvm *kvm) 631 { 632 char decode[20]; 633 struct kvm_event *event; 634 int vcpu = kvm->trace_vcpu; 635 636 pr_info("\n\n"); 637 print_vcpu_info(vcpu); 638 pr_info("%20s ", kvm->events_ops->name); 639 pr_info("%10s ", "Samples"); 640 pr_info("%9s ", "Samples%"); 641 642 pr_info("%9s ", "Time%"); 643 pr_info("%16s ", "Avg time"); 644 pr_info("\n\n"); 645 646 while ((event = pop_from_result(&kvm->result))) { 647 u64 ecount, etime; 648 649 ecount = get_event_count(event, vcpu); 650 etime = get_event_time(event, vcpu); 651 652 kvm->events_ops->decode_key(kvm, &event->key, decode); 653 pr_info("%20s ", decode); 654 pr_info("%10llu ", (unsigned long long)ecount); 655 pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); 656 pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); 657 pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3, 658 kvm_event_rel_stddev(vcpu, event)); 659 pr_info("\n"); 660 } 661 662 pr_info("\nTotal Samples:%lld, Total events handled time:%.2fus.\n\n", 663 (unsigned long long)kvm->total_count, kvm->total_time / 1e3); 664 } 665 666 static int process_sample_event(struct perf_tool *tool, 667 union perf_event *event, 668 struct perf_sample *sample, 669 struct perf_evsel *evsel, 670 struct machine *machine) 671 { 672 struct thread *thread = machine__findnew_thread(machine, sample->tid); 673 struct perf_kvm *kvm = container_of(tool, struct perf_kvm, tool); 674 675 if (thread == NULL) { 676 pr_debug("problem processing %d event, skipping it.\n", 677 event->header.type); 678 return -1; 679 } 680 681 if (!handle_kvm_event(kvm, thread, evsel, sample)) 682 return -1; 683 684 return 0; 685 } 686 687 static int get_cpu_isa(struct perf_session *session) 688 { 689 char *cpuid = session->header.env.cpuid; 690 int isa; 691 692 if (strstr(cpuid, "Intel")) 693 isa = 1; 694 else if (strstr(cpuid, "AMD")) 695 isa = 0; 696 else { 697 pr_err("CPU %s is not supported.\n", cpuid); 698 isa = -ENOTSUP; 699 } 700 701 return isa; 702 } 703 704 static int read_events(struct perf_kvm *kvm) 705 { 706 int ret; 707 708 struct perf_tool eops = { 709 .sample = process_sample_event, 710 .comm = perf_event__process_comm, 711 .ordered_samples = true, 712 }; 713 714 kvm->tool = eops; 715 kvm->session = perf_session__new(kvm->file_name, O_RDONLY, 0, false, 716 &kvm->tool); 717 if (!kvm->session) { 718 pr_err("Initializing perf session failed\n"); 719 return -EINVAL; 720 } 721 722 if (!perf_session__has_traces(kvm->session, "kvm record")) 723 return -EINVAL; 724 725 /* 726 * Do not use 'isa' recorded in kvm_exit tracepoint since it is not 727 * traced in the old kernel. 728 */ 729 ret = get_cpu_isa(kvm->session); 730 731 if (ret < 0) 732 return ret; 733 734 if (ret == 1) { 735 kvm->exit_reasons = vmx_exit_reasons; 736 kvm->exit_reasons_size = ARRAY_SIZE(vmx_exit_reasons); 737 kvm->exit_reasons_isa = "VMX"; 738 } 739 740 return perf_session__process_events(kvm->session, &kvm->tool); 741 } 742 743 static bool verify_vcpu(int vcpu) 744 { 745 if (vcpu != -1 && vcpu < 0) { 746 pr_err("Invalid vcpu:%d.\n", vcpu); 747 return false; 748 } 749 750 return true; 751 } 752 753 static int kvm_events_report_vcpu(struct perf_kvm *kvm) 754 { 755 int ret = -EINVAL; 756 int vcpu = kvm->trace_vcpu; 757 758 if (!verify_vcpu(vcpu)) 759 goto exit; 760 761 if (!select_key(kvm)) 762 goto exit; 763 764 if (!register_kvm_events_ops(kvm)) 765 goto exit; 766 767 init_kvm_event_record(kvm); 768 setup_pager(); 769 770 ret = read_events(kvm); 771 if (ret) 772 goto exit; 773 774 sort_result(kvm); 775 print_result(kvm); 776 777 exit: 778 return ret; 779 } 780 781 static const char * const record_args[] = { 782 "record", 783 "-R", 784 "-f", 785 "-m", "1024", 786 "-c", "1", 787 "-e", "kvm:kvm_entry", 788 "-e", "kvm:kvm_exit", 789 "-e", "kvm:kvm_mmio", 790 "-e", "kvm:kvm_pio", 791 }; 792 793 #define STRDUP_FAIL_EXIT(s) \ 794 ({ char *_p; \ 795 _p = strdup(s); \ 796 if (!_p) \ 797 return -ENOMEM; \ 798 _p; \ 799 }) 800 801 static int kvm_events_record(struct perf_kvm *kvm, int argc, const char **argv) 802 { 803 unsigned int rec_argc, i, j; 804 const char **rec_argv; 805 806 rec_argc = ARRAY_SIZE(record_args) + argc + 2; 807 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 808 809 if (rec_argv == NULL) 810 return -ENOMEM; 811 812 for (i = 0; i < ARRAY_SIZE(record_args); i++) 813 rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]); 814 815 rec_argv[i++] = STRDUP_FAIL_EXIT("-o"); 816 rec_argv[i++] = STRDUP_FAIL_EXIT(kvm->file_name); 817 818 for (j = 1; j < (unsigned int)argc; j++, i++) 819 rec_argv[i] = argv[j]; 820 821 return cmd_record(i, rec_argv, NULL); 822 } 823 824 static int kvm_events_report(struct perf_kvm *kvm, int argc, const char **argv) 825 { 826 const struct option kvm_events_report_options[] = { 827 OPT_STRING(0, "event", &kvm->report_event, "report event", 828 "event for reporting: vmexit, mmio, ioport"), 829 OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu, 830 "vcpu id to report"), 831 OPT_STRING('k', "key", &kvm->sort_key, "sort-key", 832 "key for sorting: sample(sort by samples number)" 833 " time (sort by avg time)"), 834 OPT_END() 835 }; 836 837 const char * const kvm_events_report_usage[] = { 838 "perf kvm stat report [<options>]", 839 NULL 840 }; 841 842 symbol__init(); 843 844 if (argc) { 845 argc = parse_options(argc, argv, 846 kvm_events_report_options, 847 kvm_events_report_usage, 0); 848 if (argc) 849 usage_with_options(kvm_events_report_usage, 850 kvm_events_report_options); 851 } 852 853 return kvm_events_report_vcpu(kvm); 854 } 855 856 static void print_kvm_stat_usage(void) 857 { 858 printf("Usage: perf kvm stat <command>\n\n"); 859 860 printf("# Available commands:\n"); 861 printf("\trecord: record kvm events\n"); 862 printf("\treport: report statistical data of kvm events\n"); 863 864 printf("\nOtherwise, it is the alias of 'perf stat':\n"); 865 } 866 867 static int kvm_cmd_stat(struct perf_kvm *kvm, int argc, const char **argv) 868 { 869 if (argc == 1) { 870 print_kvm_stat_usage(); 871 goto perf_stat; 872 } 873 874 if (!strncmp(argv[1], "rec", 3)) 875 return kvm_events_record(kvm, argc - 1, argv + 1); 876 877 if (!strncmp(argv[1], "rep", 3)) 878 return kvm_events_report(kvm, argc - 1 , argv + 1); 879 880 perf_stat: 881 return cmd_stat(argc, argv, NULL); 882 } 883 884 static int __cmd_record(struct perf_kvm *kvm, int argc, const char **argv) 885 { 886 int rec_argc, i = 0, j; 887 const char **rec_argv; 888 889 rec_argc = argc + 2; 890 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 891 rec_argv[i++] = strdup("record"); 892 rec_argv[i++] = strdup("-o"); 893 rec_argv[i++] = strdup(kvm->file_name); 894 for (j = 1; j < argc; j++, i++) 895 rec_argv[i] = argv[j]; 896 897 BUG_ON(i != rec_argc); 898 899 return cmd_record(i, rec_argv, NULL); 900 } 901 902 static int __cmd_report(struct perf_kvm *kvm, int argc, const char **argv) 903 { 904 int rec_argc, i = 0, j; 905 const char **rec_argv; 906 907 rec_argc = argc + 2; 908 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 909 rec_argv[i++] = strdup("report"); 910 rec_argv[i++] = strdup("-i"); 911 rec_argv[i++] = strdup(kvm->file_name); 912 for (j = 1; j < argc; j++, i++) 913 rec_argv[i] = argv[j]; 914 915 BUG_ON(i != rec_argc); 916 917 return cmd_report(i, rec_argv, NULL); 918 } 919 920 static int __cmd_buildid_list(struct perf_kvm *kvm, int argc, const char **argv) 921 { 922 int rec_argc, i = 0, j; 923 const char **rec_argv; 924 925 rec_argc = argc + 2; 926 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 927 rec_argv[i++] = strdup("buildid-list"); 928 rec_argv[i++] = strdup("-i"); 929 rec_argv[i++] = strdup(kvm->file_name); 930 for (j = 1; j < argc; j++, i++) 931 rec_argv[i] = argv[j]; 932 933 BUG_ON(i != rec_argc); 934 935 return cmd_buildid_list(i, rec_argv, NULL); 936 } 937 938 int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) 939 { 940 struct perf_kvm kvm = { 941 .trace_vcpu = -1, 942 .report_event = "vmexit", 943 .sort_key = "sample", 944 945 .exit_reasons = svm_exit_reasons, 946 .exit_reasons_size = ARRAY_SIZE(svm_exit_reasons), 947 .exit_reasons_isa = "SVM", 948 }; 949 950 const struct option kvm_options[] = { 951 OPT_STRING('i', "input", &kvm.file_name, "file", 952 "Input file name"), 953 OPT_STRING('o', "output", &kvm.file_name, "file", 954 "Output file name"), 955 OPT_BOOLEAN(0, "guest", &perf_guest, 956 "Collect guest os data"), 957 OPT_BOOLEAN(0, "host", &perf_host, 958 "Collect host os data"), 959 OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", 960 "guest mount directory under which every guest os" 961 " instance has a subdir"), 962 OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name, 963 "file", "file saving guest os vmlinux"), 964 OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms, 965 "file", "file saving guest os /proc/kallsyms"), 966 OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules, 967 "file", "file saving guest os /proc/modules"), 968 OPT_END() 969 }; 970 971 972 const char * const kvm_usage[] = { 973 "perf kvm [<options>] {top|record|report|diff|buildid-list|stat}", 974 NULL 975 }; 976 977 perf_host = 0; 978 perf_guest = 1; 979 980 argc = parse_options(argc, argv, kvm_options, kvm_usage, 981 PARSE_OPT_STOP_AT_NON_OPTION); 982 if (!argc) 983 usage_with_options(kvm_usage, kvm_options); 984 985 if (!perf_host) 986 perf_guest = 1; 987 988 if (!kvm.file_name) { 989 if (perf_host && !perf_guest) 990 kvm.file_name = strdup("perf.data.host"); 991 else if (!perf_host && perf_guest) 992 kvm.file_name = strdup("perf.data.guest"); 993 else 994 kvm.file_name = strdup("perf.data.kvm"); 995 996 if (!kvm.file_name) { 997 pr_err("Failed to allocate memory for filename\n"); 998 return -ENOMEM; 999 } 1000 } 1001 1002 if (!strncmp(argv[0], "rec", 3)) 1003 return __cmd_record(&kvm, argc, argv); 1004 else if (!strncmp(argv[0], "rep", 3)) 1005 return __cmd_report(&kvm, argc, argv); 1006 else if (!strncmp(argv[0], "diff", 4)) 1007 return cmd_diff(argc, argv, NULL); 1008 else if (!strncmp(argv[0], "top", 3)) 1009 return cmd_top(argc, argv, NULL); 1010 else if (!strncmp(argv[0], "buildid-list", 12)) 1011 return __cmd_buildid_list(&kvm, argc, argv); 1012 else if (!strncmp(argv[0], "stat", 4)) 1013 return kvm_cmd_stat(&kvm, argc, argv); 1014 else 1015 usage_with_options(kvm_usage, kvm_options); 1016 1017 return 0; 1018 } 1019