162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * probe-file.c : operate ftrace k/uprobe events files 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <errno.h> 862306a36Sopenharmony_ci#include <fcntl.h> 962306a36Sopenharmony_ci#include <sys/stat.h> 1062306a36Sopenharmony_ci#include <sys/types.h> 1162306a36Sopenharmony_ci#include <sys/uio.h> 1262306a36Sopenharmony_ci#include <unistd.h> 1362306a36Sopenharmony_ci#include <linux/zalloc.h> 1462306a36Sopenharmony_ci#include "namespaces.h" 1562306a36Sopenharmony_ci#include "event.h" 1662306a36Sopenharmony_ci#include "strlist.h" 1762306a36Sopenharmony_ci#include "strfilter.h" 1862306a36Sopenharmony_ci#include "debug.h" 1962306a36Sopenharmony_ci#include "build-id.h" 2062306a36Sopenharmony_ci#include "dso.h" 2162306a36Sopenharmony_ci#include "color.h" 2262306a36Sopenharmony_ci#include "symbol.h" 2362306a36Sopenharmony_ci#include "strbuf.h" 2462306a36Sopenharmony_ci#include <api/fs/tracing_path.h> 2562306a36Sopenharmony_ci#include <api/fs/fs.h> 2662306a36Sopenharmony_ci#include "probe-event.h" 2762306a36Sopenharmony_ci#include "probe-file.h" 2862306a36Sopenharmony_ci#include "session.h" 2962306a36Sopenharmony_ci#include "perf_regs.h" 3062306a36Sopenharmony_ci#include "string2.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 4096 - 2 ('\n' + '\0') */ 3362306a36Sopenharmony_ci#define MAX_CMDLEN 4094 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic bool print_common_warning(int err, bool readwrite) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci if (err == -EACCES) 3862306a36Sopenharmony_ci pr_warning("No permission to %s tracefs.\nPlease %s\n", 3962306a36Sopenharmony_ci readwrite ? "write" : "read", 4062306a36Sopenharmony_ci readwrite ? "run this command again with sudo." : 4162306a36Sopenharmony_ci "try 'sudo mount -o remount,mode=755 /sys/kernel/tracing/'"); 4262306a36Sopenharmony_ci else 4362306a36Sopenharmony_ci return false; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return true; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic bool print_configure_probe_event(int kerr, int uerr) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci const char *config, *file; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (kerr == -ENOENT && uerr == -ENOENT) { 5362306a36Sopenharmony_ci file = "{k,u}probe_events"; 5462306a36Sopenharmony_ci config = "CONFIG_KPROBE_EVENTS=y and CONFIG_UPROBE_EVENTS=y"; 5562306a36Sopenharmony_ci } else if (kerr == -ENOENT) { 5662306a36Sopenharmony_ci file = "kprobe_events"; 5762306a36Sopenharmony_ci config = "CONFIG_KPROBE_EVENTS=y"; 5862306a36Sopenharmony_ci } else if (uerr == -ENOENT) { 5962306a36Sopenharmony_ci file = "uprobe_events"; 6062306a36Sopenharmony_ci config = "CONFIG_UPROBE_EVENTS=y"; 6162306a36Sopenharmony_ci } else 6262306a36Sopenharmony_ci return false; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (!debugfs__configured() && !tracefs__configured()) 6562306a36Sopenharmony_ci pr_warning("Debugfs or tracefs is not mounted\n" 6662306a36Sopenharmony_ci "Please try 'sudo mount -t tracefs nodev /sys/kernel/tracing/'\n"); 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci pr_warning("%s/%s does not exist.\nPlease rebuild kernel with %s.\n", 6962306a36Sopenharmony_ci tracing_path_mount(), file, config); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return true; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void print_open_warning(int err, bool uprobe, bool readwrite) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci char sbuf[STRERR_BUFSIZE]; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (print_common_warning(err, readwrite)) 7962306a36Sopenharmony_ci return; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (print_configure_probe_event(uprobe ? 0 : err, uprobe ? err : 0)) 8262306a36Sopenharmony_ci return; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci pr_warning("Failed to open %s/%cprobe_events: %s\n", 8562306a36Sopenharmony_ci tracing_path_mount(), uprobe ? 'u' : 'k', 8662306a36Sopenharmony_ci str_error_r(-err, sbuf, sizeof(sbuf))); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void print_both_open_warning(int kerr, int uerr, bool readwrite) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci char sbuf[STRERR_BUFSIZE]; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (kerr == uerr && print_common_warning(kerr, readwrite)) 9462306a36Sopenharmony_ci return; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (print_configure_probe_event(kerr, uerr)) 9762306a36Sopenharmony_ci return; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (kerr < 0) 10062306a36Sopenharmony_ci pr_warning("Failed to open %s/kprobe_events: %s.\n", 10162306a36Sopenharmony_ci tracing_path_mount(), 10262306a36Sopenharmony_ci str_error_r(-kerr, sbuf, sizeof(sbuf))); 10362306a36Sopenharmony_ci if (uerr < 0) 10462306a36Sopenharmony_ci pr_warning("Failed to open %s/uprobe_events: %s.\n", 10562306a36Sopenharmony_ci tracing_path_mount(), 10662306a36Sopenharmony_ci str_error_r(-uerr, sbuf, sizeof(sbuf))); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciint open_trace_file(const char *trace_file, bool readwrite) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci char buf[PATH_MAX]; 11262306a36Sopenharmony_ci int ret; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ret = e_snprintf(buf, PATH_MAX, "%s/%s", tracing_path_mount(), trace_file); 11562306a36Sopenharmony_ci if (ret >= 0) { 11662306a36Sopenharmony_ci pr_debug("Opening %s write=%d\n", buf, readwrite); 11762306a36Sopenharmony_ci if (readwrite && !probe_event_dry_run) 11862306a36Sopenharmony_ci ret = open(buf, O_RDWR | O_APPEND, 0); 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci ret = open(buf, O_RDONLY, 0); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (ret < 0) 12362306a36Sopenharmony_ci ret = -errno; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int open_kprobe_events(bool readwrite) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci return open_trace_file("kprobe_events", readwrite); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int open_uprobe_events(bool readwrite) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci return open_trace_file("uprobe_events", readwrite); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciint probe_file__open(int flag) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int fd; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (flag & PF_FL_UPROBE) 14362306a36Sopenharmony_ci fd = open_uprobe_events(flag & PF_FL_RW); 14462306a36Sopenharmony_ci else 14562306a36Sopenharmony_ci fd = open_kprobe_events(flag & PF_FL_RW); 14662306a36Sopenharmony_ci if (fd < 0) 14762306a36Sopenharmony_ci print_open_warning(fd, flag & PF_FL_UPROBE, flag & PF_FL_RW); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return fd; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciint probe_file__open_both(int *kfd, int *ufd, int flag) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci if (!kfd || !ufd) 15562306a36Sopenharmony_ci return -EINVAL; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci *kfd = open_kprobe_events(flag & PF_FL_RW); 15862306a36Sopenharmony_ci *ufd = open_uprobe_events(flag & PF_FL_RW); 15962306a36Sopenharmony_ci if (*kfd < 0 && *ufd < 0) { 16062306a36Sopenharmony_ci print_both_open_warning(*kfd, *ufd, flag & PF_FL_RW); 16162306a36Sopenharmony_ci return *kfd; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* Get raw string list of current kprobe_events or uprobe_events */ 16862306a36Sopenharmony_cistruct strlist *probe_file__get_rawlist(int fd) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci int ret, idx, fddup; 17162306a36Sopenharmony_ci FILE *fp; 17262306a36Sopenharmony_ci char buf[MAX_CMDLEN]; 17362306a36Sopenharmony_ci char *p; 17462306a36Sopenharmony_ci struct strlist *sl; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (fd < 0) 17762306a36Sopenharmony_ci return NULL; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci sl = strlist__new(NULL, NULL); 18062306a36Sopenharmony_ci if (sl == NULL) 18162306a36Sopenharmony_ci return NULL; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci fddup = dup(fd); 18462306a36Sopenharmony_ci if (fddup < 0) 18562306a36Sopenharmony_ci goto out_free_sl; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci fp = fdopen(fddup, "r"); 18862306a36Sopenharmony_ci if (!fp) 18962306a36Sopenharmony_ci goto out_close_fddup; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci while (!feof(fp)) { 19262306a36Sopenharmony_ci p = fgets(buf, MAX_CMDLEN, fp); 19362306a36Sopenharmony_ci if (!p) 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci idx = strlen(p) - 1; 19762306a36Sopenharmony_ci if (p[idx] == '\n') 19862306a36Sopenharmony_ci p[idx] = '\0'; 19962306a36Sopenharmony_ci ret = strlist__add(sl, buf); 20062306a36Sopenharmony_ci if (ret < 0) { 20162306a36Sopenharmony_ci pr_debug("strlist__add failed (%d)\n", ret); 20262306a36Sopenharmony_ci goto out_close_fp; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci fclose(fp); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return sl; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciout_close_fp: 21062306a36Sopenharmony_ci fclose(fp); 21162306a36Sopenharmony_ci goto out_free_sl; 21262306a36Sopenharmony_ciout_close_fddup: 21362306a36Sopenharmony_ci close(fddup); 21462306a36Sopenharmony_ciout_free_sl: 21562306a36Sopenharmony_ci strlist__delete(sl); 21662306a36Sopenharmony_ci return NULL; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct strlist *__probe_file__get_namelist(int fd, bool include_group) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci char buf[128]; 22262306a36Sopenharmony_ci struct strlist *sl, *rawlist; 22362306a36Sopenharmony_ci struct str_node *ent; 22462306a36Sopenharmony_ci struct probe_trace_event tev; 22562306a36Sopenharmony_ci int ret = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci memset(&tev, 0, sizeof(tev)); 22862306a36Sopenharmony_ci rawlist = probe_file__get_rawlist(fd); 22962306a36Sopenharmony_ci if (!rawlist) 23062306a36Sopenharmony_ci return NULL; 23162306a36Sopenharmony_ci sl = strlist__new(NULL, NULL); 23262306a36Sopenharmony_ci strlist__for_each_entry(ent, rawlist) { 23362306a36Sopenharmony_ci ret = parse_probe_trace_command(ent->s, &tev); 23462306a36Sopenharmony_ci if (ret < 0) 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci if (include_group) { 23762306a36Sopenharmony_ci ret = e_snprintf(buf, 128, "%s:%s", tev.group, 23862306a36Sopenharmony_ci tev.event); 23962306a36Sopenharmony_ci if (ret >= 0) 24062306a36Sopenharmony_ci ret = strlist__add(sl, buf); 24162306a36Sopenharmony_ci } else 24262306a36Sopenharmony_ci ret = strlist__add(sl, tev.event); 24362306a36Sopenharmony_ci clear_probe_trace_event(&tev); 24462306a36Sopenharmony_ci /* Skip if there is same name multi-probe event in the list */ 24562306a36Sopenharmony_ci if (ret == -EEXIST) 24662306a36Sopenharmony_ci ret = 0; 24762306a36Sopenharmony_ci if (ret < 0) 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci strlist__delete(rawlist); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (ret < 0) { 25362306a36Sopenharmony_ci strlist__delete(sl); 25462306a36Sopenharmony_ci return NULL; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci return sl; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/* Get current perf-probe event names */ 26062306a36Sopenharmony_cistruct strlist *probe_file__get_namelist(int fd) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci return __probe_file__get_namelist(fd, false); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciint probe_file__add_event(int fd, struct probe_trace_event *tev) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci int ret = 0; 26862306a36Sopenharmony_ci char *buf = synthesize_probe_trace_command(tev); 26962306a36Sopenharmony_ci char sbuf[STRERR_BUFSIZE]; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (!buf) { 27262306a36Sopenharmony_ci pr_debug("Failed to synthesize probe trace event.\n"); 27362306a36Sopenharmony_ci return -EINVAL; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci pr_debug("Writing event: %s\n", buf); 27762306a36Sopenharmony_ci if (!probe_event_dry_run) { 27862306a36Sopenharmony_ci if (write(fd, buf, strlen(buf)) < (int)strlen(buf)) { 27962306a36Sopenharmony_ci ret = -errno; 28062306a36Sopenharmony_ci pr_warning("Failed to write event: %s\n", 28162306a36Sopenharmony_ci str_error_r(errno, sbuf, sizeof(sbuf))); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci free(buf); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return ret; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int __del_trace_probe_event(int fd, struct str_node *ent) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci char *p; 29262306a36Sopenharmony_ci char buf[128]; 29362306a36Sopenharmony_ci int ret; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Convert from perf-probe event to trace-probe event */ 29662306a36Sopenharmony_ci ret = e_snprintf(buf, 128, "-:%s", ent->s); 29762306a36Sopenharmony_ci if (ret < 0) 29862306a36Sopenharmony_ci goto error; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci p = strchr(buf + 2, ':'); 30162306a36Sopenharmony_ci if (!p) { 30262306a36Sopenharmony_ci pr_debug("Internal error: %s should have ':' but not.\n", 30362306a36Sopenharmony_ci ent->s); 30462306a36Sopenharmony_ci ret = -ENOTSUP; 30562306a36Sopenharmony_ci goto error; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci *p = '/'; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci pr_debug("Writing event: %s\n", buf); 31062306a36Sopenharmony_ci ret = write(fd, buf, strlen(buf)); 31162306a36Sopenharmony_ci if (ret < 0) { 31262306a36Sopenharmony_ci ret = -errno; 31362306a36Sopenharmony_ci goto error; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_cierror: 31862306a36Sopenharmony_ci pr_warning("Failed to delete event: %s\n", 31962306a36Sopenharmony_ci str_error_r(-ret, buf, sizeof(buf))); 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciint probe_file__get_events(int fd, struct strfilter *filter, 32462306a36Sopenharmony_ci struct strlist *plist) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct strlist *namelist; 32762306a36Sopenharmony_ci struct str_node *ent; 32862306a36Sopenharmony_ci const char *p; 32962306a36Sopenharmony_ci int ret = -ENOENT; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (!plist) 33262306a36Sopenharmony_ci return -EINVAL; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci namelist = __probe_file__get_namelist(fd, true); 33562306a36Sopenharmony_ci if (!namelist) 33662306a36Sopenharmony_ci return -ENOENT; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci strlist__for_each_entry(ent, namelist) { 33962306a36Sopenharmony_ci p = strchr(ent->s, ':'); 34062306a36Sopenharmony_ci if ((p && strfilter__compare(filter, p + 1)) || 34162306a36Sopenharmony_ci strfilter__compare(filter, ent->s)) { 34262306a36Sopenharmony_ci ret = strlist__add(plist, ent->s); 34362306a36Sopenharmony_ci if (ret == -ENOMEM) { 34462306a36Sopenharmony_ci pr_err("strlist__add failed with -ENOMEM\n"); 34562306a36Sopenharmony_ci goto out; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci ret = 0; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ciout: 35162306a36Sopenharmony_ci strlist__delete(namelist); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return ret; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciint probe_file__del_strlist(int fd, struct strlist *namelist) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci int ret = 0; 35962306a36Sopenharmony_ci struct str_node *ent; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci strlist__for_each_entry(ent, namelist) { 36262306a36Sopenharmony_ci ret = __del_trace_probe_event(fd, ent); 36362306a36Sopenharmony_ci if (ret < 0) 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciint probe_file__del_events(int fd, struct strfilter *filter) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct strlist *namelist; 37262306a36Sopenharmony_ci int ret; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci namelist = strlist__new(NULL, NULL); 37562306a36Sopenharmony_ci if (!namelist) 37662306a36Sopenharmony_ci return -ENOMEM; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ret = probe_file__get_events(fd, filter, namelist); 37962306a36Sopenharmony_ci if (ret < 0) 38062306a36Sopenharmony_ci goto out; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ret = probe_file__del_strlist(fd, namelist); 38362306a36Sopenharmony_ciout: 38462306a36Sopenharmony_ci strlist__delete(namelist); 38562306a36Sopenharmony_ci return ret; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* Caller must ensure to remove this entry from list */ 38962306a36Sopenharmony_cistatic void probe_cache_entry__delete(struct probe_cache_entry *entry) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci if (entry) { 39262306a36Sopenharmony_ci BUG_ON(!list_empty(&entry->node)); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci strlist__delete(entry->tevlist); 39562306a36Sopenharmony_ci clear_perf_probe_event(&entry->pev); 39662306a36Sopenharmony_ci zfree(&entry->spev); 39762306a36Sopenharmony_ci free(entry); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic struct probe_cache_entry * 40262306a36Sopenharmony_ciprobe_cache_entry__new(struct perf_probe_event *pev) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct probe_cache_entry *entry = zalloc(sizeof(*entry)); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (entry) { 40762306a36Sopenharmony_ci INIT_LIST_HEAD(&entry->node); 40862306a36Sopenharmony_ci entry->tevlist = strlist__new(NULL, NULL); 40962306a36Sopenharmony_ci if (!entry->tevlist) 41062306a36Sopenharmony_ci zfree(&entry); 41162306a36Sopenharmony_ci else if (pev) { 41262306a36Sopenharmony_ci entry->spev = synthesize_perf_probe_command(pev); 41362306a36Sopenharmony_ci if (!entry->spev || 41462306a36Sopenharmony_ci perf_probe_event__copy(&entry->pev, pev) < 0) { 41562306a36Sopenharmony_ci probe_cache_entry__delete(entry); 41662306a36Sopenharmony_ci return NULL; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return entry; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciint probe_cache_entry__get_event(struct probe_cache_entry *entry, 42562306a36Sopenharmony_ci struct probe_trace_event **tevs) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct probe_trace_event *tev; 42862306a36Sopenharmony_ci struct str_node *node; 42962306a36Sopenharmony_ci int ret, i; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci ret = strlist__nr_entries(entry->tevlist); 43262306a36Sopenharmony_ci if (ret > probe_conf.max_probes) 43362306a36Sopenharmony_ci return -E2BIG; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci *tevs = zalloc(ret * sizeof(*tev)); 43662306a36Sopenharmony_ci if (!*tevs) 43762306a36Sopenharmony_ci return -ENOMEM; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci i = 0; 44062306a36Sopenharmony_ci strlist__for_each_entry(node, entry->tevlist) { 44162306a36Sopenharmony_ci tev = &(*tevs)[i++]; 44262306a36Sopenharmony_ci ret = parse_probe_trace_command(node->s, tev); 44362306a36Sopenharmony_ci if (ret < 0) 44462306a36Sopenharmony_ci break; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci return i; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* For the kernel probe caches, pass target = NULL or DSO__NAME_KALLSYMS */ 45062306a36Sopenharmony_cistatic int probe_cache__open(struct probe_cache *pcache, const char *target, 45162306a36Sopenharmony_ci struct nsinfo *nsi) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci char cpath[PATH_MAX]; 45462306a36Sopenharmony_ci char sbuildid[SBUILD_ID_SIZE]; 45562306a36Sopenharmony_ci char *dir_name = NULL; 45662306a36Sopenharmony_ci bool is_kallsyms = false; 45762306a36Sopenharmony_ci int ret, fd; 45862306a36Sopenharmony_ci struct nscookie nsc; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (target && build_id_cache__cached(target)) { 46162306a36Sopenharmony_ci /* This is a cached buildid */ 46262306a36Sopenharmony_ci strlcpy(sbuildid, target, SBUILD_ID_SIZE); 46362306a36Sopenharmony_ci dir_name = build_id_cache__linkname(sbuildid, NULL, 0); 46462306a36Sopenharmony_ci goto found; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (!target || !strcmp(target, DSO__NAME_KALLSYMS)) { 46862306a36Sopenharmony_ci target = DSO__NAME_KALLSYMS; 46962306a36Sopenharmony_ci is_kallsyms = true; 47062306a36Sopenharmony_ci ret = sysfs__sprintf_build_id("/", sbuildid); 47162306a36Sopenharmony_ci } else { 47262306a36Sopenharmony_ci nsinfo__mountns_enter(nsi, &nsc); 47362306a36Sopenharmony_ci ret = filename__sprintf_build_id(target, sbuildid); 47462306a36Sopenharmony_ci nsinfo__mountns_exit(&nsc); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (ret < 0) { 47862306a36Sopenharmony_ci pr_debug("Failed to get build-id from %s.\n", target); 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* If we have no buildid cache, make it */ 48362306a36Sopenharmony_ci if (!build_id_cache__cached(sbuildid)) { 48462306a36Sopenharmony_ci ret = build_id_cache__add_s(sbuildid, target, nsi, 48562306a36Sopenharmony_ci is_kallsyms, NULL); 48662306a36Sopenharmony_ci if (ret < 0) { 48762306a36Sopenharmony_ci pr_debug("Failed to add build-id cache: %s\n", target); 48862306a36Sopenharmony_ci return ret; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci dir_name = build_id_cache__cachedir(sbuildid, target, nsi, is_kallsyms, 49362306a36Sopenharmony_ci false); 49462306a36Sopenharmony_cifound: 49562306a36Sopenharmony_ci if (!dir_name) { 49662306a36Sopenharmony_ci pr_debug("Failed to get cache from %s\n", target); 49762306a36Sopenharmony_ci return -ENOMEM; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci snprintf(cpath, PATH_MAX, "%s/probes", dir_name); 50162306a36Sopenharmony_ci fd = open(cpath, O_CREAT | O_RDWR, 0644); 50262306a36Sopenharmony_ci if (fd < 0) 50362306a36Sopenharmony_ci pr_debug("Failed to open cache(%d): %s\n", fd, cpath); 50462306a36Sopenharmony_ci free(dir_name); 50562306a36Sopenharmony_ci pcache->fd = fd; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return fd; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int probe_cache__load(struct probe_cache *pcache) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct probe_cache_entry *entry = NULL; 51362306a36Sopenharmony_ci char buf[MAX_CMDLEN], *p; 51462306a36Sopenharmony_ci int ret = 0, fddup; 51562306a36Sopenharmony_ci FILE *fp; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci fddup = dup(pcache->fd); 51862306a36Sopenharmony_ci if (fddup < 0) 51962306a36Sopenharmony_ci return -errno; 52062306a36Sopenharmony_ci fp = fdopen(fddup, "r"); 52162306a36Sopenharmony_ci if (!fp) { 52262306a36Sopenharmony_ci close(fddup); 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci while (!feof(fp)) { 52762306a36Sopenharmony_ci if (!fgets(buf, MAX_CMDLEN, fp)) 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci p = strchr(buf, '\n'); 53062306a36Sopenharmony_ci if (p) 53162306a36Sopenharmony_ci *p = '\0'; 53262306a36Sopenharmony_ci /* #perf_probe_event or %sdt_event */ 53362306a36Sopenharmony_ci if (buf[0] == '#' || buf[0] == '%') { 53462306a36Sopenharmony_ci entry = probe_cache_entry__new(NULL); 53562306a36Sopenharmony_ci if (!entry) { 53662306a36Sopenharmony_ci ret = -ENOMEM; 53762306a36Sopenharmony_ci goto out; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci if (buf[0] == '%') 54062306a36Sopenharmony_ci entry->sdt = true; 54162306a36Sopenharmony_ci entry->spev = strdup(buf + 1); 54262306a36Sopenharmony_ci if (entry->spev) 54362306a36Sopenharmony_ci ret = parse_perf_probe_command(buf + 1, 54462306a36Sopenharmony_ci &entry->pev); 54562306a36Sopenharmony_ci else 54662306a36Sopenharmony_ci ret = -ENOMEM; 54762306a36Sopenharmony_ci if (ret < 0) { 54862306a36Sopenharmony_ci probe_cache_entry__delete(entry); 54962306a36Sopenharmony_ci goto out; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci list_add_tail(&entry->node, &pcache->entries); 55262306a36Sopenharmony_ci } else { /* trace_probe_event */ 55362306a36Sopenharmony_ci if (!entry) { 55462306a36Sopenharmony_ci ret = -EINVAL; 55562306a36Sopenharmony_ci goto out; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci ret = strlist__add(entry->tevlist, buf); 55862306a36Sopenharmony_ci if (ret == -ENOMEM) { 55962306a36Sopenharmony_ci pr_err("strlist__add failed with -ENOMEM\n"); 56062306a36Sopenharmony_ci goto out; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ciout: 56562306a36Sopenharmony_ci fclose(fp); 56662306a36Sopenharmony_ci return ret; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic struct probe_cache *probe_cache__alloc(void) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct probe_cache *pcache = zalloc(sizeof(*pcache)); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (pcache) { 57462306a36Sopenharmony_ci INIT_LIST_HEAD(&pcache->entries); 57562306a36Sopenharmony_ci pcache->fd = -EINVAL; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci return pcache; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_civoid probe_cache__purge(struct probe_cache *pcache) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct probe_cache_entry *entry, *n; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci list_for_each_entry_safe(entry, n, &pcache->entries, node) { 58562306a36Sopenharmony_ci list_del_init(&entry->node); 58662306a36Sopenharmony_ci probe_cache_entry__delete(entry); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_civoid probe_cache__delete(struct probe_cache *pcache) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci if (!pcache) 59362306a36Sopenharmony_ci return; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci probe_cache__purge(pcache); 59662306a36Sopenharmony_ci if (pcache->fd > 0) 59762306a36Sopenharmony_ci close(pcache->fd); 59862306a36Sopenharmony_ci free(pcache); 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistruct probe_cache *probe_cache__new(const char *target, struct nsinfo *nsi) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct probe_cache *pcache = probe_cache__alloc(); 60462306a36Sopenharmony_ci int ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (!pcache) 60762306a36Sopenharmony_ci return NULL; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci ret = probe_cache__open(pcache, target, nsi); 61062306a36Sopenharmony_ci if (ret < 0) { 61162306a36Sopenharmony_ci pr_debug("Cache open error: %d\n", ret); 61262306a36Sopenharmony_ci goto out_err; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ret = probe_cache__load(pcache); 61662306a36Sopenharmony_ci if (ret < 0) { 61762306a36Sopenharmony_ci pr_debug("Cache read error: %d\n", ret); 61862306a36Sopenharmony_ci goto out_err; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return pcache; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ciout_err: 62462306a36Sopenharmony_ci probe_cache__delete(pcache); 62562306a36Sopenharmony_ci return NULL; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic bool streql(const char *a, const char *b) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci if (a == b) 63162306a36Sopenharmony_ci return true; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (!a || !b) 63462306a36Sopenharmony_ci return false; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return !strcmp(a, b); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistruct probe_cache_entry * 64062306a36Sopenharmony_ciprobe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct probe_cache_entry *entry = NULL; 64362306a36Sopenharmony_ci char *cmd = synthesize_perf_probe_command(pev); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (!cmd) 64662306a36Sopenharmony_ci return NULL; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci for_each_probe_cache_entry(entry, pcache) { 64962306a36Sopenharmony_ci if (pev->sdt) { 65062306a36Sopenharmony_ci if (entry->pev.event && 65162306a36Sopenharmony_ci streql(entry->pev.event, pev->event) && 65262306a36Sopenharmony_ci (!pev->group || 65362306a36Sopenharmony_ci streql(entry->pev.group, pev->group))) 65462306a36Sopenharmony_ci goto found; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci continue; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci /* Hit if same event name or same command-string */ 65962306a36Sopenharmony_ci if ((pev->event && 66062306a36Sopenharmony_ci (streql(entry->pev.group, pev->group) && 66162306a36Sopenharmony_ci streql(entry->pev.event, pev->event))) || 66262306a36Sopenharmony_ci (!strcmp(entry->spev, cmd))) 66362306a36Sopenharmony_ci goto found; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci entry = NULL; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cifound: 66862306a36Sopenharmony_ci free(cmd); 66962306a36Sopenharmony_ci return entry; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistruct probe_cache_entry * 67362306a36Sopenharmony_ciprobe_cache__find_by_name(struct probe_cache *pcache, 67462306a36Sopenharmony_ci const char *group, const char *event) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct probe_cache_entry *entry = NULL; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci for_each_probe_cache_entry(entry, pcache) { 67962306a36Sopenharmony_ci /* Hit if same event name or same command-string */ 68062306a36Sopenharmony_ci if (streql(entry->pev.group, group) && 68162306a36Sopenharmony_ci streql(entry->pev.event, event)) 68262306a36Sopenharmony_ci goto found; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci entry = NULL; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cifound: 68762306a36Sopenharmony_ci return entry; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ciint probe_cache__add_entry(struct probe_cache *pcache, 69162306a36Sopenharmony_ci struct perf_probe_event *pev, 69262306a36Sopenharmony_ci struct probe_trace_event *tevs, int ntevs) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct probe_cache_entry *entry = NULL; 69562306a36Sopenharmony_ci char *command; 69662306a36Sopenharmony_ci int i, ret = 0; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (!pcache || !pev || !tevs || ntevs <= 0) { 69962306a36Sopenharmony_ci ret = -EINVAL; 70062306a36Sopenharmony_ci goto out_err; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* Remove old cache entry */ 70462306a36Sopenharmony_ci entry = probe_cache__find(pcache, pev); 70562306a36Sopenharmony_ci if (entry) { 70662306a36Sopenharmony_ci list_del_init(&entry->node); 70762306a36Sopenharmony_ci probe_cache_entry__delete(entry); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci ret = -ENOMEM; 71162306a36Sopenharmony_ci entry = probe_cache_entry__new(pev); 71262306a36Sopenharmony_ci if (!entry) 71362306a36Sopenharmony_ci goto out_err; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci for (i = 0; i < ntevs; i++) { 71662306a36Sopenharmony_ci if (!tevs[i].point.symbol) 71762306a36Sopenharmony_ci continue; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci command = synthesize_probe_trace_command(&tevs[i]); 72062306a36Sopenharmony_ci if (!command) 72162306a36Sopenharmony_ci goto out_err; 72262306a36Sopenharmony_ci ret = strlist__add(entry->tevlist, command); 72362306a36Sopenharmony_ci if (ret == -ENOMEM) { 72462306a36Sopenharmony_ci pr_err("strlist__add failed with -ENOMEM\n"); 72562306a36Sopenharmony_ci goto out_err; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci free(command); 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci list_add_tail(&entry->node, &pcache->entries); 73162306a36Sopenharmony_ci pr_debug("Added probe cache: %d\n", ntevs); 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ciout_err: 73562306a36Sopenharmony_ci pr_debug("Failed to add probe caches\n"); 73662306a36Sopenharmony_ci probe_cache_entry__delete(entry); 73762306a36Sopenharmony_ci return ret; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci#ifdef HAVE_GELF_GETNOTE_SUPPORT 74162306a36Sopenharmony_cistatic unsigned long long sdt_note__get_addr(struct sdt_note *note) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci return note->bit32 ? 74462306a36Sopenharmony_ci (unsigned long long)note->addr.a32[SDT_NOTE_IDX_LOC] : 74562306a36Sopenharmony_ci (unsigned long long)note->addr.a64[SDT_NOTE_IDX_LOC]; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic unsigned long long sdt_note__get_ref_ctr_offset(struct sdt_note *note) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci return note->bit32 ? 75162306a36Sopenharmony_ci (unsigned long long)note->addr.a32[SDT_NOTE_IDX_REFCTR] : 75262306a36Sopenharmony_ci (unsigned long long)note->addr.a64[SDT_NOTE_IDX_REFCTR]; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic const char * const type_to_suffix[] = { 75662306a36Sopenharmony_ci ":s64", "", "", "", ":s32", "", ":s16", ":s8", 75762306a36Sopenharmony_ci "", ":u8", ":u16", "", ":u32", "", "", "", ":u64" 75862306a36Sopenharmony_ci}; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci/* 76162306a36Sopenharmony_ci * Isolate the string number and convert it into a decimal value; 76262306a36Sopenharmony_ci * this will be an index to get suffix of the uprobe name (defining 76362306a36Sopenharmony_ci * the type) 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_cistatic int sdt_arg_parse_size(char *n_ptr, const char **suffix) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci long type_idx; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci type_idx = strtol(n_ptr, NULL, 10); 77062306a36Sopenharmony_ci if (type_idx < -8 || type_idx > 8) { 77162306a36Sopenharmony_ci pr_debug4("Failed to get a valid sdt type\n"); 77262306a36Sopenharmony_ci return -1; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci *suffix = type_to_suffix[type_idx + 8]; 77662306a36Sopenharmony_ci return 0; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci char *op, *desc = strdup(arg), *new_op = NULL; 78262306a36Sopenharmony_ci const char *suffix = ""; 78362306a36Sopenharmony_ci int ret = -1; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (desc == NULL) { 78662306a36Sopenharmony_ci pr_debug4("Allocation error\n"); 78762306a36Sopenharmony_ci return ret; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* 79162306a36Sopenharmony_ci * Argument is in N@OP format. N is size of the argument and OP is 79262306a36Sopenharmony_ci * the actual assembly operand. N can be omitted; in that case 79362306a36Sopenharmony_ci * argument is just OP(without @). 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_ci op = strchr(desc, '@'); 79662306a36Sopenharmony_ci if (op) { 79762306a36Sopenharmony_ci op[0] = '\0'; 79862306a36Sopenharmony_ci op++; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (sdt_arg_parse_size(desc, &suffix)) 80162306a36Sopenharmony_ci goto error; 80262306a36Sopenharmony_ci } else { 80362306a36Sopenharmony_ci op = desc; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci ret = arch_sdt_arg_parse_op(op, &new_op); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (ret < 0) 80962306a36Sopenharmony_ci goto error; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (ret == SDT_ARG_VALID) { 81262306a36Sopenharmony_ci ret = strbuf_addf(buf, " arg%d=%s%s", i + 1, new_op, suffix); 81362306a36Sopenharmony_ci if (ret < 0) 81462306a36Sopenharmony_ci goto error; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ret = 0; 81862306a36Sopenharmony_cierror: 81962306a36Sopenharmony_ci free(desc); 82062306a36Sopenharmony_ci free(new_op); 82162306a36Sopenharmony_ci return ret; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic char *synthesize_sdt_probe_command(struct sdt_note *note, 82562306a36Sopenharmony_ci const char *pathname, 82662306a36Sopenharmony_ci const char *sdtgrp) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct strbuf buf; 82962306a36Sopenharmony_ci char *ret = NULL; 83062306a36Sopenharmony_ci int i, args_count, err; 83162306a36Sopenharmony_ci unsigned long long ref_ctr_offset; 83262306a36Sopenharmony_ci char *arg; 83362306a36Sopenharmony_ci int arg_idx = 0; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (strbuf_init(&buf, 32) < 0) 83662306a36Sopenharmony_ci return NULL; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci err = strbuf_addf(&buf, "p:%s/%s %s:0x%llx", 83962306a36Sopenharmony_ci sdtgrp, note->name, pathname, 84062306a36Sopenharmony_ci sdt_note__get_addr(note)); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci ref_ctr_offset = sdt_note__get_ref_ctr_offset(note); 84362306a36Sopenharmony_ci if (ref_ctr_offset && err >= 0) 84462306a36Sopenharmony_ci err = strbuf_addf(&buf, "(0x%llx)", ref_ctr_offset); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (err < 0) 84762306a36Sopenharmony_ci goto error; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (!note->args) 85062306a36Sopenharmony_ci goto out; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (note->args) { 85362306a36Sopenharmony_ci char **args = argv_split(note->args, &args_count); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (args == NULL) 85662306a36Sopenharmony_ci goto error; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci for (i = 0; i < args_count; ) { 85962306a36Sopenharmony_ci /* 86062306a36Sopenharmony_ci * FIXUP: Arm64 ELF section '.note.stapsdt' uses string 86162306a36Sopenharmony_ci * format "-4@[sp, NUM]" if a probe is to access data in 86262306a36Sopenharmony_ci * the stack, e.g. below is an example for the SDT 86362306a36Sopenharmony_ci * Arguments: 86462306a36Sopenharmony_ci * 86562306a36Sopenharmony_ci * Arguments: -4@[sp, 12] -4@[sp, 8] -4@[sp, 4] 86662306a36Sopenharmony_ci * 86762306a36Sopenharmony_ci * Since the string introduces an extra space character 86862306a36Sopenharmony_ci * in the middle of square brackets, the argument is 86962306a36Sopenharmony_ci * divided into two items. Fixup for this case, if an 87062306a36Sopenharmony_ci * item contains sub string "[sp,", need to concatenate 87162306a36Sopenharmony_ci * the two items. 87262306a36Sopenharmony_ci */ 87362306a36Sopenharmony_ci if (strstr(args[i], "[sp,") && (i+1) < args_count) { 87462306a36Sopenharmony_ci err = asprintf(&arg, "%s %s", args[i], args[i+1]); 87562306a36Sopenharmony_ci i += 2; 87662306a36Sopenharmony_ci } else { 87762306a36Sopenharmony_ci err = asprintf(&arg, "%s", args[i]); 87862306a36Sopenharmony_ci i += 1; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* Failed to allocate memory */ 88262306a36Sopenharmony_ci if (err < 0) { 88362306a36Sopenharmony_ci argv_free(args); 88462306a36Sopenharmony_ci goto error; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (synthesize_sdt_probe_arg(&buf, arg_idx, arg) < 0) { 88862306a36Sopenharmony_ci free(arg); 88962306a36Sopenharmony_ci argv_free(args); 89062306a36Sopenharmony_ci goto error; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci free(arg); 89462306a36Sopenharmony_ci arg_idx++; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci argv_free(args); 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ciout: 90162306a36Sopenharmony_ci ret = strbuf_detach(&buf, NULL); 90262306a36Sopenharmony_cierror: 90362306a36Sopenharmony_ci strbuf_release(&buf); 90462306a36Sopenharmony_ci return ret; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ciint probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct probe_cache_entry *entry = NULL; 91062306a36Sopenharmony_ci struct list_head sdtlist; 91162306a36Sopenharmony_ci struct sdt_note *note; 91262306a36Sopenharmony_ci char *buf; 91362306a36Sopenharmony_ci char sdtgrp[64]; 91462306a36Sopenharmony_ci int ret; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci INIT_LIST_HEAD(&sdtlist); 91762306a36Sopenharmony_ci ret = get_sdt_note_list(&sdtlist, pathname); 91862306a36Sopenharmony_ci if (ret < 0) { 91962306a36Sopenharmony_ci pr_debug4("Failed to get sdt note: %d\n", ret); 92062306a36Sopenharmony_ci return ret; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci list_for_each_entry(note, &sdtlist, note_list) { 92362306a36Sopenharmony_ci ret = snprintf(sdtgrp, 64, "sdt_%s", note->provider); 92462306a36Sopenharmony_ci if (ret < 0) 92562306a36Sopenharmony_ci break; 92662306a36Sopenharmony_ci /* Try to find same-name entry */ 92762306a36Sopenharmony_ci entry = probe_cache__find_by_name(pcache, sdtgrp, note->name); 92862306a36Sopenharmony_ci if (!entry) { 92962306a36Sopenharmony_ci entry = probe_cache_entry__new(NULL); 93062306a36Sopenharmony_ci if (!entry) { 93162306a36Sopenharmony_ci ret = -ENOMEM; 93262306a36Sopenharmony_ci break; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci entry->sdt = true; 93562306a36Sopenharmony_ci ret = asprintf(&entry->spev, "%s:%s=%s", sdtgrp, 93662306a36Sopenharmony_ci note->name, note->name); 93762306a36Sopenharmony_ci if (ret < 0) 93862306a36Sopenharmony_ci break; 93962306a36Sopenharmony_ci entry->pev.event = strdup(note->name); 94062306a36Sopenharmony_ci entry->pev.group = strdup(sdtgrp); 94162306a36Sopenharmony_ci list_add_tail(&entry->node, &pcache->entries); 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci buf = synthesize_sdt_probe_command(note, pathname, sdtgrp); 94462306a36Sopenharmony_ci if (!buf) { 94562306a36Sopenharmony_ci ret = -ENOMEM; 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci ret = strlist__add(entry->tevlist, buf); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci free(buf); 95262306a36Sopenharmony_ci entry = NULL; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (ret == -ENOMEM) { 95562306a36Sopenharmony_ci pr_err("strlist__add failed with -ENOMEM\n"); 95662306a36Sopenharmony_ci break; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci if (entry) { 96062306a36Sopenharmony_ci list_del_init(&entry->node); 96162306a36Sopenharmony_ci probe_cache_entry__delete(entry); 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci cleanup_sdt_note_list(&sdtlist); 96462306a36Sopenharmony_ci return ret; 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ci#endif 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic int probe_cache_entry__write(struct probe_cache_entry *entry, int fd) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci struct str_node *snode; 97162306a36Sopenharmony_ci struct stat st; 97262306a36Sopenharmony_ci struct iovec iov[3]; 97362306a36Sopenharmony_ci const char *prefix = entry->sdt ? "%" : "#"; 97462306a36Sopenharmony_ci int ret; 97562306a36Sopenharmony_ci /* Save stat for rollback */ 97662306a36Sopenharmony_ci ret = fstat(fd, &st); 97762306a36Sopenharmony_ci if (ret < 0) 97862306a36Sopenharmony_ci return ret; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci pr_debug("Writing cache: %s%s\n", prefix, entry->spev); 98162306a36Sopenharmony_ci iov[0].iov_base = (void *)prefix; iov[0].iov_len = 1; 98262306a36Sopenharmony_ci iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev); 98362306a36Sopenharmony_ci iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1; 98462306a36Sopenharmony_ci ret = writev(fd, iov, 3); 98562306a36Sopenharmony_ci if (ret < (int)iov[1].iov_len + 2) 98662306a36Sopenharmony_ci goto rollback; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci strlist__for_each_entry(snode, entry->tevlist) { 98962306a36Sopenharmony_ci iov[0].iov_base = (void *)snode->s; 99062306a36Sopenharmony_ci iov[0].iov_len = strlen(snode->s); 99162306a36Sopenharmony_ci iov[1].iov_base = (void *)"\n"; iov[1].iov_len = 1; 99262306a36Sopenharmony_ci ret = writev(fd, iov, 2); 99362306a36Sopenharmony_ci if (ret < (int)iov[0].iov_len + 1) 99462306a36Sopenharmony_ci goto rollback; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cirollback: 99962306a36Sopenharmony_ci /* Rollback to avoid cache file corruption */ 100062306a36Sopenharmony_ci if (ret > 0) 100162306a36Sopenharmony_ci ret = -1; 100262306a36Sopenharmony_ci if (ftruncate(fd, st.st_size) < 0) 100362306a36Sopenharmony_ci ret = -2; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return ret; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ciint probe_cache__commit(struct probe_cache *pcache) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct probe_cache_entry *entry; 101162306a36Sopenharmony_ci int ret = 0; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* TBD: if we do not update existing entries, skip it */ 101462306a36Sopenharmony_ci ret = lseek(pcache->fd, 0, SEEK_SET); 101562306a36Sopenharmony_ci if (ret < 0) 101662306a36Sopenharmony_ci goto out; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci ret = ftruncate(pcache->fd, 0); 101962306a36Sopenharmony_ci if (ret < 0) 102062306a36Sopenharmony_ci goto out; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci for_each_probe_cache_entry(entry, pcache) { 102362306a36Sopenharmony_ci ret = probe_cache_entry__write(entry, pcache->fd); 102462306a36Sopenharmony_ci pr_debug("Cache committed: %d\n", ret); 102562306a36Sopenharmony_ci if (ret < 0) 102662306a36Sopenharmony_ci break; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ciout: 102962306a36Sopenharmony_ci return ret; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic bool probe_cache_entry__compare(struct probe_cache_entry *entry, 103362306a36Sopenharmony_ci struct strfilter *filter) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci char buf[128], *ptr = entry->spev; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (entry->pev.event) { 103862306a36Sopenharmony_ci snprintf(buf, 128, "%s:%s", entry->pev.group, entry->pev.event); 103962306a36Sopenharmony_ci ptr = buf; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci return strfilter__compare(filter, ptr); 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ciint probe_cache__filter_purge(struct probe_cache *pcache, 104562306a36Sopenharmony_ci struct strfilter *filter) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct probe_cache_entry *entry, *tmp; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci list_for_each_entry_safe(entry, tmp, &pcache->entries, node) { 105062306a36Sopenharmony_ci if (probe_cache_entry__compare(entry, filter)) { 105162306a36Sopenharmony_ci pr_info("Removed cached event: %s\n", entry->spev); 105262306a36Sopenharmony_ci list_del_init(&entry->node); 105362306a36Sopenharmony_ci probe_cache_entry__delete(entry); 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci return 0; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic int probe_cache__show_entries(struct probe_cache *pcache, 106062306a36Sopenharmony_ci struct strfilter *filter) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci struct probe_cache_entry *entry; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci for_each_probe_cache_entry(entry, pcache) { 106562306a36Sopenharmony_ci if (probe_cache_entry__compare(entry, filter)) 106662306a36Sopenharmony_ci printf("%s\n", entry->spev); 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci return 0; 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci/* Show all cached probes */ 107262306a36Sopenharmony_ciint probe_cache__show_all_caches(struct strfilter *filter) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci struct probe_cache *pcache; 107562306a36Sopenharmony_ci struct strlist *bidlist; 107662306a36Sopenharmony_ci struct str_node *nd; 107762306a36Sopenharmony_ci char *buf = strfilter__string(filter); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci pr_debug("list cache with filter: %s\n", buf); 108062306a36Sopenharmony_ci free(buf); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci bidlist = build_id_cache__list_all(true); 108362306a36Sopenharmony_ci if (!bidlist) { 108462306a36Sopenharmony_ci pr_debug("Failed to get buildids: %d\n", errno); 108562306a36Sopenharmony_ci return -EINVAL; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci strlist__for_each_entry(nd, bidlist) { 108862306a36Sopenharmony_ci pcache = probe_cache__new(nd->s, NULL); 108962306a36Sopenharmony_ci if (!pcache) 109062306a36Sopenharmony_ci continue; 109162306a36Sopenharmony_ci if (!list_empty(&pcache->entries)) { 109262306a36Sopenharmony_ci buf = build_id_cache__origname(nd->s); 109362306a36Sopenharmony_ci printf("%s (%s):\n", buf, nd->s); 109462306a36Sopenharmony_ci free(buf); 109562306a36Sopenharmony_ci probe_cache__show_entries(pcache, filter); 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci probe_cache__delete(pcache); 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci strlist__delete(bidlist); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return 0; 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cienum ftrace_readme { 110562306a36Sopenharmony_ci FTRACE_README_PROBE_TYPE_X = 0, 110662306a36Sopenharmony_ci FTRACE_README_KRETPROBE_OFFSET, 110762306a36Sopenharmony_ci FTRACE_README_UPROBE_REF_CTR, 110862306a36Sopenharmony_ci FTRACE_README_USER_ACCESS, 110962306a36Sopenharmony_ci FTRACE_README_MULTIPROBE_EVENT, 111062306a36Sopenharmony_ci FTRACE_README_IMMEDIATE_VALUE, 111162306a36Sopenharmony_ci FTRACE_README_END, 111262306a36Sopenharmony_ci}; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic struct { 111562306a36Sopenharmony_ci const char *pattern; 111662306a36Sopenharmony_ci bool avail; 111762306a36Sopenharmony_ci} ftrace_readme_table[] = { 111862306a36Sopenharmony_ci#define DEFINE_TYPE(idx, pat) \ 111962306a36Sopenharmony_ci [idx] = {.pattern = pat, .avail = false} 112062306a36Sopenharmony_ci DEFINE_TYPE(FTRACE_README_PROBE_TYPE_X, "*type: * x8/16/32/64,*"), 112162306a36Sopenharmony_ci DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"), 112262306a36Sopenharmony_ci DEFINE_TYPE(FTRACE_README_UPROBE_REF_CTR, "*ref_ctr_offset*"), 112362306a36Sopenharmony_ci DEFINE_TYPE(FTRACE_README_USER_ACCESS, "*u]<offset>*"), 112462306a36Sopenharmony_ci DEFINE_TYPE(FTRACE_README_MULTIPROBE_EVENT, "*Create/append/*"), 112562306a36Sopenharmony_ci DEFINE_TYPE(FTRACE_README_IMMEDIATE_VALUE, "*\\imm-value,*"), 112662306a36Sopenharmony_ci}; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic bool scan_ftrace_readme(enum ftrace_readme type) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci int fd; 113162306a36Sopenharmony_ci FILE *fp; 113262306a36Sopenharmony_ci char *buf = NULL; 113362306a36Sopenharmony_ci size_t len = 0; 113462306a36Sopenharmony_ci bool ret = false; 113562306a36Sopenharmony_ci static bool scanned = false; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (scanned) 113862306a36Sopenharmony_ci goto result; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci fd = open_trace_file("README", false); 114162306a36Sopenharmony_ci if (fd < 0) 114262306a36Sopenharmony_ci return ret; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci fp = fdopen(fd, "r"); 114562306a36Sopenharmony_ci if (!fp) { 114662306a36Sopenharmony_ci close(fd); 114762306a36Sopenharmony_ci return ret; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci while (getline(&buf, &len, fp) > 0) 115162306a36Sopenharmony_ci for (enum ftrace_readme i = 0; i < FTRACE_README_END; i++) 115262306a36Sopenharmony_ci if (!ftrace_readme_table[i].avail) 115362306a36Sopenharmony_ci ftrace_readme_table[i].avail = 115462306a36Sopenharmony_ci strglobmatch(buf, ftrace_readme_table[i].pattern); 115562306a36Sopenharmony_ci scanned = true; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci fclose(fp); 115862306a36Sopenharmony_ci free(buf); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ciresult: 116162306a36Sopenharmony_ci if (type >= FTRACE_README_END) 116262306a36Sopenharmony_ci return false; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci return ftrace_readme_table[type].avail; 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cibool probe_type_is_available(enum probe_type type) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci if (type >= PROBE_TYPE_END) 117062306a36Sopenharmony_ci return false; 117162306a36Sopenharmony_ci else if (type == PROBE_TYPE_X) 117262306a36Sopenharmony_ci return scan_ftrace_readme(FTRACE_README_PROBE_TYPE_X); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci return true; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cibool kretprobe_offset_is_supported(void) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci return scan_ftrace_readme(FTRACE_README_KRETPROBE_OFFSET); 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cibool uprobe_ref_ctr_is_supported(void) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci return scan_ftrace_readme(FTRACE_README_UPROBE_REF_CTR); 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cibool user_access_is_supported(void) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci return scan_ftrace_readme(FTRACE_README_USER_ACCESS); 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cibool multiprobe_event_is_supported(void) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci return scan_ftrace_readme(FTRACE_README_MULTIPROBE_EVENT); 119562306a36Sopenharmony_ci} 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cibool immediate_value_is_supported(void) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci return scan_ftrace_readme(FTRACE_README_IMMEDIATE_VALUE); 120062306a36Sopenharmony_ci} 1201