Skip to content

Commit

Permalink
perf: 'perf kvm' tool for monitoring guest performance from host
Browse files Browse the repository at this point in the history
Here is the patch of userspace perf tool.

Signed-off-by: Zhang Yanmin <yanmin_zhang@linux.intel.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
  • Loading branch information
Zhang, Yanmin authored and avikivity committed Apr 19, 2010
1 parent ff9d07a commit a1645ce
Show file tree
Hide file tree
Showing 30 changed files with 1,407 additions and 316 deletions.
67 changes: 67 additions & 0 deletions tools/perf/Documentation/perf-kvm.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
perf-kvm(1)
==============

NAME
----
perf-kvm - Tool to trace/measure kvm guest os

SYNOPSIS
--------
[verse]
'perf kvm' [--host] [--guest] [--guestmount=<path>
[--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]]
{top|record|report|diff|buildid-list}
'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
| --guestvmlinux=<path>] {top|record|report|diff|buildid-list}

DESCRIPTION
-----------
There are a couple of variants of perf kvm:

'perf kvm [options] top <command>' to generates and displays
a performance counter profile of guest os in realtime
of an arbitrary workload.

'perf kvm record <command>' to record the performance couinter profile
of an arbitrary workload and save it into a perf data file. If both
--host and --guest are input, the perf data file name is perf.data.kvm.
If there is no --host but --guest, the file name is perf.data.guest.
If there is no --guest but --host, the file name is perf.data.host.

'perf kvm report' to display the performance counter profile information
recorded via perf kvm record.

'perf kvm diff' to displays the performance difference amongst two perf.data
files captured via perf record.

'perf kvm buildid-list' to display the buildids found in a perf data file,
so that other tools can be used to fetch packages with matching symbol tables
for use by perf report.

OPTIONS
-------
--host=::
Collect host side perforamnce profile.
--guest=::
Collect guest side perforamnce profile.
--guestmount=<path>::
Guest os root file system mount directory. Users mounts guest os
root directories under <path> by a specific filesystem access method,
typically, sshfs. For example, start 2 guest os. The one's pid is 8888
and the other's is 9999.
#mkdir ~/guestmount; cd ~/guestmount
#sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/
#sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/
#perf kvm --host --guest --guestmount=~/guestmount top
--guestkallsyms=<path>::
Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest
kernel symbols. Users copy it out from guest os.
--guestmodules=<path>::
Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest
kernel module information. Users copy it out from guest os.
--guestvmlinux=<path>::
Guest os kernel vmlinux.

SEE ALSO
--------
linkperf:perf-top[1] perf-record[1] perf-report[1] perf-diff[1] perf-buildid-list[1]
1 change: 1 addition & 0 deletions tools/perf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-trace.o
BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o

PERFLIBS = $(LIB_FILE)

Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-annotate.c
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ static int __cmd_annotate(void)
perf_session__fprintf(session, stdout);

if (verbose > 2)
dsos__fprintf(stdout);
dsos__fprintf(&session->kerninfo_root, stdout);

perf_session__collapse_resort(&session->hists);
perf_session__output_resort(&session->hists, session->event_total[0]);
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-buildid-list.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static int __cmd_buildid_list(void)
if (with_hits)
perf_session__process_events(session, &build_id__mark_dso_hit_ops);

dsos__fprintf_buildid(stdout, with_hits);
dsos__fprintf_buildid(&session->kerninfo_root, stdout, with_hits);

perf_session__delete(session);
return err;
Expand Down
6 changes: 5 additions & 1 deletion tools/perf/builtin-diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static int perf_session__add_hist_entry(struct perf_session *self,
return -ENOMEM;

if (hit)
he->count += count;
__perf_session__add_count(he, al, count);

return 0;
}
Expand Down Expand Up @@ -225,6 +225,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used)
input_new = argv[1];
} else
input_new = argv[0];
} else if (symbol_conf.default_guest_vmlinux_name ||
symbol_conf.default_guest_kallsyms) {
input_old = "perf.data.host";
input_new = "perf.data.guest";
}

symbol_conf.exclude_other = false;
Expand Down
11 changes: 9 additions & 2 deletions tools/perf/builtin-kmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
int n_lines, int is_caller)
{
struct rb_node *next;
struct kernel_info *kerninfo;

printf("%.102s\n", graph_dotted_line);
printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
Expand All @@ -359,19 +360,25 @@ static void __print_result(struct rb_root *root, struct perf_session *session,

next = rb_first(root);

kerninfo = kerninfo__findhost(&session->kerninfo_root);
if (!kerninfo) {
pr_err("__print_result: couldn't find kernel information\n");
return;
}
while (next && n_lines--) {
struct alloc_stat *data = rb_entry(next, struct alloc_stat,
node);
struct symbol *sym = NULL;
struct map_groups *kmaps = &kerninfo->kmaps;
struct map *map;
char buf[BUFSIZ];
u64 addr;

if (is_caller) {
addr = data->call_site;
if (!raw_ip)
sym = map_groups__find_function(&session->kmaps,
addr, &map, NULL);
sym = map_groups__find_function(kmaps, addr,
&map, NULL);
} else
addr = data->ptr;

Expand Down
144 changes: 144 additions & 0 deletions tools/perf/builtin-kvm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include "builtin.h"
#include "perf.h"

#include "util/util.h"
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/header.h"
#include "util/session.h"

#include "util/parse-options.h"
#include "util/trace-event.h"

#include "util/debug.h"

#include <sys/prctl.h>

#include <semaphore.h>
#include <pthread.h>
#include <math.h>

static char *file_name;
static char name_buffer[256];

int perf_host = 1;
int perf_guest;

static const char * const kvm_usage[] = {
"perf kvm [<options>] {top|record|report|diff|buildid-list}",
NULL
};

static const struct option kvm_options[] = {
OPT_STRING('i', "input", &file_name, "file",
"Input file name"),
OPT_STRING('o', "output", &file_name, "file",
"Output file name"),
OPT_BOOLEAN(0, "guest", &perf_guest,
"Collect guest os data"),
OPT_BOOLEAN(0, "host", &perf_host,
"Collect guest os data"),
OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
"guest mount directory under which every guest os"
" instance has a subdir"),
OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name,
"file", "file saving guest os vmlinux"),
OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms,
"file", "file saving guest os /proc/kallsyms"),
OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,
"file", "file saving guest os /proc/modules"),
OPT_END()
};

static int __cmd_record(int argc, const char **argv)
{
int rec_argc, i = 0, j;
const char **rec_argv;

rec_argc = argc + 2;
rec_argv = calloc(rec_argc + 1, sizeof(char *));
rec_argv[i++] = strdup("record");
rec_argv[i++] = strdup("-o");
rec_argv[i++] = strdup(file_name);
for (j = 1; j < argc; j++, i++)
rec_argv[i] = argv[j];

BUG_ON(i != rec_argc);

return cmd_record(i, rec_argv, NULL);
}

static int __cmd_report(int argc, const char **argv)
{
int rec_argc, i = 0, j;
const char **rec_argv;

rec_argc = argc + 2;
rec_argv = calloc(rec_argc + 1, sizeof(char *));
rec_argv[i++] = strdup("report");
rec_argv[i++] = strdup("-i");
rec_argv[i++] = strdup(file_name);
for (j = 1; j < argc; j++, i++)
rec_argv[i] = argv[j];

BUG_ON(i != rec_argc);

return cmd_report(i, rec_argv, NULL);
}

static int __cmd_buildid_list(int argc, const char **argv)
{
int rec_argc, i = 0, j;
const char **rec_argv;

rec_argc = argc + 2;
rec_argv = calloc(rec_argc + 1, sizeof(char *));
rec_argv[i++] = strdup("buildid-list");
rec_argv[i++] = strdup("-i");
rec_argv[i++] = strdup(file_name);
for (j = 1; j < argc; j++, i++)
rec_argv[i] = argv[j];

BUG_ON(i != rec_argc);

return cmd_buildid_list(i, rec_argv, NULL);
}

int cmd_kvm(int argc, const char **argv, const char *prefix __used)
{
perf_host = perf_guest = 0;

argc = parse_options(argc, argv, kvm_options, kvm_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc)
usage_with_options(kvm_usage, kvm_options);

if (!perf_host)
perf_guest = 1;

if (!file_name) {
if (perf_host && !perf_guest)
sprintf(name_buffer, "perf.data.host");
else if (!perf_host && perf_guest)
sprintf(name_buffer, "perf.data.guest");
else
sprintf(name_buffer, "perf.data.kvm");
file_name = name_buffer;
}

if (!strncmp(argv[0], "rec", 3))
return __cmd_record(argc, argv);
else if (!strncmp(argv[0], "rep", 3))
return __cmd_report(argc, argv);
else if (!strncmp(argv[0], "diff", 4))
return cmd_diff(argc, argv, NULL);
else if (!strncmp(argv[0], "top", 3))
return cmd_top(argc, argv, NULL);
else if (!strncmp(argv[0], "buildid-list", 12))
return __cmd_buildid_list(argc, argv);
else
usage_with_options(kvm_usage, kvm_options);

return 0;
}
63 changes: 60 additions & 3 deletions tools/perf/builtin-record.c
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,52 @@ static void atexit_header(void)
}
}

static void event__synthesize_guest_os(struct kernel_info *kerninfo,
void *data __attribute__((unused)))
{
int err;
char *guest_kallsyms;
char path[PATH_MAX];

if (is_host_kernel(kerninfo))
return;

/*
*As for guest kernel when processing subcommand record&report,
*we arrange module mmap prior to guest kernel mmap and trigger
*a preload dso because default guest module symbols are loaded
*from guest kallsyms instead of /lib/modules/XXX/XXX. This
*method is used to avoid symbol missing when the first addr is
*in module instead of in guest kernel.
*/
err = event__synthesize_modules(process_synthesized_event,
session,
kerninfo);
if (err < 0)
pr_err("Couldn't record guest kernel [%d]'s reference"
" relocation symbol.\n", kerninfo->pid);

if (is_default_guest(kerninfo))
guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms;
else {
sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir);
guest_kallsyms = path;
}

/*
* We use _stext for guest kernel because guest kernel's /proc/kallsyms
* have no _text sometimes.
*/
err = event__synthesize_kernel_mmap(process_synthesized_event,
session, kerninfo, "_text");
if (err < 0)
err = event__synthesize_kernel_mmap(process_synthesized_event,
session, kerninfo, "_stext");
if (err < 0)
pr_err("Couldn't record guest kernel [%d]'s reference"
" relocation symbol.\n", kerninfo->pid);
}

static int __cmd_record(int argc, const char **argv)
{
int i, counter;
Expand All @@ -467,6 +513,7 @@ static int __cmd_record(int argc, const char **argv)
int child_ready_pipe[2], go_pipe[2];
const bool forks = argc > 0;
char buf;
struct kernel_info *kerninfo;

page_size = sysconf(_SC_PAGE_SIZE);

Expand Down Expand Up @@ -635,21 +682,31 @@ static int __cmd_record(int argc, const char **argv)
advance_output(err);
}

kerninfo = kerninfo__findhost(&session->kerninfo_root);
if (!kerninfo) {
pr_err("Couldn't find native kernel information.\n");
return -1;
}

err = event__synthesize_kernel_mmap(process_synthesized_event,
session, "_text");
session, kerninfo, "_text");
if (err < 0)
err = event__synthesize_kernel_mmap(process_synthesized_event,
session, "_stext");
session, kerninfo, "_stext");
if (err < 0) {
pr_err("Couldn't record kernel reference relocation symbol.\n");
return err;
}

err = event__synthesize_modules(process_synthesized_event, session);
err = event__synthesize_modules(process_synthesized_event,
session, kerninfo);
if (err < 0) {
pr_err("Couldn't record kernel reference relocation symbol.\n");
return err;
}
if (perf_guest)
kerninfo__process_allkernels(&session->kerninfo_root,
event__synthesize_guest_os, session);

if (!system_wide && profile_cpu == -1)
event__synthesize_thread(target_tid, process_synthesized_event,
Expand Down
Loading

0 comments on commit a1645ce

Please sign in to comment.