aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorXiao Guangrong <xiaoguangrong@linux.vnet.ibm.com>2012-09-17 04:31:15 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2012-09-21 11:51:22 -0400
commitbcf6edcd6fdb8965290f0b635a530fa3c6c212e1 (patch)
treef07af361e02f82408bdad2122d93b739b4470484 /tools/perf
parent26bf264e871a4b9a8ac09c21a2b518e7f23830d5 (diff)
perf kvm: Events analysis tool
Add 'perf kvm stat' support to analyze kvm vmexit/mmio/ioport smartly Usage: - kvm stat run a command and gather performance counter statistics, it is the alias of perf stat - trace kvm events: perf kvm stat record, or, if other tracepoints are interesting as well, we can append the events like this: perf kvm stat record -e timer:* -a If many guests are running, we can track the specified guest by using -p or --pid, -a is used to track events generated by all guests. - show the result: perf kvm stat report The output example is following: 13005 13059 total 2 guests are running on the host Then, track the guest whose pid is 13059: ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.253 MB perf.data.guest (~11065 samples) ] See the vmexit events: Analyze events for all VCPUs: VM-EXIT Samples Samples% Time% Avg time APIC_ACCESS 460 70.55% 0.01% 22.44us ( +- 1.75% ) HLT 93 14.26% 99.98% 832077.26us ( +- 10.42% ) EXTERNAL_INTERRUPT 64 9.82% 0.00% 35.35us ( +- 14.21% ) PENDING_INTERRUPT 24 3.68% 0.00% 9.29us ( +- 31.39% ) CR_ACCESS 7 1.07% 0.00% 8.12us ( +- 5.76% ) IO_INSTRUCTION 3 0.46% 0.00% 18.00us ( +- 11.79% ) EXCEPTION_NMI 1 0.15% 0.00% 5.83us ( +- -nan% ) Total Samples:652, Total events handled time:77396109.80us. See the mmio events: Analyze events for all VCPUs: MMIO Access Samples Samples% Time% Avg time 0xfee00380:W 387 84.31% 79.28% 8.29us ( +- 3.32% ) 0xfee00300:W 24 5.23% 9.96% 16.79us ( +- 1.97% ) 0xfee00300:R 24 5.23% 7.83% 13.20us ( +- 3.00% ) 0xfee00310:W 24 5.23% 2.93% 4.94us ( +- 3.84% ) Total Samples:459, Total events handled time:4044.59us. See the ioport event: Analyze events for all VCPUs: IO Port Access Samples Samples% Time% Avg time 0xc050:POUT 3 100.00% 100.00% 13.75us ( +- 10.83% ) Total Samples:3, Total events handled time:41.26us. And, --vcpu is used to track the specified vcpu and --key is used to sort the result: Analyze events for VCPU 0: VM-EXIT Samples Samples% Time% Avg time HLT 27 13.85% 99.97% 405790.24us ( +- 12.70% ) EXTERNAL_INTERRUPT 13 6.67% 0.00% 27.94us ( +- 22.26% ) APIC_ACCESS 146 74.87% 0.03% 21.69us ( +- 2.91% ) IO_INSTRUCTION 2 1.03% 0.00% 17.77us ( +- 20.56% ) CR_ACCESS 2 1.03% 0.00% 8.55us ( +- 6.47% ) PENDING_INTERRUPT 5 2.56% 0.00% 6.27us ( +- 3.94% ) Total Samples:195, Total events handled time:10959950.90us. Signed-off-by: Dong Hao <haodong@linux.vnet.ibm.com> Signed-off-by: Runzhen Wang <runzhen@linux.vnet.ibm.com> [ Dong Hao <haodong@linux.vnet.ibm.com> Runzhen Wang <runzhen@linux.vnet.ibm.com>: - rebase it on current acme's tree - fix the compiling-error on i386 ] Signed-off-by: Xiao Guangrong <xiaoguangrong@linux.vnet.ibm.com> Acked-by: David Ahern <dsahern@gmail.com> Cc: Avi Kivity <avi@redhat.com> Cc: David Ahern <dsahern@gmail.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Marcelo Tosatti <mtosatti@redhat.com> Cc: kvm@vger.kernel.org Cc: Runzhen Wang <runzhen@linux.vnet.ibm.com> Link: http://lkml.kernel.org/r/1347870675-31495-4-git-send-email-haodong@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/Documentation/perf-kvm.txt30
-rw-r--r--tools/perf/MANIFEST3
-rw-r--r--tools/perf/builtin-kvm.c840
-rw-r--r--tools/perf/util/header.c59
-rw-r--r--tools/perf/util/header.h1
-rw-r--r--tools/perf/util/thread.h2
6 files changed, 929 insertions, 6 deletions
diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt
index dd84cb2f0a8..326f2cb333c 100644
--- a/tools/perf/Documentation/perf-kvm.txt
+++ b/tools/perf/Documentation/perf-kvm.txt
@@ -12,7 +12,7 @@ SYNOPSIS
12 [--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]] 12 [--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]]
13 {top|record|report|diff|buildid-list} 13 {top|record|report|diff|buildid-list}
14'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path> 14'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
15 | --guestvmlinux=<path>] {top|record|report|diff|buildid-list} 15 | --guestvmlinux=<path>] {top|record|report|diff|buildid-list|stat}
16 16
17DESCRIPTION 17DESCRIPTION
18----------- 18-----------
@@ -38,6 +38,18 @@ There are a couple of variants of perf kvm:
38 so that other tools can be used to fetch packages with matching symbol tables 38 so that other tools can be used to fetch packages with matching symbol tables
39 for use by perf report. 39 for use by perf report.
40 40
41 'perf kvm stat <command>' to run a command and gather performance counter
42 statistics.
43 Especially, perf 'kvm stat record/report' generates a statistical analysis
44 of KVM events. Currently, vmexit, mmio and ioport events are supported.
45 'perf kvm stat record <command>' records kvm events and the events between
46 start and end <command>.
47 And this command produces a file which contains tracing results of kvm
48 events.
49
50 'perf kvm stat report' reports statistical data which includes events
51 handled time, samples, and so on.
52
41OPTIONS 53OPTIONS
42------- 54-------
43-i:: 55-i::
@@ -68,7 +80,21 @@ OPTIONS
68--guestvmlinux=<path>:: 80--guestvmlinux=<path>::
69 Guest os kernel vmlinux. 81 Guest os kernel vmlinux.
70 82
83STAT REPORT OPTIONS
84-------------------
85--vcpu=<value>::
86 analyze events which occures on this vcpu. (default: all vcpus)
87
88--events=<value>::
89 events to be analyzed. Possible values: vmexit, mmio, ioport.
90 (default: vmexit)
91-k::
92--key=<value>::
93 Sorting key. Possible values: sample (default, sort by samples
94 number), time (sort by average time).
95
71SEE ALSO 96SEE ALSO
72-------- 97--------
73linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1], 98linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1],
74linkperf:perf-diff[1], linkperf:perf-buildid-list[1] 99linkperf:perf-diff[1], linkperf:perf-buildid-list[1],
100linkperf:perf-stat[1]
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index 05180799093..80db3f4bcf7 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -16,3 +16,6 @@ arch/*/lib/memset*.S
16include/linux/poison.h 16include/linux/poison.h
17include/linux/magic.h 17include/linux/magic.h
18include/linux/hw_breakpoint.h 18include/linux/hw_breakpoint.h
19arch/x86/include/asm/svm.h
20arch/x86/include/asm/vmx.h
21arch/x86/include/asm/kvm_host.h
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 4d2aa2cbeca..1878947df5c 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -1,6 +1,7 @@
1#include "builtin.h" 1#include "builtin.h"
2#include "perf.h" 2#include "perf.h"
3 3
4#include "util/evsel.h"
4#include "util/util.h" 5#include "util/util.h"
5#include "util/cache.h" 6#include "util/cache.h"
6#include "util/symbol.h" 7#include "util/symbol.h"
@@ -10,8 +11,10 @@
10 11
11#include "util/parse-options.h" 12#include "util/parse-options.h"
12#include "util/trace-event.h" 13#include "util/trace-event.h"
13
14#include "util/debug.h" 14#include "util/debug.h"
15#include "util/debugfs.h"
16#include "util/tool.h"
17#include "util/stat.h"
15 18
16#include <sys/prctl.h> 19#include <sys/prctl.h>
17 20
@@ -19,11 +22,840 @@
19#include <pthread.h> 22#include <pthread.h>
20#include <math.h> 23#include <math.h>
21 24
22static const char *file_name; 25#include "../../arch/x86/include/asm/svm.h"
26#include "../../arch/x86/include/asm/vmx.h"
27#include "../../arch/x86/include/asm/kvm.h"
28
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
23static char name_buffer[256]; 855static char name_buffer[256];
24 856
25static const char * const kvm_usage[] = { 857static const char * const kvm_usage[] = {
26 "perf kvm [<options>] {top|record|report|diff|buildid-list}", 858 "perf kvm [<options>] {top|record|report|diff|buildid-list|stat}",
27 NULL 859 NULL
28}; 860};
29 861
@@ -135,6 +967,8 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)
135 return cmd_top(argc, argv, NULL); 967 return cmd_top(argc, argv, NULL);
136 else if (!strncmp(argv[0], "buildid-list", 12)) 968 else if (!strncmp(argv[0], "buildid-list", 12))
137 return __cmd_buildid_list(argc, argv); 969 return __cmd_buildid_list(argc, argv);
970 else if (!strncmp(argv[0], "stat", 4))
971 return kvm_cmd_stat(argc, argv);
138 else 972 else
139 usage_with_options(kvm_usage, kvm_options); 973 usage_with_options(kvm_usage, kvm_options);
140 974
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index acbf6336199..ad72b2814ba 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1673,6 +1673,11 @@ static int process_build_id(struct perf_file_section *section,
1673 return 0; 1673 return 0;
1674} 1674}
1675 1675
1676static char *read_cpuid(struct perf_header *ph, int fd)
1677{
1678 return do_read_string(fd, ph);
1679}
1680
1676static struct perf_evsel * 1681static struct perf_evsel *
1677perf_evlist__find_by_index(struct perf_evlist *evlist, int idx) 1682perf_evlist__find_by_index(struct perf_evlist *evlist, int idx)
1678{ 1683{
@@ -1726,6 +1731,7 @@ process_event_desc(struct perf_file_section *section __maybe_unused,
1726struct feature_ops { 1731struct feature_ops {
1727 int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); 1732 int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
1728 void (*print)(struct perf_header *h, int fd, FILE *fp); 1733 void (*print)(struct perf_header *h, int fd, FILE *fp);
1734 char *(*read)(struct perf_header *h, int fd);
1729 int (*process)(struct perf_file_section *section, 1735 int (*process)(struct perf_file_section *section,
1730 struct perf_header *h, int feat, int fd, void *data); 1736 struct perf_header *h, int feat, int fd, void *data);
1731 const char *name; 1737 const char *name;
@@ -1740,6 +1746,9 @@ struct feature_ops {
1740#define FEAT_OPF(n, func) \ 1746#define FEAT_OPF(n, func) \
1741 [n] = { .name = #n, .write = write_##func, .print = print_##func, \ 1747 [n] = { .name = #n, .write = write_##func, .print = print_##func, \
1742 .full_only = true } 1748 .full_only = true }
1749#define FEAT_OPA_R(n, func) \
1750 [n] = { .name = #n, .write = write_##func, .print = print_##func, \
1751 .read = read_##func }
1743 1752
1744/* feature_ops not implemented: */ 1753/* feature_ops not implemented: */
1745#define print_tracing_data NULL 1754#define print_tracing_data NULL
@@ -1754,7 +1763,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
1754 FEAT_OPA(HEADER_ARCH, arch), 1763 FEAT_OPA(HEADER_ARCH, arch),
1755 FEAT_OPA(HEADER_NRCPUS, nrcpus), 1764 FEAT_OPA(HEADER_NRCPUS, nrcpus),
1756 FEAT_OPA(HEADER_CPUDESC, cpudesc), 1765 FEAT_OPA(HEADER_CPUDESC, cpudesc),
1757 FEAT_OPA(HEADER_CPUID, cpuid), 1766 FEAT_OPA_R(HEADER_CPUID, cpuid),
1758 FEAT_OPA(HEADER_TOTAL_MEM, total_mem), 1767 FEAT_OPA(HEADER_TOTAL_MEM, total_mem),
1759 FEAT_OPP(HEADER_EVENT_DESC, event_desc), 1768 FEAT_OPP(HEADER_EVENT_DESC, event_desc),
1760 FEAT_OPA(HEADER_CMDLINE, cmdline), 1769 FEAT_OPA(HEADER_CMDLINE, cmdline),
@@ -1809,6 +1818,54 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
1809 return 0; 1818 return 0;
1810} 1819}
1811 1820
1821struct header_read_data {
1822 int feat;
1823 char *result;
1824};
1825
1826static int perf_file_section__read_feature(struct perf_file_section *section,
1827 struct perf_header *ph,
1828 int feat, int fd, void *data)
1829{
1830 struct header_read_data *hd = data;
1831
1832 if (feat != hd->feat)
1833 return 0;
1834
1835 if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
1836 pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
1837 "%d, continuing...\n", section->offset, feat);
1838 return 0;
1839 }
1840
1841 if (feat >= HEADER_LAST_FEATURE) {
1842 pr_warning("unknown feature %d\n", feat);
1843 return 0;
1844 }
1845
1846 if (!feat_ops[feat].read) {
1847 pr_warning("read is not supported for feature %d\n", feat);
1848 return 0;
1849 }
1850
1851 hd->result = feat_ops[feat].read(ph, fd);
1852 return 0;
1853}
1854
1855char *perf_header__read_feature(struct perf_session *session, int feat)
1856{
1857 struct perf_header *header = &session->header;
1858 struct header_read_data hd;
1859 int fd = session->fd;
1860
1861 hd.feat = feat;
1862 hd.result = NULL;
1863
1864 perf_header__process_sections(header, fd, &hd,
1865 perf_file_section__read_feature);
1866 return hd.result;
1867}
1868
1812static int do_write_feat(int fd, struct perf_header *h, int type, 1869static int do_write_feat(int fd, struct perf_header *h, int type,
1813 struct perf_file_section **p, 1870 struct perf_file_section **p,
1814 struct perf_evlist *evlist) 1871 struct perf_evlist *evlist)
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 209dad4fee2..58de08b21bc 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -94,6 +94,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
94 int feat, int fd, void *data)); 94 int feat, int fd, void *data));
95 95
96int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); 96int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
97char *perf_header__read_feature(struct perf_session *session, int feat);
97 98
98int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, 99int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
99 const char *name, bool is_kallsyms, bool is_vdso); 100 const char *name, bool is_kallsyms, bool is_vdso);
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 70c2c13ff67..f66610b7bac 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -16,6 +16,8 @@ struct thread {
16 bool comm_set; 16 bool comm_set;
17 char *comm; 17 char *comm;
18 int comm_len; 18 int comm_len;
19
20 void *priv;
19}; 21};
20 22
21struct machine; 23struct machine;