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