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