mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 21:33:00 +00:00
perf kvm: Add stat support on s390
On s390, the vmexit event has a tree-like structure: between exit_event_begin and exit_event_end several other events may happen and with each of them refining the previous ones. This patch adds a decoder for such events to the generic code and also the files <asm/kvm_perf.h> and kvm-stat.c for s390. Commands 'perf kvm stat record', 'report' and 'live' are supported. Reviewed-by: David Ahern <dsahern@gmail.com> Signed-off-by: Alexander Yarygin <yarygin@linux.vnet.ibm.com> Acked-by: Christian Borntraeger <borntraeger@de.ibm.com> Cc: Christian Borntraeger <borntraeger@de.ibm.com> Cc: Cornelia Huck <cornelia.huck@de.ibm.com> Cc: David Ahern <dsahern@gmail.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/r/1404397747-20939-5-git-send-email-yarygin@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
54c801ff71
commit
3be8e2a0a5
@ -16,6 +16,7 @@ header-y += ioctls.h
|
||||
header-y += ipcbuf.h
|
||||
header-y += kvm.h
|
||||
header-y += kvm_para.h
|
||||
header-y += kvm_perf.h
|
||||
header-y += kvm_virtio.h
|
||||
header-y += mman.h
|
||||
header-y += monwriter.h
|
||||
|
25
arch/s390/include/uapi/asm/kvm_perf.h
Normal file
25
arch/s390/include/uapi/asm/kvm_perf.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Definitions for perf-kvm on s390
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License (version 2 only)
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_KVM_PERF_S390_H
|
||||
#define __LINUX_KVM_PERF_S390_H
|
||||
|
||||
#include <asm/sie.h>
|
||||
|
||||
#define DECODE_STR_LEN 40
|
||||
|
||||
#define VCPU_ID "id"
|
||||
|
||||
#define KVM_ENTRY_TRACE "kvm:kvm_s390_sie_enter"
|
||||
#define KVM_EXIT_TRACE "kvm:kvm_s390_sie_exit"
|
||||
#define KVM_EXIT_REASON "icptcode"
|
||||
|
||||
#endif
|
@ -51,9 +51,9 @@ There are a couple of variants of perf kvm:
|
||||
'perf kvm stat <command>' to run a command and gather performance counter
|
||||
statistics.
|
||||
Especially, perf 'kvm stat record/report' generates a statistical analysis
|
||||
of KVM events. Currently, vmexit, mmio and ioport events are supported.
|
||||
'perf kvm stat record <command>' records kvm events and the events between
|
||||
start and end <command>.
|
||||
of KVM events. Currently, vmexit, mmio (x86 only) and ioport (x86 only)
|
||||
events are supported. 'perf kvm stat record <command>' records kvm events
|
||||
and the events between start and end <command>.
|
||||
And this command produces a file which contains tracing results of kvm
|
||||
events.
|
||||
|
||||
@ -103,8 +103,8 @@ STAT REPORT OPTIONS
|
||||
analyze events which occures on this vcpu. (default: all vcpus)
|
||||
|
||||
--event=<value>::
|
||||
event to be analyzed. Possible values: vmexit, mmio, ioport.
|
||||
(default: vmexit)
|
||||
event to be analyzed. Possible values: vmexit, mmio (x86 only),
|
||||
ioport (x86 only). (default: vmexit)
|
||||
-k::
|
||||
--key=<value>::
|
||||
Sorting key. Possible values: sample (default, sort by samples
|
||||
@ -138,7 +138,8 @@ STAT LIVE OPTIONS
|
||||
|
||||
|
||||
--event=<value>::
|
||||
event to be analyzed. Possible values: vmexit, mmio, ioport.
|
||||
event to be analyzed. Possible values: vmexit,
|
||||
mmio (x86 only), ioport (x86 only).
|
||||
(default: vmexit)
|
||||
|
||||
-k::
|
||||
@ -147,7 +148,8 @@ STAT LIVE OPTIONS
|
||||
number), time (sort by average time).
|
||||
|
||||
--duration=<value>::
|
||||
Show events other than HLT that take longer than duration usecs.
|
||||
Show events other than HLT (x86 only) or Wait state (s390 only)
|
||||
that take longer than duration usecs.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
@ -38,3 +38,5 @@ arch/x86/include/uapi/asm/svm.h
|
||||
arch/x86/include/uapi/asm/vmx.h
|
||||
arch/x86/include/uapi/asm/kvm.h
|
||||
arch/x86/include/uapi/asm/kvm_perf.h
|
||||
arch/s390/include/uapi/asm/sie.h
|
||||
arch/s390/include/uapi/asm/kvm_perf.h
|
||||
|
@ -3,3 +3,5 @@ PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o
|
||||
|
105
tools/perf/arch/s390/util/kvm-stat.c
Normal file
105
tools/perf/arch/s390/util/kvm-stat.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Arch specific functions for perf kvm stat.
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License (version 2 only)
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "../../util/kvm-stat.h"
|
||||
#include <asm/kvm_perf.h>
|
||||
|
||||
define_exit_reasons_table(sie_exit_reasons, sie_intercept_code);
|
||||
define_exit_reasons_table(sie_icpt_insn_codes, icpt_insn_codes);
|
||||
define_exit_reasons_table(sie_sigp_order_codes, sigp_order_codes);
|
||||
define_exit_reasons_table(sie_diagnose_codes, diagnose_codes);
|
||||
define_exit_reasons_table(sie_icpt_prog_codes, icpt_prog_codes);
|
||||
|
||||
static void event_icpt_insn_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
unsigned long insn;
|
||||
|
||||
insn = perf_evsel__intval(evsel, sample, "instruction");
|
||||
key->key = icpt_insn_decoder(insn);
|
||||
key->exit_reasons = sie_icpt_insn_codes;
|
||||
}
|
||||
|
||||
static void event_sigp_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "order_code");
|
||||
key->exit_reasons = sie_sigp_order_codes;
|
||||
}
|
||||
|
||||
static void event_diag_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "code");
|
||||
key->exit_reasons = sie_diagnose_codes;
|
||||
}
|
||||
|
||||
static void event_icpt_prog_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
key->key = perf_evsel__intval(evsel, sample, "code");
|
||||
key->exit_reasons = sie_icpt_prog_codes;
|
||||
}
|
||||
|
||||
static struct child_event_ops child_events[] = {
|
||||
{ .name = "kvm:kvm_s390_intercept_instruction",
|
||||
.get_key = event_icpt_insn_get_key },
|
||||
{ .name = "kvm:kvm_s390_handle_sigp",
|
||||
.get_key = event_sigp_get_key },
|
||||
{ .name = "kvm:kvm_s390_handle_diag",
|
||||
.get_key = event_diag_get_key },
|
||||
{ .name = "kvm:kvm_s390_intercept_prog",
|
||||
.get_key = event_icpt_prog_get_key },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct kvm_events_ops exit_events = {
|
||||
.is_begin_event = exit_event_begin,
|
||||
.is_end_event = exit_event_end,
|
||||
.child_ops = child_events,
|
||||
.decode_key = exit_event_decode_key,
|
||||
.name = "VM-EXIT"
|
||||
};
|
||||
|
||||
const char * const kvm_events_tp[] = {
|
||||
"kvm:kvm_s390_sie_enter",
|
||||
"kvm:kvm_s390_sie_exit",
|
||||
"kvm:kvm_s390_intercept_instruction",
|
||||
"kvm:kvm_s390_handle_sigp",
|
||||
"kvm:kvm_s390_handle_diag",
|
||||
"kvm:kvm_s390_intercept_prog",
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct kvm_reg_events_ops kvm_reg_events_ops[] = {
|
||||
{ .name = "vmexit", .ops = &exit_events },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
const char * const kvm_skip_events[] = {
|
||||
"Wait state",
|
||||
NULL,
|
||||
};
|
||||
|
||||
int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
|
||||
{
|
||||
if (strstr(cpuid, "IBM/S390")) {
|
||||
kvm->exit_reasons = sie_exit_reasons;
|
||||
kvm->exit_reasons_isa = "SIE";
|
||||
} else
|
||||
return -ENOTSUP;
|
||||
|
||||
return 0;
|
||||
}
|
@ -88,7 +88,7 @@ void exit_event_decode_key(struct perf_kvm_stat *kvm,
|
||||
struct event_key *key,
|
||||
char *decode)
|
||||
{
|
||||
const char *exit_reason = get_exit_reason(kvm, kvm->exit_reasons,
|
||||
const char *exit_reason = get_exit_reason(kvm, key->exit_reasons,
|
||||
key->key);
|
||||
|
||||
scnprintf(decode, DECODE_STR_LEN, "%s", exit_reason);
|
||||
@ -261,6 +261,43 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_child_event(struct perf_kvm_stat *kvm,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
struct child_event_ops *child_ops;
|
||||
|
||||
child_ops = kvm->events_ops->child_ops;
|
||||
|
||||
if (!child_ops)
|
||||
return false;
|
||||
|
||||
for (; child_ops->name; child_ops++) {
|
||||
if (!strcmp(evsel->name, child_ops->name)) {
|
||||
child_ops->get_key(evsel, sample, key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handle_child_event(struct perf_kvm_stat *kvm,
|
||||
struct vcpu_event_record *vcpu_record,
|
||||
struct event_key *key,
|
||||
struct perf_sample *sample __maybe_unused)
|
||||
{
|
||||
struct kvm_event *event = NULL;
|
||||
|
||||
if (key->key != INVALID_KEY)
|
||||
event = find_create_kvm_event(kvm, key);
|
||||
|
||||
vcpu_record->last_event = event;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool skip_event(const char *event)
|
||||
{
|
||||
const char * const *skip_events;
|
||||
@ -361,7 +398,8 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct vcpu_event_record *vcpu_record;
|
||||
struct event_key key = {.key = INVALID_KEY};
|
||||
struct event_key key = { .key = INVALID_KEY,
|
||||
.exit_reasons = kvm->exit_reasons };
|
||||
|
||||
vcpu_record = per_vcpu_record(thread, evsel, sample);
|
||||
if (!vcpu_record)
|
||||
@ -375,6 +413,9 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,
|
||||
if (kvm->events_ops->is_begin_event(evsel, sample, &key))
|
||||
return handle_begin_event(kvm, vcpu_record, &key, sample->time);
|
||||
|
||||
if (is_child_event(kvm, evsel, sample, &key))
|
||||
return handle_child_event(kvm, vcpu_record, &key, sample);
|
||||
|
||||
if (kvm->events_ops->is_end_event(evsel, sample, &key))
|
||||
return handle_end_event(kvm, vcpu_record, &key, sample);
|
||||
|
||||
@ -1143,7 +1184,8 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
|
||||
{
|
||||
const struct option kvm_events_report_options[] = {
|
||||
OPT_STRING(0, "event", &kvm->report_event, "report event",
|
||||
"event for reporting: vmexit, mmio, ioport"),
|
||||
"event for reporting: vmexit, "
|
||||
"mmio (x86 only), ioport (x86 only)"),
|
||||
OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu,
|
||||
"vcpu id to report"),
|
||||
OPT_STRING('k', "key", &kvm->sort_key, "sort-key",
|
||||
@ -1249,7 +1291,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
||||
"key for sorting: sample(sort by samples number)"
|
||||
" time (sort by avg time)"),
|
||||
OPT_U64(0, "duration", &kvm->duration,
|
||||
"show events other than HALT that take longer than duration usecs"),
|
||||
"show events other than"
|
||||
" HLT (x86 only) or Wait state (s390 only)"
|
||||
" that take longer than duration usecs"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const live_usage[] = {
|
||||
|
@ -12,6 +12,7 @@ struct event_key {
|
||||
#define INVALID_KEY (~0ULL)
|
||||
u64 key;
|
||||
int info;
|
||||
struct exit_reasons_table *exit_reasons;
|
||||
};
|
||||
|
||||
struct kvm_event_stats {
|
||||
@ -41,12 +42,20 @@ struct kvm_event_key {
|
||||
|
||||
struct perf_kvm_stat;
|
||||
|
||||
struct child_event_ops {
|
||||
void (*get_key)(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct kvm_events_ops {
|
||||
bool (*is_begin_event)(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key);
|
||||
bool (*is_end_event)(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample, struct event_key *key);
|
||||
struct child_event_ops *child_ops;
|
||||
void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key,
|
||||
char *decode);
|
||||
const char *name;
|
||||
|
Loading…
Reference in New Issue
Block a user