linux/tools/perf/util/data.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/compiler.h>
   3#include <linux/kernel.h>
   4#include <sys/types.h>
   5#include <sys/stat.h>
   6#include <errno.h>
   7#include <fcntl.h>
   8#include <unistd.h>
   9#include <string.h>
  10#include <asm/bug.h>
  11#include <sys/types.h>
  12#include <dirent.h>
  13
  14#include "data.h"
  15#include "util.h"
  16#include "debug.h"
  17#include "header.h"
  18
  19static void close_dir(struct perf_data_file *files, int nr)
  20{
  21        while (--nr >= 1) {
  22                close(files[nr].fd);
  23                free(files[nr].path);
  24        }
  25        free(files);
  26}
  27
  28void perf_data__close_dir(struct perf_data *data)
  29{
  30        close_dir(data->dir.files, data->dir.nr);
  31}
  32
  33int perf_data__create_dir(struct perf_data *data, int nr)
  34{
  35        struct perf_data_file *files = NULL;
  36        int i, ret = -1;
  37
  38        if (WARN_ON(!data->is_dir))
  39                return -EINVAL;
  40
  41        files = zalloc(nr * sizeof(*files));
  42        if (!files)
  43                return -ENOMEM;
  44
  45        data->dir.version = PERF_DIR_VERSION;
  46        data->dir.files   = files;
  47        data->dir.nr      = nr;
  48
  49        for (i = 0; i < nr; i++) {
  50                struct perf_data_file *file = &files[i];
  51
  52                if (asprintf(&file->path, "%s/data.%d", data->path, i) < 0)
  53                        goto out_err;
  54
  55                ret = open(file->path, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
  56                if (ret < 0)
  57                        goto out_err;
  58
  59                file->fd = ret;
  60        }
  61
  62        return 0;
  63
  64out_err:
  65        close_dir(files, i);
  66        return ret;
  67}
  68
  69int perf_data__open_dir(struct perf_data *data)
  70{
  71        struct perf_data_file *files = NULL;
  72        struct dirent *dent;
  73        int ret = -1;
  74        DIR *dir;
  75        int nr = 0;
  76
  77        if (WARN_ON(!data->is_dir))
  78                return -EINVAL;
  79
  80        /* The version is provided by DIR_FORMAT feature. */
  81        if (WARN_ON(data->dir.version != PERF_DIR_VERSION))
  82                return -1;
  83
  84        dir = opendir(data->path);
  85        if (!dir)
  86                return -EINVAL;
  87
  88        while ((dent = readdir(dir)) != NULL) {
  89                struct perf_data_file *file;
  90                char path[PATH_MAX];
  91                struct stat st;
  92
  93                snprintf(path, sizeof(path), "%s/%s", data->path, dent->d_name);
  94                if (stat(path, &st))
  95                        continue;
  96
  97                if (!S_ISREG(st.st_mode) || strncmp(dent->d_name, "data", 4))
  98                        continue;
  99
 100                ret = -ENOMEM;
 101
 102                file = realloc(files, (nr + 1) * sizeof(*files));
 103                if (!file)
 104                        goto out_err;
 105
 106                files = file;
 107                file = &files[nr++];
 108
 109                file->path = strdup(path);
 110                if (!file->path)
 111                        goto out_err;
 112
 113                ret = open(file->path, O_RDONLY);
 114                if (ret < 0)
 115                        goto out_err;
 116
 117                file->fd = ret;
 118                file->size = st.st_size;
 119        }
 120
 121        if (!files)
 122                return -EINVAL;
 123
 124        data->dir.files = files;
 125        data->dir.nr    = nr;
 126        return 0;
 127
 128out_err:
 129        close_dir(files, nr);
 130        return ret;
 131}
 132
 133int perf_data__update_dir(struct perf_data *data)
 134{
 135        int i;
 136
 137        if (WARN_ON(!data->is_dir))
 138                return -EINVAL;
 139
 140        for (i = 0; i < data->dir.nr; i++) {
 141                struct perf_data_file *file = &data->dir.files[i];
 142                struct stat st;
 143
 144                if (fstat(file->fd, &st))
 145                        return -1;
 146
 147                file->size = st.st_size;
 148        }
 149
 150        return 0;
 151}
 152
 153static bool check_pipe(struct perf_data *data)
 154{
 155        struct stat st;
 156        bool is_pipe = false;
 157        int fd = perf_data__is_read(data) ?
 158                 STDIN_FILENO : STDOUT_FILENO;
 159
 160        if (!data->path) {
 161                if (!fstat(fd, &st) && S_ISFIFO(st.st_mode))
 162                        is_pipe = true;
 163        } else {
 164                if (!strcmp(data->path, "-"))
 165                        is_pipe = true;
 166        }
 167
 168        if (is_pipe)
 169                data->file.fd = fd;
 170
 171        return data->is_pipe = is_pipe;
 172}
 173
 174static int check_backup(struct perf_data *data)
 175{
 176        struct stat st;
 177
 178        if (perf_data__is_read(data))
 179                return 0;
 180
 181        if (!stat(data->path, &st) && st.st_size) {
 182                char oldname[PATH_MAX];
 183                int ret;
 184
 185                snprintf(oldname, sizeof(oldname), "%s.old",
 186                         data->path);
 187
 188                ret = rm_rf_perf_data(oldname);
 189                if (ret) {
 190                        pr_err("Can't remove old data: %s (%s)\n",
 191                               ret == -2 ?
 192                               "Unknown file found" : strerror(errno),
 193                               oldname);
 194                        return -1;
 195                }
 196
 197                if (rename(data->path, oldname)) {
 198                        pr_err("Can't move data: %s (%s to %s)\n",
 199                               strerror(errno),
 200                               data->path, oldname);
 201                        return -1;
 202                }
 203        }
 204
 205        return 0;
 206}
 207
 208static bool is_dir(struct perf_data *data)
 209{
 210        struct stat st;
 211
 212        if (stat(data->path, &st))
 213                return false;
 214
 215        return (st.st_mode & S_IFMT) == S_IFDIR;
 216}
 217
 218static int open_file_read(struct perf_data *data)
 219{
 220        struct stat st;
 221        int fd;
 222        char sbuf[STRERR_BUFSIZE];
 223
 224        fd = open(data->file.path, O_RDONLY);
 225        if (fd < 0) {
 226                int err = errno;
 227
 228                pr_err("failed to open %s: %s", data->file.path,
 229                        str_error_r(err, sbuf, sizeof(sbuf)));
 230                if (err == ENOENT && !strcmp(data->file.path, "perf.data"))
 231                        pr_err("  (try 'perf record' first)");
 232                pr_err("\n");
 233                return -err;
 234        }
 235
 236        if (fstat(fd, &st) < 0)
 237                goto out_close;
 238
 239        if (!data->force && st.st_uid && (st.st_uid != geteuid())) {
 240                pr_err("File %s not owned by current user or root (use -f to override)\n",
 241                       data->file.path);
 242                goto out_close;
 243        }
 244
 245        if (!st.st_size) {
 246                pr_info("zero-sized data (%s), nothing to do!\n",
 247                        data->file.path);
 248                goto out_close;
 249        }
 250
 251        data->file.size = st.st_size;
 252        return fd;
 253
 254 out_close:
 255        close(fd);
 256        return -1;
 257}
 258
 259static int open_file_write(struct perf_data *data)
 260{
 261        int fd;
 262        char sbuf[STRERR_BUFSIZE];
 263
 264        fd = open(data->file.path, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC,
 265                  S_IRUSR|S_IWUSR);
 266
 267        if (fd < 0)
 268                pr_err("failed to open %s : %s\n", data->file.path,
 269                        str_error_r(errno, sbuf, sizeof(sbuf)));
 270
 271        return fd;
 272}
 273
 274static int open_file(struct perf_data *data)
 275{
 276        int fd;
 277
 278        fd = perf_data__is_read(data) ?
 279             open_file_read(data) : open_file_write(data);
 280
 281        if (fd < 0) {
 282                zfree(&data->file.path);
 283                return -1;
 284        }
 285
 286        data->file.fd = fd;
 287        return 0;
 288}
 289
 290static int open_file_dup(struct perf_data *data)
 291{
 292        data->file.path = strdup(data->path);
 293        if (!data->file.path)
 294                return -ENOMEM;
 295
 296        return open_file(data);
 297}
 298
 299static int open_dir(struct perf_data *data)
 300{
 301        int ret;
 302
 303        /*
 304         * So far we open only the header, so we can read the data version and
 305         * layout.
 306         */
 307        if (asprintf(&data->file.path, "%s/header", data->path) < 0)
 308                return -1;
 309
 310        if (perf_data__is_write(data) &&
 311            mkdir(data->path, S_IRWXU) < 0)
 312                return -1;
 313
 314        ret = open_file(data);
 315
 316        /* Cleanup whatever we managed to create so far. */
 317        if (ret && perf_data__is_write(data))
 318                rm_rf_perf_data(data->path);
 319
 320        return ret;
 321}
 322
 323int perf_data__open(struct perf_data *data)
 324{
 325        if (check_pipe(data))
 326                return 0;
 327
 328        if (!data->path)
 329                data->path = "perf.data";
 330
 331        if (check_backup(data))
 332                return -1;
 333
 334        if (perf_data__is_read(data))
 335                data->is_dir = is_dir(data);
 336
 337        return perf_data__is_dir(data) ?
 338               open_dir(data) : open_file_dup(data);
 339}
 340
 341void perf_data__close(struct perf_data *data)
 342{
 343        if (perf_data__is_dir(data))
 344                perf_data__close_dir(data);
 345
 346        zfree(&data->file.path);
 347        close(data->file.fd);
 348}
 349
 350ssize_t perf_data_file__write(struct perf_data_file *file,
 351                              void *buf, size_t size)
 352{
 353        return writen(file->fd, buf, size);
 354}
 355
 356ssize_t perf_data__write(struct perf_data *data,
 357                              void *buf, size_t size)
 358{
 359        return perf_data_file__write(&data->file, buf, size);
 360}
 361
 362int perf_data__switch(struct perf_data *data,
 363                           const char *postfix,
 364                           size_t pos, bool at_exit,
 365                           char **new_filepath)
 366{
 367        int ret;
 368
 369        if (check_pipe(data))
 370                return -EINVAL;
 371        if (perf_data__is_read(data))
 372                return -EINVAL;
 373
 374        if (asprintf(new_filepath, "%s.%s", data->path, postfix) < 0)
 375                return -ENOMEM;
 376
 377        /*
 378         * Only fire a warning, don't return error, continue fill
 379         * original file.
 380         */
 381        if (rename(data->path, *new_filepath))
 382                pr_warning("Failed to rename %s to %s\n", data->path, *new_filepath);
 383
 384        if (!at_exit) {
 385                close(data->file.fd);
 386                ret = perf_data__open(data);
 387                if (ret < 0)
 388                        goto out;
 389
 390                if (lseek(data->file.fd, pos, SEEK_SET) == (off_t)-1) {
 391                        ret = -errno;
 392                        pr_debug("Failed to lseek to %zu: %s",
 393                                 pos, strerror(errno));
 394                        goto out;
 395                }
 396        }
 397        ret = data->file.fd;
 398out:
 399        return ret;
 400}
 401
 402unsigned long perf_data__size(struct perf_data *data)
 403{
 404        u64 size = data->file.size;
 405        int i;
 406
 407        if (!data->is_dir)
 408                return size;
 409
 410        for (i = 0; i < data->dir.nr; i++) {
 411                struct perf_data_file *file = &data->dir.files[i];
 412
 413                size += file->size;
 414        }
 415
 416        return size;
 417}
 418