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