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