Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566// SPDX-License-Identifier: GPL-2.0#include "perf.h"#include "util.h"#include "debug.h"#include "event.h"#include <api/fs/fs.h>#include <sys/stat.h>#include <sys/utsname.h>#include <dirent.h>#include <fcntl.h>#include <inttypes.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <limits.h>#include <linux/capability.h>#include <linux/kernel.h>#include <linux/log2.h>#include <linux/time64.h>#include <linux/overflow.h>#include <unistd.h>#include "cap.h"#include "strlist.h"#include "string2.h"
/* * XXX We need to find a better place for these things... */
const char *input_name;
bool perf_singlethreaded = true;
void perf_set_singlethreaded(void){ perf_singlethreaded = true;}
void perf_set_multithreaded(void){ perf_singlethreaded = false;}
int sysctl_perf_event_max_stack = PERF_MAX_STACK_DEPTH;int sysctl_perf_event_max_contexts_per_stack = PERF_MAX_CONTEXTS_PER_STACK;
int sysctl__max_stack(void){ int value;
if (sysctl__read_int("kernel/perf_event_max_stack", &value) == 0) sysctl_perf_event_max_stack = value;
if (sysctl__read_int("kernel/perf_event_max_contexts_per_stack", &value) == 0) sysctl_perf_event_max_contexts_per_stack = value;
return sysctl_perf_event_max_stack;}
bool sysctl__nmi_watchdog_enabled(void){ static bool cached; static bool nmi_watchdog; int value;
if (cached) return nmi_watchdog;
if (sysctl__read_int("kernel/nmi_watchdog", &value) < 0) return false;
nmi_watchdog = (value > 0) ? true : false; cached = true;
return nmi_watchdog;}
bool exclude_GH_default;
bool perf_host = true;bool perf_guest = false;
void event_attr_init(struct perf_event_attr *attr){ /* to capture ABI version */ attr->size = sizeof(*attr);
if (!exclude_GH_default) return;
if (!perf_host) attr->exclude_host = 1; if (!perf_guest) attr->exclude_guest = 1;}
int mkdir_p(char *path, mode_t mode){ struct stat st; int err; char *d = path;
if (*d != '/') return -1;
if (stat(path, &st) == 0) return 0;
while (*++d == '/');
while ((d = strchr(d, '/'))) { *d = '\0'; err = stat(path, &st) && mkdir(path, mode); *d++ = '/'; if (err) return -1; while (*d == '/') ++d; } return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;}
static bool match_pat(char *file, const char **pat){ int i = 0;
if (!pat) return true;
while (pat[i]) { if (strglobmatch(file, pat[i])) return true;
i++; }
return false;}
/* * The depth specify how deep the removal will go. * 0 - will remove only files under the 'path' directory * 1 .. x - will dive in x-level deep under the 'path' directory * * If specified the pat is array of string patterns ended with NULL, * which are checked upon every file/directory found. Only matching * ones are removed. * * The function returns: * 0 on success * -1 on removal failure with errno set * -2 on pattern failure */static int rm_rf_depth_pat(const char *path, int depth, const char **pat){ DIR *dir; int ret; struct dirent *d; char namebuf[PATH_MAX]; struct stat statbuf;
/* Do not fail if there's no file. */ ret = lstat(path, &statbuf); if (ret) return 0;
/* Try to remove any file we get. */ if (!(statbuf.st_mode & S_IFDIR)) return unlink(path);
/* We have directory in path. */ dir = opendir(path); if (dir == NULL) return -1;
while ((d = readdir(dir)) != NULL && !ret) {
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue;
if (!match_pat(d->d_name, pat)) { ret = -2; break; }
scnprintf(namebuf, sizeof(namebuf), "%s/%s", path, d->d_name);
/* We have to check symbolic link itself */ ret = lstat(namebuf, &statbuf); if (ret < 0) { pr_debug("stat failed: %s\n", namebuf); break; }
if (S_ISDIR(statbuf.st_mode)) ret = depth ? rm_rf_depth_pat(namebuf, depth - 1, pat) : 0; else ret = unlink(namebuf); } closedir(dir);
if (ret < 0) return ret;
return rmdir(path);}
static int rm_rf_a_kcore_dir(const char *path, const char *name){ char kcore_dir_path[PATH_MAX]; const char *pat[] = { "kcore", "kallsyms", "modules", NULL, };
snprintf(kcore_dir_path, sizeof(kcore_dir_path), "%s/%s", path, name);
return rm_rf_depth_pat(kcore_dir_path, 0, pat);}
static bool kcore_dir_filter(const char *name __maybe_unused, struct dirent *d){ const char *pat[] = { "kcore_dir", "kcore_dir__[1-9]*", NULL, };
return match_pat(d->d_name, pat);}
static int rm_rf_kcore_dir(const char *path){ struct strlist *kcore_dirs; struct str_node *nd; int ret;
kcore_dirs = lsdir(path, kcore_dir_filter);
if (!kcore_dirs) return 0;
strlist__for_each_entry(nd, kcore_dirs) { ret = rm_rf_a_kcore_dir(path, nd->s); if (ret) return ret; }
strlist__delete(kcore_dirs);
return 0;}
void cpumask_to_cpulist(char *cpumask, char *cpulist){ int i, j, bm_size, nbits; int len = strlen(cpumask); unsigned long *bm; char cpus[MAX_NR_CPUS];
for (i = 0; i < len; i++) { if (cpumask[i] == ',') { for (j = i; j < len; j++) cpumask[j] = cpumask[j + 1]; } }
len = strlen(cpumask); bm_size = (len + 15) / 16; nbits = bm_size * 64; if (nbits <= 0) return;
bm = calloc(bm_size, sizeof(unsigned long)); if (!bm) goto free_bm;
for (i = 0; i < bm_size; i++) { char blk[17]; int blklen = len > 16 ? 16 : len;
strncpy(blk, cpumask + len - blklen, blklen); blk[blklen] = '\0'; bm[i] = strtoul(blk, NULL, 16); cpumask[len - blklen] = '\0'; len = strlen(cpumask); }
bitmap_scnprintf(bm, nbits, cpus, sizeof(cpus)); strcpy(cpulist, cpus);
free_bm: free(bm);}
void print_separator2(int pre_dash_cnt, const char *s, int post_dash_cnt){ printf("%.*s%s%.*s\n", pre_dash_cnt, graph_dotted_line, s, post_dash_cnt, graph_dotted_line);}
int rm_rf_perf_data(const char *path){ const char *pat[] = { "data", "data.*", NULL, };
rm_rf_kcore_dir(path);
return rm_rf_depth_pat(path, 0, pat);}
int rm_rf(const char *path){ return rm_rf_depth_pat(path, INT_MAX, NULL);}
/* A filter which removes dot files */bool lsdir_no_dot_filter(const char *name __maybe_unused, struct dirent *d){ return d->d_name[0] != '.';}
/* lsdir reads a directory and store it in strlist */struct strlist *lsdir(const char *name, bool (*filter)(const char *, struct dirent *)){ struct strlist *list = NULL; DIR *dir; struct dirent *d;
dir = opendir(name); if (!dir) return NULL;
list = strlist__new(NULL, NULL); if (!list) { errno = ENOMEM; goto out; }
while ((d = readdir(dir)) != NULL) { if (!filter || filter(name, d)) strlist__add(list, d->d_name); }
out: closedir(dir); return list;}
size_t hex_width(u64 v){ size_t n = 1;
while ((v >>= 4)) ++n;
return n;}
int perf_event_paranoid(void){ int value;
if (sysctl__read_int("kernel/perf_event_paranoid", &value)) return INT_MAX;
return value;}
bool perf_event_paranoid_check(int max_level){ bool used_root;
if (perf_cap__capable(CAP_SYS_ADMIN, &used_root)) return true;
if (!used_root && perf_cap__capable(CAP_PERFMON, &used_root)) return true;
return perf_event_paranoid() <= max_level;}
int perf_tip(char **strp, const char *dirpath){ struct strlist *tips; struct str_node *node; struct strlist_config conf = { .dirname = dirpath, .file_only = true, }; int ret = 0;
*strp = NULL; tips = strlist__new("tips.txt", &conf); if (tips == NULL) return -errno;
if (strlist__nr_entries(tips) == 0) goto out;
node = strlist__entry(tips, random() % strlist__nr_entries(tips)); if (asprintf(strp, "Tip: %s", node->s) < 0) ret = -ENOMEM;
out: strlist__delete(tips);
return ret;}
char *perf_exe(char *buf, int len){ int n;
if (len <= 0) return buf;
n = readlink("/proc/self/exe", buf, len - 1); if (n > 0) { buf[n] = 0; return buf; } if (len < (int)sizeof("perf")) { buf[0] = '\0'; return buf; }
return strcpy(buf, "perf");}
void perf_debuginfod_setup(struct perf_debuginfod *di){ /* * By default '!di->set' we clear DEBUGINFOD_URLS, so debuginfod * processing is not triggered, otherwise we set it to 'di->urls' * value. If 'di->urls' is "system" we keep DEBUGINFOD_URLS value. */ if (!di->set) setenv("DEBUGINFOD_URLS", "", 1); else if (di->urls && strcmp(di->urls, "system")) setenv("DEBUGINFOD_URLS", di->urls, 1);
pr_debug("DEBUGINFOD_URLS=%s\n", getenv("DEBUGINFOD_URLS"));
#ifndef HAVE_DEBUGINFOD_SUPPORT if (di->set) pr_warning("WARNING: debuginfod support requested, but perf is not built with it\n");#endif}
/* * Return a new filename prepended with task's root directory if it's in * a chroot. Callers should free the returned string. */char *filename_with_chroot(int pid, const char *filename){ char buf[PATH_MAX]; char proc_root[32]; char *new_name = NULL; int ret;
scnprintf(proc_root, sizeof(proc_root), "/proc/%d/root", pid); ret = readlink(proc_root, buf, sizeof(buf) - 1); if (ret <= 0) return NULL;
/* readlink(2) does not append a null byte to buf */ buf[ret] = '\0';
if (!strcmp(buf, "/")) return NULL;
if (strstr(buf, "(deleted)")) return NULL;
if (asprintf(&new_name, "%s/%s", buf, filename) < 0) return NULL;
return new_name;}
/* * Reallocate an array *arr of size *arr_sz so that it is big enough to contain * x elements of size msz, initializing new entries to *init_val or zero if * init_val is NULL */int do_realloc_array_as_needed(void **arr, size_t *arr_sz, size_t x, size_t msz, const void *init_val){ size_t new_sz = *arr_sz; void *new_arr; size_t i;
if (!new_sz) new_sz = msz >= 64 ? 1 : roundup(64, msz); /* Start with at least 64 bytes */ while (x >= new_sz) { if (check_mul_overflow(new_sz, (size_t)2, &new_sz)) return -ENOMEM; } if (new_sz == *arr_sz) return 0; new_arr = calloc(new_sz, msz); if (!new_arr) return -ENOMEM; if (*arr_sz) memcpy(new_arr, *arr, *arr_sz * msz); if (init_val) { for (i = *arr_sz; i < new_sz; i++) memcpy(new_arr + (i * msz), init_val, msz); } *arr = new_arr; *arr_sz = new_sz; return 0;}
#ifndef HAVE_SCHED_GETCPU_SUPPORTint sched_getcpu(void){#ifdef __NR_getcpu unsigned int cpu; int err = syscall(__NR_getcpu, &cpu, NULL, NULL);
if (!err) return cpu;#else errno = ENOSYS;#endif return -1;}#endif
#ifndef HAVE_SCANDIRAT_SUPPORTint scandirat(int dirfd, const char *dirp, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)){ char path[PATH_MAX]; int err, fd = openat(dirfd, dirp, O_PATH);
if (fd < 0) return fd;
snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd); err = scandir(path, namelist, filter, compar); close(fd); return err;}#endif
/* basename version that takes a const input string */const char *perf_basename(const char *path){ const char *base = strrchr(path, '/');
return base ? base + 1 : path;}