perf: 'perf kvm' tool for monitoring guest performance from host
Here is the patch of userspace perf tool. Signed-off-by: Zhang Yanmin <yanmin_zhang@linux.intel.com> Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
Родитель
ff9d07a0e7
Коммит
a1645ce12a
|
@ -0,0 +1,67 @@
|
||||||
|
perf-kvm(1)
|
||||||
|
==============
|
||||||
|
|
||||||
|
NAME
|
||||||
|
----
|
||||||
|
perf-kvm - Tool to trace/measure kvm guest os
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
[verse]
|
||||||
|
'perf kvm' [--host] [--guest] [--guestmount=<path>
|
||||||
|
[--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]]
|
||||||
|
{top|record|report|diff|buildid-list}
|
||||||
|
'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
|
||||||
|
| --guestvmlinux=<path>] {top|record|report|diff|buildid-list}
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
There are a couple of variants of perf kvm:
|
||||||
|
|
||||||
|
'perf kvm [options] top <command>' to generates and displays
|
||||||
|
a performance counter profile of guest os in realtime
|
||||||
|
of an arbitrary workload.
|
||||||
|
|
||||||
|
'perf kvm record <command>' to record the performance couinter profile
|
||||||
|
of an arbitrary workload and save it into a perf data file. If both
|
||||||
|
--host and --guest are input, the perf data file name is perf.data.kvm.
|
||||||
|
If there is no --host but --guest, the file name is perf.data.guest.
|
||||||
|
If there is no --guest but --host, the file name is perf.data.host.
|
||||||
|
|
||||||
|
'perf kvm report' to display the performance counter profile information
|
||||||
|
recorded via perf kvm record.
|
||||||
|
|
||||||
|
'perf kvm diff' to displays the performance difference amongst two perf.data
|
||||||
|
files captured via perf record.
|
||||||
|
|
||||||
|
'perf kvm buildid-list' to display the buildids found in a perf data file,
|
||||||
|
so that other tools can be used to fetch packages with matching symbol tables
|
||||||
|
for use by perf report.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-------
|
||||||
|
--host=::
|
||||||
|
Collect host side perforamnce profile.
|
||||||
|
--guest=::
|
||||||
|
Collect guest side perforamnce profile.
|
||||||
|
--guestmount=<path>::
|
||||||
|
Guest os root file system mount directory. Users mounts guest os
|
||||||
|
root directories under <path> by a specific filesystem access method,
|
||||||
|
typically, sshfs. For example, start 2 guest os. The one's pid is 8888
|
||||||
|
and the other's is 9999.
|
||||||
|
#mkdir ~/guestmount; cd ~/guestmount
|
||||||
|
#sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/
|
||||||
|
#sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/
|
||||||
|
#perf kvm --host --guest --guestmount=~/guestmount top
|
||||||
|
--guestkallsyms=<path>::
|
||||||
|
Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest
|
||||||
|
kernel symbols. Users copy it out from guest os.
|
||||||
|
--guestmodules=<path>::
|
||||||
|
Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest
|
||||||
|
kernel module information. Users copy it out from guest os.
|
||||||
|
--guestvmlinux=<path>::
|
||||||
|
Guest os kernel vmlinux.
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
linkperf:perf-top[1] perf-record[1] perf-report[1] perf-diff[1] perf-buildid-list[1]
|
|
@ -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)
|
||||||
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)
|
||||||
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,
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
#include "builtin.h"
|
||||||
|
#include "perf.h"
|
||||||
|
|
||||||
|
#include "util/util.h"
|
||||||
|
#include "util/cache.h"
|
||||||
|
#include "util/symbol.h"
|
||||||
|
#include "util/thread.h"
|
||||||
|
#include "util/header.h"
|
||||||
|
#include "util/session.h"
|
||||||
|
|
||||||
|
#include "util/parse-options.h"
|
||||||
|
#include "util/trace-event.h"
|
||||||
|
|
||||||
|
#include "util/debug.h"
|
||||||
|
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
static char *file_name;
|
||||||
|
static char name_buffer[256];
|
||||||
|
|
||||||
|
int perf_host = 1;
|
||||||
|
int perf_guest;
|
||||||
|
|
||||||
|
static const char * const kvm_usage[] = {
|
||||||
|
"perf kvm [<options>] {top|record|report|diff|buildid-list}",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct option kvm_options[] = {
|
||||||
|
OPT_STRING('i', "input", &file_name, "file",
|
||||||
|
"Input file name"),
|
||||||
|
OPT_STRING('o', "output", &file_name, "file",
|
||||||
|
"Output file name"),
|
||||||
|
OPT_BOOLEAN(0, "guest", &perf_guest,
|
||||||
|
"Collect guest os data"),
|
||||||
|
OPT_BOOLEAN(0, "host", &perf_host,
|
||||||
|
"Collect guest os data"),
|
||||||
|
OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
|
||||||
|
"guest mount directory under which every guest os"
|
||||||
|
" instance has a subdir"),
|
||||||
|
OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name,
|
||||||
|
"file", "file saving guest os vmlinux"),
|
||||||
|
OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms,
|
||||||
|
"file", "file saving guest os /proc/kallsyms"),
|
||||||
|
OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,
|
||||||
|
"file", "file saving guest os /proc/modules"),
|
||||||
|
OPT_END()
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __cmd_record(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int rec_argc, i = 0, j;
|
||||||
|
const char **rec_argv;
|
||||||
|
|
||||||
|
rec_argc = argc + 2;
|
||||||
|
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||||
|
rec_argv[i++] = strdup("record");
|
||||||
|
rec_argv[i++] = strdup("-o");
|
||||||
|
rec_argv[i++] = strdup(file_name);
|
||||||
|
for (j = 1; j < argc; j++, i++)
|
||||||
|
rec_argv[i] = argv[j];
|
||||||
|
|
||||||
|
BUG_ON(i != rec_argc);
|
||||||
|
|
||||||
|
return cmd_record(i, rec_argv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __cmd_report(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int rec_argc, i = 0, j;
|
||||||
|
const char **rec_argv;
|
||||||
|
|
||||||
|
rec_argc = argc + 2;
|
||||||
|
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||||
|
rec_argv[i++] = strdup("report");
|
||||||
|
rec_argv[i++] = strdup("-i");
|
||||||
|
rec_argv[i++] = strdup(file_name);
|
||||||
|
for (j = 1; j < argc; j++, i++)
|
||||||
|
rec_argv[i] = argv[j];
|
||||||
|
|
||||||
|
BUG_ON(i != rec_argc);
|
||||||
|
|
||||||
|
return cmd_report(i, rec_argv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __cmd_buildid_list(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int rec_argc, i = 0, j;
|
||||||
|
const char **rec_argv;
|
||||||
|
|
||||||
|
rec_argc = argc + 2;
|
||||||
|
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||||
|
rec_argv[i++] = strdup("buildid-list");
|
||||||
|
rec_argv[i++] = strdup("-i");
|
||||||
|
rec_argv[i++] = strdup(file_name);
|
||||||
|
for (j = 1; j < argc; j++, i++)
|
||||||
|
rec_argv[i] = argv[j];
|
||||||
|
|
||||||
|
BUG_ON(i != rec_argc);
|
||||||
|
|
||||||
|
return cmd_buildid_list(i, rec_argv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_kvm(int argc, const char **argv, const char *prefix __used)
|
||||||
|
{
|
||||||
|
perf_host = perf_guest = 0;
|
||||||
|
|
||||||
|
argc = parse_options(argc, argv, kvm_options, kvm_usage,
|
||||||
|
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||||
|
if (!argc)
|
||||||
|
usage_with_options(kvm_usage, kvm_options);
|
||||||
|
|
||||||
|
if (!perf_host)
|
||||||
|
perf_guest = 1;
|
||||||
|
|
||||||
|
if (!file_name) {
|
||||||
|
if (perf_host && !perf_guest)
|
||||||
|
sprintf(name_buffer, "perf.data.host");
|
||||||
|
else if (!perf_host && perf_guest)
|
||||||
|
sprintf(name_buffer, "perf.data.guest");
|
||||||
|
else
|
||||||
|
sprintf(name_buffer, "perf.data.kvm");
|
||||||
|
file_name = name_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp(argv[0], "rec", 3))
|
||||||
|
return __cmd_record(argc, argv);
|
||||||
|
else if (!strncmp(argv[0], "rep", 3))
|
||||||
|
return __cmd_report(argc, argv);
|
||||||
|
else if (!strncmp(argv[0], "diff", 4))
|
||||||
|
return cmd_diff(argc, argv, NULL);
|
||||||
|
else if (!strncmp(argv[0], "top", 3))
|
||||||
|
return cmd_top(argc, argv, NULL);
|
||||||
|
else if (!strncmp(argv[0], "buildid-list", 12))
|
||||||
|
return __cmd_buildid_list(argc, argv);
|
||||||
|
else
|
||||||
|
usage_with_options(kvm_usage, kvm_options);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -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)
|
||||||
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)
|
||||||
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,
|
||||||
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)
|
||||||
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[] = {
|
||||||
"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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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[] = "[H[2J";
|
static const char CONSOLE_CLEAR[] = "[H[2J";
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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) {
|
||||||
samples_per_sec,
|
printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%"
|
||||||
100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)),
|
" exact: %4.1f%% [",
|
||||||
esamples_percent);
|
samples_per_sec,
|
||||||
|
100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) /
|
||||||
|
samples_per_sec)),
|
||||||
|
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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 @@ repeat:
|
||||||
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,
|
||||||
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;
|
break;
|
||||||
|
case PERF_RECORD_MISC_GUEST_KERNEL:
|
||||||
|
++guest_kernel_samples;
|
||||||
|
kerninfo = kerninfo__find(&session->kerninfo_root,
|
||||||
|
self->ip.pid);
|
||||||
|
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,
|
||||||
* --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)
|
||||||
|
|
||||||
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);
|
||||||
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
|
||||||
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)
|
||||||
{ "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 {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]);
|
/*
|
||||||
|
* 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(&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,
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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;
|
||||||
|
|
||||||
dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n",
|
kerninfo = kerninfo__findnew(&session->kerninfo_root, self->mmap.pid);
|
||||||
self->mmap.pid, self->mmap.tid, self->mmap.start,
|
if (!kerninfo) {
|
||||||
self->mmap.len, self->mmap.pgoff, self->mmap.filename);
|
pr_err("Can't find id %d's kerninfo\n", self->mmap.pid);
|
||||||
|
goto out_problem;
|
||||||
|
}
|
||||||
|
|
||||||
if (self->mmap.pid == 0) {
|
kern_mmap_name(kerninfo, kmmap_prefix);
|
||||||
static const char kmmap_prefix[] = "[kernel.kallsyms.";
|
if (is_host_kernel(kerninfo))
|
||||||
|
kernel_type = DSO_TYPE_KERNEL;
|
||||||
|
else
|
||||||
|
kernel_type = DSO_TYPE_GUEST_KERNEL;
|
||||||
|
|
||||||
|
is_kernel_mmap = memcmp(self->mmap.filename,
|
||||||
|
kmmap_prefix,
|
||||||
|
strlen(kmmap_prefix)) == 0;
|
||||||
|
if (self->mmap.filename[0] == '/' ||
|
||||||
|
(!is_kernel_mmap && self->mmap.filename[0] == '[')) {
|
||||||
|
|
||||||
|
char short_module_name[1024];
|
||||||
|
char *name, *dot;
|
||||||
|
|
||||||
if (self->mmap.filename[0] == '/') {
|
if (self->mmap.filename[0] == '/') {
|
||||||
char short_module_name[1024];
|
name = strrchr(self->mmap.filename, '/');
|
||||||
char *name = strrchr(self->mmap.filename, '/'), *dot;
|
|
||||||
|
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
goto out_problem;
|
goto out_problem;
|
||||||
|
|
||||||
|
@ -352,59 +423,86 @@ 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,
|
||||||
if (map == NULL)
|
kerninfo);
|
||||||
goto out_problem;
|
if (map == NULL)
|
||||||
|
goto out_problem;
|
||||||
|
|
||||||
name = strdup(short_module_name);
|
name = strdup(short_module_name);
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
goto out_problem;
|
goto out_problem;
|
||||||
|
|
||||||
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 +
|
strlen(kmmap_prefix));
|
||||||
sizeof(kmmap_prefix) - 1);
|
/*
|
||||||
|
* Should be there already, from the build-id table in
|
||||||
|
* the header.
|
||||||
|
*/
|
||||||
|
struct dso *kernel = __dsos__findnew(&kerninfo->dsos__kernel,
|
||||||
|
kmmap_prefix);
|
||||||
|
if (kernel == NULL)
|
||||||
|
goto out_problem;
|
||||||
|
|
||||||
|
kernel->kernel = kernel_type;
|
||||||
|
if (__map_groups__create_kernel_maps(&kerninfo->kmaps,
|
||||||
|
kerninfo->vmlinux_maps, kernel) < 0)
|
||||||
|
goto out_problem;
|
||||||
|
|
||||||
|
event_set_kernel_mmap_len(kerninfo->vmlinux_maps, self);
|
||||||
|
perf_session__set_kallsyms_ref_reloc_sym(kerninfo->vmlinux_maps,
|
||||||
|
symbol_name,
|
||||||
|
self->mmap.pgoff);
|
||||||
|
if (is_default_guest(kerninfo)) {
|
||||||
/*
|
/*
|
||||||
* Should be there already, from the build-id table in
|
* preload dso of guest kernel and modules
|
||||||
* the header.
|
|
||||||
*/
|
*/
|
||||||
struct dso *kernel = __dsos__findnew(&dsos__kernel,
|
dso__load(kernel,
|
||||||
"[kernel.kallsyms]");
|
kerninfo->vmlinux_maps[MAP__FUNCTION],
|
||||||
if (kernel == NULL)
|
NULL);
|
||||||
goto out_problem;
|
|
||||||
|
|
||||||
kernel->kernel = 1;
|
|
||||||
if (__perf_session__create_kernel_maps(session, kernel) < 0)
|
|
||||||
goto out_problem;
|
|
||||||
|
|
||||||
session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start;
|
|
||||||
session->vmlinux_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 (session->vmlinux_maps[MAP__FUNCTION]->end == 0)
|
|
||||||
session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL;
|
|
||||||
|
|
||||||
perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name,
|
|
||||||
self->mmap.pgoff);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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;
|
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)
|
||||||
|
|
||||||
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);
|
||||||
al->level = 'H';
|
} 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->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 @@ try_again:
|
||||||
* "[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 @@ try_again:
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
!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 {
|
||||||
|
|
||||||
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[];
|
||||||
};
|
};
|
||||||
|
@ -154,10 +155,13 @@ int event__synthesize_thread(pid_t pid, event__handler_t process,
|
||||||
void event__synthesize_threads(event__handler_t process,
|
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,
|
||||||
const char *symbol_name);
|
struct kernel_info *kerninfo,
|
||||||
|
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,
|
||||||
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)
|
||||||
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)
|
||||||
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);
|
||||||
if (err == 0)
|
struct rb_node *nd;
|
||||||
err = __dsos__write_buildid_table(&dsos__user,
|
int err = 0;
|
||||||
PERF_RECORD_MISC_USER, fd);
|
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)
|
||||||
|
err = __dsos__write_buildid_table(&pos->dsos__user,
|
||||||
|
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
/* 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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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)) {
|
||||||
if (err == 0)
|
kmisc = PERF_RECORD_MISC_KERNEL;
|
||||||
err = __event_synthesize_build_ids(&dsos__user,
|
umisc = PERF_RECORD_MISC_USER;
|
||||||
PERF_RECORD_MISC_USER,
|
} else {
|
||||||
process, session);
|
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)
|
||||||
|
err = __event_synthesize_build_ids(&pos->dsos__user,
|
||||||
|
umisc, process, pos, 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,
|
||||||
|
|
||||||
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 = {
|
||||||
.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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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;
|
||||||
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 @@
|
||||||
#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,
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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];
|
||||||
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 {
|
||||||
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 {
|
||||||
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);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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];
|
||||||
/* 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)
|
||||||
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)
|
||||||
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
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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 {
|
||||||
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,
|
||||||
|
|
||||||
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;
|
||||||
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);
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
* 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,
|
||||||
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,
|
||||||
*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 &&
|
||||||
if (curr_map == NULL) {
|
self->kernel == DSO_TYPE_GUEST_KERNEL &&
|
||||||
pr_debug("/proc/{kallsyms,modules} "
|
is_default_guest(kerninfo)) {
|
||||||
"inconsistency while looking "
|
/*
|
||||||
"for \"%s\" module!\n", module);
|
* We assume all symbols of a module are
|
||||||
return -1;
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curr_map->dso->loaded)
|
curr_map = map_groups__find_by_name(kmaps,
|
||||||
|
map->type, module);
|
||||||
|
if (curr_map == NULL) {
|
||||||
|
pr_err("%s/proc/{kallsyms,modules} "
|
||||||
|
"inconsistency while looking "
|
||||||
|
"for \"%s\" module!\n",
|
||||||
|
kerninfo->root_dir, module);
|
||||||
|
curr_map = map;
|
||||||
|
goto discard_symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
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)
|
||||||
kernel_range++);
|
snprintf(dso_name, sizeof(dso_name),
|
||||||
|
"[guest.kernel].%d",
|
||||||
|
kernel_range++);
|
||||||
|
else
|
||||||
|
snprintf(dso_name, sizeof(dso_name),
|
||||||
|
"[kernel].%d",
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,7 +599,10 @@ 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]);
|
||||||
self->origin = DSO__ORIG_KERNEL;
|
if (self->kernel == DSO_TYPE_GUEST_KERNEL)
|
||||||
|
self->origin = DSO__ORIG_GUEST_KERNEL;
|
||||||
|
else
|
||||||
|
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
[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)
|
||||||
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 @@ more:
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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 @@ failure:
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
if (map == NULL)
|
if (map == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
dso->origin = DSO__ORIG_KMODULE;
|
if (is_host_kernel(kerninfo))
|
||||||
|
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)
|
||||||
*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 @@ out_fixup:
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
|
|
||||||
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;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dso__read_running_kernel_build_id(struct dso *self)
|
static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo,
|
||||||
|
const char *name)
|
||||||
{
|
{
|
||||||
if (sysfs__read_build_id("/sys/kernel/notes", self->build_id,
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dso__read_running_kernel_build_id(struct dso *self,
|
||||||
|
struct kernel_info *kerninfo)
|
||||||
|
{
|
||||||
|
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 @@ out_free_comm_list:
|
||||||
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)
|
||||||
|
|
||||||
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,10 +69,15 @@ 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;
|
||||||
char *dso_list_str,
|
const char *default_guest_vmlinux_name,
|
||||||
|
*default_guest_kallsyms,
|
||||||
|
*default_guest_modules;
|
||||||
|
const char *guestmount;
|
||||||
|
char *dso_list_str,
|
||||||
*comm_list_str,
|
*comm_list_str,
|
||||||
*sym_list_str,
|
*sym_list_str,
|
||||||
*col_width_list_str;
|
*col_width_list_str;
|
||||||
|
@ -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 {
|
||||||
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)
|
||||||
|
|
||||||
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 {
|
||||||
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,
|
||||||
|
|
||||||
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 */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче