Commit a1645ce1 authored by Zhang, Yanmin's avatar Zhang, Yanmin Committed by Avi Kivity

perf: 'perf kvm' tool for monitoring guest performance from host

Here is the patch of userspace perf tool.
Signed-off-by: default avatarZhang Yanmin <yanmin_zhang@linux.intel.com>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent ff9d07a0
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]
...@@ -472,6 +472,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-trace.o ...@@ -472,6 +472,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-trace.o
BUILTIN_OBJS += $(OUTPUT)builtin-probe.o BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
BUILTIN_OBJS += $(OUTPUT)builtin-lock.o BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
PERFLIBS = $(LIB_FILE) PERFLIBS = $(LIB_FILE)
......
...@@ -571,7 +571,7 @@ static int __cmd_annotate(void) ...@@ -571,7 +571,7 @@ static int __cmd_annotate(void)
perf_session__fprintf(session, stdout); perf_session__fprintf(session, stdout);
if (verbose > 2) if (verbose > 2)
dsos__fprintf(stdout); dsos__fprintf(&session->kerninfo_root, stdout);
perf_session__collapse_resort(&session->hists); perf_session__collapse_resort(&session->hists);
perf_session__output_resort(&session->hists, session->event_total[0]); perf_session__output_resort(&session->hists, session->event_total[0]);
......
...@@ -46,7 +46,7 @@ static int __cmd_buildid_list(void) ...@@ -46,7 +46,7 @@ static int __cmd_buildid_list(void)
if (with_hits) if (with_hits)
perf_session__process_events(session, &build_id__mark_dso_hit_ops); 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); perf_session__delete(session);
return err; return err;
......
...@@ -33,7 +33,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, ...@@ -33,7 +33,7 @@ static int perf_session__add_hist_entry(struct perf_session *self,
return -ENOMEM; return -ENOMEM;
if (hit) if (hit)
he->count += count; __perf_session__add_count(he, al, count);
return 0; return 0;
} }
...@@ -225,6 +225,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used) ...@@ -225,6 +225,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used)
input_new = argv[1]; input_new = argv[1];
} else } else
input_new = argv[0]; 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; symbol_conf.exclude_other = false;
......
...@@ -351,6 +351,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, ...@@ -351,6 +351,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
int n_lines, int is_caller) int n_lines, int is_caller)
{ {
struct rb_node *next; struct rb_node *next;
struct kernel_info *kerninfo;
printf("%.102s\n", graph_dotted_line); printf("%.102s\n", graph_dotted_line);
printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
...@@ -359,10 +360,16 @@ static void __print_result(struct rb_root *root, struct perf_session *session, ...@@ -359,10 +360,16 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
next = rb_first(root); 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--) { while (next && n_lines--) {
struct alloc_stat *data = rb_entry(next, struct alloc_stat, struct alloc_stat *data = rb_entry(next, struct alloc_stat,
node); node);
struct symbol *sym = NULL; struct symbol *sym = NULL;
struct map_groups *kmaps = &kerninfo->kmaps;
struct map *map; struct map *map;
char buf[BUFSIZ]; char buf[BUFSIZ];
u64 addr; u64 addr;
...@@ -370,8 +377,8 @@ static void __print_result(struct rb_root *root, struct perf_session *session, ...@@ -370,8 +377,8 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
if (is_caller) { if (is_caller) {
addr = data->call_site; addr = data->call_site;
if (!raw_ip) if (!raw_ip)
sym = map_groups__find_function(&session->kmaps, sym = map_groups__find_function(kmaps, addr,
addr, &map, NULL); &map, NULL);
} else } else
addr = data->ptr; addr = data->ptr;
......
#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;
}
...@@ -456,6 +456,52 @@ static void atexit_header(void) ...@@ -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) static int __cmd_record(int argc, const char **argv)
{ {
int i, counter; int i, counter;
...@@ -467,6 +513,7 @@ static int __cmd_record(int argc, const char **argv) ...@@ -467,6 +513,7 @@ static int __cmd_record(int argc, const char **argv)
int child_ready_pipe[2], go_pipe[2]; int child_ready_pipe[2], go_pipe[2];
const bool forks = argc > 0; const bool forks = argc > 0;
char buf; char buf;
struct kernel_info *kerninfo;
page_size = sysconf(_SC_PAGE_SIZE); page_size = sysconf(_SC_PAGE_SIZE);
...@@ -635,21 +682,31 @@ static int __cmd_record(int argc, const char **argv) ...@@ -635,21 +682,31 @@ static int __cmd_record(int argc, const char **argv)
advance_output(err); 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, err = event__synthesize_kernel_mmap(process_synthesized_event,
session, "_text"); session, kerninfo, "_text");
if (err < 0) if (err < 0)
err = event__synthesize_kernel_mmap(process_synthesized_event, err = event__synthesize_kernel_mmap(process_synthesized_event,
session, "_stext"); session, kerninfo, "_stext");
if (err < 0) { if (err < 0) {
pr_err("Couldn't record kernel reference relocation symbol.\n"); pr_err("Couldn't record kernel reference relocation symbol.\n");
return err; return err;
} }
err = event__synthesize_modules(process_synthesized_event, session); err = event__synthesize_modules(process_synthesized_event,
session, kerninfo);
if (err < 0) { if (err < 0) {
pr_err("Couldn't record kernel reference relocation symbol.\n"); pr_err("Couldn't record kernel reference relocation symbol.\n");
return err; return err;
} }
if (perf_guest)
kerninfo__process_allkernels(&session->kerninfo_root,
event__synthesize_guest_os, session);
if (!system_wide && profile_cpu == -1) if (!system_wide && profile_cpu == -1)
event__synthesize_thread(target_tid, process_synthesized_event, event__synthesize_thread(target_tid, process_synthesized_event,
......
...@@ -108,7 +108,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, ...@@ -108,7 +108,7 @@ static int perf_session__add_hist_entry(struct perf_session *self,
return -ENOMEM; return -ENOMEM;
if (hit) if (hit)
he->count += data->period; __perf_session__add_count(he, al, data->period);
if (symbol_conf.use_callchain) { if (symbol_conf.use_callchain) {
if (!hit) if (!hit)
...@@ -313,7 +313,7 @@ static int __cmd_report(void) ...@@ -313,7 +313,7 @@ static int __cmd_report(void)
perf_session__fprintf(session, stdout); perf_session__fprintf(session, stdout);
if (verbose > 2) if (verbose > 2)
dsos__fprintf(stdout); dsos__fprintf(&session->kerninfo_root, stdout);
next = rb_first(&session->stats_by_id); next = rb_first(&session->stats_by_id);
while (next) { while (next) {
...@@ -450,6 +450,8 @@ static const struct option options[] = { ...@@ -450,6 +450,8 @@ static const struct option options[] = {
"sort by key(s): pid, comm, dso, symbol, parent"), "sort by key(s): pid, comm, dso, symbol, parent"),
OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths,
"Don't shorten the pathnames taking into account the cwd"), "Don't shorten the pathnames taking into account the cwd"),
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
"Show sample percentage for different cpu modes"),
OPT_STRING('p', "parent", &parent_pattern, "regex", OPT_STRING('p', "parent", &parent_pattern, "regex",
"regex filter to identify parent, see: '--sort parent'"), "regex filter to identify parent, see: '--sort parent'"),
OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
......
...@@ -420,8 +420,9 @@ static double sym_weight(const struct sym_entry *sym) ...@@ -420,8 +420,9 @@ static double sym_weight(const struct sym_entry *sym)
} }
static long samples; static long samples;
static long userspace_samples; static long kernel_samples, us_samples;
static long exact_samples; static long exact_samples;
static long guest_us_samples, guest_kernel_samples;
static const char CONSOLE_CLEAR[] = ""; static const char CONSOLE_CLEAR[] = "";
static void __list_insert_active_sym(struct sym_entry *syme) static void __list_insert_active_sym(struct sym_entry *syme)
...@@ -461,7 +462,10 @@ static void print_sym_table(void) ...@@ -461,7 +462,10 @@ static void print_sym_table(void)
int printed = 0, j; int printed = 0, j;
int counter, snap = !display_weighted ? sym_counter : 0; int counter, snap = !display_weighted ? sym_counter : 0;
float samples_per_sec = samples/delay_secs; float samples_per_sec = samples/delay_secs;
float ksamples_per_sec = (samples-userspace_samples)/delay_secs; float ksamples_per_sec = kernel_samples/delay_secs;
float us_samples_per_sec = (us_samples)/delay_secs;
float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs;
float guest_us_samples_per_sec = (guest_us_samples)/delay_secs;
float esamples_percent = (100.0*exact_samples)/samples; float esamples_percent = (100.0*exact_samples)/samples;
float sum_ksamples = 0.0; float sum_ksamples = 0.0;
struct sym_entry *syme, *n; struct sym_entry *syme, *n;
...@@ -470,7 +474,8 @@ static void print_sym_table(void) ...@@ -470,7 +474,8 @@ static void print_sym_table(void)
int sym_width = 0, dso_width = 0, dso_short_width = 0; int sym_width = 0, dso_width = 0, dso_short_width = 0;
const int win_width = winsize.ws_col - 1; const int win_width = winsize.ws_col - 1;
samples = userspace_samples = exact_samples = 0; samples = us_samples = kernel_samples = exact_samples = 0;
guest_kernel_samples = guest_us_samples = 0;
/* Sort the active symbols */ /* Sort the active symbols */
pthread_mutex_lock(&active_symbols_lock); pthread_mutex_lock(&active_symbols_lock);
...@@ -501,10 +506,30 @@ static void print_sym_table(void) ...@@ -501,10 +506,30 @@ static void print_sym_table(void)
puts(CONSOLE_CLEAR); puts(CONSOLE_CLEAR);
printf("%-*.*s\n", win_width, win_width, graph_dotted_line); printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% exact: %4.1f%% [", if (!perf_guest) {
printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%"
" exact: %4.1f%% [",
samples_per_sec, samples_per_sec,
100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)), 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) /
samples_per_sec)),
esamples_percent); esamples_percent);
} else {
printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%"
" guest kernel:%4.1f%% guest us:%4.1f%%"
" exact: %4.1f%% [",
samples_per_sec,
100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) /
samples_per_sec)),
100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) /
samples_per_sec)),
100.0 - (100.0 * ((samples_per_sec -
guest_kernel_samples_per_sec) /
samples_per_sec)),
100.0 - (100.0 * ((samples_per_sec -
guest_us_samples_per_sec) /
samples_per_sec)),
esamples_percent);
}
if (nr_counters == 1 || !display_weighted) { if (nr_counters == 1 || !display_weighted) {
printf("%Ld", (u64)attrs[0].sample_period); printf("%Ld", (u64)attrs[0].sample_period);
...@@ -597,7 +622,6 @@ static void print_sym_table(void) ...@@ -597,7 +622,6 @@ static void print_sym_table(void)
syme = rb_entry(nd, struct sym_entry, rb_node); syme = rb_entry(nd, struct sym_entry, rb_node);
sym = sym_entry__symbol(syme); sym = sym_entry__symbol(syme);
if (++printed > print_entries || (int)syme->snap_count < count_filter) if (++printed > print_entries || (int)syme->snap_count < count_filter)
continue; continue;
...@@ -761,7 +785,7 @@ static int key_mapped(int c) ...@@ -761,7 +785,7 @@ static int key_mapped(int c)
return 0; return 0;
} }
static void handle_keypress(int c) static void handle_keypress(struct perf_session *session, int c)
{ {
if (!key_mapped(c)) { if (!key_mapped(c)) {
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
...@@ -830,7 +854,7 @@ static void handle_keypress(int c) ...@@ -830,7 +854,7 @@ static void handle_keypress(int c)
case 'Q': case 'Q':
printf("exiting.\n"); printf("exiting.\n");
if (dump_symtab) if (dump_symtab)
dsos__fprintf(stderr); dsos__fprintf(&session->kerninfo_root, stderr);
exit(0); exit(0);
case 's': case 's':
prompt_symbol(&sym_filter_entry, "Enter details symbol"); prompt_symbol(&sym_filter_entry, "Enter details symbol");
...@@ -866,6 +890,7 @@ static void *display_thread(void *arg __used) ...@@ -866,6 +890,7 @@ static void *display_thread(void *arg __used)
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
struct termios tc, save; struct termios tc, save;
int delay_msecs, c; int delay_msecs, c;
struct perf_session *session = (struct perf_session *) arg;
tcgetattr(0, &save); tcgetattr(0, &save);
tc = save; tc = save;
...@@ -886,7 +911,7 @@ static void *display_thread(void *arg __used) ...@@ -886,7 +911,7 @@ static void *display_thread(void *arg __used)
c = getc(stdin); c = getc(stdin);
tcsetattr(0, TCSAFLUSH, &save); tcsetattr(0, TCSAFLUSH, &save);
handle_keypress(c); handle_keypress(session, c);
goto repeat; goto repeat;
return NULL; return NULL;
...@@ -957,24 +982,46 @@ static void event__process_sample(const event_t *self, ...@@ -957,24 +982,46 @@ static void event__process_sample(const event_t *self,
u64 ip = self->ip.ip; u64 ip = self->ip.ip;
struct sym_entry *syme; struct sym_entry *syme;
struct addr_location al; struct addr_location al;
struct kernel_info *kerninfo;
u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
++samples; ++samples;
switch (origin) { switch (origin) {
case PERF_RECORD_MISC_USER: case PERF_RECORD_MISC_USER:
++userspace_samples; ++us_samples;
if (hide_user_symbols) if (hide_user_symbols)
return; return;
kerninfo = kerninfo__findhost(&session->kerninfo_root);
break; break;
case PERF_RECORD_MISC_KERNEL: case PERF_RECORD_MISC_KERNEL:
++kernel_samples;
if (hide_kernel_symbols) if (hide_kernel_symbols)
return; return;
kerninfo = kerninfo__findhost(&session->kerninfo_root);
break;
case PERF_RECORD_MISC_GUEST_KERNEL:
++guest_kernel_samples;
kerninfo = kerninfo__find(&session->kerninfo_root,
self->ip.pid);
break; break;
case PERF_RECORD_MISC_GUEST_USER:
++guest_us_samples;
/*
* TODO: we don't process guest user from host side
* except simple counting.
*/
return;
default: default:
return; return;
} }
if (!kerninfo && perf_guest) {
pr_err("Can't find guest [%d]'s kernel information\n",
self->ip.pid);
return;
}
if (self->header.misc & PERF_RECORD_MISC_EXACT) if (self->header.misc & PERF_RECORD_MISC_EXACT)
exact_samples++; exact_samples++;
...@@ -994,7 +1041,7 @@ static void event__process_sample(const event_t *self, ...@@ -994,7 +1041,7 @@ static void event__process_sample(const event_t *self,
* --hide-kernel-symbols, even if the user specifies an * --hide-kernel-symbols, even if the user specifies an
* invalid --vmlinux ;-) * invalid --vmlinux ;-)
*/ */
if (al.map == session->vmlinux_maps[MAP__FUNCTION] && if (al.map == kerninfo->vmlinux_maps[MAP__FUNCTION] &&
RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
pr_err("The %s file can't be used\n", pr_err("The %s file can't be used\n",
symbol_conf.vmlinux_name); symbol_conf.vmlinux_name);
...@@ -1261,7 +1308,7 @@ static int __cmd_top(void) ...@@ -1261,7 +1308,7 @@ static int __cmd_top(void)
perf_session__mmap_read(session); perf_session__mmap_read(session);
if (pthread_create(&thread, NULL, display_thread, NULL)) { if (pthread_create(&thread, NULL, display_thread, session)) {
printf("Could not create display thread.\n"); printf("Could not create display thread.\n");
exit(-1); exit(-1);
} }
......
...@@ -32,5 +32,6 @@ extern int cmd_version(int argc, const char **argv, const char *prefix); ...@@ -32,5 +32,6 @@ extern int cmd_version(int argc, const char **argv, const char *prefix);
extern int cmd_probe(int argc, const char **argv, const char *prefix); extern int cmd_probe(int argc, const char **argv, const char *prefix);
extern int cmd_kmem(int argc, const char **argv, const char *prefix); extern int cmd_kmem(int argc, const char **argv, const char *prefix);
extern int cmd_lock(int argc, const char **argv, const char *prefix); extern int cmd_lock(int argc, const char **argv, const char *prefix);
extern int cmd_kvm(int argc, const char **argv, const char *prefix);
#endif #endif
...@@ -19,3 +19,4 @@ perf-trace mainporcelain common ...@@ -19,3 +19,4 @@ perf-trace mainporcelain common
perf-probe mainporcelain common perf-probe mainporcelain common
perf-kmem mainporcelain common perf-kmem mainporcelain common
perf-lock mainporcelain common perf-lock mainporcelain common
perf-kvm mainporcelain common
...@@ -307,6 +307,7 @@ static void handle_internal_command(int argc, const char **argv) ...@@ -307,6 +307,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "probe", cmd_probe, 0 }, { "probe", cmd_probe, 0 },
{ "kmem", cmd_kmem, 0 }, { "kmem", cmd_kmem, 0 },
{ "lock", cmd_lock, 0 }, { "lock", cmd_lock, 0 },
{ "kvm", cmd_kvm, 0 },
}; };
unsigned int i; unsigned int i;
static const char ext[] = STRIP_EXTENSION; static const char ext[] = STRIP_EXTENSION;
......
...@@ -131,4 +131,6 @@ struct ip_callchain { ...@@ -131,4 +131,6 @@ struct ip_callchain {
u64 ips[0]; u64 ips[0];
}; };
extern int perf_host, perf_guest;
#endif #endif
...@@ -24,7 +24,7 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) ...@@ -24,7 +24,7 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session)
} }
thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
event->ip.ip, &al); event->ip.pid, event->ip.ip, &al);
if (al.map != NULL) if (al.map != NULL)
al.map->dso->hit = 1; al.map->dso->hit = 1;
......
...@@ -112,7 +112,11 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, ...@@ -112,7 +112,11 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
event_t ev = { event_t ev = {
.header = { .header = {
.type = PERF_RECORD_MMAP, .type = PERF_RECORD_MMAP,
.misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ /*
* Just like the kernel, see __perf_event_mmap
* in kernel/perf_event.c
*/
.misc = PERF_RECORD_MISC_USER,
}, },
}; };
int n; int n;
...@@ -167,11 +171,23 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, ...@@ -167,11 +171,23 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
} }
int event__synthesize_modules(event__handler_t process, int event__synthesize_modules(event__handler_t process,
struct perf_session *session) struct perf_session *session,
struct kernel_info *kerninfo)
{ {
struct rb_node *nd; struct rb_node *nd;
struct map_groups *kmaps = &kerninfo->kmaps;
u16 misc;
/*
* kernel uses 0 for user space maps, see kernel/perf_event.c
* __perf_event_mmap
*/
if (is_host_kernel(kerninfo))
misc = PERF_RECORD_MISC_KERNEL;
else
misc = PERF_RECORD_MISC_GUEST_KERNEL;
for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]); for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]);
nd; nd = rb_next(nd)) { nd; nd = rb_next(nd)) {
event_t ev; event_t ev;
size_t size; size_t size;
...@@ -182,12 +198,13 @@ int event__synthesize_modules(event__handler_t process, ...@@ -182,12 +198,13 @@ int event__synthesize_modules(event__handler_t process,
size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
memset(&ev, 0, sizeof(ev)); memset(&ev, 0, sizeof(ev));
ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ ev.mmap.header.misc = misc;
ev.mmap.header.type = PERF_RECORD_MMAP; ev.mmap.header.type = PERF_RECORD_MMAP;
ev.mmap.header.size = (sizeof(ev.mmap) - ev.mmap.header.size = (sizeof(ev.mmap) -
(sizeof(ev.mmap.filename) - size)); (sizeof(ev.mmap.filename) - size));
ev.mmap.start = pos->start; ev.mmap.start = pos->start;
ev.mmap.len = pos->end - pos->start; ev.mmap.len = pos->end - pos->start;
ev.mmap.pid = kerninfo->pid;
memcpy(ev.mmap.filename, pos->dso->long_name, memcpy(ev.mmap.filename, pos->dso->long_name,
pos->dso->long_name_len + 1); pos->dso->long_name_len + 1);
...@@ -250,13 +267,18 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start) ...@@ -250,13 +267,18 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start)
int event__synthesize_kernel_mmap(event__handler_t process, int event__synthesize_kernel_mmap(event__handler_t process,
struct perf_session *session, struct perf_session *session,
struct kernel_info *kerninfo,
const char *symbol_name) const char *symbol_name)
{ {
size_t size; size_t size;
const char *filename, *mmap_name;
char path[PATH_MAX];
char name_buff[PATH_MAX];
struct map *map;
event_t ev = { event_t ev = {
.header = { .header = {
.type = PERF_RECORD_MMAP, .type = PERF_RECORD_MMAP,
.misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */
}, },
}; };
/* /*
...@@ -266,16 +288,37 @@ int event__synthesize_kernel_mmap(event__handler_t process, ...@@ -266,16 +288,37 @@ int event__synthesize_kernel_mmap(event__handler_t process,
*/ */
struct process_symbol_args args = { .name = symbol_name, }; struct process_symbol_args args = { .name = symbol_name, };
if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) mmap_name = kern_mmap_name(kerninfo, name_buff);
if (is_host_kernel(kerninfo)) {
/*
* kernel uses PERF_RECORD_MISC_USER for user space maps,
* see kernel/perf_event.c __perf_event_mmap
*/
ev.header.misc = PERF_RECORD_MISC_KERNEL;
filename = "/proc/kallsyms";
} else {
ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
if (is_default_guest(kerninfo))
filename = (char *) symbol_conf.default_guest_kallsyms;
else {
sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir);
filename = path;
}
}
if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0)
return -ENOENT; return -ENOENT;
map = kerninfo->vmlinux_maps[MAP__FUNCTION];
size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename),
"[kernel.kallsyms.%s]", symbol_name) + 1; "%s%s", mmap_name, symbol_name) + 1;
size = ALIGN(size, sizeof(u64)); size = ALIGN(size, sizeof(u64));
ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); ev.mmap.header.size = (sizeof(ev.mmap) -
(sizeof(ev.mmap.filename) - size));
ev.mmap.pgoff = args.start; ev.mmap.pgoff = args.start;
ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start; ev.mmap.start = map->start;
ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; ev.mmap.len = map->end - ev.mmap.start;
ev.mmap.pid = kerninfo->pid;
return process(&ev, session); return process(&ev, session);
} }
...@@ -329,22 +372,50 @@ int event__process_lost(event_t *self, struct perf_session *session) ...@@ -329,22 +372,50 @@ int event__process_lost(event_t *self, struct perf_session *session)
return 0; return 0;
} }
int event__process_mmap(event_t *self, struct perf_session *session) static void event_set_kernel_mmap_len(struct map **maps, event_t *self)
{
maps[MAP__FUNCTION]->start = self->mmap.start;
maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len;
/*
* Be a bit paranoid here, some perf.data file came with
* a zero sized synthesized MMAP event for the kernel.
*/
if (maps[MAP__FUNCTION]->end == 0)
maps[MAP__FUNCTION]->end = ~0UL;
}
static int event__process_kernel_mmap(event_t *self,
struct perf_session *session)
{ {
struct thread *thread;
struct map *map; struct map *map;
char kmmap_prefix[PATH_MAX];
struct kernel_info *kerninfo;
enum dso_kernel_type kernel_type;
bool is_kernel_mmap;
kerninfo = kerninfo__findnew(&session->kerninfo_root, self->mmap.pid);
if (!kerninfo) {
pr_err("Can't find id %d's kerninfo\n", self->mmap.pid);
goto out_problem;
}
dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", kern_mmap_name(kerninfo, kmmap_prefix);
self->mmap.pid, self->mmap.tid, self->mmap.start, if (is_host_kernel(kerninfo))
self->mmap.len, self->mmap.pgoff, self->mmap.filename); kernel_type = DSO_TYPE_KERNEL;
else
kernel_type = DSO_TYPE_GUEST_KERNEL;
if (self->mmap.pid == 0) { is_kernel_mmap = memcmp(self->mmap.filename,
static const char kmmap_prefix[] = "[kernel.kallsyms."; kmmap_prefix,
strlen(kmmap_prefix)) == 0;
if (self->mmap.filename[0] == '/' ||
(!is_kernel_mmap && self->mmap.filename[0] == '[')) {
if (self->mmap.filename[0] == '/') {
char short_module_name[1024]; char short_module_name[1024];
char *name = strrchr(self->mmap.filename, '/'), *dot; char *name, *dot;
if (self->mmap.filename[0] == '/') {
name = strrchr(self->mmap.filename, '/');
if (name == NULL) if (name == NULL)
goto out_problem; goto out_problem;
...@@ -352,14 +423,16 @@ int event__process_mmap(event_t *self, struct perf_session *session) ...@@ -352,14 +423,16 @@ int event__process_mmap(event_t *self, struct perf_session *session)
dot = strrchr(name, '.'); dot = strrchr(name, '.');
if (dot == NULL) if (dot == NULL)
goto out_problem; goto out_problem;
snprintf(short_module_name, sizeof(short_module_name), snprintf(short_module_name, sizeof(short_module_name),
"[%.*s]", (int)(dot - name), name); "[%.*s]", (int)(dot - name), name);
strxfrchar(short_module_name, '-', '_'); strxfrchar(short_module_name, '-', '_');
} else
strcpy(short_module_name, self->mmap.filename);
map = perf_session__new_module_map(session, map = map_groups__new_module(&kerninfo->kmaps,
self->mmap.start, self->mmap.start,
self->mmap.filename); self->mmap.filename,
kerninfo);
if (map == NULL) if (map == NULL)
goto out_problem; goto out_problem;
...@@ -369,42 +442,67 @@ int event__process_mmap(event_t *self, struct perf_session *session) ...@@ -369,42 +442,67 @@ int event__process_mmap(event_t *self, struct perf_session *session)
map->dso->short_name = name; map->dso->short_name = name;
map->end = map->start + self->mmap.len; map->end = map->start + self->mmap.len;
} else if (memcmp(self->mmap.filename, kmmap_prefix, } else if (is_kernel_mmap) {
sizeof(kmmap_prefix) - 1) == 0) {
const char *symbol_name = (self->mmap.filename + const char *symbol_name = (self->mmap.filename +
sizeof(kmmap_prefix) - 1); strlen(kmmap_prefix));
/* /*
* Should be there already, from the build-id table in * Should be there already, from the build-id table in
* the header. * the header.
*/ */
struct dso *kernel = __dsos__findnew(&dsos__kernel, struct dso *kernel = __dsos__findnew(&kerninfo->dsos__kernel,
"[kernel.kallsyms]"); kmmap_prefix);
if (kernel == NULL) if (kernel == NULL)
goto out_problem; goto out_problem;
kernel->kernel = 1; kernel->kernel = kernel_type;
if (__perf_session__create_kernel_maps(session, kernel) < 0) if (__map_groups__create_kernel_maps(&kerninfo->kmaps,
kerninfo->vmlinux_maps, kernel) < 0)
goto out_problem; goto out_problem;
session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; event_set_kernel_mmap_len(kerninfo->vmlinux_maps, self);
session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; perf_session__set_kallsyms_ref_reloc_sym(kerninfo->vmlinux_maps,
symbol_name,
self->mmap.pgoff);
if (is_default_guest(kerninfo)) {
/* /*
* Be a bit paranoid here, some perf.data file came with * preload dso of guest kernel and modules
* a zero sized synthesized MMAP event for the kernel.
*/ */
if (session->vmlinux_maps[MAP__FUNCTION]->end == 0) dso__load(kernel,
session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL; kerninfo->vmlinux_maps[MAP__FUNCTION],
NULL);
perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, }
self->mmap.pgoff);
} }
return 0; return 0;
out_problem:
return -1;
}
int event__process_mmap(event_t *self, struct perf_session *session)
{
struct kernel_info *kerninfo;
struct thread *thread;
struct map *map;
u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
int ret = 0;
dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n",
self->mmap.pid, self->mmap.tid, self->mmap.start,
self->mmap.len, self->mmap.pgoff, self->mmap.filename);
if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
cpumode == PERF_RECORD_MISC_KERNEL) {
ret = event__process_kernel_mmap(self, session);
if (ret < 0)
goto out_problem;
return 0;
} }
thread = perf_session__findnew(session, self->mmap.pid); thread = perf_session__findnew(session, self->mmap.pid);
map = map__new(self->mmap.start, self->mmap.len, self->mmap.pgoff, kerninfo = kerninfo__findhost(&session->kerninfo_root);
self->mmap.pid, self->mmap.filename, MAP__FUNCTION, map = map__new(&kerninfo->dsos__user, self->mmap.start,
session->cwd, session->cwdlen); self->mmap.len, self->mmap.pgoff,
self->mmap.pid, self->mmap.filename,
MAP__FUNCTION, session->cwd, session->cwdlen);
if (thread == NULL || map == NULL) if (thread == NULL || map == NULL)
goto out_problem; goto out_problem;
...@@ -444,22 +542,52 @@ int event__process_task(event_t *self, struct perf_session *session) ...@@ -444,22 +542,52 @@ int event__process_task(event_t *self, struct perf_session *session)
void thread__find_addr_map(struct thread *self, void thread__find_addr_map(struct thread *self,
struct perf_session *session, u8 cpumode, struct perf_session *session, u8 cpumode,
enum map_type type, u64 addr, enum map_type type, pid_t pid, u64 addr,
struct addr_location *al) struct addr_location *al)
{ {
struct map_groups *mg = &self->mg; struct map_groups *mg = &self->mg;
struct kernel_info *kerninfo = NULL;
al->thread = self; al->thread = self;
al->addr = addr; al->addr = addr;
al->cpumode = cpumode;
al->filtered = false;
if (cpumode == PERF_RECORD_MISC_KERNEL) { if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
al->level = 'k'; al->level = 'k';
mg = &session->kmaps; kerninfo = kerninfo__findhost(&session->kerninfo_root);
} else if (cpumode == PERF_RECORD_MISC_USER) mg = &kerninfo->kmaps;
} else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
al->level = '.'; al->level = '.';
else { kerninfo = kerninfo__findhost(&session->kerninfo_root);
} else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
al->level = 'g';
kerninfo = kerninfo__find(&session->kerninfo_root, pid);
if (!kerninfo) {
al->map = NULL;
return;
}
mg = &kerninfo->kmaps;
} else {
/*
* 'u' means guest os user space.
* TODO: We don't support guest user space. Might support late.
*/
if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest)
al->level = 'u';
else
al->level = 'H'; al->level = 'H';
al->map = NULL; al->map = NULL;
if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||
cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&
!perf_guest)
al->filtered = true;
if ((cpumode == PERF_RECORD_MISC_USER ||
cpumode == PERF_RECORD_MISC_KERNEL) &&
!perf_host)
al->filtered = true;
return; return;
} }
try_again: try_again:
...@@ -474,8 +602,11 @@ void thread__find_addr_map(struct thread *self, ...@@ -474,8 +602,11 @@ void thread__find_addr_map(struct thread *self,
* "[vdso]" dso, but for now lets use the old trick of looking * "[vdso]" dso, but for now lets use the old trick of looking
* in the whole kernel symbol list. * in the whole kernel symbol list.
*/ */
if ((long long)al->addr < 0 && mg != &session->kmaps) { if ((long long)al->addr < 0 &&
mg = &session->kmaps; cpumode == PERF_RECORD_MISC_KERNEL &&
kerninfo &&
mg != &kerninfo->kmaps) {
mg = &kerninfo->kmaps;
goto try_again; goto try_again;
} }
} else } else
...@@ -484,11 +615,11 @@ void thread__find_addr_map(struct thread *self, ...@@ -484,11 +615,11 @@ void thread__find_addr_map(struct thread *self,
void thread__find_addr_location(struct thread *self, void thread__find_addr_location(struct thread *self,
struct perf_session *session, u8 cpumode, struct perf_session *session, u8 cpumode,
enum map_type type, u64 addr, enum map_type type, pid_t pid, u64 addr,
struct addr_location *al, struct addr_location *al,
symbol_filter_t filter) symbol_filter_t filter)
{ {
thread__find_addr_map(self, session, cpumode, type, addr, al); thread__find_addr_map(self, session, cpumode, type, pid, addr, al);
if (al->map != NULL) if (al->map != NULL)
al->sym = map__find_symbol(al->map, al->addr, filter); al->sym = map__find_symbol(al->map, al->addr, filter);
else else
...@@ -524,7 +655,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, ...@@ -524,7 +655,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
self->ip.ip, al); self->ip.pid, self->ip.ip, al);
dump_printf(" ...... dso: %s\n", dump_printf(" ...... dso: %s\n",
al->map ? al->map->dso->long_name : al->map ? al->map->dso->long_name :
al->level == 'H' ? "[hypervisor]" : "<not found>"); al->level == 'H' ? "[hypervisor]" : "<not found>");
...@@ -554,7 +685,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, ...@@ -554,7 +685,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
!strlist__has_entry(symbol_conf.sym_list, al->sym->name)) !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
goto out_filtered; goto out_filtered;
al->filtered = false;
return 0; return 0;
out_filtered: out_filtered:
......
...@@ -79,6 +79,7 @@ struct sample_data { ...@@ -79,6 +79,7 @@ struct sample_data {
struct build_id_event { struct build_id_event {
struct perf_event_header header; struct perf_event_header header;
pid_t pid;
u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
char filename[]; char filename[];
}; };
...@@ -155,9 +156,12 @@ void event__synthesize_threads(event__handler_t process, ...@@ -155,9 +156,12 @@ void event__synthesize_threads(event__handler_t process,
struct perf_session *session); struct perf_session *session);
int event__synthesize_kernel_mmap(event__handler_t process, int event__synthesize_kernel_mmap(event__handler_t process,
struct perf_session *session, struct perf_session *session,
struct kernel_info *kerninfo,
const char *symbol_name); const char *symbol_name);
int event__synthesize_modules(event__handler_t process, int event__synthesize_modules(event__handler_t process,
struct perf_session *session); struct perf_session *session,
struct kernel_info *kerninfo);
int event__process_comm(event_t *self, struct perf_session *session); int event__process_comm(event_t *self, struct perf_session *session);
int event__process_lost(event_t *self, struct perf_session *session); int event__process_lost(event_t *self, struct perf_session *session);
......
...@@ -190,7 +190,8 @@ static int write_padded(int fd, const void *bf, size_t count, ...@@ -190,7 +190,8 @@ static int write_padded(int fd, const void *bf, size_t count,
continue; \ continue; \
else else
static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
u16 misc, int fd)
{ {
struct dso *pos; struct dso *pos;
...@@ -205,6 +206,7 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) ...@@ -205,6 +206,7 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd)
len = ALIGN(len, NAME_ALIGN); len = ALIGN(len, NAME_ALIGN);
memset(&b, 0, sizeof(b)); memset(&b, 0, sizeof(b));
memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
b.pid = pid;
b.header.misc = misc; b.header.misc = misc;
b.header.size = sizeof(b) + len; b.header.size = sizeof(b) + len;
err = do_write(fd, &b, sizeof(b)); err = do_write(fd, &b, sizeof(b));
...@@ -219,13 +221,33 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) ...@@ -219,13 +221,33 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd)
return 0; return 0;
} }
static int dsos__write_buildid_table(int fd) static int dsos__write_buildid_table(struct perf_header *header, int fd)
{ {
int err = __dsos__write_buildid_table(&dsos__kernel, struct perf_session *session = container_of(header,
PERF_RECORD_MISC_KERNEL, fd); struct perf_session, header);
struct rb_node *nd;
int err = 0;
u16 kmisc, umisc;
for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) {
struct kernel_info *pos = rb_entry(nd, struct kernel_info,
rb_node);
if (is_host_kernel(pos)) {
kmisc = PERF_RECORD_MISC_KERNEL;
umisc = PERF_RECORD_MISC_USER;
} else {
kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
umisc = PERF_RECORD_MISC_GUEST_USER;
}
err = __dsos__write_buildid_table(&pos->dsos__kernel, pos->pid,
kmisc, fd);
if (err == 0) if (err == 0)
err = __dsos__write_buildid_table(&dsos__user, err = __dsos__write_buildid_table(&pos->dsos__user,
PERF_RECORD_MISC_USER, fd); pos->pid, umisc, fd);
if (err)
break;
}
return err; return err;
} }
...@@ -342,9 +364,12 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) ...@@ -342,9 +364,12 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
return err; return err;
} }
static int dsos__cache_build_ids(void) static int dsos__cache_build_ids(struct perf_header *self)
{ {
int err_kernel, err_user; struct perf_session *session = container_of(self,
struct perf_session, header);
struct rb_node *nd;
int ret = 0;
char debugdir[PATH_MAX]; char debugdir[PATH_MAX];
snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"),
...@@ -353,9 +378,30 @@ static int dsos__cache_build_ids(void) ...@@ -353,9 +378,30 @@ static int dsos__cache_build_ids(void)
if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
return -1; return -1;
err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) {
err_user = __dsos__cache_build_ids(&dsos__user, debugdir); struct kernel_info *pos = rb_entry(nd, struct kernel_info,
return err_kernel || err_user ? -1 : 0; rb_node);
ret |= __dsos__cache_build_ids(&pos->dsos__kernel, debugdir);
ret |= __dsos__cache_build_ids(&pos->dsos__user, debugdir);
}
return ret ? -1 : 0;
}
static bool dsos__read_build_ids(struct perf_header *self, bool with_hits)
{
bool ret = false;
struct perf_session *session = container_of(self,
struct perf_session, header);
struct rb_node *nd;
for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) {
struct kernel_info *pos = rb_entry(nd, struct kernel_info,
rb_node);
ret |= __dsos__read_build_ids(&pos->dsos__kernel, with_hits);
ret |= __dsos__read_build_ids(&pos->dsos__user, with_hits);
}
return ret;
} }
static int perf_header__adds_write(struct perf_header *self, int fd) static int perf_header__adds_write(struct perf_header *self, int fd)
...@@ -366,7 +412,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) ...@@ -366,7 +412,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
u64 sec_start; u64 sec_start;
int idx = 0, err; int idx = 0, err;
if (dsos__read_build_ids(true)) if (dsos__read_build_ids(self, true))
perf_header__set_feat(self, HEADER_BUILD_ID); perf_header__set_feat(self, HEADER_BUILD_ID);
nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
...@@ -401,14 +447,14 @@ static int perf_header__adds_write(struct perf_header *self, int fd) ...@@ -401,14 +447,14 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
/* Write build-ids */ /* Write build-ids */
buildid_sec->offset = lseek(fd, 0, SEEK_CUR); buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
err = dsos__write_buildid_table(fd); err = dsos__write_buildid_table(self, fd);
if (err < 0) { if (err < 0) {
pr_debug("failed to write buildid table\n"); pr_debug("failed to write buildid table\n");
goto out_free; goto out_free;
} }
buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->size = lseek(fd, 0, SEEK_CUR) -
buildid_sec->offset; buildid_sec->offset;
dsos__cache_build_ids(); dsos__cache_build_ids(self);
} }
lseek(fd, sec_start, SEEK_SET); lseek(fd, sec_start, SEEK_SET);
...@@ -633,6 +679,85 @@ int perf_file_header__read(struct perf_file_header *self, ...@@ -633,6 +679,85 @@ int perf_file_header__read(struct perf_file_header *self,
return 0; return 0;
} }
static int __event_process_build_id(struct build_id_event *bev,
char *filename,
struct perf_session *session)
{
int err = -1;
struct list_head *head;
struct kernel_info *kerninfo;
u16 misc;
struct dso *dso;
enum dso_kernel_type dso_type;
kerninfo = kerninfo__findnew(&session->kerninfo_root, bev->pid);
if (!kerninfo)
goto out;
misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
switch (misc) {
case PERF_RECORD_MISC_KERNEL:
dso_type = DSO_TYPE_KERNEL;
head = &kerninfo->dsos__kernel;
break;
case PERF_RECORD_MISC_GUEST_KERNEL:
dso_type = DSO_TYPE_GUEST_KERNEL;
head = &kerninfo->dsos__kernel;
break;
case PERF_RECORD_MISC_USER:
case PERF_RECORD_MISC_GUEST_USER:
dso_type = DSO_TYPE_USER;
head = &kerninfo->dsos__user;
break;
default:
goto out;
}
dso = __dsos__findnew(head, filename);
if (dso != NULL) {
dso__set_build_id(dso, &bev->build_id);
if (filename[0] == '[')
dso->kernel = dso_type;
}
err = 0;
out:
return err;
}
static int perf_header__read_build_ids(struct perf_header *self,
int input, u64 offset, u64 size)
{
struct perf_session *session = container_of(self,
struct perf_session, header);
struct build_id_event bev;
char filename[PATH_MAX];
u64 limit = offset + size;
int err = -1;
while (offset < limit) {
ssize_t len;
if (read(input, &bev, sizeof(bev)) != sizeof(bev))
goto out;
if (self->needs_swap)
perf_event_header__bswap(&bev.header);
len = bev.header.size - sizeof(bev);
if (read(input, filename, len) != len)
goto out;
__event_process_build_id(&bev, filename, session);
offset += bev.header.size;
}
err = 0;
out:
return err;
}
static int perf_file_section__process(struct perf_file_section *self, static int perf_file_section__process(struct perf_file_section *self,
struct perf_header *ph, struct perf_header *ph,
int feat, int fd) int feat, int fd)
...@@ -989,6 +1114,7 @@ int event__process_tracing_data(event_t *self, ...@@ -989,6 +1114,7 @@ int event__process_tracing_data(event_t *self,
int event__synthesize_build_id(struct dso *pos, u16 misc, int event__synthesize_build_id(struct dso *pos, u16 misc,
event__handler_t process, event__handler_t process,
struct kernel_info *kerninfo,
struct perf_session *session) struct perf_session *session)
{ {
event_t ev; event_t ev;
...@@ -1005,6 +1131,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, ...@@ -1005,6 +1131,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc,
memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id));
ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
ev.build_id.header.misc = misc; ev.build_id.header.misc = misc;
ev.build_id.pid = kerninfo->pid;
ev.build_id.header.size = sizeof(ev.build_id) + len; ev.build_id.header.size = sizeof(ev.build_id) + len;
memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
...@@ -1015,6 +1142,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, ...@@ -1015,6 +1142,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc,
static int __event_synthesize_build_ids(struct list_head *head, u16 misc, static int __event_synthesize_build_ids(struct list_head *head, u16 misc,
event__handler_t process, event__handler_t process,
struct kernel_info *kerninfo,
struct perf_session *session) struct perf_session *session)
{ {
struct dso *pos; struct dso *pos;
...@@ -1024,7 +1152,8 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc, ...@@ -1024,7 +1152,8 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc,
if (!pos->hit) if (!pos->hit)
continue; continue;
err = event__synthesize_build_id(pos, misc, process, session); err = event__synthesize_build_id(pos, misc, process,
kerninfo, session);
if (err < 0) if (err < 0)
return err; return err;
} }
...@@ -1035,44 +1164,48 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc, ...@@ -1035,44 +1164,48 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc,
int event__synthesize_build_ids(event__handler_t process, int event__synthesize_build_ids(event__handler_t process,
struct perf_session *session) struct perf_session *session)
{ {
int err; int err = 0;
u16 kmisc, umisc;
struct kernel_info *pos;
struct rb_node *nd;
if (!dsos__read_build_ids(true)) if (!dsos__read_build_ids(&session->header, true))
return 0; return 0;
err = __event_synthesize_build_ids(&dsos__kernel, for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) {
PERF_RECORD_MISC_KERNEL, pos = rb_entry(nd, struct kernel_info, rb_node);
process, session); if (is_host_kernel(pos)) {
kmisc = PERF_RECORD_MISC_KERNEL;
umisc = PERF_RECORD_MISC_USER;
} else {
kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
umisc = PERF_RECORD_MISC_GUEST_USER;
}
err = __event_synthesize_build_ids(&pos->dsos__kernel,
kmisc, process, pos, session);
if (err == 0) if (err == 0)
err = __event_synthesize_build_ids(&dsos__user, err = __event_synthesize_build_ids(&pos->dsos__user,
PERF_RECORD_MISC_USER, umisc, process, pos, session);
process, session); if (err)
break;
}
if (err < 0) { if (err < 0) {
pr_debug("failed to synthesize build ids\n"); pr_debug("failed to synthesize build ids\n");
return err; return err;
} }
dsos__cache_build_ids(); dsos__cache_build_ids(&session->header);
return 0; return 0;
} }
int event__process_build_id(event_t *self, int event__process_build_id(event_t *self,
struct perf_session *session __unused) struct perf_session *session)
{ {
struct list_head *head = &dsos__user; __event_process_build_id(&self->build_id,
struct dso *dso; self->build_id.filename,
session);
if (self->build_id.header.misc & PERF_RECORD_MISC_KERNEL)
head = &dsos__kernel;
dso = __dsos__findnew(head, self->build_id.filename);
if (dso != NULL) {
dso__set_build_id(dso, &self->build_id.build_id);
if (head == &dsos__kernel && self->build_id.filename[0] == '[')
dso->kernel = 1;
}
return 0; return 0;
} }
...@@ -120,6 +120,7 @@ int event__process_tracing_data(event_t *self, ...@@ -120,6 +120,7 @@ int event__process_tracing_data(event_t *self,
int event__synthesize_build_id(struct dso *pos, u16 misc, int event__synthesize_build_id(struct dso *pos, u16 misc,
event__handler_t process, event__handler_t process,
struct kernel_info *kerninfo,
struct perf_session *session); struct perf_session *session);
int event__synthesize_build_ids(event__handler_t process, int event__synthesize_build_ids(event__handler_t process,
struct perf_session *session); struct perf_session *session);
......
...@@ -8,6 +8,30 @@ struct callchain_param callchain_param = { ...@@ -8,6 +8,30 @@ struct callchain_param callchain_param = {
.min_percent = 0.5 .min_percent = 0.5
}; };
void __perf_session__add_count(struct hist_entry *he,
struct addr_location *al,
u64 count)
{
he->count += count;
switch (al->cpumode) {
case PERF_RECORD_MISC_KERNEL:
he->count_sys += count;
break;
case PERF_RECORD_MISC_USER:
he->count_us += count;
break;
case PERF_RECORD_MISC_GUEST_KERNEL:
he->count_guest_sys += count;
break;
case PERF_RECORD_MISC_GUEST_USER:
he->count_guest_us += count;
break;
default:
break;
}
}
/* /*
* histogram, sorted on item, collects counts * histogram, sorted on item, collects counts
*/ */
...@@ -464,7 +488,7 @@ int hist_entry__snprintf(struct hist_entry *self, ...@@ -464,7 +488,7 @@ int hist_entry__snprintf(struct hist_entry *self,
u64 session_total) u64 session_total)
{ {
struct sort_entry *se; struct sort_entry *se;
u64 count, total; u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us;
const char *sep = symbol_conf.field_sep; const char *sep = symbol_conf.field_sep;
int ret; int ret;
...@@ -474,9 +498,17 @@ int hist_entry__snprintf(struct hist_entry *self, ...@@ -474,9 +498,17 @@ int hist_entry__snprintf(struct hist_entry *self,
if (pair_session) { if (pair_session) {
count = self->pair ? self->pair->count : 0; count = self->pair ? self->pair->count : 0;
total = pair_session->events_stats.total; total = pair_session->events_stats.total;
count_sys = self->pair ? self->pair->count_sys : 0;
count_us = self->pair ? self->pair->count_us : 0;
count_guest_sys = self->pair ? self->pair->count_guest_sys : 0;
count_guest_us = self->pair ? self->pair->count_guest_us : 0;
} else { } else {
count = self->count; count = self->count;
total = session_total; total = session_total;
count_sys = self->count_sys;
count_us = self->count_us;
count_guest_sys = self->count_guest_sys;
count_guest_us = self->count_guest_us;
} }
if (total) { if (total) {
...@@ -487,6 +519,26 @@ int hist_entry__snprintf(struct hist_entry *self, ...@@ -487,6 +519,26 @@ int hist_entry__snprintf(struct hist_entry *self,
else else
ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%",
(count * 100.0) / total); (count * 100.0) / total);
if (symbol_conf.show_cpu_utilization) {
ret += percent_color_snprintf(s + ret, size - ret,
sep ? "%.2f" : " %6.2f%%",
(count_sys * 100.0) / total);
ret += percent_color_snprintf(s + ret, size - ret,
sep ? "%.2f" : " %6.2f%%",
(count_us * 100.0) / total);
if (perf_guest) {
ret += percent_color_snprintf(s + ret,
size - ret,
sep ? "%.2f" : " %6.2f%%",
(count_guest_sys * 100.0) /
total);
ret += percent_color_snprintf(s + ret,
size - ret,
sep ? "%.2f" : " %6.2f%%",
(count_guest_us * 100.0) /
total);
}
}
} else } else
ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count);
...@@ -597,6 +649,24 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, ...@@ -597,6 +649,24 @@ size_t perf_session__fprintf_hists(struct rb_root *hists,
fputs(" Samples ", fp); fputs(" Samples ", fp);
} }
if (symbol_conf.show_cpu_utilization) {
if (sep) {
ret += fprintf(fp, "%csys", *sep);
ret += fprintf(fp, "%cus", *sep);
if (perf_guest) {
ret += fprintf(fp, "%cguest sys", *sep);
ret += fprintf(fp, "%cguest us", *sep);
}
} else {
ret += fprintf(fp, " sys ");
ret += fprintf(fp, " us ");
if (perf_guest) {
ret += fprintf(fp, " guest sys ");
ret += fprintf(fp, " guest us ");
}
}
}
if (pair) { if (pair) {
if (sep) if (sep)
ret += fprintf(fp, "%cDelta", *sep); ret += fprintf(fp, "%cDelta", *sep);
......
...@@ -12,6 +12,9 @@ struct addr_location; ...@@ -12,6 +12,9 @@ struct addr_location;
struct symbol; struct symbol;
struct rb_root; struct rb_root;
void __perf_session__add_count(struct hist_entry *he,
struct addr_location *al,
u64 count);
struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists,
struct addr_location *al, struct addr_location *al,
struct symbol *parent, struct symbol *parent,
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include "map.h" #include "map.h"
const char *map_type__name[MAP__NR_TYPES] = { const char *map_type__name[MAP__NR_TYPES] = {
...@@ -37,9 +38,11 @@ void map__init(struct map *self, enum map_type type, ...@@ -37,9 +38,11 @@ void map__init(struct map *self, enum map_type type,
self->map_ip = map__map_ip; self->map_ip = map__map_ip;
self->unmap_ip = map__unmap_ip; self->unmap_ip = map__unmap_ip;
RB_CLEAR_NODE(&self->rb_node); RB_CLEAR_NODE(&self->rb_node);
self->groups = NULL;
} }
struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
u64 pgoff, u32 pid, char *filename,
enum map_type type, char *cwd, int cwdlen) enum map_type type, char *cwd, int cwdlen)
{ {
struct map *self = malloc(sizeof(*self)); struct map *self = malloc(sizeof(*self));
...@@ -66,7 +69,7 @@ struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, ...@@ -66,7 +69,7 @@ struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename,
filename = newfilename; filename = newfilename;
} }
dso = dsos__findnew(filename); dso = __dsos__findnew(dsos__list, filename);
if (dso == NULL) if (dso == NULL)
goto out_delete; goto out_delete;
...@@ -242,6 +245,7 @@ void map_groups__init(struct map_groups *self) ...@@ -242,6 +245,7 @@ void map_groups__init(struct map_groups *self)
self->maps[i] = RB_ROOT; self->maps[i] = RB_ROOT;
INIT_LIST_HEAD(&self->removed_maps[i]); INIT_LIST_HEAD(&self->removed_maps[i]);
} }
self->this_kerninfo = NULL;
} }
void map_groups__flush(struct map_groups *self) void map_groups__flush(struct map_groups *self)
...@@ -508,3 +512,134 @@ struct map *maps__find(struct rb_root *maps, u64 ip) ...@@ -508,3 +512,134 @@ struct map *maps__find(struct rb_root *maps, u64 ip)
return NULL; return NULL;
} }
struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root,
pid_t pid, const char *root_dir)
{
struct rb_node **p = &kerninfo_root->rb_node;
struct rb_node *parent = NULL;
struct kernel_info *kerninfo, *pos;
kerninfo = malloc(sizeof(struct kernel_info));
if (!kerninfo)
return NULL;
kerninfo->pid = pid;
map_groups__init(&kerninfo->kmaps);
kerninfo->root_dir = strdup(root_dir);
RB_CLEAR_NODE(&kerninfo->rb_node);
INIT_LIST_HEAD(&kerninfo->dsos__user);
INIT_LIST_HEAD(&kerninfo->dsos__kernel);
kerninfo->kmaps.this_kerninfo = kerninfo;
while (*p != NULL) {
parent = *p;
pos = rb_entry(parent, struct kernel_info, rb_node);
if (pid < pos->pid)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&kerninfo->rb_node, parent, p);
rb_insert_color(&kerninfo->rb_node, kerninfo_root);
return kerninfo;
}
struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid)
{
struct rb_node **p = &kerninfo_root->rb_node;
struct rb_node *parent = NULL;
struct kernel_info *kerninfo;
struct kernel_info *default_kerninfo = NULL;
while (*p != NULL) {
parent = *p;
kerninfo = rb_entry(parent, struct kernel_info, rb_node);
if (pid < kerninfo->pid)
p = &(*p)->rb_left;
else if (pid > kerninfo->pid)
p = &(*p)->rb_right;
else
return kerninfo;
if (!kerninfo->pid)
default_kerninfo = kerninfo;
}
return default_kerninfo;
}
struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root)
{
struct rb_node **p = &kerninfo_root->rb_node;
struct rb_node *parent = NULL;
struct kernel_info *kerninfo;
pid_t pid = HOST_KERNEL_ID;
while (*p != NULL) {
parent = *p;
kerninfo = rb_entry(parent, struct kernel_info, rb_node);
if (pid < kerninfo->pid)
p = &(*p)->rb_left;
else if (pid > kerninfo->pid)
p = &(*p)->rb_right;
else
return kerninfo;
}
return NULL;
}
struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid)
{
char path[PATH_MAX];
const char *root_dir;
int ret;
struct kernel_info *kerninfo = kerninfo__find(kerninfo_root, pid);
if (!kerninfo || kerninfo->pid != pid) {
if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID)
root_dir = "";
else {
if (!symbol_conf.guestmount)
goto out;
sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
ret = access(path, R_OK);
if (ret) {
pr_err("Can't access file %s\n", path);
goto out;
}
root_dir = path;
}
kerninfo = add_new_kernel_info(kerninfo_root, pid, root_dir);
}
out:
return kerninfo;
}
void kerninfo__process_allkernels(struct rb_root *kerninfo_root,
process_kernel_info process,
void *data)
{
struct rb_node *nd;
for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) {
struct kernel_info *pos = rb_entry(nd, struct kernel_info,
rb_node);
process(pos, data);
}
}
char *kern_mmap_name(struct kernel_info *kerninfo, char *buff)
{
if (is_host_kernel(kerninfo))
sprintf(buff, "[%s]", "kernel.kallsyms");
else if (is_default_guest(kerninfo))
sprintf(buff, "[%s]", "guest.kernel.kallsyms");
else
sprintf(buff, "[%s.%d]", "guest.kernel.kallsyms", kerninfo->pid);
return buff;
}
...@@ -19,6 +19,7 @@ extern const char *map_type__name[MAP__NR_TYPES]; ...@@ -19,6 +19,7 @@ extern const char *map_type__name[MAP__NR_TYPES];
struct dso; struct dso;
struct ref_reloc_sym; struct ref_reloc_sym;
struct map_groups; struct map_groups;
struct kernel_info;
struct map { struct map {
union { union {
...@@ -36,6 +37,7 @@ struct map { ...@@ -36,6 +37,7 @@ struct map {
u64 (*unmap_ip)(struct map *, u64); u64 (*unmap_ip)(struct map *, u64);
struct dso *dso; struct dso *dso;
struct map_groups *groups;
}; };
struct kmap { struct kmap {
...@@ -43,6 +45,26 @@ struct kmap { ...@@ -43,6 +45,26 @@ struct kmap {
struct map_groups *kmaps; struct map_groups *kmaps;
}; };
struct map_groups {
struct rb_root maps[MAP__NR_TYPES];
struct list_head removed_maps[MAP__NR_TYPES];
struct kernel_info *this_kerninfo;
};
/* Native host kernel uses -1 as pid index in kernel_info */
#define HOST_KERNEL_ID (-1)
#define DEFAULT_GUEST_KERNEL_ID (0)
struct kernel_info {
struct rb_node rb_node;
pid_t pid;
char *root_dir;
struct list_head dsos__user;
struct list_head dsos__kernel;
struct map_groups kmaps;
struct map *vmlinux_maps[MAP__NR_TYPES];
};
static inline struct kmap *map__kmap(struct map *self) static inline struct kmap *map__kmap(struct map *self)
{ {
return (struct kmap *)(self + 1); return (struct kmap *)(self + 1);
...@@ -74,7 +96,8 @@ typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); ...@@ -74,7 +96,8 @@ typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
void map__init(struct map *self, enum map_type type, void map__init(struct map *self, enum map_type type,
u64 start, u64 end, u64 pgoff, struct dso *dso); u64 start, u64 end, u64 pgoff, struct dso *dso);
struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
u64 pgoff, u32 pid, char *filename,
enum map_type type, char *cwd, int cwdlen); enum map_type type, char *cwd, int cwdlen);
void map__delete(struct map *self); void map__delete(struct map *self);
struct map *map__clone(struct map *self); struct map *map__clone(struct map *self);
...@@ -91,11 +114,6 @@ void map__fixup_end(struct map *self); ...@@ -91,11 +114,6 @@ void map__fixup_end(struct map *self);
void map__reloc_vmlinux(struct map *self); void map__reloc_vmlinux(struct map *self);
struct map_groups {
struct rb_root maps[MAP__NR_TYPES];
struct list_head removed_maps[MAP__NR_TYPES];
};
size_t __map_groups__fprintf_maps(struct map_groups *self, size_t __map_groups__fprintf_maps(struct map_groups *self,
enum map_type type, int verbose, FILE *fp); enum map_type type, int verbose, FILE *fp);
void maps__insert(struct rb_root *maps, struct map *map); void maps__insert(struct rb_root *maps, struct map *map);
...@@ -106,9 +124,40 @@ int map_groups__clone(struct map_groups *self, ...@@ -106,9 +124,40 @@ int map_groups__clone(struct map_groups *self,
size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp);
size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp);
struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root,
pid_t pid, const char *root_dir);
struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid);
struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid);
struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root);
char *kern_mmap_name(struct kernel_info *kerninfo, char *buff);
/*
* Default guest kernel is defined by parameter --guestkallsyms
* and --guestmodules
*/
static inline int is_default_guest(struct kernel_info *kerninfo)
{
if (!kerninfo)
return 0;
return kerninfo->pid == DEFAULT_GUEST_KERNEL_ID;
}
static inline int is_host_kernel(struct kernel_info *kerninfo)
{
if (!kerninfo)
return 0;
return kerninfo->pid == HOST_KERNEL_ID;
}
typedef void (*process_kernel_info)(struct kernel_info *kerninfo, void *data);
void kerninfo__process_allkernels(struct rb_root *kerninfo_root,
process_kernel_info process,
void *data);
static inline void map_groups__insert(struct map_groups *self, struct map *map) static inline void map_groups__insert(struct map_groups *self, struct map *map)
{ {
maps__insert(&self->maps[map->type], map); maps__insert(&self->maps[map->type], map);
map->groups = self;
} }
static inline struct map *map_groups__find(struct map_groups *self, static inline struct map *map_groups__find(struct map_groups *self,
...@@ -148,13 +197,11 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, ...@@ -148,13 +197,11 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
struct map *map_groups__find_by_name(struct map_groups *self, struct map *map_groups__find_by_name(struct map_groups *self,
enum map_type type, const char *name); enum map_type type, const char *name);
int __map_groups__create_kernel_maps(struct map_groups *self, struct map *map_groups__new_module(struct map_groups *self,
struct map *vmlinux_maps[MAP__NR_TYPES], u64 start,
struct dso *kernel); const char *filename,
int map_groups__create_kernel_maps(struct map_groups *self, struct kernel_info *kerninfo);
struct map *vmlinux_maps[MAP__NR_TYPES]);
struct map *map_groups__new_module(struct map_groups *self, u64 start,
const char *filename);
void map_groups__flush(struct map_groups *self); void map_groups__flush(struct map_groups *self);
#endif /* __PERF_MAP_H */ #endif /* __PERF_MAP_H */
...@@ -78,6 +78,7 @@ static struct map *kmaps[MAP__NR_TYPES]; ...@@ -78,6 +78,7 @@ static struct map *kmaps[MAP__NR_TYPES];
/* Initialize symbol maps and path of vmlinux */ /* Initialize symbol maps and path of vmlinux */
static int init_vmlinux(void) static int init_vmlinux(void)
{ {
struct dso *kernel;
int ret; int ret;
symbol_conf.sort_by_name = true; symbol_conf.sort_by_name = true;
...@@ -91,8 +92,12 @@ static int init_vmlinux(void) ...@@ -91,8 +92,12 @@ static int init_vmlinux(void)
goto out; goto out;
} }
kernel = dso__new_kernel(symbol_conf.vmlinux_name);
if (kernel == NULL)
die("Failed to create kernel dso.");
map_groups__init(&kmap_groups); map_groups__init(&kmap_groups);
ret = map_groups__create_kernel_maps(&kmap_groups, kmaps); ret = __map_groups__create_kernel_maps(&kmap_groups, kmaps, kernel);
if (ret < 0) if (ret < 0)
pr_debug("Failed to create kernel maps.\n"); pr_debug("Failed to create kernel maps.\n");
......
...@@ -67,6 +67,17 @@ void perf_session__update_sample_type(struct perf_session *self) ...@@ -67,6 +67,17 @@ void perf_session__update_sample_type(struct perf_session *self)
self->sample_type = perf_header__sample_type(&self->header); self->sample_type = perf_header__sample_type(&self->header);
} }
int perf_session__create_kernel_maps(struct perf_session *self)
{
int ret;
struct rb_root *root = &self->kerninfo_root;
ret = map_groups__create_kernel_maps(root, HOST_KERNEL_ID);
if (ret >= 0)
ret = map_groups__create_guest_kernel_maps(root);
return ret;
}
struct perf_session *perf_session__new(const char *filename, int mode, bool force) struct perf_session *perf_session__new(const char *filename, int mode, bool force)
{ {
size_t len = filename ? strlen(filename) + 1 : 0; size_t len = filename ? strlen(filename) + 1 : 0;
...@@ -86,7 +97,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc ...@@ -86,7 +97,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
self->cwd = NULL; self->cwd = NULL;
self->cwdlen = 0; self->cwdlen = 0;
self->unknown_events = 0; self->unknown_events = 0;
map_groups__init(&self->kmaps); self->kerninfo_root = RB_ROOT;
if (mode == O_RDONLY) { if (mode == O_RDONLY) {
if (perf_session__open(self, force) < 0) if (perf_session__open(self, force) < 0)
...@@ -157,8 +168,9 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, ...@@ -157,8 +168,9 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
continue; continue;
} }
al.filtered = false;
thread__find_addr_location(thread, self, cpumode, thread__find_addr_location(thread, self, cpumode,
MAP__FUNCTION, ip, &al, NULL); MAP__FUNCTION, thread->pid, ip, &al, NULL);
if (al.sym != NULL) { if (al.sym != NULL) {
if (sort__has_parent && !*parent && if (sort__has_parent && !*parent &&
symbol__match_parent_regex(al.sym)) symbol__match_parent_regex(al.sym))
...@@ -399,46 +411,6 @@ void perf_event_header__bswap(struct perf_event_header *self) ...@@ -399,46 +411,6 @@ void perf_event_header__bswap(struct perf_event_header *self)
self->size = bswap_16(self->size); self->size = bswap_16(self->size);
} }
int perf_header__read_build_ids(struct perf_header *self,
int input, u64 offset, u64 size)
{
struct build_id_event bev;
char filename[PATH_MAX];
u64 limit = offset + size;
int err = -1;
while (offset < limit) {
struct dso *dso;
ssize_t len;
struct list_head *head = &dsos__user;
if (read(input, &bev, sizeof(bev)) != sizeof(bev))
goto out;
if (self->needs_swap)
perf_event_header__bswap(&bev.header);
len = bev.header.size - sizeof(bev);
if (read(input, filename, len) != len)
goto out;
if (bev.header.misc & PERF_RECORD_MISC_KERNEL)
head = &dsos__kernel;
dso = __dsos__findnew(head, filename);
if (dso != NULL) {
dso__set_build_id(dso, &bev.build_id);
if (head == &dsos__kernel && filename[0] == '[')
dso->kernel = 1;
}
offset += bev.header.size;
}
err = 0;
out:
return err;
}
static struct thread *perf_session__register_idle_thread(struct perf_session *self) static struct thread *perf_session__register_idle_thread(struct perf_session *self)
{ {
struct thread *thread = perf_session__findnew(self, 0); struct thread *thread = perf_session__findnew(self, 0);
...@@ -690,26 +662,33 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg) ...@@ -690,26 +662,33 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg)
return true; return true;
} }
int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
const char *symbol_name, const char *symbol_name,
u64 addr) u64 addr)
{ {
char *bracket; char *bracket;
enum map_type i; enum map_type i;
struct ref_reloc_sym *ref;
self->ref_reloc_sym.name = strdup(symbol_name); ref = zalloc(sizeof(struct ref_reloc_sym));
if (self->ref_reloc_sym.name == NULL) if (ref == NULL)
return -ENOMEM; return -ENOMEM;
bracket = strchr(self->ref_reloc_sym.name, ']'); ref->name = strdup(symbol_name);
if (ref->name == NULL) {
free(ref);
return -ENOMEM;
}
bracket = strchr(ref->name, ']');
if (bracket) if (bracket)
*bracket = '\0'; *bracket = '\0';
self->ref_reloc_sym.addr = addr; ref->addr = addr;
for (i = 0; i < MAP__NR_TYPES; ++i) { for (i = 0; i < MAP__NR_TYPES; ++i) {
struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); struct kmap *kmap = map__kmap(maps[i]);
kmap->ref_reloc_sym = &self->ref_reloc_sym; kmap->ref_reloc_sym = ref;
} }
return 0; return 0;
......
...@@ -15,17 +15,15 @@ struct perf_session { ...@@ -15,17 +15,15 @@ struct perf_session {
struct perf_header header; struct perf_header header;
unsigned long size; unsigned long size;
unsigned long mmap_window; unsigned long mmap_window;
struct map_groups kmaps;
struct rb_root threads; struct rb_root threads;
struct thread *last_match; struct thread *last_match;
struct map *vmlinux_maps[MAP__NR_TYPES]; struct rb_root kerninfo_root;
struct events_stats events_stats; struct events_stats events_stats;
struct rb_root stats_by_id; struct rb_root stats_by_id;
unsigned long event_total[PERF_RECORD_MAX]; unsigned long event_total[PERF_RECORD_MAX];
unsigned long unknown_events; unsigned long unknown_events;
struct rb_root hists; struct rb_root hists;
u64 sample_type; u64 sample_type;
struct ref_reloc_sym ref_reloc_sym;
int fd; int fd;
bool fd_pipe; bool fd_pipe;
int cwdlen; int cwdlen;
...@@ -69,33 +67,13 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, ...@@ -69,33 +67,13 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
bool perf_session__has_traces(struct perf_session *self, const char *msg); bool perf_session__has_traces(struct perf_session *self, const char *msg);
int perf_header__read_build_ids(struct perf_header *self, int input, int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
u64 offset, u64 file_size);
int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self,
const char *symbol_name, const char *symbol_name,
u64 addr); u64 addr);
void mem_bswap_64(void *src, int byte_size); void mem_bswap_64(void *src, int byte_size);
static inline int __perf_session__create_kernel_maps(struct perf_session *self, int perf_session__create_kernel_maps(struct perf_session *self);
struct dso *kernel)
{
return __map_groups__create_kernel_maps(&self->kmaps,
self->vmlinux_maps, kernel);
}
static inline int perf_session__create_kernel_maps(struct perf_session *self)
{
return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps);
}
static inline struct map *
perf_session__new_module_map(struct perf_session *self,
u64 start, const char *filename)
{
return map_groups__new_module(&self->kmaps, start, filename);
}
int do_read(int fd, void *buf, size_t size); int do_read(int fd, void *buf, size_t size);
void perf_session__update_sample_type(struct perf_session *self); void perf_session__update_sample_type(struct perf_session *self);
......
...@@ -44,6 +44,11 @@ extern enum sort_type sort__first_dimension; ...@@ -44,6 +44,11 @@ extern enum sort_type sort__first_dimension;
struct hist_entry { struct hist_entry {
struct rb_node rb_node; struct rb_node rb_node;
u64 count; u64 count;
u64 count_sys;
u64 count_us;
u64 count_guest_sys;
u64 count_guest_us;
/* /*
* XXX WARNING! * XXX WARNING!
* thread _has_ to come after ms, see * thread _has_ to come after ms, see
......
...@@ -28,6 +28,8 @@ static void dsos__add(struct list_head *head, struct dso *dso); ...@@ -28,6 +28,8 @@ static void dsos__add(struct list_head *head, struct dso *dso);
static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
static int dso__load_kernel_sym(struct dso *self, struct map *map, static int dso__load_kernel_sym(struct dso *self, struct map *map,
symbol_filter_t filter); symbol_filter_t filter);
static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
symbol_filter_t filter);
static int vmlinux_path__nr_entries; static int vmlinux_path__nr_entries;
static char **vmlinux_path; static char **vmlinux_path;
...@@ -186,6 +188,7 @@ struct dso *dso__new(const char *name) ...@@ -186,6 +188,7 @@ struct dso *dso__new(const char *name)
self->loaded = 0; self->loaded = 0;
self->sorted_by_name = 0; self->sorted_by_name = 0;
self->has_build_id = 0; self->has_build_id = 0;
self->kernel = DSO_TYPE_USER;
} }
return self; return self;
...@@ -402,12 +405,9 @@ int kallsyms__parse(const char *filename, void *arg, ...@@ -402,12 +405,9 @@ int kallsyms__parse(const char *filename, void *arg,
char *symbol_name; char *symbol_name;
line_len = getline(&line, &n, file); line_len = getline(&line, &n, file);
if (line_len < 0) if (line_len < 0 || !line)
break; break;
if (!line)
goto out_failure;
line[--line_len] = '\0'; /* \n */ line[--line_len] = '\0'; /* \n */
len = hex2u64(line, &start); len = hex2u64(line, &start);
...@@ -459,6 +459,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name, ...@@ -459,6 +459,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
* map__split_kallsyms, when we have split the maps per module * map__split_kallsyms, when we have split the maps per module
*/ */
symbols__insert(root, sym); symbols__insert(root, sym);
return 0; return 0;
} }
...@@ -483,6 +484,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, ...@@ -483,6 +484,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
symbol_filter_t filter) symbol_filter_t filter)
{ {
struct map_groups *kmaps = map__kmap(map)->kmaps; struct map_groups *kmaps = map__kmap(map)->kmaps;
struct kernel_info *kerninfo = kmaps->this_kerninfo;
struct map *curr_map = map; struct map *curr_map = map;
struct symbol *pos; struct symbol *pos;
int count = 0; int count = 0;
...@@ -504,15 +506,33 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, ...@@ -504,15 +506,33 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
*module++ = '\0'; *module++ = '\0';
if (strcmp(curr_map->dso->short_name, module)) { if (strcmp(curr_map->dso->short_name, module)) {
curr_map = map_groups__find_by_name(kmaps, map->type, module); if (curr_map != map &&
self->kernel == DSO_TYPE_GUEST_KERNEL &&
is_default_guest(kerninfo)) {
/*
* We assume all symbols of a module are
* continuous in * kallsyms, so curr_map
* points to a module and all its
* symbols are in its kmap. Mark it as
* loaded.
*/
dso__set_loaded(curr_map->dso,
curr_map->type);
}
curr_map = map_groups__find_by_name(kmaps,
map->type, module);
if (curr_map == NULL) { if (curr_map == NULL) {
pr_debug("/proc/{kallsyms,modules} " pr_err("%s/proc/{kallsyms,modules} "
"inconsistency while looking " "inconsistency while looking "
"for \"%s\" module!\n", module); "for \"%s\" module!\n",
return -1; kerninfo->root_dir, module);
curr_map = map;
goto discard_symbol;
} }
if (curr_map->dso->loaded) if (curr_map->dso->loaded &&
!is_default_guest(kmaps->this_kerninfo))
goto discard_symbol; goto discard_symbol;
} }
/* /*
...@@ -525,13 +545,21 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, ...@@ -525,13 +545,21 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
char dso_name[PATH_MAX]; char dso_name[PATH_MAX];
struct dso *dso; struct dso *dso;
snprintf(dso_name, sizeof(dso_name), "[kernel].%d", if (self->kernel == DSO_TYPE_GUEST_KERNEL)
snprintf(dso_name, sizeof(dso_name),
"[guest.kernel].%d",
kernel_range++);
else
snprintf(dso_name, sizeof(dso_name),
"[kernel].%d",
kernel_range++); kernel_range++);
dso = dso__new(dso_name); dso = dso__new(dso_name);
if (dso == NULL) if (dso == NULL)
return -1; return -1;
dso->kernel = self->kernel;
curr_map = map__new2(pos->start, dso, map->type); curr_map = map__new2(pos->start, dso, map->type);
if (curr_map == NULL) { if (curr_map == NULL) {
dso__delete(dso); dso__delete(dso);
...@@ -555,6 +583,12 @@ discard_symbol: rb_erase(&pos->rb_node, root); ...@@ -555,6 +583,12 @@ discard_symbol: rb_erase(&pos->rb_node, root);
} }
} }
if (curr_map != map &&
self->kernel == DSO_TYPE_GUEST_KERNEL &&
is_default_guest(kmaps->this_kerninfo)) {
dso__set_loaded(curr_map->dso, curr_map->type);
}
return count; return count;
} }
...@@ -565,6 +599,9 @@ int dso__load_kallsyms(struct dso *self, const char *filename, ...@@ -565,6 +599,9 @@ int dso__load_kallsyms(struct dso *self, const char *filename,
return -1; return -1;
symbols__fixup_end(&self->symbols[map->type]); symbols__fixup_end(&self->symbols[map->type]);
if (self->kernel == DSO_TYPE_GUEST_KERNEL)
self->origin = DSO__ORIG_GUEST_KERNEL;
else
self->origin = DSO__ORIG_KERNEL; self->origin = DSO__ORIG_KERNEL;
return dso__split_kallsyms(self, map, filter); return dso__split_kallsyms(self, map, filter);
...@@ -952,7 +989,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, ...@@ -952,7 +989,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
nr_syms = shdr.sh_size / shdr.sh_entsize; nr_syms = shdr.sh_size / shdr.sh_entsize;
memset(&sym, 0, sizeof(sym)); memset(&sym, 0, sizeof(sym));
if (!self->kernel) { if (self->kernel == DSO_TYPE_USER) {
self->adjust_symbols = (ehdr.e_type == ET_EXEC || self->adjust_symbols = (ehdr.e_type == ET_EXEC ||
elf_section_by_name(elf, &ehdr, &shdr, elf_section_by_name(elf, &ehdr, &shdr,
".gnu.prelink_undo", ".gnu.prelink_undo",
...@@ -984,7 +1021,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, ...@@ -984,7 +1021,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
section_name = elf_sec__name(&shdr, secstrs); section_name = elf_sec__name(&shdr, secstrs);
if (self->kernel || kmodule) { if (self->kernel != DSO_TYPE_USER || kmodule) {
char dso_name[PATH_MAX]; char dso_name[PATH_MAX];
if (strcmp(section_name, if (strcmp(section_name,
...@@ -1011,6 +1048,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, ...@@ -1011,6 +1048,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
curr_dso = dso__new(dso_name); curr_dso = dso__new(dso_name);
if (curr_dso == NULL) if (curr_dso == NULL)
goto out_elf_end; goto out_elf_end;
curr_dso->kernel = self->kernel;
curr_map = map__new2(start, curr_dso, curr_map = map__new2(start, curr_dso,
map->type); map->type);
if (curr_map == NULL) { if (curr_map == NULL) {
...@@ -1021,7 +1059,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, ...@@ -1021,7 +1059,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
curr_map->unmap_ip = identity__map_ip; curr_map->unmap_ip = identity__map_ip;
curr_dso->origin = self->origin; curr_dso->origin = self->origin;
map_groups__insert(kmap->kmaps, curr_map); map_groups__insert(kmap->kmaps, curr_map);
dsos__add(&dsos__kernel, curr_dso); dsos__add(&self->node, curr_dso);
dso__set_loaded(curr_dso, map->type); dso__set_loaded(curr_dso, map->type);
} else } else
curr_dso = curr_map->dso; curr_dso = curr_map->dso;
...@@ -1083,7 +1121,7 @@ static bool dso__build_id_equal(const struct dso *self, u8 *build_id) ...@@ -1083,7 +1121,7 @@ static bool dso__build_id_equal(const struct dso *self, u8 *build_id)
return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0;
} }
static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
{ {
bool have_build_id = false; bool have_build_id = false;
struct dso *pos; struct dso *pos;
...@@ -1101,13 +1139,6 @@ static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) ...@@ -1101,13 +1139,6 @@ static bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
return have_build_id; return have_build_id;
} }
bool dsos__read_build_ids(bool with_hits)
{
bool kbuildids = __dsos__read_build_ids(&dsos__kernel, with_hits),
ubuildids = __dsos__read_build_ids(&dsos__user, with_hits);
return kbuildids || ubuildids;
}
/* /*
* Align offset to 4 bytes as needed for note name and descriptor data. * Align offset to 4 bytes as needed for note name and descriptor data.
*/ */
...@@ -1242,6 +1273,8 @@ char dso__symtab_origin(const struct dso *self) ...@@ -1242,6 +1273,8 @@ char dso__symtab_origin(const struct dso *self)
[DSO__ORIG_BUILDID] = 'b', [DSO__ORIG_BUILDID] = 'b',
[DSO__ORIG_DSO] = 'd', [DSO__ORIG_DSO] = 'd',
[DSO__ORIG_KMODULE] = 'K', [DSO__ORIG_KMODULE] = 'K',
[DSO__ORIG_GUEST_KERNEL] = 'g',
[DSO__ORIG_GUEST_KMODULE] = 'G',
}; };
if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND)
...@@ -1257,11 +1290,20 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) ...@@ -1257,11 +1290,20 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
char build_id_hex[BUILD_ID_SIZE * 2 + 1]; char build_id_hex[BUILD_ID_SIZE * 2 + 1];
int ret = -1; int ret = -1;
int fd; int fd;
struct kernel_info *kerninfo;
const char *root_dir;
dso__set_loaded(self, map->type); dso__set_loaded(self, map->type);
if (self->kernel) if (self->kernel == DSO_TYPE_KERNEL)
return dso__load_kernel_sym(self, map, filter); return dso__load_kernel_sym(self, map, filter);
else if (self->kernel == DSO_TYPE_GUEST_KERNEL)
return dso__load_guest_kernel_sym(self, map, filter);
if (map->groups && map->groups->this_kerninfo)
kerninfo = map->groups->this_kerninfo;
else
kerninfo = NULL;
name = malloc(size); name = malloc(size);
if (!name) if (!name)
...@@ -1315,6 +1357,13 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) ...@@ -1315,6 +1357,13 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
case DSO__ORIG_DSO: case DSO__ORIG_DSO:
snprintf(name, size, "%s", self->long_name); snprintf(name, size, "%s", self->long_name);
break; break;
case DSO__ORIG_GUEST_KMODULE:
if (map->groups && map->groups->this_kerninfo)
root_dir = map->groups->this_kerninfo->root_dir;
else
root_dir = "";
snprintf(name, size, "%s%s", root_dir, self->long_name);
break;
default: default:
goto out; goto out;
...@@ -1368,7 +1417,8 @@ struct map *map_groups__find_by_name(struct map_groups *self, ...@@ -1368,7 +1417,8 @@ struct map *map_groups__find_by_name(struct map_groups *self,
return NULL; return NULL;
} }
static int dso__kernel_module_get_build_id(struct dso *self) static int dso__kernel_module_get_build_id(struct dso *self,
const char *root_dir)
{ {
char filename[PATH_MAX]; char filename[PATH_MAX];
/* /*
...@@ -1378,8 +1428,8 @@ static int dso__kernel_module_get_build_id(struct dso *self) ...@@ -1378,8 +1428,8 @@ static int dso__kernel_module_get_build_id(struct dso *self)
const char *name = self->short_name + 1; const char *name = self->short_name + 1;
snprintf(filename, sizeof(filename), snprintf(filename, sizeof(filename),
"/sys/module/%.*s/notes/.note.gnu.build-id", "%s/sys/module/%.*s/notes/.note.gnu.build-id",
(int)strlen(name - 1), name); root_dir, (int)strlen(name) - 1, name);
if (sysfs__read_build_id(filename, self->build_id, if (sysfs__read_build_id(filename, self->build_id,
sizeof(self->build_id)) == 0) sizeof(self->build_id)) == 0)
...@@ -1388,7 +1438,8 @@ static int dso__kernel_module_get_build_id(struct dso *self) ...@@ -1388,7 +1438,8 @@ static int dso__kernel_module_get_build_id(struct dso *self)
return 0; return 0;
} }
static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_name) static int map_groups__set_modules_path_dir(struct map_groups *self,
const char *dir_name)
{ {
struct dirent *dent; struct dirent *dent;
DIR *dir = opendir(dir_name); DIR *dir = opendir(dir_name);
...@@ -1400,8 +1451,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n ...@@ -1400,8 +1451,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n
while ((dent = readdir(dir)) != NULL) { while ((dent = readdir(dir)) != NULL) {
char path[PATH_MAX]; char path[PATH_MAX];
struct stat st;
if (dent->d_type == DT_DIR) { /*sshfs might return bad dent->d_type, so we have to stat*/
sprintf(path, "%s/%s", dir_name, dent->d_name);
if (stat(path, &st))
continue;
if (S_ISDIR(st.st_mode)) {
if (!strcmp(dent->d_name, ".") || if (!strcmp(dent->d_name, ".") ||
!strcmp(dent->d_name, "..")) !strcmp(dent->d_name, ".."))
continue; continue;
...@@ -1433,7 +1490,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n ...@@ -1433,7 +1490,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n
if (long_name == NULL) if (long_name == NULL)
goto failure; goto failure;
dso__set_long_name(map->dso, long_name); dso__set_long_name(map->dso, long_name);
dso__kernel_module_get_build_id(map->dso); dso__kernel_module_get_build_id(map->dso, "");
} }
} }
...@@ -1443,16 +1500,46 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n ...@@ -1443,16 +1500,46 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n
return -1; return -1;
} }
static int map_groups__set_modules_path(struct map_groups *self) static char *get_kernel_version(const char *root_dir)
{ {
struct utsname uts; char version[PATH_MAX];
FILE *file;
char *name, *tmp;
const char *prefix = "Linux version ";
sprintf(version, "%s/proc/version", root_dir);
file = fopen(version, "r");
if (!file)
return NULL;
version[0] = '\0';
tmp = fgets(version, sizeof(version), file);
fclose(file);
name = strstr(version, prefix);
if (!name)
return NULL;
name += strlen(prefix);
tmp = strchr(name, ' ');
if (tmp)
*tmp = '\0';
return strdup(name);
}
static int map_groups__set_modules_path(struct map_groups *self,
const char *root_dir)
{
char *version;
char modules_path[PATH_MAX]; char modules_path[PATH_MAX];
if (uname(&uts) < 0) version = get_kernel_version(root_dir);
if (!version)
return -1; return -1;
snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel",
uts.release); root_dir, version);
free(version);
return map_groups__set_modules_path_dir(self, modules_path); return map_groups__set_modules_path_dir(self, modules_path);
} }
...@@ -1477,11 +1564,13 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) ...@@ -1477,11 +1564,13 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
} }
struct map *map_groups__new_module(struct map_groups *self, u64 start, struct map *map_groups__new_module(struct map_groups *self, u64 start,
const char *filename) const char *filename,
struct kernel_info *kerninfo)
{ {
struct map *map; struct map *map;
struct dso *dso = __dsos__findnew(&dsos__kernel, filename); struct dso *dso;
dso = __dsos__findnew(&kerninfo->dsos__kernel, filename);
if (dso == NULL) if (dso == NULL)
return NULL; return NULL;
...@@ -1489,21 +1578,37 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start, ...@@ -1489,21 +1578,37 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start,
if (map == NULL) if (map == NULL)
return NULL; return NULL;
if (is_host_kernel(kerninfo))
dso->origin = DSO__ORIG_KMODULE; dso->origin = DSO__ORIG_KMODULE;
else
dso->origin = DSO__ORIG_GUEST_KMODULE;
map_groups__insert(self, map); map_groups__insert(self, map);
return map; return map;
} }
static int map_groups__create_modules(struct map_groups *self) static int map_groups__create_modules(struct kernel_info *kerninfo)
{ {
char *line = NULL; char *line = NULL;
size_t n; size_t n;
FILE *file = fopen("/proc/modules", "r"); FILE *file;
struct map *map; struct map *map;
const char *root_dir;
const char *modules;
char path[PATH_MAX];
if (is_default_guest(kerninfo))
modules = symbol_conf.default_guest_modules;
else {
sprintf(path, "%s/proc/modules", kerninfo->root_dir);
modules = path;
}
file = fopen(modules, "r");
if (file == NULL) if (file == NULL)
return -1; return -1;
root_dir = kerninfo->root_dir;
while (!feof(file)) { while (!feof(file)) {
char name[PATH_MAX]; char name[PATH_MAX];
u64 start; u64 start;
...@@ -1532,16 +1637,17 @@ static int map_groups__create_modules(struct map_groups *self) ...@@ -1532,16 +1637,17 @@ static int map_groups__create_modules(struct map_groups *self)
*sep = '\0'; *sep = '\0';
snprintf(name, sizeof(name), "[%s]", line); snprintf(name, sizeof(name), "[%s]", line);
map = map_groups__new_module(self, start, name); map = map_groups__new_module(&kerninfo->kmaps,
start, name, kerninfo);
if (map == NULL) if (map == NULL)
goto out_delete_line; goto out_delete_line;
dso__kernel_module_get_build_id(map->dso); dso__kernel_module_get_build_id(map->dso, root_dir);
} }
free(line); free(line);
fclose(file); fclose(file);
return map_groups__set_modules_path(self); return map_groups__set_modules_path(&kerninfo->kmaps, root_dir);
out_delete_line: out_delete_line:
free(line); free(line);
...@@ -1708,8 +1814,57 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, ...@@ -1708,8 +1814,57 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
return err; return err;
} }
LIST_HEAD(dsos__user); static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
LIST_HEAD(dsos__kernel); symbol_filter_t filter)
{
int err;
const char *kallsyms_filename = NULL;
struct kernel_info *kerninfo;
char path[PATH_MAX];
if (!map->groups) {
pr_debug("Guest kernel map hasn't the point to groups\n");
return -1;
}
kerninfo = map->groups->this_kerninfo;
if (is_default_guest(kerninfo)) {
/*
* if the user specified a vmlinux filename, use it and only
* it, reporting errors to the user if it cannot be used.
* Or use file guest_kallsyms inputted by user on commandline
*/
if (symbol_conf.default_guest_vmlinux_name != NULL) {
err = dso__load_vmlinux(self, map,
symbol_conf.default_guest_vmlinux_name, filter);
goto out_try_fixup;
}
kallsyms_filename = symbol_conf.default_guest_kallsyms;
if (!kallsyms_filename)
return -1;
} else {
sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir);
kallsyms_filename = path;
}
err = dso__load_kallsyms(self, kallsyms_filename, map, filter);
if (err > 0)
pr_debug("Using %s for symbols\n", kallsyms_filename);
out_try_fixup:
if (err > 0) {
if (kallsyms_filename != NULL) {
kern_mmap_name(kerninfo, path);
dso__set_long_name(self,
strdup(path));
}
map__fixup_start(map);
map__fixup_end(map);
}
return err;
}
static void dsos__add(struct list_head *head, struct dso *dso) static void dsos__add(struct list_head *head, struct dso *dso)
{ {
...@@ -1752,10 +1907,16 @@ static void __dsos__fprintf(struct list_head *head, FILE *fp) ...@@ -1752,10 +1907,16 @@ static void __dsos__fprintf(struct list_head *head, FILE *fp)
} }
} }
void dsos__fprintf(FILE *fp) void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp)
{ {
__dsos__fprintf(&dsos__kernel, fp); struct rb_node *nd;
__dsos__fprintf(&dsos__user, fp);
for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) {
struct kernel_info *pos = rb_entry(nd, struct kernel_info,
rb_node);
__dsos__fprintf(&pos->dsos__kernel, fp);
__dsos__fprintf(&pos->dsos__user, fp);
}
} }
static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
...@@ -1773,10 +1934,21 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, ...@@ -1773,10 +1934,21 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
return ret; return ret;
} }
size_t dsos__fprintf_buildid(FILE *fp, bool with_hits) size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root,
FILE *fp, bool with_hits)
{ {
return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + struct rb_node *nd;
__dsos__fprintf_buildid(&dsos__user, fp, with_hits)); size_t ret = 0;
for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) {
struct kernel_info *pos = rb_entry(nd, struct kernel_info,
rb_node);
ret += __dsos__fprintf_buildid(&pos->dsos__kernel,
fp, with_hits);
ret += __dsos__fprintf_buildid(&pos->dsos__user,
fp, with_hits);
}
return ret;
} }
struct dso *dso__new_kernel(const char *name) struct dso *dso__new_kernel(const char *name)
...@@ -1785,28 +1957,59 @@ struct dso *dso__new_kernel(const char *name) ...@@ -1785,28 +1957,59 @@ struct dso *dso__new_kernel(const char *name)
if (self != NULL) { if (self != NULL) {
dso__set_short_name(self, "[kernel]"); dso__set_short_name(self, "[kernel]");
self->kernel = 1; self->kernel = DSO_TYPE_KERNEL;
}
return self;
}
static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo,
const char *name)
{
char buff[PATH_MAX];
struct dso *self;
kern_mmap_name(kerninfo, buff);
self = dso__new(name ?: buff);
if (self != NULL) {
dso__set_short_name(self, "[guest.kernel]");
self->kernel = DSO_TYPE_GUEST_KERNEL;
} }
return self; return self;
} }
void dso__read_running_kernel_build_id(struct dso *self) void dso__read_running_kernel_build_id(struct dso *self,
struct kernel_info *kerninfo)
{ {
if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, char path[PATH_MAX];
if (is_default_guest(kerninfo))
return;
sprintf(path, "%s/sys/kernel/notes", kerninfo->root_dir);
if (sysfs__read_build_id(path, self->build_id,
sizeof(self->build_id)) == 0) sizeof(self->build_id)) == 0)
self->has_build_id = true; self->has_build_id = true;
} }
static struct dso *dsos__create_kernel(const char *vmlinux) static struct dso *dsos__create_kernel(struct kernel_info *kerninfo)
{ {
struct dso *kernel = dso__new_kernel(vmlinux); const char *vmlinux_name = NULL;
struct dso *kernel;
if (kernel != NULL) { if (is_host_kernel(kerninfo)) {
dso__read_running_kernel_build_id(kernel); vmlinux_name = symbol_conf.vmlinux_name;
dsos__add(&dsos__kernel, kernel); kernel = dso__new_kernel(vmlinux_name);
} else {
if (is_default_guest(kerninfo))
vmlinux_name = symbol_conf.default_guest_vmlinux_name;
kernel = dso__new_guest_kernel(kerninfo, vmlinux_name);
} }
if (kernel != NULL) {
dso__read_running_kernel_build_id(kernel, kerninfo);
dsos__add(&kerninfo->dsos__kernel, kernel);
}
return kernel; return kernel;
} }
...@@ -1950,23 +2153,29 @@ int symbol__init(void) ...@@ -1950,23 +2153,29 @@ int symbol__init(void)
return -1; return -1;
} }
int map_groups__create_kernel_maps(struct map_groups *self, int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid)
struct map *vmlinux_maps[MAP__NR_TYPES])
{ {
struct dso *kernel = dsos__create_kernel(symbol_conf.vmlinux_name); struct kernel_info *kerninfo;
struct dso *kernel;
kerninfo = kerninfo__findnew(kerninfo_root, pid);
if (kerninfo == NULL)
return -1;
kernel = dsos__create_kernel(kerninfo);
if (kernel == NULL) if (kernel == NULL)
return -1; return -1;
if (__map_groups__create_kernel_maps(self, vmlinux_maps, kernel) < 0) if (__map_groups__create_kernel_maps(&kerninfo->kmaps,
kerninfo->vmlinux_maps, kernel) < 0)
return -1; return -1;
if (symbol_conf.use_modules && map_groups__create_modules(self) < 0) if (symbol_conf.use_modules &&
map_groups__create_modules(kerninfo) < 0)
pr_debug("Problems creating module maps, continuing anyway...\n"); pr_debug("Problems creating module maps, continuing anyway...\n");
/* /*
* Now that we have all the maps created, just set the ->end of them: * Now that we have all the maps created, just set the ->end of them:
*/ */
map_groups__fixup_end(self); map_groups__fixup_end(&kerninfo->kmaps);
return 0; return 0;
} }
...@@ -2012,3 +2221,46 @@ char *strxfrchar(char *s, char from, char to) ...@@ -2012,3 +2221,46 @@ char *strxfrchar(char *s, char from, char to)
return s; return s;
} }
int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root)
{
int ret = 0;
struct dirent **namelist = NULL;
int i, items = 0;
char path[PATH_MAX];
pid_t pid;
if (symbol_conf.default_guest_vmlinux_name ||
symbol_conf.default_guest_modules ||
symbol_conf.default_guest_kallsyms) {
map_groups__create_kernel_maps(kerninfo_root,
DEFAULT_GUEST_KERNEL_ID);
}
if (symbol_conf.guestmount) {
items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL);
if (items <= 0)
return -ENOENT;
for (i = 0; i < items; i++) {
if (!isdigit(namelist[i]->d_name[0])) {
/* Filter out . and .. */
continue;
}
pid = atoi(namelist[i]->d_name);
sprintf(path, "%s/%s/proc/kallsyms",
symbol_conf.guestmount,
namelist[i]->d_name);
ret = access(path, R_OK);
if (ret) {
pr_debug("Can't access file %s\n", path);
goto failure;
}
map_groups__create_kernel_maps(kerninfo_root,
pid);
}
failure:
free(namelist);
}
return ret;
}
...@@ -69,9 +69,14 @@ struct symbol_conf { ...@@ -69,9 +69,14 @@ struct symbol_conf {
show_nr_samples, show_nr_samples,
use_callchain, use_callchain,
exclude_other, exclude_other,
full_paths; full_paths,
show_cpu_utilization;
const char *vmlinux_name, const char *vmlinux_name,
*field_sep; *field_sep;
const char *default_guest_vmlinux_name,
*default_guest_kallsyms,
*default_guest_modules;
const char *guestmount;
char *dso_list_str, char *dso_list_str,
*comm_list_str, *comm_list_str,
*sym_list_str, *sym_list_str,
...@@ -106,6 +111,13 @@ struct addr_location { ...@@ -106,6 +111,13 @@ struct addr_location {
u64 addr; u64 addr;
char level; char level;
bool filtered; bool filtered;
unsigned int cpumode;
};
enum dso_kernel_type {
DSO_TYPE_USER = 0,
DSO_TYPE_KERNEL,
DSO_TYPE_GUEST_KERNEL
}; };
struct dso { struct dso {
...@@ -115,7 +127,7 @@ struct dso { ...@@ -115,7 +127,7 @@ struct dso {
u8 adjust_symbols:1; u8 adjust_symbols:1;
u8 slen_calculated:1; u8 slen_calculated:1;
u8 has_build_id:1; u8 has_build_id:1;
u8 kernel:1; enum dso_kernel_type kernel;
u8 hit:1; u8 hit:1;
u8 annotate_warned:1; u8 annotate_warned:1;
unsigned char origin; unsigned char origin;
...@@ -143,34 +155,30 @@ static inline void dso__set_loaded(struct dso *self, enum map_type type) ...@@ -143,34 +155,30 @@ static inline void dso__set_loaded(struct dso *self, enum map_type type)
void dso__sort_by_name(struct dso *self, enum map_type type); void dso__sort_by_name(struct dso *self, enum map_type type);
extern struct list_head dsos__user, dsos__kernel;
struct dso *__dsos__findnew(struct list_head *head, const char *name); struct dso *__dsos__findnew(struct list_head *head, const char *name);
static inline struct dso *dsos__findnew(const char *name)
{
return __dsos__findnew(&dsos__user, name);
}
int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
int dso__load_vmlinux_path(struct dso *self, struct map *map, int dso__load_vmlinux_path(struct dso *self, struct map *map,
symbol_filter_t filter); symbol_filter_t filter);
int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map,
symbol_filter_t filter); symbol_filter_t filter);
void dsos__fprintf(FILE *fp); void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp);
size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root,
FILE *fp, bool with_hits);
size_t dso__fprintf_buildid(struct dso *self, FILE *fp); size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
enum dso_origin { enum dso_origin {
DSO__ORIG_KERNEL = 0, DSO__ORIG_KERNEL = 0,
DSO__ORIG_GUEST_KERNEL,
DSO__ORIG_JAVA_JIT, DSO__ORIG_JAVA_JIT,
DSO__ORIG_BUILD_ID_CACHE, DSO__ORIG_BUILD_ID_CACHE,
DSO__ORIG_FEDORA, DSO__ORIG_FEDORA,
DSO__ORIG_UBUNTU, DSO__ORIG_UBUNTU,
DSO__ORIG_BUILDID, DSO__ORIG_BUILDID,
DSO__ORIG_DSO, DSO__ORIG_DSO,
DSO__ORIG_GUEST_KMODULE,
DSO__ORIG_KMODULE, DSO__ORIG_KMODULE,
DSO__ORIG_NOT_FOUND, DSO__ORIG_NOT_FOUND,
}; };
...@@ -178,19 +186,26 @@ enum dso_origin { ...@@ -178,19 +186,26 @@ enum dso_origin {
char dso__symtab_origin(const struct dso *self); char dso__symtab_origin(const struct dso *self);
void dso__set_long_name(struct dso *self, char *name); void dso__set_long_name(struct dso *self, char *name);
void dso__set_build_id(struct dso *self, void *build_id); void dso__set_build_id(struct dso *self, void *build_id);
void dso__read_running_kernel_build_id(struct dso *self); void dso__read_running_kernel_build_id(struct dso *self,
struct kernel_info *kerninfo);
struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
const char *name); const char *name);
int filename__read_build_id(const char *filename, void *bf, size_t size); int filename__read_build_id(const char *filename, void *bf, size_t size);
int sysfs__read_build_id(const char *filename, void *bf, size_t size); int sysfs__read_build_id(const char *filename, void *bf, size_t size);
bool dsos__read_build_ids(bool with_hits); bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
int build_id__sprintf(const u8 *self, int len, char *bf); int build_id__sprintf(const u8 *self, int len, char *bf);
int kallsyms__parse(const char *filename, void *arg, int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name, int (*process_symbol)(void *arg, const char *name,
char type, u64 start)); char type, u64 start));
int __map_groups__create_kernel_maps(struct map_groups *self,
struct map *vmlinux_maps[MAP__NR_TYPES],
struct dso *kernel);
int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid);
int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root);
int symbol__init(void); int symbol__init(void);
bool symbol_type__is_a(char symbol_type, enum map_type map_type); bool symbol_type__is_a(char symbol_type, enum map_type map_type);
......
...@@ -33,12 +33,12 @@ static inline struct map *thread__find_map(struct thread *self, ...@@ -33,12 +33,12 @@ static inline struct map *thread__find_map(struct thread *self,
void thread__find_addr_map(struct thread *self, void thread__find_addr_map(struct thread *self,
struct perf_session *session, u8 cpumode, struct perf_session *session, u8 cpumode,
enum map_type type, u64 addr, enum map_type type, pid_t pid, u64 addr,
struct addr_location *al); struct addr_location *al);
void thread__find_addr_location(struct thread *self, void thread__find_addr_location(struct thread *self,
struct perf_session *session, u8 cpumode, struct perf_session *session, u8 cpumode,
enum map_type type, u64 addr, enum map_type type, pid_t pid, u64 addr,
struct addr_location *al, struct addr_location *al,
symbol_filter_t filter); symbol_filter_t filter);
#endif /* __PERF_THREAD_H */ #endif /* __PERF_THREAD_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment