perf/core improvements, fixes and code move
. Initialize 'page_size' variable in the python binding, this was sent for perf/urgent by mistake, then when merging Ingo removed it, fixing the problem for perf/urgent, but when perf/urgent was merged with perf/core, where that initialization is needed, made the python binding mmap call to fail, fix it by initializing page_size again. . Add a browser for 'perf script' and make it available from the report and annotate browsers. It does filtering to find the scripts that handle events found in the perf.data file used. From Feng Tang . Move some functions from symbol.c to more appropriate files, creating dso.[ch] in the process, no code changes. From Jiri Olsa . Fix mmap error output message for when perf_mmap fails and returns !-EPERM, where the default for mmap_pages, INT_MAX, was causing a !power of 2 error message, fix from Jiri Olsa. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJQjpe9AAoJENZQFvNTUqpAc0QP/2FQQYbJ6vCTB1b/iIgWr8cS C2KgMgf/yZxuSsj7svd8OHX8GklfgJ3d5+Sl2INGQOAfvBfSGduCRmDkQp811vCI 9wQ5OA2fLUJ6VBMULLnoEmqgc/g1rska9Mc0fqALz1iQG1FZDdjh4MeOI8DpWjFr KCTIC7Ds7wB2xUbNF7yPm/J/7tChfFOPkXoZoH7aOobm61pk8muWWaTDXp7SoBiM flcGlRbJNiZil3RUB8wWyyCrBrMH32zAgZ6Z+nQAoE1krs/QHWwfDp5JsWaEGAMx q18pWf6N/oLHgdJD10ufxIy/VwOrAyRzGszHUPKHsqrfMM7Si17hJvSdcFMEi6sS OspxLca4Z8eqM7XhxwoMGOf/ND7HWCTEOXxOJBXg9fqunSJsbGdq0wdlPp85JkKR VcGZ+l5d2aAANlwKGWVIyco8ujnMYOZvlNom2NMl5z7VkyAhYlza+4GLbfZBKaQy YaXKuthNn3fUSGcIW3OSa/NWnp2OMwelO87khyU+RtLEaeFWUYpdMMzvbBtHb4ux 5dVBLXktdlXeHfe7OnVmnyoEONPIsxe1bSTMOhYQxhwM0lpVExAJboiFz9cy9y8Z FjC2B03fhPNYZDtAaFM8b1HYSaayfHoe/Fzfgfcl5Eet8P51umIcxE7aVnlGrgsA 8Oy6/9WqGX7itArF4tt+ =j6bL -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements, fixes and code move from Arnaldo Carvalho de Melo: * Initialize 'page_size' variable in the python binding, this was sent for perf/urgent by mistake, then when merging Ingo removed it, fixing the problem for perf/urgent, but when perf/urgent was merged with perf/core, where that initialization is needed, made the python binding mmap call to fail, fix it by initializing page_size again. * Add a browser for 'perf script' and make it available from the report and annotate browsers. It does filtering to find the scripts that handle events found in the perf.data file used. From Feng Tang * Move some functions from symbol.c to more appropriate files, creating dso.[ch] in the process, no code changes. From Jiri Olsa * Fix mmap error output message for when perf_mmap fails and returns !-EPERM, where the default for mmap_pages, INT_MAX, was causing a !power of 2 error message, fix from Jiri Olsa. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
8748dd9b99
|
@ -327,6 +327,7 @@ LIB_H += util/svghelper.h
|
|||
LIB_H += util/tool.h
|
||||
LIB_H += util/run-command.h
|
||||
LIB_H += util/sigchain.h
|
||||
LIB_H += util/dso.h
|
||||
LIB_H += util/symbol.h
|
||||
LIB_H += util/color.h
|
||||
LIB_H += util/values.h
|
||||
|
@ -385,6 +386,7 @@ LIB_OBJS += $(OUTPUT)util/top.o
|
|||
LIB_OBJS += $(OUTPUT)util/usage.o
|
||||
LIB_OBJS += $(OUTPUT)util/wrapper.o
|
||||
LIB_OBJS += $(OUTPUT)util/sigchain.o
|
||||
LIB_OBJS += $(OUTPUT)util/dso.o
|
||||
LIB_OBJS += $(OUTPUT)util/symbol.o
|
||||
LIB_OBJS += $(OUTPUT)util/symbol-elf.o
|
||||
LIB_OBJS += $(OUTPUT)util/dso-test-data.o
|
||||
|
@ -591,6 +593,7 @@ ifndef NO_NEWT
|
|||
LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/map.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o
|
||||
LIB_OBJS += $(OUTPUT)ui/progress.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/setup.o
|
||||
|
@ -907,6 +910,9 @@ $(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
|
|||
$(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
$(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
struct perf_annotate {
|
||||
struct perf_tool tool;
|
||||
char const *input_name;
|
||||
bool force, use_tui, use_stdio;
|
||||
bool full_paths;
|
||||
bool print_line;
|
||||
|
@ -175,7 +174,7 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
|||
struct perf_evsel *pos;
|
||||
u64 total_nr_samples;
|
||||
|
||||
session = perf_session__new(ann->input_name, O_RDONLY,
|
||||
session = perf_session__new(input_name, O_RDONLY,
|
||||
ann->force, false, &ann->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -260,7 +259,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
},
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &annotate.input_name, "file",
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
|
||||
"only consider symbols in these dsos"),
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "util/header.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/strlist.h"
|
||||
#include "util/build-id.h"
|
||||
#include "util/symbol.h"
|
||||
|
||||
static int build_id_cache__add_file(const char *filename, const char *debugdir)
|
||||
|
|
|
@ -44,8 +44,7 @@ static int filename__fprintf_build_id(const char *name, FILE *fp)
|
|||
return fprintf(fp, "%s\n", sbuild_id);
|
||||
}
|
||||
|
||||
static int perf_session__list_build_ids(const char *input_name,
|
||||
bool force, bool with_hits)
|
||||
static int perf_session__list_build_ids(bool force, bool with_hits)
|
||||
{
|
||||
struct perf_session *session;
|
||||
|
||||
|
@ -81,7 +80,6 @@ int cmd_buildid_list(int argc, const char **argv,
|
|||
bool show_kernel = false;
|
||||
bool with_hits = false;
|
||||
bool force = false;
|
||||
const char *input_name = NULL;
|
||||
const struct option options[] = {
|
||||
OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"),
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
|
@ -101,5 +99,5 @@ int cmd_buildid_list(int argc, const char **argv,
|
|||
if (show_kernel)
|
||||
return sysfs__fprintf_build_id(stdout);
|
||||
|
||||
return perf_session__list_build_ids(input_name, force, with_hits);
|
||||
return perf_session__list_build_ids(force, with_hits);
|
||||
}
|
||||
|
|
|
@ -48,12 +48,12 @@ static int __if_print(bool *first, const char *field, u64 value)
|
|||
|
||||
#define if_print(field) __if_print(&first, #field, pos->attr.field)
|
||||
|
||||
static int __cmd_evlist(const char *input_name, struct perf_attr_details *details)
|
||||
static int __cmd_evlist(const char *file_name, struct perf_attr_details *details)
|
||||
{
|
||||
struct perf_session *session;
|
||||
struct perf_evsel *pos;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false, NULL);
|
||||
session = perf_session__new(file_name, O_RDONLY, 0, false, NULL);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -111,7 +111,6 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail
|
|||
int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct perf_attr_details details = { .verbose = false, };
|
||||
const char *input_name = NULL;
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file", "Input file name"),
|
||||
OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"),
|
||||
|
|
|
@ -477,7 +477,7 @@ static void sort_result(void)
|
|||
__sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
|
||||
}
|
||||
|
||||
static int __cmd_kmem(const char *input_name)
|
||||
static int __cmd_kmem(void)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct perf_session *session;
|
||||
|
@ -743,7 +743,6 @@ static int __cmd_record(int argc, const char **argv)
|
|||
int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
const char * const default_sort_order = "frag,hit,bytes";
|
||||
const char *input_name = NULL;
|
||||
const struct option kmem_options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
|
||||
|
@ -779,7 +778,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
if (list_empty(&alloc_sort))
|
||||
setup_sorting(&alloc_sort, default_sort_order);
|
||||
|
||||
return __cmd_kmem(input_name);
|
||||
return __cmd_kmem();
|
||||
} else
|
||||
usage_with_options(kmem_usage, kmem_options);
|
||||
|
||||
|
|
|
@ -335,8 +335,6 @@ alloc_failed:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static const char *input_name;
|
||||
|
||||
struct trace_lock_handler {
|
||||
int (*acquire_event)(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample);
|
||||
|
|
|
@ -363,7 +363,8 @@ try_again:
|
|||
"or try again with a smaller value of -m/--mmap_pages.\n"
|
||||
"(current value: %d)\n", opts->mmap_pages);
|
||||
rc = -errno;
|
||||
} else if (!is_power_of_2(opts->mmap_pages)) {
|
||||
} else if (!is_power_of_2(opts->mmap_pages) &&
|
||||
(opts->mmap_pages != UINT_MAX)) {
|
||||
pr_err("--mmap_pages/-m value must be a power of two.");
|
||||
rc = -EINVAL;
|
||||
} else {
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
struct perf_report {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
char const *input_name;
|
||||
bool force, use_tui, use_gtk, use_stdio;
|
||||
bool hide_unresolved;
|
||||
bool dont_use_callchains;
|
||||
|
@ -571,7 +570,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
.pretty_printing_style = "normal",
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &report.input_name, "file",
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
|
@ -657,13 +656,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
if (report.inverted_callchain)
|
||||
callchain_param.order = ORDER_CALLER;
|
||||
|
||||
if (!report.input_name || !strlen(report.input_name)) {
|
||||
if (!input_name || !strlen(input_name)) {
|
||||
if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
|
||||
report.input_name = "-";
|
||||
input_name = "-";
|
||||
else
|
||||
report.input_name = "perf.data";
|
||||
input_name = "perf.data";
|
||||
}
|
||||
session = perf_session__new(report.input_name, O_RDONLY,
|
||||
session = perf_session__new(input_name, O_RDONLY,
|
||||
report.force, false, &report.tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -694,7 +693,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
|
||||
}
|
||||
|
||||
if (strcmp(report.input_name, "-") != 0)
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
|
|
|
@ -120,7 +120,6 @@ struct trace_sched_handler {
|
|||
|
||||
struct perf_sched {
|
||||
struct perf_tool tool;
|
||||
const char *input_name;
|
||||
const char *sort_order;
|
||||
unsigned long nr_tasks;
|
||||
struct task_desc *pid_to_task[MAX_PID];
|
||||
|
@ -1460,7 +1459,7 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
|
|||
};
|
||||
struct perf_session *session;
|
||||
|
||||
session = perf_session__new(sched->input_name, O_RDONLY, 0, false, &sched->tool);
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false, &sched->tool);
|
||||
if (session == NULL) {
|
||||
pr_debug("No Memory for session\n");
|
||||
return -1;
|
||||
|
@ -1708,7 +1707,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_END()
|
||||
};
|
||||
const struct option sched_options[] = {
|
||||
OPT_STRING('i', "input", &sched.input_name, "file",
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
|
|
|
@ -1029,6 +1029,68 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
|
|||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some scripts specify the required events in their "xxx-record" file,
|
||||
* this function will check if the events in perf.data match those
|
||||
* mentioned in the "xxx-record".
|
||||
*
|
||||
* Fixme: All existing "xxx-record" are all in good formats "-e event ",
|
||||
* which is covered well now. And new parsing code should be added to
|
||||
* cover the future complexing formats like event groups etc.
|
||||
*/
|
||||
static int check_ev_match(char *dir_name, char *scriptname,
|
||||
struct perf_session *session)
|
||||
{
|
||||
char filename[MAXPATHLEN], evname[128];
|
||||
char line[BUFSIZ], *p;
|
||||
struct perf_evsel *pos;
|
||||
int match, len;
|
||||
FILE *fp;
|
||||
|
||||
sprintf(filename, "%s/bin/%s-record", dir_name, scriptname);
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
p = ltrim(line);
|
||||
if (*p == '#')
|
||||
continue;
|
||||
|
||||
while (strlen(p)) {
|
||||
p = strstr(p, "-e");
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
p += 2;
|
||||
p = ltrim(p);
|
||||
len = strcspn(p, " \t");
|
||||
if (!len)
|
||||
break;
|
||||
|
||||
snprintf(evname, len + 1, "%s", p);
|
||||
|
||||
match = 0;
|
||||
list_for_each_entry(pos,
|
||||
&session->evlist->entries, node) {
|
||||
if (!strcmp(perf_evsel__name(pos), evname)) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return -1 if none is found, otherwise the actual scripts number.
|
||||
*
|
||||
|
@ -1039,17 +1101,23 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
|
|||
int find_scripts(char **scripts_array, char **scripts_path_array)
|
||||
{
|
||||
struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
|
||||
char scripts_path[MAXPATHLEN];
|
||||
char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN];
|
||||
DIR *scripts_dir, *lang_dir;
|
||||
char lang_path[MAXPATHLEN];
|
||||
struct perf_session *session;
|
||||
char *temp;
|
||||
int i = 0;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false, NULL);
|
||||
if (!session)
|
||||
return -1;
|
||||
|
||||
snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
|
||||
|
||||
scripts_dir = opendir(scripts_path);
|
||||
if (!scripts_dir)
|
||||
if (!scripts_dir) {
|
||||
perf_session__delete(session);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
|
||||
snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path,
|
||||
|
@ -1077,10 +1145,18 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
|
|||
snprintf(scripts_array[i],
|
||||
(temp - script_dirent.d_name) + 1,
|
||||
"%s", script_dirent.d_name);
|
||||
|
||||
if (check_ev_match(lang_path,
|
||||
scripts_array[i], session))
|
||||
continue;
|
||||
|
||||
i++;
|
||||
}
|
||||
closedir(lang_dir);
|
||||
}
|
||||
|
||||
closedir(scripts_dir);
|
||||
perf_session__delete(session);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
@ -1175,7 +1251,6 @@ static int have_cmd(int argc, const char **argv)
|
|||
int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
bool show_full_info = false;
|
||||
const char *input_name = NULL;
|
||||
char *rec_script_path = NULL;
|
||||
char *rep_script_path = NULL;
|
||||
struct perf_session *session;
|
||||
|
|
|
@ -965,7 +965,7 @@ static void write_svg_file(const char *filename)
|
|||
svg_close();
|
||||
}
|
||||
|
||||
static int __cmd_timechart(const char *input_name, const char *output_name)
|
||||
static int __cmd_timechart(const char *output_name)
|
||||
{
|
||||
struct perf_tool perf_timechart = {
|
||||
.comm = process_comm_event,
|
||||
|
@ -1061,7 +1061,6 @@ parse_process(const struct option *opt __maybe_unused, const char *arg,
|
|||
int cmd_timechart(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
const char *input_name;
|
||||
const char *output_name = "output.svg";
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
|
@ -1092,5 +1091,5 @@ int cmd_timechart(int argc, const char **argv,
|
|||
|
||||
setup_pager();
|
||||
|
||||
return __cmd_timechart(input_name, output_name);
|
||||
return __cmd_timechart(output_name);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ const char perf_more_info_string[] =
|
|||
|
||||
int use_browser = -1;
|
||||
static int use_pager = -1;
|
||||
const char *input_name;
|
||||
|
||||
struct cmd_struct {
|
||||
const char *cmd;
|
||||
|
|
|
@ -208,6 +208,7 @@ struct branch_stack {
|
|||
struct branch_entry entries[0];
|
||||
};
|
||||
|
||||
extern const char *input_name;
|
||||
extern bool perf_host, perf_guest;
|
||||
extern const char perf_version_string[];
|
||||
|
||||
|
|
|
@ -676,8 +676,14 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
|||
"o Toggle disassembler output/simplified view\n"
|
||||
"s Toggle source code view\n"
|
||||
"/ Search string\n"
|
||||
"r Run available scripts\n"
|
||||
"? Search previous string\n");
|
||||
continue;
|
||||
case 'r':
|
||||
{
|
||||
script_browse(NULL);
|
||||
continue;
|
||||
}
|
||||
case 'H':
|
||||
nd = browser->curr_hot;
|
||||
break;
|
||||
|
|
|
@ -1141,6 +1141,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
int nr_options = 0;
|
||||
int key = -1;
|
||||
char buf[64];
|
||||
char script_opt[64];
|
||||
|
||||
if (browser == NULL)
|
||||
return -1;
|
||||
|
@ -1159,6 +1160,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
int choice = 0,
|
||||
annotate = -2, zoom_dso = -2, zoom_thread = -2,
|
||||
annotate_f = -2, annotate_t = -2, browse_map = -2;
|
||||
int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2;
|
||||
|
||||
nr_options = 0;
|
||||
|
||||
|
@ -1211,6 +1213,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
hist_browser__reset(browser);
|
||||
}
|
||||
continue;
|
||||
case 'r':
|
||||
goto do_scripts;
|
||||
case K_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
|
@ -1229,6 +1233,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
"E Expand all callchains\n"
|
||||
"d Zoom into current DSO\n"
|
||||
"t Zoom into current Thread\n"
|
||||
"r Run available scripts\n"
|
||||
"P Print histograms to perf.hist.N\n"
|
||||
"V Verbose (DSO names in callchains, etc)\n"
|
||||
"/ Filter symbol by name");
|
||||
|
@ -1317,6 +1322,25 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
browser->selection->map != NULL &&
|
||||
asprintf(&options[nr_options], "Browse map details") > 0)
|
||||
browse_map = nr_options++;
|
||||
|
||||
/* perf script support */
|
||||
if (browser->he_selection) {
|
||||
struct symbol *sym;
|
||||
|
||||
if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
|
||||
browser->he_selection->thread->comm) > 0)
|
||||
scripts_comm = nr_options++;
|
||||
|
||||
sym = browser->he_selection->ms.sym;
|
||||
if (sym && sym->namelen &&
|
||||
asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
|
||||
sym->name) > 0)
|
||||
scripts_symbol = nr_options++;
|
||||
}
|
||||
|
||||
if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
|
||||
scripts_all = nr_options++;
|
||||
|
||||
add_exit_option:
|
||||
options[nr_options++] = (char *)"Exit";
|
||||
retry_popup_menu:
|
||||
|
@ -1411,6 +1435,20 @@ zoom_out_thread:
|
|||
hists__filter_by_thread(hists);
|
||||
hist_browser__reset(browser);
|
||||
}
|
||||
/* perf scripts support */
|
||||
else if (choice == scripts_all || choice == scripts_comm ||
|
||||
choice == scripts_symbol) {
|
||||
do_scripts:
|
||||
memset(script_opt, 0, 64);
|
||||
|
||||
if (choice == scripts_comm)
|
||||
sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
|
||||
|
||||
if (choice == scripts_symbol)
|
||||
sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
|
||||
|
||||
script_browse(script_opt);
|
||||
}
|
||||
}
|
||||
out_free_stack:
|
||||
pstack__delete(fstack);
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
#include <elf.h>
|
||||
#include <newt.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/ttydefaults.h>
|
||||
#include <string.h>
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/util.h"
|
||||
#include "../../util/hist.h"
|
||||
#include "../../util/debug.h"
|
||||
#include "../../util/symbol.h"
|
||||
#include "../browser.h"
|
||||
#include "../helpline.h"
|
||||
#include "../libslang.h"
|
||||
|
||||
/* 2048 lines should be enough for a script output */
|
||||
#define MAX_LINES 2048
|
||||
|
||||
/* 160 bytes for one output line */
|
||||
#define AVERAGE_LINE_LEN 160
|
||||
|
||||
struct script_line {
|
||||
struct list_head node;
|
||||
char line[AVERAGE_LINE_LEN];
|
||||
};
|
||||
|
||||
struct perf_script_browser {
|
||||
struct ui_browser b;
|
||||
struct list_head entries;
|
||||
const char *script_name;
|
||||
int nr_lines;
|
||||
};
|
||||
|
||||
#define SCRIPT_NAMELEN 128
|
||||
#define SCRIPT_MAX_NO 64
|
||||
/*
|
||||
* Usually the full path for a script is:
|
||||
* /home/username/libexec/perf-core/scripts/python/xxx.py
|
||||
* /home/username/libexec/perf-core/scripts/perl/xxx.pl
|
||||
* So 256 should be long enough to contain the full path.
|
||||
*/
|
||||
#define SCRIPT_FULLPATH_LEN 256
|
||||
|
||||
/*
|
||||
* When success, will copy the full path of the selected script
|
||||
* into the buffer pointed by script_name, and return 0.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
static int list_scripts(char *script_name)
|
||||
{
|
||||
char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO];
|
||||
int i, num, choice, ret = -1;
|
||||
|
||||
/* Preset the script name to SCRIPT_NAMELEN */
|
||||
buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
|
||||
if (!buf)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < SCRIPT_MAX_NO; i++) {
|
||||
names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
|
||||
paths[i] = names[i] + SCRIPT_NAMELEN;
|
||||
}
|
||||
|
||||
num = find_scripts(names, paths);
|
||||
if (num > 0) {
|
||||
choice = ui__popup_menu(num, names);
|
||||
if (choice < num && choice >= 0) {
|
||||
strcpy(script_name, paths[choice]);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void script_browser__write(struct ui_browser *browser,
|
||||
void *entry, int row)
|
||||
{
|
||||
struct script_line *sline = list_entry(entry, struct script_line, node);
|
||||
bool current_entry = ui_browser__is_current_entry(browser, row);
|
||||
|
||||
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
|
||||
HE_COLORSET_NORMAL);
|
||||
|
||||
slsmg_write_nstring(sline->line, browser->width);
|
||||
}
|
||||
|
||||
static int script_browser__run(struct perf_script_browser *self)
|
||||
{
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(&self->b, self->script_name,
|
||||
"Press <- or ESC to exit") < 0)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
key = ui_browser__run(&self->b, 0);
|
||||
|
||||
/* We can add some special key handling here if needed */
|
||||
break;
|
||||
}
|
||||
|
||||
ui_browser__hide(&self->b);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
int script_browse(const char *script_opt)
|
||||
{
|
||||
char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN];
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t retlen;
|
||||
int ret = -1, nr_entries = 0;
|
||||
FILE *fp;
|
||||
void *buf;
|
||||
struct script_line *sline;
|
||||
|
||||
struct perf_script_browser script = {
|
||||
.b = {
|
||||
.refresh = ui_browser__list_head_refresh,
|
||||
.seek = ui_browser__list_head_seek,
|
||||
.write = script_browser__write,
|
||||
},
|
||||
.script_name = script_name,
|
||||
};
|
||||
|
||||
INIT_LIST_HEAD(&script.entries);
|
||||
|
||||
/* Save each line of the output in one struct script_line object. */
|
||||
buf = zalloc((sizeof(*sline)) * MAX_LINES);
|
||||
if (!buf)
|
||||
return -1;
|
||||
sline = buf;
|
||||
|
||||
memset(script_name, 0, SCRIPT_FULLPATH_LEN);
|
||||
if (list_scripts(script_name))
|
||||
goto exit;
|
||||
|
||||
sprintf(cmd, "perf script -s %s ", script_name);
|
||||
|
||||
if (script_opt)
|
||||
strcat(cmd, script_opt);
|
||||
|
||||
if (input_name) {
|
||||
strcat(cmd, " -i ");
|
||||
strcat(cmd, input_name);
|
||||
}
|
||||
|
||||
strcat(cmd, " 2>&1");
|
||||
|
||||
fp = popen(cmd, "r");
|
||||
if (!fp)
|
||||
goto exit;
|
||||
|
||||
while ((retlen = getline(&line, &len, fp)) != -1) {
|
||||
strncpy(sline->line, line, AVERAGE_LINE_LEN);
|
||||
|
||||
/* If one output line is very large, just cut it short */
|
||||
if (retlen >= AVERAGE_LINE_LEN) {
|
||||
sline->line[AVERAGE_LINE_LEN - 1] = '\0';
|
||||
sline->line[AVERAGE_LINE_LEN - 2] = '\n';
|
||||
}
|
||||
list_add_tail(&sline->node, &script.entries);
|
||||
|
||||
if (script.b.width < retlen)
|
||||
script.b.width = retlen;
|
||||
|
||||
if (nr_entries++ >= MAX_LINES - 1)
|
||||
break;
|
||||
sline++;
|
||||
}
|
||||
|
||||
if (script.b.width > AVERAGE_LINE_LEN)
|
||||
script.b.width = AVERAGE_LINE_LEN;
|
||||
|
||||
if (line)
|
||||
free(line);
|
||||
pclose(fp);
|
||||
|
||||
script.nr_lines = nr_entries;
|
||||
script.b.nr_entries = nr_entries;
|
||||
script.b.entries = &script.entries;
|
||||
|
||||
ret = script_browser__run(&script);
|
||||
exit:
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
#include "debug.h"
|
||||
#include "annotate.h"
|
||||
#include <pthread.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
const char *disassembler_style;
|
||||
const char *objdump_path;
|
||||
|
|
|
@ -70,6 +70,21 @@ struct perf_tool build_id__mark_dso_hit_ops = {
|
|||
.build_id = perf_event__process_build_id,
|
||||
};
|
||||
|
||||
int build_id__sprintf(const u8 *build_id, int len, char *bf)
|
||||
{
|
||||
char *bid = bf;
|
||||
const u8 *raw = build_id;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
sprintf(bid, "%02x", *raw);
|
||||
++raw;
|
||||
bid += 2;
|
||||
}
|
||||
|
||||
return raw - build_id;
|
||||
}
|
||||
|
||||
char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
|
||||
{
|
||||
char build_id_hex[BUILD_ID_SIZE * 2 + 1];
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
#ifndef PERF_BUILD_ID_H_
|
||||
#define PERF_BUILD_ID_H_ 1
|
||||
|
||||
#include "session.h"
|
||||
#define BUILD_ID_SIZE 20
|
||||
|
||||
#include "tool.h"
|
||||
#include "types.h"
|
||||
|
||||
extern struct perf_tool build_id__mark_dso_hit_ops;
|
||||
struct dso;
|
||||
|
||||
int build_id__sprintf(const u8 *build_id, int len, char *bf);
|
||||
char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
|
||||
|
||||
int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,
|
||||
|
|
|
@ -0,0 +1,594 @@
|
|||
#include "symbol.h"
|
||||
#include "dso.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
|
||||
char dso__symtab_origin(const struct dso *dso)
|
||||
{
|
||||
static const char origin[] = {
|
||||
[DSO_BINARY_TYPE__KALLSYMS] = 'k',
|
||||
[DSO_BINARY_TYPE__VMLINUX] = 'v',
|
||||
[DSO_BINARY_TYPE__JAVA_JIT] = 'j',
|
||||
[DSO_BINARY_TYPE__DEBUGLINK] = 'l',
|
||||
[DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B',
|
||||
[DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f',
|
||||
[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u',
|
||||
[DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b',
|
||||
[DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd',
|
||||
[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K',
|
||||
[DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g',
|
||||
[DSO_BINARY_TYPE__GUEST_KMODULE] = 'G',
|
||||
[DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V',
|
||||
};
|
||||
|
||||
if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND)
|
||||
return '!';
|
||||
return origin[dso->symtab_type];
|
||||
}
|
||||
|
||||
int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
|
||||
char *root_dir, char *file, size_t size)
|
||||
{
|
||||
char build_id_hex[BUILD_ID_SIZE * 2 + 1];
|
||||
int ret = 0;
|
||||
|
||||
switch (type) {
|
||||
case DSO_BINARY_TYPE__DEBUGLINK: {
|
||||
char *debuglink;
|
||||
|
||||
strncpy(file, dso->long_name, size);
|
||||
debuglink = file + dso->long_name_len;
|
||||
while (debuglink != file && *debuglink != '/')
|
||||
debuglink--;
|
||||
if (*debuglink == '/')
|
||||
debuglink++;
|
||||
filename__read_debuglink(dso->long_name, debuglink,
|
||||
size - (debuglink - file));
|
||||
}
|
||||
break;
|
||||
case DSO_BINARY_TYPE__BUILD_ID_CACHE:
|
||||
/* skip the locally configured cache if a symfs is given */
|
||||
if (symbol_conf.symfs[0] ||
|
||||
(dso__build_id_filename(dso, file, size) == NULL))
|
||||
ret = -1;
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
|
||||
snprintf(file, size, "%s/usr/lib/debug%s.debug",
|
||||
symbol_conf.symfs, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
|
||||
snprintf(file, size, "%s/usr/lib/debug%s",
|
||||
symbol_conf.symfs, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
|
||||
if (!dso->has_build_id) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
build_id__sprintf(dso->build_id,
|
||||
sizeof(dso->build_id),
|
||||
build_id_hex);
|
||||
snprintf(file, size,
|
||||
"%s/usr/lib/debug/.build-id/%.2s/%s.debug",
|
||||
symbol_conf.symfs, build_id_hex, build_id_hex + 2);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
|
||||
snprintf(file, size, "%s%s",
|
||||
symbol_conf.symfs, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__GUEST_KMODULE:
|
||||
snprintf(file, size, "%s%s%s", symbol_conf.symfs,
|
||||
root_dir, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
|
||||
snprintf(file, size, "%s%s", symbol_conf.symfs,
|
||||
dso->long_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
case DSO_BINARY_TYPE__KALLSYMS:
|
||||
case DSO_BINARY_TYPE__VMLINUX:
|
||||
case DSO_BINARY_TYPE__GUEST_KALLSYMS:
|
||||
case DSO_BINARY_TYPE__GUEST_VMLINUX:
|
||||
case DSO_BINARY_TYPE__JAVA_JIT:
|
||||
case DSO_BINARY_TYPE__NOT_FOUND:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int open_dso(struct dso *dso, struct machine *machine)
|
||||
{
|
||||
char *root_dir = (char *) "";
|
||||
char *name;
|
||||
int fd;
|
||||
|
||||
name = malloc(PATH_MAX);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
if (machine)
|
||||
root_dir = machine->root_dir;
|
||||
|
||||
if (dso__binary_type_file(dso, dso->data_type,
|
||||
root_dir, name, PATH_MAX)) {
|
||||
free(name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = open(name, O_RDONLY);
|
||||
free(name);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int dso__data_fd(struct dso *dso, struct machine *machine)
|
||||
{
|
||||
static enum dso_binary_type binary_type_data[] = {
|
||||
DSO_BINARY_TYPE__BUILD_ID_CACHE,
|
||||
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
|
||||
DSO_BINARY_TYPE__NOT_FOUND,
|
||||
};
|
||||
int i = 0;
|
||||
|
||||
if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND)
|
||||
return open_dso(dso, machine);
|
||||
|
||||
do {
|
||||
int fd;
|
||||
|
||||
dso->data_type = binary_type_data[i++];
|
||||
|
||||
fd = open_dso(dso, machine);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
|
||||
} while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
dso_cache__free(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *next = rb_first(root);
|
||||
|
||||
while (next) {
|
||||
struct dso_cache *cache;
|
||||
|
||||
cache = rb_entry(next, struct dso_cache, rb_node);
|
||||
next = rb_next(&cache->rb_node);
|
||||
rb_erase(&cache->rb_node, root);
|
||||
free(cache);
|
||||
}
|
||||
}
|
||||
|
||||
static struct dso_cache*
|
||||
dso_cache__find(struct rb_root *root, u64 offset)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct dso_cache *cache;
|
||||
|
||||
while (*p != NULL) {
|
||||
u64 end;
|
||||
|
||||
parent = *p;
|
||||
cache = rb_entry(parent, struct dso_cache, rb_node);
|
||||
end = cache->offset + DSO__DATA_CACHE_SIZE;
|
||||
|
||||
if (offset < cache->offset)
|
||||
p = &(*p)->rb_left;
|
||||
else if (offset >= end)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
return cache;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
dso_cache__insert(struct rb_root *root, struct dso_cache *new)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct dso_cache *cache;
|
||||
u64 offset = new->offset;
|
||||
|
||||
while (*p != NULL) {
|
||||
u64 end;
|
||||
|
||||
parent = *p;
|
||||
cache = rb_entry(parent, struct dso_cache, rb_node);
|
||||
end = cache->offset + DSO__DATA_CACHE_SIZE;
|
||||
|
||||
if (offset < cache->offset)
|
||||
p = &(*p)->rb_left;
|
||||
else if (offset >= end)
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
rb_link_node(&new->rb_node, parent, p);
|
||||
rb_insert_color(&new->rb_node, root);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
dso_cache__memcpy(struct dso_cache *cache, u64 offset,
|
||||
u8 *data, u64 size)
|
||||
{
|
||||
u64 cache_offset = offset - cache->offset;
|
||||
u64 cache_size = min(cache->size - cache_offset, size);
|
||||
|
||||
memcpy(data, cache->data + cache_offset, cache_size);
|
||||
return cache_size;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
dso_cache__read(struct dso *dso, struct machine *machine,
|
||||
u64 offset, u8 *data, ssize_t size)
|
||||
{
|
||||
struct dso_cache *cache;
|
||||
ssize_t ret;
|
||||
int fd;
|
||||
|
||||
fd = dso__data_fd(dso, machine);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
do {
|
||||
u64 cache_offset;
|
||||
|
||||
ret = -ENOMEM;
|
||||
|
||||
cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
|
||||
if (!cache)
|
||||
break;
|
||||
|
||||
cache_offset = offset & DSO__DATA_CACHE_MASK;
|
||||
ret = -EINVAL;
|
||||
|
||||
if (-1 == lseek(fd, cache_offset, SEEK_SET))
|
||||
break;
|
||||
|
||||
ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
|
||||
cache->offset = cache_offset;
|
||||
cache->size = ret;
|
||||
dso_cache__insert(&dso->cache, cache);
|
||||
|
||||
ret = dso_cache__memcpy(cache, offset, data, size);
|
||||
|
||||
} while (0);
|
||||
|
||||
if (ret <= 0)
|
||||
free(cache);
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dso_cache_read(struct dso *dso, struct machine *machine,
|
||||
u64 offset, u8 *data, ssize_t size)
|
||||
{
|
||||
struct dso_cache *cache;
|
||||
|
||||
cache = dso_cache__find(&dso->cache, offset);
|
||||
if (cache)
|
||||
return dso_cache__memcpy(cache, offset, data, size);
|
||||
else
|
||||
return dso_cache__read(dso, machine, offset, data, size);
|
||||
}
|
||||
|
||||
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
|
||||
u64 offset, u8 *data, ssize_t size)
|
||||
{
|
||||
ssize_t r = 0;
|
||||
u8 *p = data;
|
||||
|
||||
do {
|
||||
ssize_t ret;
|
||||
|
||||
ret = dso_cache_read(dso, machine, offset, p, size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Reached EOF, return what we have. */
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
BUG_ON(ret > size);
|
||||
|
||||
r += ret;
|
||||
p += ret;
|
||||
offset += ret;
|
||||
size -= ret;
|
||||
|
||||
} while (size);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
|
||||
struct machine *machine, u64 addr,
|
||||
u8 *data, ssize_t size)
|
||||
{
|
||||
u64 offset = map->map_ip(map, addr);
|
||||
return dso__data_read_offset(dso, machine, offset, data, size);
|
||||
}
|
||||
|
||||
struct map *dso__new_map(const char *name)
|
||||
{
|
||||
struct map *map = NULL;
|
||||
struct dso *dso = dso__new(name);
|
||||
|
||||
if (dso)
|
||||
map = map__new2(0, dso, MAP__FUNCTION);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
|
||||
const char *short_name, int dso_type)
|
||||
{
|
||||
/*
|
||||
* The kernel dso could be created by build_id processing.
|
||||
*/
|
||||
struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name);
|
||||
|
||||
/*
|
||||
* We need to run this in all cases, since during the build_id
|
||||
* processing we had no idea this was the kernel dso.
|
||||
*/
|
||||
if (dso != NULL) {
|
||||
dso__set_short_name(dso, short_name);
|
||||
dso->kernel = dso_type;
|
||||
}
|
||||
|
||||
return dso;
|
||||
}
|
||||
|
||||
void dso__set_long_name(struct dso *dso, char *name)
|
||||
{
|
||||
if (name == NULL)
|
||||
return;
|
||||
dso->long_name = name;
|
||||
dso->long_name_len = strlen(name);
|
||||
}
|
||||
|
||||
void dso__set_short_name(struct dso *dso, const char *name)
|
||||
{
|
||||
if (name == NULL)
|
||||
return;
|
||||
dso->short_name = name;
|
||||
dso->short_name_len = strlen(name);
|
||||
}
|
||||
|
||||
static void dso__set_basename(struct dso *dso)
|
||||
{
|
||||
dso__set_short_name(dso, basename(dso->long_name));
|
||||
}
|
||||
|
||||
int dso__name_len(const struct dso *dso)
|
||||
{
|
||||
if (!dso)
|
||||
return strlen("[unknown]");
|
||||
if (verbose)
|
||||
return dso->long_name_len;
|
||||
|
||||
return dso->short_name_len;
|
||||
}
|
||||
|
||||
bool dso__loaded(const struct dso *dso, enum map_type type)
|
||||
{
|
||||
return dso->loaded & (1 << type);
|
||||
}
|
||||
|
||||
bool dso__sorted_by_name(const struct dso *dso, enum map_type type)
|
||||
{
|
||||
return dso->sorted_by_name & (1 << type);
|
||||
}
|
||||
|
||||
void dso__set_sorted_by_name(struct dso *dso, enum map_type type)
|
||||
{
|
||||
dso->sorted_by_name |= (1 << type);
|
||||
}
|
||||
|
||||
struct dso *dso__new(const char *name)
|
||||
{
|
||||
struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1);
|
||||
|
||||
if (dso != NULL) {
|
||||
int i;
|
||||
strcpy(dso->name, name);
|
||||
dso__set_long_name(dso, dso->name);
|
||||
dso__set_short_name(dso, dso->name);
|
||||
for (i = 0; i < MAP__NR_TYPES; ++i)
|
||||
dso->symbols[i] = dso->symbol_names[i] = RB_ROOT;
|
||||
dso->cache = RB_ROOT;
|
||||
dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
|
||||
dso->data_type = DSO_BINARY_TYPE__NOT_FOUND;
|
||||
dso->loaded = 0;
|
||||
dso->sorted_by_name = 0;
|
||||
dso->has_build_id = 0;
|
||||
dso->kernel = DSO_TYPE_USER;
|
||||
dso->needs_swap = DSO_SWAP__UNSET;
|
||||
INIT_LIST_HEAD(&dso->node);
|
||||
}
|
||||
|
||||
return dso;
|
||||
}
|
||||
|
||||
void dso__delete(struct dso *dso)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAP__NR_TYPES; ++i)
|
||||
symbols__delete(&dso->symbols[i]);
|
||||
if (dso->sname_alloc)
|
||||
free((char *)dso->short_name);
|
||||
if (dso->lname_alloc)
|
||||
free(dso->long_name);
|
||||
dso_cache__free(&dso->cache);
|
||||
free(dso);
|
||||
}
|
||||
|
||||
void dso__set_build_id(struct dso *dso, void *build_id)
|
||||
{
|
||||
memcpy(dso->build_id, build_id, sizeof(dso->build_id));
|
||||
dso->has_build_id = 1;
|
||||
}
|
||||
|
||||
bool dso__build_id_equal(const struct dso *dso, u8 *build_id)
|
||||
{
|
||||
return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0;
|
||||
}
|
||||
|
||||
void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (machine__is_default_guest(machine))
|
||||
return;
|
||||
sprintf(path, "%s/sys/kernel/notes", machine->root_dir);
|
||||
if (sysfs__read_build_id(path, dso->build_id,
|
||||
sizeof(dso->build_id)) == 0)
|
||||
dso->has_build_id = true;
|
||||
}
|
||||
|
||||
int dso__kernel_module_get_build_id(struct dso *dso,
|
||||
const char *root_dir)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
/*
|
||||
* kernel module short names are of the form "[module]" and
|
||||
* we need just "module" here.
|
||||
*/
|
||||
const char *name = dso->short_name + 1;
|
||||
|
||||
snprintf(filename, sizeof(filename),
|
||||
"%s/sys/module/%.*s/notes/.note.gnu.build-id",
|
||||
root_dir, (int)strlen(name) - 1, name);
|
||||
|
||||
if (sysfs__read_build_id(filename, dso->build_id,
|
||||
sizeof(dso->build_id)) == 0)
|
||||
dso->has_build_id = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
|
||||
{
|
||||
bool have_build_id = false;
|
||||
struct dso *pos;
|
||||
|
||||
list_for_each_entry(pos, head, node) {
|
||||
if (with_hits && !pos->hit)
|
||||
continue;
|
||||
if (pos->has_build_id) {
|
||||
have_build_id = true;
|
||||
continue;
|
||||
}
|
||||
if (filename__read_build_id(pos->long_name, pos->build_id,
|
||||
sizeof(pos->build_id)) > 0) {
|
||||
have_build_id = true;
|
||||
pos->has_build_id = true;
|
||||
}
|
||||
}
|
||||
|
||||
return have_build_id;
|
||||
}
|
||||
|
||||
void dsos__add(struct list_head *head, struct dso *dso)
|
||||
{
|
||||
list_add_tail(&dso->node, head);
|
||||
}
|
||||
|
||||
struct dso *dsos__find(struct list_head *head, const char *name)
|
||||
{
|
||||
struct dso *pos;
|
||||
|
||||
list_for_each_entry(pos, head, node)
|
||||
if (strcmp(pos->long_name, name) == 0)
|
||||
return pos;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dso *__dsos__findnew(struct list_head *head, const char *name)
|
||||
{
|
||||
struct dso *dso = dsos__find(head, name);
|
||||
|
||||
if (!dso) {
|
||||
dso = dso__new(name);
|
||||
if (dso != NULL) {
|
||||
dsos__add(head, dso);
|
||||
dso__set_basename(dso);
|
||||
}
|
||||
}
|
||||
|
||||
return dso;
|
||||
}
|
||||
|
||||
size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
|
||||
bool with_hits)
|
||||
{
|
||||
struct dso *pos;
|
||||
size_t ret = 0;
|
||||
|
||||
list_for_each_entry(pos, head, node) {
|
||||
if (with_hits && !pos->hit)
|
||||
continue;
|
||||
ret += dso__fprintf_buildid(pos, fp);
|
||||
ret += fprintf(fp, " %s\n", pos->long_name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t __dsos__fprintf(struct list_head *head, FILE *fp)
|
||||
{
|
||||
struct dso *pos;
|
||||
size_t ret = 0;
|
||||
|
||||
list_for_each_entry(pos, head, node) {
|
||||
int i;
|
||||
for (i = 0; i < MAP__NR_TYPES; ++i)
|
||||
ret += dso__fprintf(pos, i, fp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t dso__fprintf_buildid(struct dso *dso, FILE *fp)
|
||||
{
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
|
||||
build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
|
||||
return fprintf(fp, "%s", sbuild_id);
|
||||
}
|
||||
|
||||
size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
size_t ret = fprintf(fp, "dso: %s (", dso->short_name);
|
||||
|
||||
if (dso->short_name != dso->long_name)
|
||||
ret += fprintf(fp, "%s, ", dso->long_name);
|
||||
ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type],
|
||||
dso->loaded ? "" : "NOT ");
|
||||
ret += dso__fprintf_buildid(dso, fp);
|
||||
ret += fprintf(fp, ")\n");
|
||||
for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) {
|
||||
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
|
||||
ret += symbol__fprintf(pos, fp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
#ifndef __PERF_DSO
|
||||
#define __PERF_DSO
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include "types.h"
|
||||
#include "map.h"
|
||||
|
||||
enum dso_binary_type {
|
||||
DSO_BINARY_TYPE__KALLSYMS = 0,
|
||||
DSO_BINARY_TYPE__GUEST_KALLSYMS,
|
||||
DSO_BINARY_TYPE__VMLINUX,
|
||||
DSO_BINARY_TYPE__GUEST_VMLINUX,
|
||||
DSO_BINARY_TYPE__JAVA_JIT,
|
||||
DSO_BINARY_TYPE__DEBUGLINK,
|
||||
DSO_BINARY_TYPE__BUILD_ID_CACHE,
|
||||
DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
|
||||
DSO_BINARY_TYPE__GUEST_KMODULE,
|
||||
DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
|
||||
DSO_BINARY_TYPE__NOT_FOUND,
|
||||
};
|
||||
|
||||
enum dso_kernel_type {
|
||||
DSO_TYPE_USER = 0,
|
||||
DSO_TYPE_KERNEL,
|
||||
DSO_TYPE_GUEST_KERNEL
|
||||
};
|
||||
|
||||
enum dso_swap_type {
|
||||
DSO_SWAP__UNSET,
|
||||
DSO_SWAP__NO,
|
||||
DSO_SWAP__YES,
|
||||
};
|
||||
|
||||
#define DSO__SWAP(dso, type, val) \
|
||||
({ \
|
||||
type ____r = val; \
|
||||
BUG_ON(dso->needs_swap == DSO_SWAP__UNSET); \
|
||||
if (dso->needs_swap == DSO_SWAP__YES) { \
|
||||
switch (sizeof(____r)) { \
|
||||
case 2: \
|
||||
____r = bswap_16(val); \
|
||||
break; \
|
||||
case 4: \
|
||||
____r = bswap_32(val); \
|
||||
break; \
|
||||
case 8: \
|
||||
____r = bswap_64(val); \
|
||||
break; \
|
||||
default: \
|
||||
BUG_ON(1); \
|
||||
} \
|
||||
} \
|
||||
____r; \
|
||||
})
|
||||
|
||||
#define DSO__DATA_CACHE_SIZE 4096
|
||||
#define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1)
|
||||
|
||||
struct dso_cache {
|
||||
struct rb_node rb_node;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
struct dso {
|
||||
struct list_head node;
|
||||
struct rb_root symbols[MAP__NR_TYPES];
|
||||
struct rb_root symbol_names[MAP__NR_TYPES];
|
||||
struct rb_root cache;
|
||||
enum dso_kernel_type kernel;
|
||||
enum dso_swap_type needs_swap;
|
||||
enum dso_binary_type symtab_type;
|
||||
enum dso_binary_type data_type;
|
||||
u8 adjust_symbols:1;
|
||||
u8 has_build_id:1;
|
||||
u8 hit:1;
|
||||
u8 annotate_warned:1;
|
||||
u8 sname_alloc:1;
|
||||
u8 lname_alloc:1;
|
||||
u8 sorted_by_name;
|
||||
u8 loaded;
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
const char *short_name;
|
||||
char *long_name;
|
||||
u16 long_name_len;
|
||||
u16 short_name_len;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
static inline void dso__set_loaded(struct dso *dso, enum map_type type)
|
||||
{
|
||||
dso->loaded |= (1 << type);
|
||||
}
|
||||
|
||||
struct dso *dso__new(const char *name);
|
||||
void dso__delete(struct dso *dso);
|
||||
|
||||
void dso__set_short_name(struct dso *dso, const char *name);
|
||||
void dso__set_long_name(struct dso *dso, char *name);
|
||||
|
||||
int dso__name_len(const struct dso *dso);
|
||||
|
||||
bool dso__loaded(const struct dso *dso, enum map_type type);
|
||||
|
||||
bool dso__sorted_by_name(const struct dso *dso, enum map_type type);
|
||||
void dso__set_sorted_by_name(struct dso *dso, enum map_type type);
|
||||
void dso__sort_by_name(struct dso *dso, enum map_type type);
|
||||
|
||||
void dso__set_build_id(struct dso *dso, void *build_id);
|
||||
bool dso__build_id_equal(const struct dso *dso, u8 *build_id);
|
||||
void dso__read_running_kernel_build_id(struct dso *dso,
|
||||
struct machine *machine);
|
||||
int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir);
|
||||
|
||||
char dso__symtab_origin(const struct dso *dso);
|
||||
int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
|
||||
char *root_dir, char *file, size_t size);
|
||||
|
||||
int dso__data_fd(struct dso *dso, struct machine *machine);
|
||||
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
|
||||
u64 offset, u8 *data, ssize_t size);
|
||||
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
|
||||
struct machine *machine, u64 addr,
|
||||
u8 *data, ssize_t size);
|
||||
|
||||
struct map *dso__new_map(const char *name);
|
||||
struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
|
||||
const char *short_name, int dso_type);
|
||||
|
||||
void dsos__add(struct list_head *head, struct dso *dso);
|
||||
struct dso *dsos__find(struct list_head *head, const char *name);
|
||||
struct dso *__dsos__findnew(struct list_head *head, const char *name);
|
||||
bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
|
||||
|
||||
size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
|
||||
bool with_hits);
|
||||
size_t __dsos__fprintf(struct list_head *head, FILE *fp);
|
||||
|
||||
size_t dso__fprintf_buildid(struct dso *dso, FILE *fp);
|
||||
size_t dso__fprintf_symbols_by_name(struct dso *dso,
|
||||
enum map_type type, FILE *fp);
|
||||
size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp);
|
||||
#endif /* __PERF_DSO */
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "../perf.h"
|
||||
#include "map.h"
|
||||
#include "build-id.h"
|
||||
|
||||
/*
|
||||
* PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
|
||||
|
@ -96,8 +97,6 @@ struct perf_sample {
|
|||
struct stack_dump user_stack;
|
||||
};
|
||||
|
||||
#define BUILD_ID_SIZE 20
|
||||
|
||||
struct build_id_event {
|
||||
struct perf_event_header header;
|
||||
pid_t pid;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "pmu.h"
|
||||
#include "vdso.h"
|
||||
#include "strbuf.h"
|
||||
#include "build-id.h"
|
||||
|
||||
static bool no_buildid_cache = false;
|
||||
|
||||
|
@ -2340,6 +2341,16 @@ static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool is_perf_magic(u64 magic)
|
||||
{
|
||||
if (!memcmp(&magic, __perf_magic1, sizeof(magic))
|
||||
|| magic == __perf_magic2
|
||||
|| magic == __perf_magic2_sw)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int check_magic_endian(u64 magic, uint64_t hdr_sz,
|
||||
bool is_pipe, struct perf_header *ph)
|
||||
{
|
||||
|
|
|
@ -154,6 +154,7 @@ int perf_event__synthesize_build_id(struct perf_tool *tool,
|
|||
int perf_event__process_build_id(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_session *session);
|
||||
bool is_perf_magic(u64 magic);
|
||||
|
||||
/*
|
||||
* arch specific callback
|
||||
|
|
|
@ -165,6 +165,7 @@ int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
|||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int refresh);
|
||||
int script_browse(const char *script_opt);
|
||||
#else
|
||||
static inline
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
||||
|
@ -186,6 +187,12 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int script_browse(const char *script_opt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define K_LEFT -1
|
||||
#define K_RIGHT -2
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "thread.h"
|
||||
#include "strlist.h"
|
||||
#include "vdso.h"
|
||||
#include "build-id.h"
|
||||
|
||||
const char *map_type__name[MAP__NR_TYPES] = {
|
||||
[MAP__FUNCTION] = "Functions",
|
||||
|
|
|
@ -827,8 +827,6 @@ int parse_events(struct perf_evlist *evlist, const char *str,
|
|||
* Both call perf_evlist__delete in case of error, so we dont
|
||||
* need to bother.
|
||||
*/
|
||||
fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
|
||||
fprintf(stderr, "Run 'perf list' for a list of valid events\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -836,7 +834,13 @@ int parse_events_option(const struct option *opt, const char *str,
|
|||
int unset __maybe_unused)
|
||||
{
|
||||
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
|
||||
return parse_events(evlist, str, unset);
|
||||
int ret = parse_events(evlist, str, unset);
|
||||
|
||||
if (ret) {
|
||||
fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
|
||||
fprintf(stderr, "Run 'perf list' for a list of valid events\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int parse_filter(const struct option *opt, const char *str,
|
||||
|
|
|
@ -1015,6 +1015,8 @@ PyMODINIT_FUNC initperf(void)
|
|||
pyrf_cpu_map__setup_types() < 0)
|
||||
return;
|
||||
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
Py_INCREF(&pyrf_evlist__type);
|
||||
PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type);
|
||||
|
||||
|
|
|
@ -313,6 +313,24 @@ int strtailcmp(const char *s1, const char *s2)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* strxfrchar - Locate and replace character in @s
|
||||
* @s: The string to be searched/changed.
|
||||
* @from: Source character to be replaced.
|
||||
* @to: Destination character.
|
||||
*
|
||||
* Return pointer to the changed string.
|
||||
*/
|
||||
char *strxfrchar(char *s, char from, char to)
|
||||
{
|
||||
char *p = s;
|
||||
|
||||
while ((p = strchr(p, from)) != NULL)
|
||||
*p++ = to;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* rtrim - Removes trailing whitespace from @s.
|
||||
* @s: The string to be stripped.
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#define KSYM_NAME_LEN 256
|
||||
#endif
|
||||
|
||||
static void dso_cache__free(struct rb_root *root);
|
||||
static int dso__load_kernel_sym(struct dso *dso, struct map *map,
|
||||
symbol_filter_t filter);
|
||||
static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
|
||||
|
@ -56,39 +55,6 @@ static enum dso_binary_type binary_type_symtab[] = {
|
|||
|
||||
#define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab)
|
||||
|
||||
static enum dso_binary_type binary_type_data[] = {
|
||||
DSO_BINARY_TYPE__BUILD_ID_CACHE,
|
||||
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
|
||||
DSO_BINARY_TYPE__NOT_FOUND,
|
||||
};
|
||||
|
||||
#define DSO_BINARY_TYPE__DATA_CNT ARRAY_SIZE(binary_type_data)
|
||||
|
||||
int dso__name_len(const struct dso *dso)
|
||||
{
|
||||
if (!dso)
|
||||
return strlen("[unknown]");
|
||||
if (verbose)
|
||||
return dso->long_name_len;
|
||||
|
||||
return dso->short_name_len;
|
||||
}
|
||||
|
||||
bool dso__loaded(const struct dso *dso, enum map_type type)
|
||||
{
|
||||
return dso->loaded & (1 << type);
|
||||
}
|
||||
|
||||
bool dso__sorted_by_name(const struct dso *dso, enum map_type type)
|
||||
{
|
||||
return dso->sorted_by_name & (1 << type);
|
||||
}
|
||||
|
||||
static void dso__set_sorted_by_name(struct dso *dso, enum map_type type)
|
||||
{
|
||||
dso->sorted_by_name |= (1 << type);
|
||||
}
|
||||
|
||||
bool symbol_type__is_a(char symbol_type, enum map_type map_type)
|
||||
{
|
||||
symbol_type = toupper(symbol_type);
|
||||
|
@ -270,7 +236,7 @@ void symbol__delete(struct symbol *sym)
|
|||
free(((void *)sym) - symbol_conf.priv_size);
|
||||
}
|
||||
|
||||
static size_t symbol__fprintf(struct symbol *sym, FILE *fp)
|
||||
size_t symbol__fprintf(struct symbol *sym, FILE *fp)
|
||||
{
|
||||
return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
|
||||
sym->start, sym->end,
|
||||
|
@ -301,53 +267,7 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp)
|
|||
return symbol__fprintf_symname_offs(sym, NULL, fp);
|
||||
}
|
||||
|
||||
void dso__set_long_name(struct dso *dso, char *name)
|
||||
{
|
||||
if (name == NULL)
|
||||
return;
|
||||
dso->long_name = name;
|
||||
dso->long_name_len = strlen(name);
|
||||
}
|
||||
|
||||
static void dso__set_short_name(struct dso *dso, const char *name)
|
||||
{
|
||||
if (name == NULL)
|
||||
return;
|
||||
dso->short_name = name;
|
||||
dso->short_name_len = strlen(name);
|
||||
}
|
||||
|
||||
static void dso__set_basename(struct dso *dso)
|
||||
{
|
||||
dso__set_short_name(dso, basename(dso->long_name));
|
||||
}
|
||||
|
||||
struct dso *dso__new(const char *name)
|
||||
{
|
||||
struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1);
|
||||
|
||||
if (dso != NULL) {
|
||||
int i;
|
||||
strcpy(dso->name, name);
|
||||
dso__set_long_name(dso, dso->name);
|
||||
dso__set_short_name(dso, dso->name);
|
||||
for (i = 0; i < MAP__NR_TYPES; ++i)
|
||||
dso->symbols[i] = dso->symbol_names[i] = RB_ROOT;
|
||||
dso->cache = RB_ROOT;
|
||||
dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
|
||||
dso->data_type = DSO_BINARY_TYPE__NOT_FOUND;
|
||||
dso->loaded = 0;
|
||||
dso->sorted_by_name = 0;
|
||||
dso->has_build_id = 0;
|
||||
dso->kernel = DSO_TYPE_USER;
|
||||
dso->needs_swap = DSO_SWAP__UNSET;
|
||||
INIT_LIST_HEAD(&dso->node);
|
||||
}
|
||||
|
||||
return dso;
|
||||
}
|
||||
|
||||
static void symbols__delete(struct rb_root *symbols)
|
||||
void symbols__delete(struct rb_root *symbols)
|
||||
{
|
||||
struct symbol *pos;
|
||||
struct rb_node *next = rb_first(symbols);
|
||||
|
@ -360,25 +280,6 @@ static void symbols__delete(struct rb_root *symbols)
|
|||
}
|
||||
}
|
||||
|
||||
void dso__delete(struct dso *dso)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAP__NR_TYPES; ++i)
|
||||
symbols__delete(&dso->symbols[i]);
|
||||
if (dso->sname_alloc)
|
||||
free((char *)dso->short_name);
|
||||
if (dso->lname_alloc)
|
||||
free(dso->long_name);
|
||||
dso_cache__free(&dso->cache);
|
||||
free(dso);
|
||||
}
|
||||
|
||||
void dso__set_build_id(struct dso *dso, void *build_id)
|
||||
{
|
||||
memcpy(dso->build_id, build_id, sizeof(dso->build_id));
|
||||
dso->has_build_id = 1;
|
||||
}
|
||||
|
||||
void symbols__insert(struct rb_root *symbols, struct symbol *sym)
|
||||
{
|
||||
struct rb_node **p = &symbols->rb_node;
|
||||
|
@ -504,29 +405,6 @@ void dso__sort_by_name(struct dso *dso, enum map_type type)
|
|||
&dso->symbols[type]);
|
||||
}
|
||||
|
||||
int build_id__sprintf(const u8 *build_id, int len, char *bf)
|
||||
{
|
||||
char *bid = bf;
|
||||
const u8 *raw = build_id;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
sprintf(bid, "%02x", *raw);
|
||||
++raw;
|
||||
bid += 2;
|
||||
}
|
||||
|
||||
return raw - build_id;
|
||||
}
|
||||
|
||||
size_t dso__fprintf_buildid(struct dso *dso, FILE *fp)
|
||||
{
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
|
||||
build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
|
||||
return fprintf(fp, "%s", sbuild_id);
|
||||
}
|
||||
|
||||
size_t dso__fprintf_symbols_by_name(struct dso *dso,
|
||||
enum map_type type, FILE *fp)
|
||||
{
|
||||
|
@ -542,25 +420,6 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso,
|
|||
return ret;
|
||||
}
|
||||
|
||||
size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
size_t ret = fprintf(fp, "dso: %s (", dso->short_name);
|
||||
|
||||
if (dso->short_name != dso->long_name)
|
||||
ret += fprintf(fp, "%s, ", dso->long_name);
|
||||
ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type],
|
||||
dso->loaded ? "" : "NOT ");
|
||||
ret += dso__fprintf_buildid(dso, fp);
|
||||
ret += fprintf(fp, ")\n");
|
||||
for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) {
|
||||
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
|
||||
ret += symbol__fprintf(pos, fp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kallsyms__parse(const char *filename, void *arg,
|
||||
int (*process_symbol)(void *arg, const char *name,
|
||||
char type, u64 start))
|
||||
|
@ -892,136 +751,6 @@ out_failure:
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool dso__build_id_equal(const struct dso *dso, u8 *build_id)
|
||||
{
|
||||
return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0;
|
||||
}
|
||||
|
||||
bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
|
||||
{
|
||||
bool have_build_id = false;
|
||||
struct dso *pos;
|
||||
|
||||
list_for_each_entry(pos, head, node) {
|
||||
if (with_hits && !pos->hit)
|
||||
continue;
|
||||
if (pos->has_build_id) {
|
||||
have_build_id = true;
|
||||
continue;
|
||||
}
|
||||
if (filename__read_build_id(pos->long_name, pos->build_id,
|
||||
sizeof(pos->build_id)) > 0) {
|
||||
have_build_id = true;
|
||||
pos->has_build_id = true;
|
||||
}
|
||||
}
|
||||
|
||||
return have_build_id;
|
||||
}
|
||||
|
||||
char dso__symtab_origin(const struct dso *dso)
|
||||
{
|
||||
static const char origin[] = {
|
||||
[DSO_BINARY_TYPE__KALLSYMS] = 'k',
|
||||
[DSO_BINARY_TYPE__VMLINUX] = 'v',
|
||||
[DSO_BINARY_TYPE__JAVA_JIT] = 'j',
|
||||
[DSO_BINARY_TYPE__DEBUGLINK] = 'l',
|
||||
[DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B',
|
||||
[DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f',
|
||||
[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u',
|
||||
[DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b',
|
||||
[DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd',
|
||||
[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K',
|
||||
[DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g',
|
||||
[DSO_BINARY_TYPE__GUEST_KMODULE] = 'G',
|
||||
[DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V',
|
||||
};
|
||||
|
||||
if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND)
|
||||
return '!';
|
||||
return origin[dso->symtab_type];
|
||||
}
|
||||
|
||||
int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
|
||||
char *root_dir, char *file, size_t size)
|
||||
{
|
||||
char build_id_hex[BUILD_ID_SIZE * 2 + 1];
|
||||
int ret = 0;
|
||||
|
||||
switch (type) {
|
||||
case DSO_BINARY_TYPE__DEBUGLINK: {
|
||||
char *debuglink;
|
||||
|
||||
strncpy(file, dso->long_name, size);
|
||||
debuglink = file + dso->long_name_len;
|
||||
while (debuglink != file && *debuglink != '/')
|
||||
debuglink--;
|
||||
if (*debuglink == '/')
|
||||
debuglink++;
|
||||
filename__read_debuglink(dso->long_name, debuglink,
|
||||
size - (debuglink - file));
|
||||
}
|
||||
break;
|
||||
case DSO_BINARY_TYPE__BUILD_ID_CACHE:
|
||||
/* skip the locally configured cache if a symfs is given */
|
||||
if (symbol_conf.symfs[0] ||
|
||||
(dso__build_id_filename(dso, file, size) == NULL))
|
||||
ret = -1;
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
|
||||
snprintf(file, size, "%s/usr/lib/debug%s.debug",
|
||||
symbol_conf.symfs, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
|
||||
snprintf(file, size, "%s/usr/lib/debug%s",
|
||||
symbol_conf.symfs, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
|
||||
if (!dso->has_build_id) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
build_id__sprintf(dso->build_id,
|
||||
sizeof(dso->build_id),
|
||||
build_id_hex);
|
||||
snprintf(file, size,
|
||||
"%s/usr/lib/debug/.build-id/%.2s/%s.debug",
|
||||
symbol_conf.symfs, build_id_hex, build_id_hex + 2);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
|
||||
snprintf(file, size, "%s%s",
|
||||
symbol_conf.symfs, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__GUEST_KMODULE:
|
||||
snprintf(file, size, "%s%s%s", symbol_conf.symfs,
|
||||
root_dir, dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
|
||||
snprintf(file, size, "%s%s", symbol_conf.symfs,
|
||||
dso->long_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
case DSO_BINARY_TYPE__KALLSYMS:
|
||||
case DSO_BINARY_TYPE__VMLINUX:
|
||||
case DSO_BINARY_TYPE__GUEST_KALLSYMS:
|
||||
case DSO_BINARY_TYPE__GUEST_VMLINUX:
|
||||
case DSO_BINARY_TYPE__JAVA_JIT:
|
||||
case DSO_BINARY_TYPE__NOT_FOUND:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
|
||||
{
|
||||
char *name;
|
||||
|
@ -1157,27 +886,6 @@ struct map *map_groups__find_by_name(struct map_groups *mg,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int dso__kernel_module_get_build_id(struct dso *dso,
|
||||
const char *root_dir)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
/*
|
||||
* kernel module short names are of the form "[module]" and
|
||||
* we need just "module" here.
|
||||
*/
|
||||
const char *name = dso->short_name + 1;
|
||||
|
||||
snprintf(filename, sizeof(filename),
|
||||
"%s/sys/module/%.*s/notes/.note.gnu.build-id",
|
||||
root_dir, (int)strlen(name) - 1, name);
|
||||
|
||||
if (sysfs__read_build_id(filename, dso->build_id,
|
||||
sizeof(dso->build_id)) == 0)
|
||||
dso->has_build_id = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int map_groups__set_modules_path_dir(struct map_groups *mg,
|
||||
const char *dir_name)
|
||||
{
|
||||
|
@ -1591,50 +1299,6 @@ out_try_fixup:
|
|||
return err;
|
||||
}
|
||||
|
||||
void dsos__add(struct list_head *head, struct dso *dso)
|
||||
{
|
||||
list_add_tail(&dso->node, head);
|
||||
}
|
||||
|
||||
struct dso *dsos__find(struct list_head *head, const char *name)
|
||||
{
|
||||
struct dso *pos;
|
||||
|
||||
list_for_each_entry(pos, head, node)
|
||||
if (strcmp(pos->long_name, name) == 0)
|
||||
return pos;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dso *__dsos__findnew(struct list_head *head, const char *name)
|
||||
{
|
||||
struct dso *dso = dsos__find(head, name);
|
||||
|
||||
if (!dso) {
|
||||
dso = dso__new(name);
|
||||
if (dso != NULL) {
|
||||
dsos__add(head, dso);
|
||||
dso__set_basename(dso);
|
||||
}
|
||||
}
|
||||
|
||||
return dso;
|
||||
}
|
||||
|
||||
size_t __dsos__fprintf(struct list_head *head, FILE *fp)
|
||||
{
|
||||
struct dso *pos;
|
||||
size_t ret = 0;
|
||||
|
||||
list_for_each_entry(pos, head, node) {
|
||||
int i;
|
||||
for (i = 0; i < MAP__NR_TYPES; ++i)
|
||||
ret += dso__fprintf(pos, i, fp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
@ -1649,21 +1313,6 @@ size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
|
||||
bool with_hits)
|
||||
{
|
||||
struct dso *pos;
|
||||
size_t ret = 0;
|
||||
|
||||
list_for_each_entry(pos, head, node) {
|
||||
if (with_hits && !pos->hit)
|
||||
continue;
|
||||
ret += dso__fprintf_buildid(pos, fp);
|
||||
ret += fprintf(fp, " %s\n", pos->long_name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp,
|
||||
bool with_hits)
|
||||
{
|
||||
|
@ -1684,39 +1333,6 @@ size_t machines__fprintf_dsos_buildid(struct rb_root *machines,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct dso*
|
||||
dso__kernel_findnew(struct machine *machine, const char *name,
|
||||
const char *short_name, int dso_type)
|
||||
{
|
||||
/*
|
||||
* The kernel dso could be created by build_id processing.
|
||||
*/
|
||||
struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name);
|
||||
|
||||
/*
|
||||
* We need to run this in all cases, since during the build_id
|
||||
* processing we had no idea this was the kernel dso.
|
||||
*/
|
||||
if (dso != NULL) {
|
||||
dso__set_short_name(dso, short_name);
|
||||
dso->kernel = dso_type;
|
||||
}
|
||||
|
||||
return dso;
|
||||
}
|
||||
|
||||
void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (machine__is_default_guest(machine))
|
||||
return;
|
||||
sprintf(path, "%s/sys/kernel/notes", machine->root_dir);
|
||||
if (sysfs__read_build_id(path, dso->build_id,
|
||||
sizeof(dso->build_id)) == 0)
|
||||
dso->has_build_id = true;
|
||||
}
|
||||
|
||||
static struct dso *machine__get_kernel(struct machine *machine)
|
||||
{
|
||||
const char *vmlinux_name = NULL;
|
||||
|
@ -2065,49 +1681,6 @@ int machines__create_kernel_maps(struct rb_root *machines, pid_t pid)
|
|||
return machine__create_kernel_maps(machine);
|
||||
}
|
||||
|
||||
static int hex(char ch)
|
||||
{
|
||||
if ((ch >= '0') && (ch <= '9'))
|
||||
return ch - '0';
|
||||
if ((ch >= 'a') && (ch <= 'f'))
|
||||
return ch - 'a' + 10;
|
||||
if ((ch >= 'A') && (ch <= 'F'))
|
||||
return ch - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* While we find nice hex chars, build a long_val.
|
||||
* Return number of chars processed.
|
||||
*/
|
||||
int hex2u64(const char *ptr, u64 *long_val)
|
||||
{
|
||||
const char *p = ptr;
|
||||
*long_val = 0;
|
||||
|
||||
while (*p) {
|
||||
const int hex_val = hex(*p);
|
||||
|
||||
if (hex_val < 0)
|
||||
break;
|
||||
|
||||
*long_val = (*long_val << 4) | hex_val;
|
||||
p++;
|
||||
}
|
||||
|
||||
return p - ptr;
|
||||
}
|
||||
|
||||
char *strxfrchar(char *s, char from, char to)
|
||||
{
|
||||
char *p = s;
|
||||
|
||||
while ((p = strchr(p, from)) != NULL)
|
||||
*p++ = to;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int machines__create_guest_kernel_maps(struct rb_root *machines)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -2202,229 +1775,3 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct map *dso__new_map(const char *name)
|
||||
{
|
||||
struct map *map = NULL;
|
||||
struct dso *dso = dso__new(name);
|
||||
|
||||
if (dso)
|
||||
map = map__new2(0, dso, MAP__FUNCTION);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static int open_dso(struct dso *dso, struct machine *machine)
|
||||
{
|
||||
char *root_dir = (char *) "";
|
||||
char *name;
|
||||
int fd;
|
||||
|
||||
name = malloc(PATH_MAX);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
if (machine)
|
||||
root_dir = machine->root_dir;
|
||||
|
||||
if (dso__binary_type_file(dso, dso->data_type,
|
||||
root_dir, name, PATH_MAX)) {
|
||||
free(name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = open(name, O_RDONLY);
|
||||
free(name);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int dso__data_fd(struct dso *dso, struct machine *machine)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND)
|
||||
return open_dso(dso, machine);
|
||||
|
||||
do {
|
||||
int fd;
|
||||
|
||||
dso->data_type = binary_type_data[i++];
|
||||
|
||||
fd = open_dso(dso, machine);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
|
||||
} while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
dso_cache__free(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *next = rb_first(root);
|
||||
|
||||
while (next) {
|
||||
struct dso_cache *cache;
|
||||
|
||||
cache = rb_entry(next, struct dso_cache, rb_node);
|
||||
next = rb_next(&cache->rb_node);
|
||||
rb_erase(&cache->rb_node, root);
|
||||
free(cache);
|
||||
}
|
||||
}
|
||||
|
||||
static struct dso_cache*
|
||||
dso_cache__find(struct rb_root *root, u64 offset)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct dso_cache *cache;
|
||||
|
||||
while (*p != NULL) {
|
||||
u64 end;
|
||||
|
||||
parent = *p;
|
||||
cache = rb_entry(parent, struct dso_cache, rb_node);
|
||||
end = cache->offset + DSO__DATA_CACHE_SIZE;
|
||||
|
||||
if (offset < cache->offset)
|
||||
p = &(*p)->rb_left;
|
||||
else if (offset >= end)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
return cache;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
dso_cache__insert(struct rb_root *root, struct dso_cache *new)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct dso_cache *cache;
|
||||
u64 offset = new->offset;
|
||||
|
||||
while (*p != NULL) {
|
||||
u64 end;
|
||||
|
||||
parent = *p;
|
||||
cache = rb_entry(parent, struct dso_cache, rb_node);
|
||||
end = cache->offset + DSO__DATA_CACHE_SIZE;
|
||||
|
||||
if (offset < cache->offset)
|
||||
p = &(*p)->rb_left;
|
||||
else if (offset >= end)
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
rb_link_node(&new->rb_node, parent, p);
|
||||
rb_insert_color(&new->rb_node, root);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
dso_cache__memcpy(struct dso_cache *cache, u64 offset,
|
||||
u8 *data, u64 size)
|
||||
{
|
||||
u64 cache_offset = offset - cache->offset;
|
||||
u64 cache_size = min(cache->size - cache_offset, size);
|
||||
|
||||
memcpy(data, cache->data + cache_offset, cache_size);
|
||||
return cache_size;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
dso_cache__read(struct dso *dso, struct machine *machine,
|
||||
u64 offset, u8 *data, ssize_t size)
|
||||
{
|
||||
struct dso_cache *cache;
|
||||
ssize_t ret;
|
||||
int fd;
|
||||
|
||||
fd = dso__data_fd(dso, machine);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
do {
|
||||
u64 cache_offset;
|
||||
|
||||
ret = -ENOMEM;
|
||||
|
||||
cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
|
||||
if (!cache)
|
||||
break;
|
||||
|
||||
cache_offset = offset & DSO__DATA_CACHE_MASK;
|
||||
ret = -EINVAL;
|
||||
|
||||
if (-1 == lseek(fd, cache_offset, SEEK_SET))
|
||||
break;
|
||||
|
||||
ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
|
||||
cache->offset = cache_offset;
|
||||
cache->size = ret;
|
||||
dso_cache__insert(&dso->cache, cache);
|
||||
|
||||
ret = dso_cache__memcpy(cache, offset, data, size);
|
||||
|
||||
} while (0);
|
||||
|
||||
if (ret <= 0)
|
||||
free(cache);
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dso_cache_read(struct dso *dso, struct machine *machine,
|
||||
u64 offset, u8 *data, ssize_t size)
|
||||
{
|
||||
struct dso_cache *cache;
|
||||
|
||||
cache = dso_cache__find(&dso->cache, offset);
|
||||
if (cache)
|
||||
return dso_cache__memcpy(cache, offset, data, size);
|
||||
else
|
||||
return dso_cache__read(dso, machine, offset, data, size);
|
||||
}
|
||||
|
||||
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
|
||||
u64 offset, u8 *data, ssize_t size)
|
||||
{
|
||||
ssize_t r = 0;
|
||||
u8 *p = data;
|
||||
|
||||
do {
|
||||
ssize_t ret;
|
||||
|
||||
ret = dso_cache_read(dso, machine, offset, p, size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Reached EOF, return what we have. */
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
BUG_ON(ret > size);
|
||||
|
||||
r += ret;
|
||||
p += ret;
|
||||
offset += ret;
|
||||
size -= ret;
|
||||
|
||||
} while (size);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
|
||||
struct machine *machine, u64 addr,
|
||||
u8 *data, ssize_t size)
|
||||
{
|
||||
u64 offset = map->map_ip(map, addr);
|
||||
return dso__data_read_offset(dso, machine, offset, data, size);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <stdio.h>
|
||||
#include <byteswap.h>
|
||||
#include <libgen.h>
|
||||
#include "build-id.h"
|
||||
|
||||
#ifdef LIBELF_SUPPORT
|
||||
#include <libelf.h>
|
||||
|
@ -18,6 +19,8 @@
|
|||
#include <elf.h>
|
||||
#endif
|
||||
|
||||
#include "dso.h"
|
||||
|
||||
#ifdef HAVE_CPLUS_DEMANGLE
|
||||
extern char *cplus_demangle(const char *, int);
|
||||
|
||||
|
@ -39,9 +42,6 @@ static inline char *bfd_demangle(void __maybe_unused *v,
|
|||
#endif
|
||||
#endif
|
||||
|
||||
int hex2u64(const char *ptr, u64 *val);
|
||||
char *strxfrchar(char *s, char from, char to);
|
||||
|
||||
/*
|
||||
* libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;
|
||||
* for newer versions we can use mmap to reduce memory usage:
|
||||
|
@ -57,8 +57,6 @@ char *strxfrchar(char *s, char from, char to);
|
|||
#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
|
||||
#endif
|
||||
|
||||
#define BUILD_ID_SIZE 20
|
||||
|
||||
/** struct symbol - symtab entry
|
||||
*
|
||||
* @ignore - resolvable but tools ignore it (e.g. idle routines)
|
||||
|
@ -74,6 +72,7 @@ struct symbol {
|
|||
};
|
||||
|
||||
void symbol__delete(struct symbol *sym);
|
||||
void symbols__delete(struct rb_root *symbols);
|
||||
|
||||
static inline size_t symbol__size(const struct symbol *sym)
|
||||
{
|
||||
|
@ -164,70 +163,6 @@ struct addr_location {
|
|||
s32 cpu;
|
||||
};
|
||||
|
||||
enum dso_binary_type {
|
||||
DSO_BINARY_TYPE__KALLSYMS = 0,
|
||||
DSO_BINARY_TYPE__GUEST_KALLSYMS,
|
||||
DSO_BINARY_TYPE__VMLINUX,
|
||||
DSO_BINARY_TYPE__GUEST_VMLINUX,
|
||||
DSO_BINARY_TYPE__JAVA_JIT,
|
||||
DSO_BINARY_TYPE__DEBUGLINK,
|
||||
DSO_BINARY_TYPE__BUILD_ID_CACHE,
|
||||
DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
|
||||
DSO_BINARY_TYPE__GUEST_KMODULE,
|
||||
DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
|
||||
DSO_BINARY_TYPE__NOT_FOUND,
|
||||
};
|
||||
|
||||
enum dso_kernel_type {
|
||||
DSO_TYPE_USER = 0,
|
||||
DSO_TYPE_KERNEL,
|
||||
DSO_TYPE_GUEST_KERNEL
|
||||
};
|
||||
|
||||
enum dso_swap_type {
|
||||
DSO_SWAP__UNSET,
|
||||
DSO_SWAP__NO,
|
||||
DSO_SWAP__YES,
|
||||
};
|
||||
|
||||
#define DSO__DATA_CACHE_SIZE 4096
|
||||
#define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1)
|
||||
|
||||
struct dso_cache {
|
||||
struct rb_node rb_node;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
struct dso {
|
||||
struct list_head node;
|
||||
struct rb_root symbols[MAP__NR_TYPES];
|
||||
struct rb_root symbol_names[MAP__NR_TYPES];
|
||||
struct rb_root cache;
|
||||
enum dso_kernel_type kernel;
|
||||
enum dso_swap_type needs_swap;
|
||||
enum dso_binary_type symtab_type;
|
||||
enum dso_binary_type data_type;
|
||||
u8 adjust_symbols:1;
|
||||
u8 has_build_id:1;
|
||||
u8 hit:1;
|
||||
u8 annotate_warned:1;
|
||||
u8 sname_alloc:1;
|
||||
u8 lname_alloc:1;
|
||||
u8 sorted_by_name;
|
||||
u8 loaded;
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
const char *short_name;
|
||||
char *long_name;
|
||||
u16 long_name_len;
|
||||
u16 short_name_len;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
struct symsrc {
|
||||
char *name;
|
||||
int fd;
|
||||
|
@ -258,47 +193,6 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
|
|||
bool symsrc__has_symtab(struct symsrc *ss);
|
||||
bool symsrc__possibly_runtime(struct symsrc *ss);
|
||||
|
||||
#define DSO__SWAP(dso, type, val) \
|
||||
({ \
|
||||
type ____r = val; \
|
||||
BUG_ON(dso->needs_swap == DSO_SWAP__UNSET); \
|
||||
if (dso->needs_swap == DSO_SWAP__YES) { \
|
||||
switch (sizeof(____r)) { \
|
||||
case 2: \
|
||||
____r = bswap_16(val); \
|
||||
break; \
|
||||
case 4: \
|
||||
____r = bswap_32(val); \
|
||||
break; \
|
||||
case 8: \
|
||||
____r = bswap_64(val); \
|
||||
break; \
|
||||
default: \
|
||||
BUG_ON(1); \
|
||||
} \
|
||||
} \
|
||||
____r; \
|
||||
})
|
||||
|
||||
struct dso *dso__new(const char *name);
|
||||
void dso__delete(struct dso *dso);
|
||||
|
||||
int dso__name_len(const struct dso *dso);
|
||||
|
||||
bool dso__loaded(const struct dso *dso, enum map_type type);
|
||||
bool dso__sorted_by_name(const struct dso *dso, enum map_type type);
|
||||
|
||||
static inline void dso__set_loaded(struct dso *dso, enum map_type type)
|
||||
{
|
||||
dso->loaded |= (1 << type);
|
||||
}
|
||||
|
||||
void dso__sort_by_name(struct dso *dso, enum map_type type);
|
||||
|
||||
void dsos__add(struct list_head *head, struct dso *dso);
|
||||
struct dso *dsos__find(struct list_head *head, const char *name);
|
||||
struct dso *__dsos__findnew(struct list_head *head, const char *name);
|
||||
|
||||
int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter);
|
||||
int dso__load_vmlinux(struct dso *dso, struct map *map,
|
||||
const char *vmlinux, symbol_filter_t filter);
|
||||
|
@ -311,25 +205,12 @@ int machine__load_kallsyms(struct machine *machine, const char *filename,
|
|||
int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
|
||||
symbol_filter_t filter);
|
||||
|
||||
size_t __dsos__fprintf(struct list_head *head, FILE *fp);
|
||||
|
||||
size_t machine__fprintf_dsos_buildid(struct machine *machine,
|
||||
FILE *fp, bool with_hits);
|
||||
size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp);
|
||||
size_t machines__fprintf_dsos_buildid(struct rb_root *machines,
|
||||
FILE *fp, bool with_hits);
|
||||
size_t dso__fprintf_buildid(struct dso *dso, FILE *fp);
|
||||
size_t dso__fprintf_symbols_by_name(struct dso *dso,
|
||||
enum map_type type, FILE *fp);
|
||||
size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp);
|
||||
|
||||
char dso__symtab_origin(const struct dso *dso);
|
||||
void dso__set_long_name(struct dso *dso, char *name);
|
||||
void dso__set_build_id(struct dso *dso, void *build_id);
|
||||
bool dso__build_id_equal(const struct dso *dso, u8 *build_id);
|
||||
void dso__read_running_kernel_build_id(struct dso *dso,
|
||||
struct machine *machine);
|
||||
struct map *dso__new_map(const char *name);
|
||||
struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,
|
||||
u64 addr);
|
||||
struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
|
||||
|
@ -337,8 +218,6 @@ struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
|
|||
|
||||
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);
|
||||
bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
|
||||
int build_id__sprintf(const u8 *build_id, int len, char *bf);
|
||||
int kallsyms__parse(const char *filename, void *arg,
|
||||
int (*process_symbol)(void *arg, const char *name,
|
||||
char type, u64 start));
|
||||
|
@ -360,19 +239,11 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name);
|
|||
size_t symbol__fprintf_symname_offs(const struct symbol *sym,
|
||||
const struct addr_location *al, FILE *fp);
|
||||
size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);
|
||||
size_t symbol__fprintf(struct symbol *sym, FILE *fp);
|
||||
bool symbol_type__is_a(char symbol_type, enum map_type map_type);
|
||||
|
||||
size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp);
|
||||
|
||||
int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
|
||||
char *root_dir, char *file, size_t size);
|
||||
|
||||
int dso__data_fd(struct dso *dso, struct machine *machine);
|
||||
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
|
||||
u64 offset, u8 *data, ssize_t size);
|
||||
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
|
||||
struct machine *machine, u64 addr,
|
||||
u8 *data, ssize_t size);
|
||||
int dso__test_data(void);
|
||||
int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
|
||||
struct symsrc *runtime_ss, symbol_filter_t filter,
|
||||
|
|
|
@ -166,6 +166,39 @@ size_t hex_width(u64 v)
|
|||
return n;
|
||||
}
|
||||
|
||||
static int hex(char ch)
|
||||
{
|
||||
if ((ch >= '0') && (ch <= '9'))
|
||||
return ch - '0';
|
||||
if ((ch >= 'a') && (ch <= 'f'))
|
||||
return ch - 'a' + 10;
|
||||
if ((ch >= 'A') && (ch <= 'F'))
|
||||
return ch - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* While we find nice hex chars, build a long_val.
|
||||
* Return number of chars processed.
|
||||
*/
|
||||
int hex2u64(const char *ptr, u64 *long_val)
|
||||
{
|
||||
const char *p = ptr;
|
||||
*long_val = 0;
|
||||
|
||||
while (*p) {
|
||||
const int hex_val = hex(*p);
|
||||
|
||||
if (hex_val < 0)
|
||||
break;
|
||||
|
||||
*long_val = (*long_val << 4) | hex_val;
|
||||
p++;
|
||||
}
|
||||
|
||||
return p - ptr;
|
||||
}
|
||||
|
||||
/* Obtain a backtrace and print it to stdout. */
|
||||
#ifdef BACKTRACE_SUPPORT
|
||||
void dump_stack(void)
|
||||
|
|
|
@ -240,6 +240,7 @@ void argv_free(char **argv);
|
|||
bool strglobmatch(const char *str, const char *pat);
|
||||
bool strlazymatch(const char *str, const char *pat);
|
||||
int strtailcmp(const char *s1, const char *s2);
|
||||
char *strxfrchar(char *s, char from, char to);
|
||||
unsigned long convert_unit(unsigned long value, char *unit);
|
||||
int readn(int fd, void *buf, size_t size);
|
||||
|
||||
|
@ -262,6 +263,7 @@ bool is_power_of_2(unsigned long n)
|
|||
}
|
||||
|
||||
size_t hex_width(u64 v);
|
||||
int hex2u64(const char *ptr, u64 *val);
|
||||
|
||||
char *rtrim(char *s);
|
||||
|
||||
|
|
Loading…
Reference in New Issue