|
@@ -51,6 +51,7 @@
|
|
|
#define PERFPROBE_GROUP "probe"
|
|
|
|
|
|
bool probe_event_dry_run; /* Dry run flag */
|
|
|
+struct probe_conf probe_conf;
|
|
|
|
|
|
#define semantic_error(msg ...) pr_err("Semantic error :" msg)
|
|
|
|
|
@@ -344,15 +345,14 @@ out:
|
|
|
|
|
|
static int get_alternative_probe_event(struct debuginfo *dinfo,
|
|
|
struct perf_probe_event *pev,
|
|
|
- struct perf_probe_point *tmp,
|
|
|
- const char *target)
|
|
|
+ struct perf_probe_point *tmp)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
memcpy(tmp, &pev->point, sizeof(*tmp));
|
|
|
memset(&pev->point, 0, sizeof(pev->point));
|
|
|
ret = find_alternative_probe_point(dinfo, tmp, &pev->point,
|
|
|
- target, pev->uprobes);
|
|
|
+ pev->target, pev->uprobes);
|
|
|
if (ret < 0)
|
|
|
memcpy(&pev->point, tmp, sizeof(*tmp));
|
|
|
|
|
@@ -557,8 +557,9 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
|
|
|
bool uprobe)
|
|
|
{
|
|
|
struct ref_reloc_sym *reloc_sym;
|
|
|
+ u64 etext_addr;
|
|
|
char *tmp;
|
|
|
- int i;
|
|
|
+ int i, skipped = 0;
|
|
|
|
|
|
if (uprobe)
|
|
|
return add_exec_to_probe_trace_events(tevs, ntevs, module);
|
|
@@ -572,33 +573,45 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
|
|
|
pr_warning("Relocated base symbol is not found!\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
+ /* Get the address of _etext for checking non-probable text symbol */
|
|
|
+ etext_addr = kernel_get_symbol_address_by_name("_etext", false);
|
|
|
|
|
|
for (i = 0; i < ntevs; i++) {
|
|
|
if (tevs[i].point.address && !tevs[i].point.retprobe) {
|
|
|
- tmp = strdup(reloc_sym->name);
|
|
|
- if (!tmp)
|
|
|
- return -ENOMEM;
|
|
|
- free(tevs[i].point.symbol);
|
|
|
+ /* If we found a wrong one, mark it by NULL symbol */
|
|
|
+ if (etext_addr < tevs[i].point.address) {
|
|
|
+ pr_warning("%s+%lu is out of .text, skip it.\n",
|
|
|
+ tevs[i].point.symbol, tevs[i].point.offset);
|
|
|
+ tmp = NULL;
|
|
|
+ skipped++;
|
|
|
+ } else {
|
|
|
+ tmp = strdup(reloc_sym->name);
|
|
|
+ if (!tmp)
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ /* If we have no realname, use symbol for it */
|
|
|
+ if (!tevs[i].point.realname)
|
|
|
+ tevs[i].point.realname = tevs[i].point.symbol;
|
|
|
+ else
|
|
|
+ free(tevs[i].point.symbol);
|
|
|
tevs[i].point.symbol = tmp;
|
|
|
tevs[i].point.offset = tevs[i].point.address -
|
|
|
reloc_sym->unrelocated_addr;
|
|
|
}
|
|
|
}
|
|
|
- return 0;
|
|
|
+ return skipped;
|
|
|
}
|
|
|
|
|
|
/* Try to find perf_probe_event with debuginfo */
|
|
|
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
|
|
- struct probe_trace_event **tevs,
|
|
|
- int max_tevs, const char *target)
|
|
|
+ struct probe_trace_event **tevs)
|
|
|
{
|
|
|
bool need_dwarf = perf_probe_event_need_dwarf(pev);
|
|
|
struct perf_probe_point tmp;
|
|
|
struct debuginfo *dinfo;
|
|
|
int ntevs, ret = 0;
|
|
|
|
|
|
- dinfo = open_debuginfo(target, !need_dwarf);
|
|
|
-
|
|
|
+ dinfo = open_debuginfo(pev->target, !need_dwarf);
|
|
|
if (!dinfo) {
|
|
|
if (need_dwarf)
|
|
|
return -ENOENT;
|
|
@@ -608,13 +621,12 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
|
|
|
|
|
pr_debug("Try to find probe point from debuginfo.\n");
|
|
|
/* Searching trace events corresponding to a probe event */
|
|
|
- ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);
|
|
|
+ ntevs = debuginfo__find_trace_events(dinfo, pev, tevs);
|
|
|
|
|
|
if (ntevs == 0) { /* Not found, retry with an alternative */
|
|
|
- ret = get_alternative_probe_event(dinfo, pev, &tmp, target);
|
|
|
+ ret = get_alternative_probe_event(dinfo, pev, &tmp);
|
|
|
if (!ret) {
|
|
|
- ntevs = debuginfo__find_trace_events(dinfo, pev,
|
|
|
- tevs, max_tevs);
|
|
|
+ ntevs = debuginfo__find_trace_events(dinfo, pev, tevs);
|
|
|
/*
|
|
|
* Write back to the original probe_event for
|
|
|
* setting appropriate (user given) event name
|
|
@@ -629,12 +641,15 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
|
|
if (ntevs > 0) { /* Succeeded to find trace events */
|
|
|
pr_debug("Found %d probe_trace_events.\n", ntevs);
|
|
|
ret = post_process_probe_trace_events(*tevs, ntevs,
|
|
|
- target, pev->uprobes);
|
|
|
- if (ret < 0) {
|
|
|
+ pev->target, pev->uprobes);
|
|
|
+ if (ret < 0 || ret == ntevs) {
|
|
|
clear_probe_trace_events(*tevs, ntevs);
|
|
|
zfree(tevs);
|
|
|
}
|
|
|
- return ret < 0 ? ret : ntevs;
|
|
|
+ if (ret != ntevs)
|
|
|
+ return ret < 0 ? ret : ntevs;
|
|
|
+ ntevs = 0;
|
|
|
+ /* Fall through */
|
|
|
}
|
|
|
|
|
|
if (ntevs == 0) { /* No error but failed to find probe point. */
|
|
@@ -809,8 +824,7 @@ int show_line_range(struct line_range *lr, const char *module, bool user)
|
|
|
|
|
|
static int show_available_vars_at(struct debuginfo *dinfo,
|
|
|
struct perf_probe_event *pev,
|
|
|
- int max_vls, struct strfilter *_filter,
|
|
|
- bool externs, const char *target)
|
|
|
+ struct strfilter *_filter)
|
|
|
{
|
|
|
char *buf;
|
|
|
int ret, i, nvars;
|
|
@@ -824,13 +838,12 @@ static int show_available_vars_at(struct debuginfo *dinfo,
|
|
|
return -EINVAL;
|
|
|
pr_debug("Searching variables at %s\n", buf);
|
|
|
|
|
|
- ret = debuginfo__find_available_vars_at(dinfo, pev, &vls,
|
|
|
- max_vls, externs);
|
|
|
+ ret = debuginfo__find_available_vars_at(dinfo, pev, &vls);
|
|
|
if (!ret) { /* Not found, retry with an alternative */
|
|
|
- ret = get_alternative_probe_event(dinfo, pev, &tmp, target);
|
|
|
+ ret = get_alternative_probe_event(dinfo, pev, &tmp);
|
|
|
if (!ret) {
|
|
|
ret = debuginfo__find_available_vars_at(dinfo, pev,
|
|
|
- &vls, max_vls, externs);
|
|
|
+ &vls);
|
|
|
/* Release the old probe_point */
|
|
|
clear_perf_probe_point(&tmp);
|
|
|
}
|
|
@@ -877,8 +890,7 @@ end:
|
|
|
|
|
|
/* Show available variables on given probe point */
|
|
|
int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
|
|
- int max_vls, const char *module,
|
|
|
- struct strfilter *_filter, bool externs)
|
|
|
+ struct strfilter *_filter)
|
|
|
{
|
|
|
int i, ret = 0;
|
|
|
struct debuginfo *dinfo;
|
|
@@ -887,7 +899,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- dinfo = open_debuginfo(module, false);
|
|
|
+ dinfo = open_debuginfo(pevs->target, false);
|
|
|
if (!dinfo) {
|
|
|
ret = -ENOENT;
|
|
|
goto out;
|
|
@@ -896,8 +908,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
|
|
setup_pager();
|
|
|
|
|
|
for (i = 0; i < npevs && ret >= 0; i++)
|
|
|
- ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter,
|
|
|
- externs, module);
|
|
|
+ ret = show_available_vars_at(dinfo, &pevs[i], _filter);
|
|
|
|
|
|
debuginfo__delete(dinfo);
|
|
|
out:
|
|
@@ -916,9 +927,7 @@ find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused,
|
|
|
}
|
|
|
|
|
|
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
|
|
- struct probe_trace_event **tevs __maybe_unused,
|
|
|
- int max_tevs __maybe_unused,
|
|
|
- const char *target __maybe_unused)
|
|
|
+ struct probe_trace_event **tevs __maybe_unused)
|
|
|
{
|
|
|
if (perf_probe_event_need_dwarf(pev)) {
|
|
|
pr_warning("Debuginfo-analysis is not supported.\n");
|
|
@@ -937,10 +946,8 @@ int show_line_range(struct line_range *lr __maybe_unused,
|
|
|
}
|
|
|
|
|
|
int show_available_vars(struct perf_probe_event *pevs __maybe_unused,
|
|
|
- int npevs __maybe_unused, int max_vls __maybe_unused,
|
|
|
- const char *module __maybe_unused,
|
|
|
- struct strfilter *filter __maybe_unused,
|
|
|
- bool externs __maybe_unused)
|
|
|
+ int npevs __maybe_unused,
|
|
|
+ struct strfilter *filter __maybe_unused)
|
|
|
{
|
|
|
pr_warning("Debuginfo-analysis is not supported.\n");
|
|
|
return -ENOSYS;
|
|
@@ -980,6 +987,18 @@ static int parse_line_num(char **ptr, int *val, const char *what)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* Check the name is good for event, group or function */
|
|
|
+static bool is_c_func_name(const char *name)
|
|
|
+{
|
|
|
+ if (!isalpha(*name) && *name != '_')
|
|
|
+ return false;
|
|
|
+ while (*++name != '\0') {
|
|
|
+ if (!isalpha(*name) && !isdigit(*name) && *name != '_')
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Stuff 'lr' according to the line range described by 'arg'.
|
|
|
* The line range syntax is described by:
|
|
@@ -1048,10 +1067,15 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
|
|
|
goto err;
|
|
|
}
|
|
|
lr->function = name;
|
|
|
- } else if (strchr(name, '.'))
|
|
|
+ } else if (strchr(name, '/') || strchr(name, '.'))
|
|
|
lr->file = name;
|
|
|
- else
|
|
|
+ else if (is_c_func_name(name))/* We reuse it for checking funcname */
|
|
|
lr->function = name;
|
|
|
+ else { /* Invalid name */
|
|
|
+ semantic_error("'%s' is not a valid function name.\n", name);
|
|
|
+ err = -EINVAL;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
err:
|
|
@@ -1059,18 +1083,6 @@ err:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-/* Check the name is good for event/group */
|
|
|
-static bool check_event_name(const char *name)
|
|
|
-{
|
|
|
- if (!isalpha(*name) && *name != '_')
|
|
|
- return false;
|
|
|
- while (*++name != '\0') {
|
|
|
- if (!isalpha(*name) && !isdigit(*name) && *name != '_')
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
/* Parse probepoint definition. */
|
|
|
static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
|
|
|
{
|
|
@@ -1094,7 +1106,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
|
|
|
semantic_error("Group name is not supported yet.\n");
|
|
|
return -ENOTSUP;
|
|
|
}
|
|
|
- if (!check_event_name(arg)) {
|
|
|
+ if (!is_c_func_name(arg)) {
|
|
|
semantic_error("%s is bad for event name -it must "
|
|
|
"follow C symbol-naming rule.\n", arg);
|
|
|
return -EINVAL;
|
|
@@ -1892,6 +1904,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
|
|
|
free(tev->event);
|
|
|
free(tev->group);
|
|
|
free(tev->point.symbol);
|
|
|
+ free(tev->point.realname);
|
|
|
free(tev->point.module);
|
|
|
for (i = 0; i < tev->nargs; i++) {
|
|
|
free(tev->args[i].name);
|
|
@@ -1969,7 +1982,7 @@ static int open_probe_events(const char *trace_file, bool readwrite)
|
|
|
if (ret >= 0) {
|
|
|
pr_debug("Opening %s write=%d\n", buf, readwrite);
|
|
|
if (readwrite && !probe_event_dry_run)
|
|
|
- ret = open(buf, O_RDWR, O_APPEND);
|
|
|
+ ret = open(buf, O_RDWR | O_APPEND, 0);
|
|
|
else
|
|
|
ret = open(buf, O_RDONLY, 0);
|
|
|
|
|
@@ -2369,6 +2382,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
|
|
struct strlist *namelist;
|
|
|
LIST_HEAD(blacklist);
|
|
|
struct kprobe_blacklist_node *node;
|
|
|
+ bool safename;
|
|
|
|
|
|
if (pev->uprobes)
|
|
|
fd = open_uprobe_events(true);
|
|
@@ -2384,7 +2398,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
|
|
namelist = get_probe_trace_event_names(fd, false);
|
|
|
if (!namelist) {
|
|
|
pr_debug("Failed to get current event list.\n");
|
|
|
- return -EIO;
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto close_out;
|
|
|
}
|
|
|
/* Get kprobe blacklist if exists */
|
|
|
if (!pev->uprobes) {
|
|
@@ -2393,10 +2408,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
|
|
pr_debug("No kprobe blacklist support, ignored\n");
|
|
|
}
|
|
|
|
|
|
+ safename = (pev->point.function && !strisglob(pev->point.function));
|
|
|
ret = 0;
|
|
|
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
|
|
|
for (i = 0; i < ntevs; i++) {
|
|
|
tev = &tevs[i];
|
|
|
+ /* Skip if the symbol is out of .text (marked previously) */
|
|
|
+ if (!tev->point.symbol)
|
|
|
+ continue;
|
|
|
/* Ensure that the address is NOT blacklisted */
|
|
|
node = kprobe_blacklist__find_by_address(&blacklist,
|
|
|
tev->point.address);
|
|
@@ -2408,10 +2427,10 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
|
|
if (pev->event)
|
|
|
event = pev->event;
|
|
|
else
|
|
|
- if (pev->point.function)
|
|
|
+ if (safename)
|
|
|
event = pev->point.function;
|
|
|
else
|
|
|
- event = tev->point.symbol;
|
|
|
+ event = tev->point.realname;
|
|
|
if (pev->group)
|
|
|
group = pev->group;
|
|
|
else
|
|
@@ -2467,6 +2486,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
|
|
|
|
|
kprobe_blacklist__delete(&blacklist);
|
|
|
strlist__delete(namelist);
|
|
|
+close_out:
|
|
|
close(fd);
|
|
|
return ret;
|
|
|
}
|
|
@@ -2475,9 +2495,11 @@ static int find_probe_functions(struct map *map, char *name)
|
|
|
{
|
|
|
int found = 0;
|
|
|
struct symbol *sym;
|
|
|
+ struct rb_node *tmp;
|
|
|
|
|
|
- map__for_each_symbol_by_name(map, name, sym) {
|
|
|
- found++;
|
|
|
+ map__for_each_symbol(map, sym, tmp) {
|
|
|
+ if (strglobmatch(sym->name, name))
|
|
|
+ found++;
|
|
|
}
|
|
|
|
|
|
return found;
|
|
@@ -2495,8 +2517,7 @@ void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
|
|
|
* Return an error or the number of found probe_trace_event
|
|
|
*/
|
|
|
static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
|
|
|
- struct probe_trace_event **tevs,
|
|
|
- int max_tevs, const char *target)
|
|
|
+ struct probe_trace_event **tevs)
|
|
|
{
|
|
|
struct map *map = NULL;
|
|
|
struct ref_reloc_sym *reloc_sym = NULL;
|
|
@@ -2507,7 +2528,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
|
|
|
int num_matched_functions;
|
|
|
int ret, i;
|
|
|
|
|
|
- map = get_target_map(target, pev->uprobes);
|
|
|
+ map = get_target_map(pev->target, pev->uprobes);
|
|
|
if (!map) {
|
|
|
ret = -EINVAL;
|
|
|
goto out;
|
|
@@ -2520,12 +2541,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
|
|
|
num_matched_functions = find_probe_functions(map, pp->function);
|
|
|
if (num_matched_functions == 0) {
|
|
|
pr_err("Failed to find symbol %s in %s\n", pp->function,
|
|
|
- target ? : "kernel");
|
|
|
+ pev->target ? : "kernel");
|
|
|
ret = -ENOENT;
|
|
|
goto out;
|
|
|
- } else if (num_matched_functions > max_tevs) {
|
|
|
+ } else if (num_matched_functions > probe_conf.max_probes) {
|
|
|
pr_err("Too many functions matched in %s\n",
|
|
|
- target ? : "kernel");
|
|
|
+ pev->target ? : "kernel");
|
|
|
ret = -E2BIG;
|
|
|
goto out;
|
|
|
}
|
|
@@ -2573,8 +2594,9 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
|
|
|
tp->offset = pp->offset;
|
|
|
}
|
|
|
tp->retprobe = pp->retprobe;
|
|
|
- if (target)
|
|
|
- tev->point.module = strdup_or_goto(target, nomem_out);
|
|
|
+ if (pev->target)
|
|
|
+ tev->point.module = strdup_or_goto(pev->target,
|
|
|
+ nomem_out);
|
|
|
tev->uprobes = pev->uprobes;
|
|
|
tev->nargs = pev->nargs;
|
|
|
if (tev->nargs) {
|
|
@@ -2614,14 +2636,13 @@ err_out:
|
|
|
bool __weak arch__prefers_symtab(void) { return false; }
|
|
|
|
|
|
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
|
|
|
- struct probe_trace_event **tevs,
|
|
|
- int max_tevs, const char *target)
|
|
|
+ struct probe_trace_event **tevs)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
if (pev->uprobes && !pev->group) {
|
|
|
/* Replace group name if not given */
|
|
|
- ret = convert_exec_to_group(target, &pev->group);
|
|
|
+ ret = convert_exec_to_group(pev->target, &pev->group);
|
|
|
if (ret != 0) {
|
|
|
pr_warning("Failed to make a group name.\n");
|
|
|
return ret;
|
|
@@ -2629,17 +2650,17 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
|
|
|
}
|
|
|
|
|
|
if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) {
|
|
|
- ret = find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
|
|
|
+ ret = find_probe_trace_events_from_map(pev, tevs);
|
|
|
if (ret > 0)
|
|
|
return ret; /* Found in symbol table */
|
|
|
}
|
|
|
|
|
|
/* Convert perf_probe_event with debuginfo */
|
|
|
- ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
|
|
|
+ ret = try_to_find_probe_trace_events(pev, tevs);
|
|
|
if (ret != 0)
|
|
|
return ret; /* Found in debuginfo or got an error */
|
|
|
|
|
|
- return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
|
|
|
+ return find_probe_trace_events_from_map(pev, tevs);
|
|
|
}
|
|
|
|
|
|
struct __event_package {
|
|
@@ -2648,8 +2669,7 @@ struct __event_package {
|
|
|
int ntevs;
|
|
|
};
|
|
|
|
|
|
-int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
|
|
|
- int max_tevs, bool force_add)
|
|
|
+int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
|
|
|
{
|
|
|
int i, j, ret;
|
|
|
struct __event_package *pkgs;
|
|
@@ -2671,9 +2691,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
|
|
|
pkgs[i].pev = &pevs[i];
|
|
|
/* Convert with or without debuginfo */
|
|
|
ret = convert_to_probe_trace_events(pkgs[i].pev,
|
|
|
- &pkgs[i].tevs,
|
|
|
- max_tevs,
|
|
|
- pkgs[i].pev->target);
|
|
|
+ &pkgs[i].tevs);
|
|
|
if (ret < 0)
|
|
|
goto end;
|
|
|
pkgs[i].ntevs = ret;
|
|
@@ -2682,7 +2700,8 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
|
|
|
/* Loop 2: add all events */
|
|
|
for (i = 0; i < npevs; i++) {
|
|
|
ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
|
|
|
- pkgs[i].ntevs, force_add);
|
|
|
+ pkgs[i].ntevs,
|
|
|
+ probe_conf.force_add);
|
|
|
if (ret < 0)
|
|
|
break;
|
|
|
}
|