--- zzzz-none-000/linux-3.10.107/tools/perf/util/util.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/tools/perf/util/util.c 2021-02-04 17:41:59.000000000 +0000 @@ -1,24 +1,40 @@ #include "../perf.h" #include "util.h" +#include "debug.h" +#include #include -#ifdef BACKTRACE_SUPPORT +#include +#ifdef HAVE_BACKTRACE_SUPPORT #include #endif #include #include +#include +#include +#include +#include +#include +#include +#include "callchain.h" + +struct callchain_param callchain_param = { + .mode = CHAIN_GRAPH_ABS, + .min_percent = 0.5, + .order = ORDER_CALLEE, + .key = CCKEY_FUNCTION +}; /* * XXX We need to find a better place for these things... */ unsigned int page_size; +int cacheline_size; bool test_attr__enabled; bool perf_host = true; bool perf_guest = false; -char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; - void event_attr_init(struct perf_event_attr *attr) { if (!perf_host) @@ -55,9 +71,52 @@ return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; } +int rm_rf(char *path) +{ + DIR *dir; + int ret = 0; + struct dirent *d; + char namebuf[PATH_MAX]; + + dir = opendir(path); + if (dir == NULL) + return 0; + + while ((d = readdir(dir)) != NULL && !ret) { + struct stat statbuf; + + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + scnprintf(namebuf, sizeof(namebuf), "%s/%s", + path, d->d_name); + + ret = stat(namebuf, &statbuf); + if (ret < 0) { + pr_debug("stat failed: %s\n", namebuf); + break; + } + + if (S_ISREG(statbuf.st_mode)) + ret = unlink(namebuf); + else if (S_ISDIR(statbuf.st_mode)) + ret = rm_rf(namebuf); + else { + pr_debug("unknown file: %s\n", namebuf); + ret = -1; + } + } + closedir(dir); + + if (ret < 0) + return ret; + + return rmdir(path); +} + static int slow_copyfile(const char *from, const char *to) { - int err = 0; + int err = -1; char *line = NULL; size_t n; FILE *from_fp = fopen(from, "r"), *to_fp; @@ -82,45 +141,89 @@ return err; } -int copyfile(const char *from, const char *to) +int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size) +{ + void *ptr; + loff_t pgoff; + + pgoff = off_in & ~(page_size - 1); + off_in -= pgoff; + + ptr = mmap(NULL, off_in + size, PROT_READ, MAP_PRIVATE, ifd, pgoff); + if (ptr == MAP_FAILED) + return -1; + + while (size) { + ssize_t ret = pwrite(ofd, ptr + off_in, size, off_out); + if (ret < 0 && errno == EINTR) + continue; + if (ret <= 0) + break; + + size -= ret; + off_in += ret; + off_out -= ret; + } + munmap(ptr, off_in + size); + + return size ? -1 : 0; +} + +int copyfile_mode(const char *from, const char *to, mode_t mode) { int fromfd, tofd; struct stat st; - void *addr; int err = -1; + char *tmp = NULL, *ptr = NULL; if (stat(from, &st)) goto out; - if (st.st_size == 0) /* /proc? do it slowly... */ - return slow_copyfile(from, to); - - fromfd = open(from, O_RDONLY); - if (fromfd < 0) + /* extra 'x' at the end is to reserve space for '.' */ + if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) { + tmp = NULL; + goto out; + } + ptr = strrchr(tmp, '/'); + if (!ptr) goto out; + ptr = memmove(ptr + 1, ptr, strlen(ptr) - 1); + *ptr = '.'; - tofd = creat(to, 0755); + tofd = mkstemp(tmp); if (tofd < 0) - goto out_close_from; + goto out; - addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0); - if (addr == MAP_FAILED) + if (fchmod(tofd, mode)) goto out_close_to; - if (write(tofd, addr, st.st_size) == st.st_size) - err = 0; + if (st.st_size == 0) { /* /proc? do it slowly... */ + err = slow_copyfile(from, tmp); + goto out_close_to; + } - munmap(addr, st.st_size); + fromfd = open(from, O_RDONLY); + if (fromfd < 0) + goto out_close_to; + + err = copyfile_offset(fromfd, 0, tofd, 0, st.st_size); + + close(fromfd); out_close_to: close(tofd); - if (err) - unlink(to); -out_close_from: - close(fromfd); + if (!err) + err = link(tmp, to); + unlink(tmp); out: + free(tmp); return err; } +int copyfile(const char *from, const char *to) +{ + return copyfile_mode(from, to, 0755); +} + unsigned long convert_unit(unsigned long value, char *unit) { *unit = ' '; @@ -143,21 +246,42 @@ return value; } -int readn(int fd, void *buf, size_t n) +static ssize_t ion(bool is_read, int fd, void *buf, size_t n) { void *buf_start = buf; + size_t left = n; - while (n) { - int ret = read(fd, buf, n); + while (left) { + ssize_t ret = is_read ? read(fd, buf, left) : + write(fd, buf, left); + if (ret < 0 && errno == EINTR) + continue; if (ret <= 0) return ret; - n -= ret; - buf += ret; + left -= ret; + buf += ret; } - return buf - buf_start; + BUG_ON((size_t)(buf - buf_start) != n); + return n; +} + +/* + * Read exactly 'n' bytes or return an error. + */ +ssize_t readn(int fd, void *buf, size_t n) +{ + return ion(true, fd, buf, n); +} + +/* + * Write exactly 'n' bytes or return an error. + */ +ssize_t writen(int fd, void *buf, size_t n) +{ + return ion(false, fd, buf, n); } size_t hex_width(u64 v) @@ -204,7 +328,7 @@ } /* Obtain a backtrace and print it to stdout. */ -#ifdef BACKTRACE_SUPPORT +#ifdef HAVE_BACKTRACE_SUPPORT void dump_stack(void) { void *array[16]; @@ -223,6 +347,13 @@ void dump_stack(void) {} #endif +void sighandler_dump_stack(int sig) +{ + psignal(sig, "perf"); + dump_stack(); + exit(sig); +} + void get_term_dimensions(struct winsize *ws) { char *s = getenv("LINES"); @@ -245,27 +376,322 @@ ws->ws_col = 80; } -static void set_tracing_events_path(const char *mountpoint) +void set_term_quiet_input(struct termios *old) { - snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", - mountpoint, "tracing/events"); + struct termios tc; + + tcgetattr(0, old); + tc = *old; + tc.c_lflag &= ~(ICANON | ECHO); + tc.c_cc[VMIN] = 0; + tc.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &tc); } -const char *perf_debugfs_mount(const char *mountpoint) +int parse_nsec_time(const char *str, u64 *ptime) { - const char *mnt; + u64 time_sec, time_nsec; + char *end; + + time_sec = strtoul(str, &end, 10); + if (*end != '.' && *end != '\0') + return -1; + + if (*end == '.') { + int i; + char nsec_buf[10]; + + if (strlen(++end) > 9) + return -1; - mnt = debugfs_mount(mountpoint); - if (!mnt) - return NULL; + strncpy(nsec_buf, end, 9); + nsec_buf[9] = '\0'; - set_tracing_events_path(mnt); + /* make it nsec precision */ + for (i = strlen(nsec_buf); i < 9; i++) + nsec_buf[i] = '0'; - return mnt; + time_nsec = strtoul(nsec_buf, &end, 10); + if (*end != '\0') + return -1; + } else + time_nsec = 0; + + *ptime = time_sec * NSEC_PER_SEC + time_nsec; + return 0; } -void perf_debugfs_set_path(const char *mntpt) +unsigned long parse_tag_value(const char *str, struct parse_tag *tags) { - snprintf(debugfs_mountpoint, strlen(debugfs_mountpoint), "%s", mntpt); - set_tracing_events_path(mntpt); + struct parse_tag *i = tags; + + while (i->tag) { + char *s; + + s = strchr(str, i->tag); + if (s) { + unsigned long int value; + char *endptr; + + value = strtoul(str, &endptr, 10); + if (s != endptr) + break; + + if (value > ULONG_MAX / i->mult) + break; + value *= i->mult; + return value; + } + i++; + } + + return (unsigned long) -1; +} + +int get_stack_size(const char *str, unsigned long *_size) +{ + char *endptr; + unsigned long size; + unsigned long max_size = round_down(USHRT_MAX, sizeof(u64)); + + size = strtoul(str, &endptr, 0); + + do { + if (*endptr) + break; + + size = round_up(size, sizeof(u64)); + if (!size || size > max_size) + break; + + *_size = size; + return 0; + + } while (0); + + pr_err("callchain: Incorrect stack dump size (max %ld): %s\n", + max_size, str); + return -1; +} + +int parse_callchain_record(const char *arg, struct callchain_param *param) +{ + char *tok, *name, *saveptr = NULL; + char *buf; + int ret = -1; + + /* We need buffer that we know we can write to. */ + buf = malloc(strlen(arg) + 1); + if (!buf) + return -ENOMEM; + + strcpy(buf, arg); + + tok = strtok_r((char *)buf, ",", &saveptr); + name = tok ? : (char *)buf; + + do { + /* Framepointer style */ + if (!strncmp(name, "fp", sizeof("fp"))) { + if (!strtok_r(NULL, ",", &saveptr)) { + param->record_mode = CALLCHAIN_FP; + ret = 0; + } else + pr_err("callchain: No more arguments " + "needed for --call-graph fp\n"); + break; + +#ifdef HAVE_DWARF_UNWIND_SUPPORT + /* Dwarf style */ + } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { + const unsigned long default_stack_dump_size = 8192; + + ret = 0; + param->record_mode = CALLCHAIN_DWARF; + param->dump_size = default_stack_dump_size; + + tok = strtok_r(NULL, ",", &saveptr); + if (tok) { + unsigned long size = 0; + + ret = get_stack_size(tok, &size); + param->dump_size = size; + } +#endif /* HAVE_DWARF_UNWIND_SUPPORT */ + } else if (!strncmp(name, "lbr", sizeof("lbr"))) { + if (!strtok_r(NULL, ",", &saveptr)) { + param->record_mode = CALLCHAIN_LBR; + ret = 0; + } else + pr_err("callchain: No more arguments " + "needed for --call-graph lbr\n"); + break; + } else { + pr_err("callchain: Unknown --call-graph option " + "value: %s\n", arg); + break; + } + + } while (0); + + free(buf); + return ret; +} + +int filename__read_str(const char *filename, char **buf, size_t *sizep) +{ + size_t size = 0, alloc_size = 0; + void *bf = NULL, *nbf; + int fd, n, err = 0; + char sbuf[STRERR_BUFSIZE]; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return -errno; + + do { + if (size == alloc_size) { + alloc_size += BUFSIZ; + nbf = realloc(bf, alloc_size); + if (!nbf) { + err = -ENOMEM; + break; + } + + bf = nbf; + } + + n = read(fd, bf + size, alloc_size - size); + if (n < 0) { + if (size) { + pr_warning("read failed %d: %s\n", errno, + strerror_r(errno, sbuf, sizeof(sbuf))); + err = 0; + } else + err = -errno; + + break; + } + + size += n; + } while (n > 0); + + if (!err) { + *sizep = size; + *buf = bf; + } else + free(bf); + + close(fd); + return err; +} + +const char *get_filename_for_perf_kvm(void) +{ + const char *filename; + + if (perf_host && !perf_guest) + filename = strdup("perf.data.host"); + else if (!perf_host && perf_guest) + filename = strdup("perf.data.guest"); + else + filename = strdup("perf.data.kvm"); + + return filename; +} + +int perf_event_paranoid(void) +{ + int value; + + if (sysctl__read_int("kernel/perf_event_paranoid", &value)) + return INT_MAX; + + return value; +} + +void mem_bswap_32(void *src, int byte_size) +{ + u32 *m = src; + while (byte_size > 0) { + *m = bswap_32(*m); + byte_size -= sizeof(u32); + ++m; + } +} + +void mem_bswap_64(void *src, int byte_size) +{ + u64 *m = src; + + while (byte_size > 0) { + *m = bswap_64(*m); + byte_size -= sizeof(u64); + ++m; + } +} + +bool find_process(const char *name) +{ + size_t len = strlen(name); + DIR *dir; + struct dirent *d; + int ret = -1; + + dir = opendir(procfs__mountpoint()); + if (!dir) + return false; + + /* Walk through the directory. */ + while (ret && (d = readdir(dir)) != NULL) { + char path[PATH_MAX]; + char *data; + size_t size; + + if ((d->d_type != DT_DIR) || + !strcmp(".", d->d_name) || + !strcmp("..", d->d_name)) + continue; + + scnprintf(path, sizeof(path), "%s/%s/comm", + procfs__mountpoint(), d->d_name); + + if (filename__read_str(path, &data, &size)) + continue; + + ret = strncmp(name, data, len); + free(data); + } + + closedir(dir); + return ret ? false : true; +} + +int +fetch_kernel_version(unsigned int *puint, char *str, + size_t str_size) +{ + struct utsname utsname; + int version, patchlevel, sublevel, err; + + if (uname(&utsname)) + return -1; + + if (str && str_size) { + strncpy(str, utsname.release, str_size); + str[str_size - 1] = '\0'; + } + + err = sscanf(utsname.release, "%d.%d.%d", + &version, &patchlevel, &sublevel); + + if (err != 3) { + pr_debug("Unablt to get kernel version from uname '%s'\n", + utsname.release); + return -1; + } + + if (puint) + *puint = (version << 16) + (patchlevel << 8) + sublevel; + return 0; }