linux/tools/perf/util/util.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include "../perf.h"
   3#include "util.h"
   4#include "debug.h"
   5#include <api/fs/fs.h>
   6#include <sys/mman.h>
   7#include <sys/stat.h>
   8#include <sys/utsname.h>
   9#include <dirent.h>
  10#include <fcntl.h>
  11#include <inttypes.h>
  12#include <signal.h>
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <string.h>
  16#include <errno.h>
  17#include <limits.h>
  18#include <linux/kernel.h>
  19#include <linux/log2.h>
  20#include <linux/time64.h>
  21#include <unistd.h>
  22#include "strlist.h"
  23
  24/*
  25 * XXX We need to find a better place for these things...
  26 */
  27
  28bool perf_singlethreaded = true;
  29
  30void perf_set_singlethreaded(void)
  31{
  32        perf_singlethreaded = true;
  33}
  34
  35void perf_set_multithreaded(void)
  36{
  37        perf_singlethreaded = false;
  38}
  39
  40unsigned int page_size;
  41int cacheline_size;
  42
  43int sysctl_perf_event_max_stack = PERF_MAX_STACK_DEPTH;
  44int sysctl_perf_event_max_contexts_per_stack = PERF_MAX_CONTEXTS_PER_STACK;
  45
  46bool test_attr__enabled;
  47
  48bool perf_host  = true;
  49bool perf_guest = false;
  50
  51void event_attr_init(struct perf_event_attr *attr)
  52{
  53        if (!perf_host)
  54                attr->exclude_host  = 1;
  55        if (!perf_guest)
  56                attr->exclude_guest = 1;
  57        /* to capture ABI version */
  58        attr->size = sizeof(*attr);
  59}
  60
  61int mkdir_p(char *path, mode_t mode)
  62{
  63        struct stat st;
  64        int err;
  65        char *d = path;
  66
  67        if (*d != '/')
  68                return -1;
  69
  70        if (stat(path, &st) == 0)
  71                return 0;
  72
  73        while (*++d == '/');
  74
  75        while ((d = strchr(d, '/'))) {
  76                *d = '\0';
  77                err = stat(path, &st) && mkdir(path, mode);
  78                *d++ = '/';
  79                if (err)
  80                        return -1;
  81                while (*d == '/')
  82                        ++d;
  83        }
  84        return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;
  85}
  86
  87int rm_rf(const char *path)
  88{
  89        DIR *dir;
  90        int ret = 0;
  91        struct dirent *d;
  92        char namebuf[PATH_MAX];
  93
  94        dir = opendir(path);
  95        if (dir == NULL)
  96                return 0;
  97
  98        while ((d = readdir(dir)) != NULL && !ret) {
  99                struct stat statbuf;
 100
 101                if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
 102                        continue;
 103
 104                scnprintf(namebuf, sizeof(namebuf), "%s/%s",
 105                          path, d->d_name);
 106
 107                /* We have to check symbolic link itself */
 108                ret = lstat(namebuf, &statbuf);
 109                if (ret < 0) {
 110                        pr_debug("stat failed: %s\n", namebuf);
 111                        break;
 112                }
 113
 114                if (S_ISDIR(statbuf.st_mode))
 115                        ret = rm_rf(namebuf);
 116                else
 117                        ret = unlink(namebuf);
 118        }
 119        closedir(dir);
 120
 121        if (ret < 0)
 122                return ret;
 123
 124        return rmdir(path);
 125}
 126
 127/* A filter which removes dot files */
 128bool lsdir_no_dot_filter(const char *name __maybe_unused, struct dirent *d)
 129{
 130        return d->d_name[0] != '.';
 131}
 132
 133/* lsdir reads a directory and store it in strlist */
 134struct strlist *lsdir(const char *name,
 135                      bool (*filter)(const char *, struct dirent *))
 136{
 137        struct strlist *list = NULL;
 138        DIR *dir;
 139        struct dirent *d;
 140
 141        dir = opendir(name);
 142        if (!dir)
 143                return NULL;
 144
 145        list = strlist__new(NULL, NULL);
 146        if (!list) {
 147                errno = ENOMEM;
 148                goto out;
 149        }
 150
 151        while ((d = readdir(dir)) != NULL) {
 152                if (!filter || filter(name, d))
 153                        strlist__add(list, d->d_name);
 154        }
 155
 156out:
 157        closedir(dir);
 158        return list;
 159}
 160
 161static int slow_copyfile(const char *from, const char *to, struct nsinfo *nsi)
 162{
 163        int err = -1;
 164        char *line = NULL;
 165        size_t n;
 166        FILE *from_fp, *to_fp;
 167        struct nscookie nsc;
 168
 169        nsinfo__mountns_enter(nsi, &nsc);
 170        from_fp = fopen(from, "r");
 171        nsinfo__mountns_exit(&nsc);
 172        if (from_fp == NULL)
 173                goto out;
 174
 175        to_fp = fopen(to, "w");
 176        if (to_fp == NULL)
 177                goto out_fclose_from;
 178
 179        while (getline(&line, &n, from_fp) > 0)
 180                if (fputs(line, to_fp) == EOF)
 181                        goto out_fclose_to;
 182        err = 0;
 183out_fclose_to:
 184        fclose(to_fp);
 185        free(line);
 186out_fclose_from:
 187        fclose(from_fp);
 188out:
 189        return err;
 190}
 191
 192static int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size)
 193{
 194        void *ptr;
 195        loff_t pgoff;
 196
 197        pgoff = off_in & ~(page_size - 1);
 198        off_in -= pgoff;
 199
 200        ptr = mmap(NULL, off_in + size, PROT_READ, MAP_PRIVATE, ifd, pgoff);
 201        if (ptr == MAP_FAILED)
 202                return -1;
 203
 204        while (size) {
 205                ssize_t ret = pwrite(ofd, ptr + off_in, size, off_out);
 206                if (ret < 0 && errno == EINTR)
 207                        continue;
 208                if (ret <= 0)
 209                        break;
 210
 211                size -= ret;
 212                off_in += ret;
 213                off_out += ret;
 214        }
 215        munmap(ptr, off_in + size);
 216
 217        return size ? -1 : 0;
 218}
 219
 220static int copyfile_mode_ns(const char *from, const char *to, mode_t mode,
 221                            struct nsinfo *nsi)
 222{
 223        int fromfd, tofd;
 224        struct stat st;
 225        int err;
 226        char *tmp = NULL, *ptr = NULL;
 227        struct nscookie nsc;
 228
 229        nsinfo__mountns_enter(nsi, &nsc);
 230        err = stat(from, &st);
 231        nsinfo__mountns_exit(&nsc);
 232        if (err)
 233                goto out;
 234        err = -1;
 235
 236        /* extra 'x' at the end is to reserve space for '.' */
 237        if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) {
 238                tmp = NULL;
 239                goto out;
 240        }
 241        ptr = strrchr(tmp, '/');
 242        if (!ptr)
 243                goto out;
 244        ptr = memmove(ptr + 1, ptr, strlen(ptr) - 1);
 245        *ptr = '.';
 246
 247        tofd = mkstemp(tmp);
 248        if (tofd < 0)
 249                goto out;
 250
 251        if (fchmod(tofd, mode))
 252                goto out_close_to;
 253
 254        if (st.st_size == 0) { /* /proc? do it slowly... */
 255                err = slow_copyfile(from, tmp, nsi);
 256                goto out_close_to;
 257        }
 258
 259        nsinfo__mountns_enter(nsi, &nsc);
 260        fromfd = open(from, O_RDONLY);
 261        nsinfo__mountns_exit(&nsc);
 262        if (fromfd < 0)
 263                goto out_close_to;
 264
 265        err = copyfile_offset(fromfd, 0, tofd, 0, st.st_size);
 266
 267        close(fromfd);
 268out_close_to:
 269        close(tofd);
 270        if (!err)
 271                err = link(tmp, to);
 272        unlink(tmp);
 273out:
 274        free(tmp);
 275        return err;
 276}
 277
 278int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi)
 279{
 280        return copyfile_mode_ns(from, to, 0755, nsi);
 281}
 282
 283int copyfile_mode(const char *from, const char *to, mode_t mode)
 284{
 285        return copyfile_mode_ns(from, to, mode, NULL);
 286}
 287
 288int copyfile(const char *from, const char *to)
 289{
 290        return copyfile_mode(from, to, 0755);
 291}
 292
 293static ssize_t ion(bool is_read, int fd, void *buf, size_t n)
 294{
 295        void *buf_start = buf;
 296        size_t left = n;
 297
 298        while (left) {
 299                /* buf must be treated as const if !is_read. */
 300                ssize_t ret = is_read ? read(fd, buf, left) :
 301                                        write(fd, buf, left);
 302
 303                if (ret < 0 && errno == EINTR)
 304                        continue;
 305                if (ret <= 0)
 306                        return ret;
 307
 308                left -= ret;
 309                buf  += ret;
 310        }
 311
 312        BUG_ON((size_t)(buf - buf_start) != n);
 313        return n;
 314}
 315
 316/*
 317 * Read exactly 'n' bytes or return an error.
 318 */
 319ssize_t readn(int fd, void *buf, size_t n)
 320{
 321        return ion(true, fd, buf, n);
 322}
 323
 324/*
 325 * Write exactly 'n' bytes or return an error.
 326 */
 327ssize_t writen(int fd, const void *buf, size_t n)
 328{
 329        /* ion does not modify buf. */
 330        return ion(false, fd, (void *)buf, n);
 331}
 332
 333size_t hex_width(u64 v)
 334{
 335        size_t n = 1;
 336
 337        while ((v >>= 4))
 338                ++n;
 339
 340        return n;
 341}
 342
 343/*
 344 * While we find nice hex chars, build a long_val.
 345 * Return number of chars processed.
 346 */
 347int hex2u64(const char *ptr, u64 *long_val)
 348{
 349        char *p;
 350
 351        *long_val = strtoull(ptr, &p, 16);
 352
 353        return p - ptr;
 354}
 355
 356int perf_event_paranoid(void)
 357{
 358        int value;
 359
 360        if (sysctl__read_int("kernel/perf_event_paranoid", &value))
 361                return INT_MAX;
 362
 363        return value;
 364}
 365static int
 366fetch_ubuntu_kernel_version(unsigned int *puint)
 367{
 368        ssize_t len;
 369        size_t line_len = 0;
 370        char *ptr, *line = NULL;
 371        int version, patchlevel, sublevel, err;
 372        FILE *vsig;
 373
 374        if (!puint)
 375                return 0;
 376
 377        vsig = fopen("/proc/version_signature", "r");
 378        if (!vsig) {
 379                pr_debug("Open /proc/version_signature failed: %s\n",
 380                         strerror(errno));
 381                return -1;
 382        }
 383
 384        len = getline(&line, &line_len, vsig);
 385        fclose(vsig);
 386        err = -1;
 387        if (len <= 0) {
 388                pr_debug("Reading from /proc/version_signature failed: %s\n",
 389                         strerror(errno));
 390                goto errout;
 391        }
 392
 393        ptr = strrchr(line, ' ');
 394        if (!ptr) {
 395                pr_debug("Parsing /proc/version_signature failed: %s\n", line);
 396                goto errout;
 397        }
 398
 399        err = sscanf(ptr + 1, "%d.%d.%d",
 400                     &version, &patchlevel, &sublevel);
 401        if (err != 3) {
 402                pr_debug("Unable to get kernel version from /proc/version_signature '%s'\n",
 403                         line);
 404                goto errout;
 405        }
 406
 407        *puint = (version << 16) + (patchlevel << 8) + sublevel;
 408        err = 0;
 409errout:
 410        free(line);
 411        return err;
 412}
 413
 414int
 415fetch_kernel_version(unsigned int *puint, char *str,
 416                     size_t str_size)
 417{
 418        struct utsname utsname;
 419        int version, patchlevel, sublevel, err;
 420        bool int_ver_ready = false;
 421
 422        if (access("/proc/version_signature", R_OK) == 0)
 423                if (!fetch_ubuntu_kernel_version(puint))
 424                        int_ver_ready = true;
 425
 426        if (uname(&utsname))
 427                return -1;
 428
 429        if (str && str_size) {
 430                strncpy(str, utsname.release, str_size);
 431                str[str_size - 1] = '\0';
 432        }
 433
 434        if (!puint || int_ver_ready)
 435                return 0;
 436
 437        err = sscanf(utsname.release, "%d.%d.%d",
 438                     &version, &patchlevel, &sublevel);
 439
 440        if (err != 3) {
 441                pr_debug("Unable to get kernel version from uname '%s'\n",
 442                         utsname.release);
 443                return -1;
 444        }
 445
 446        *puint = (version << 16) + (patchlevel << 8) + sublevel;
 447        return 0;
 448}
 449
 450const char *perf_tip(const char *dirpath)
 451{
 452        struct strlist *tips;
 453        struct str_node *node;
 454        char *tip = NULL;
 455        struct strlist_config conf = {
 456                .dirname = dirpath,
 457                .file_only = true,
 458        };
 459
 460        tips = strlist__new("tips.txt", &conf);
 461        if (tips == NULL)
 462                return errno == ENOENT ? NULL :
 463                        "Tip: check path of tips.txt or get more memory! ;-p";
 464
 465        if (strlist__nr_entries(tips) == 0)
 466                goto out;
 467
 468        node = strlist__entry(tips, random() % strlist__nr_entries(tips));
 469        if (asprintf(&tip, "Tip: %s", node->s) < 0)
 470                tip = (char *)"Tip: get more memory! ;-)";
 471
 472out:
 473        strlist__delete(tips);
 474
 475        return tip;
 476}
 477