1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
4 *
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <fcntl.h>
9 #include <time.h>
10 #include <poll.h>
11 #include <unistd.h>
12 #include <errno.h>
13
14 #include "tracefs.h"
15 #include "trace-cmd-private.h"
16 #include "trace-cmd-local.h"
17 #include "event-utils.h"
18
19 /* F_GETPIPE_SZ was introduced in 2.6.35, older systems don't have it */
20 #ifndef F_GETPIPE_SZ
21 # define F_GETPIPE_SZ 1032 /* The Linux number for the option */
22 #endif
23
24 #ifndef SPLICE_F_MOVE
25 # define SPLICE_F_MOVE 1
26 # define SPLICE_F_NONBLOCK 2
27 # define SPLICE_F_MORE 4
28 # define SPLICE_F_GIFT 8
29 #endif
30
31 #define POLL_TIMEOUT_MS 1000
32
33 struct tracecmd_recorder {
34 struct tracefs_cpu *tcpu;
35 int fd;
36 int fd1;
37 int fd2;
38 int page_size;
39 int subbuf_size;
40 int cpu;
41 int stop;
42 int max;
43 int pages;
44 int count;
45 unsigned flags;
46 };
47
append_file(int size,int dst,int src)48 static int append_file(int size, int dst, int src)
49 {
50 char buf[size];
51 int r;
52
53 lseek(src, 0, SEEK_SET);
54
55 /* If there's an error, then we are pretty much screwed :-p */
56 do {
57 r = read(src, buf, size);
58 if (r < 0)
59 return r;
60 r = write(dst, buf, r);
61 if (r < 0)
62 return r;
63 } while (r);
64 return 0;
65 }
66
tracecmd_free_recorder(struct tracecmd_recorder * recorder)67 void tracecmd_free_recorder(struct tracecmd_recorder *recorder)
68 {
69 if (!recorder)
70 return;
71
72 if (recorder->max) {
73 /* Need to put everything into fd1 */
74 if (recorder->fd == recorder->fd1) {
75 int ret;
76 /*
77 * Crap, the older data is in fd2, and we need
78 * to append fd1 onto it, and then copy over to fd1
79 */
80 ret = append_file(recorder->page_size,
81 recorder->fd2, recorder->fd1);
82 /* Error on copying, then just keep fd1 */
83 if (ret) {
84 lseek(recorder->fd1, 0, SEEK_END);
85 goto close;
86 }
87 lseek(recorder->fd1, 0, SEEK_SET);
88 ftruncate(recorder->fd1, 0);
89 }
90 append_file(recorder->page_size, recorder->fd1, recorder->fd2);
91 }
92 close:
93 tracefs_cpu_close(recorder->tcpu);
94
95 if (recorder->fd1 >= 0)
96 close(recorder->fd1);
97
98 if (recorder->fd2 >= 0)
99 close(recorder->fd2);
100
101 free(recorder);
102 }
103
set_nonblock(struct tracecmd_recorder * recorder)104 static int set_nonblock(struct tracecmd_recorder *recorder)
105 {
106 return tracefs_cpu_stop(recorder->tcpu);
107 }
108
109 static struct tracecmd_recorder *
create_buffer_recorder_fd2(int fd,int fd2,int cpu,unsigned flags,struct tracefs_instance * instance,int maxkb,int tfd)110 create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
111 struct tracefs_instance *instance, int maxkb, int tfd)
112 {
113 struct tracecmd_recorder *recorder;
114 bool nonblock = false;
115
116 recorder = malloc(sizeof(*recorder));
117 if (!recorder)
118 return NULL;
119
120 recorder->flags = flags;
121
122 recorder->page_size = getpagesize();
123 if (maxkb) {
124 int kb_per_page = recorder->page_size >> 10;
125
126 if (!kb_per_page)
127 kb_per_page = 1;
128 recorder->max = maxkb / kb_per_page;
129 /* keep max half */
130 recorder->max >>= 1;
131 if (!recorder->max)
132 recorder->max = 1;
133 } else
134 recorder->max = 0;
135
136 recorder->count = 0;
137 recorder->pages = 0;
138
139 /* fd always points to what to write to */
140 recorder->fd = fd;
141 recorder->fd1 = fd;
142 recorder->fd2 = fd2;
143
144 if (recorder->flags & TRACECMD_RECORD_POLL)
145 nonblock = true;
146
147 if (tfd >= 0)
148 recorder->tcpu = tracefs_cpu_alloc_fd(tfd, recorder->page_size, nonblock);
149 else
150 recorder->tcpu = tracefs_cpu_open(instance, cpu, nonblock);
151
152 if (!recorder->tcpu)
153 goto out_free;
154
155 recorder->subbuf_size = tracefs_cpu_read_size(recorder->tcpu);
156 return recorder;
157
158 out_free:
159 tracecmd_free_recorder(recorder);
160 return NULL;
161 }
162
163 struct tracecmd_recorder *
tracecmd_create_buffer_recorder_fd2(int fd,int fd2,int cpu,unsigned flags,struct tracefs_instance * instance,int maxkb)164 tracecmd_create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
165 struct tracefs_instance *instance, int maxkb)
166 {
167 return create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb, -1);
168 }
169
170 struct tracecmd_recorder *
tracecmd_create_buffer_recorder_fd(int fd,int cpu,unsigned flags,struct tracefs_instance * instance)171 tracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, struct tracefs_instance *instance)
172 {
173 return tracecmd_create_buffer_recorder_fd2(fd, -1, cpu, flags, instance, 0);
174 }
175
176 static struct tracecmd_recorder *
__tracecmd_create_buffer_recorder(const char * file,int cpu,unsigned flags,struct tracefs_instance * instance,int tfd,int maxkb)177 __tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags,
178 struct tracefs_instance *instance, int tfd, int maxkb)
179 {
180 struct tracecmd_recorder *recorder;
181 int fd, fd2 = -1;
182 char *file2;
183
184 fd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
185 if (fd < 0)
186 return NULL;
187
188 if (maxkb) {
189 int len = strlen(file);
190
191 file2 = malloc(len + 3);
192 if (!file2)
193 return NULL;
194
195 sprintf(file2, "%s.1", file);
196
197 fd2 = open(file2, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
198 if (fd2 < 0) {
199 close(fd);
200 unlink(file);
201 free(file2);
202 return NULL;
203 }
204 }
205
206 recorder = create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb, tfd);
207 if (!recorder) {
208 close(fd);
209 unlink(file);
210 if (fd2 != -1)
211 close(fd2);
212 }
213
214 if (fd2 != -1) {
215 /* Unlink file2, we need to add everything to file at the end */
216 unlink(file2);
217 free(file2);
218 }
219
220 return recorder;
221 }
222
223 struct tracecmd_recorder *
tracecmd_create_buffer_recorder_maxkb(const char * file,int cpu,unsigned flags,struct tracefs_instance * instance,int maxkb)224 tracecmd_create_buffer_recorder_maxkb(const char *file, int cpu, unsigned flags,
225 struct tracefs_instance *instance, int maxkb)
226 {
227 struct tracecmd_recorder *recorder = NULL;
228 char *file2;
229 int len;
230 int fd;
231 int fd2;
232
233 if (!maxkb)
234 return tracecmd_create_buffer_recorder(file, cpu, flags, instance);
235
236 len = strlen(file);
237 file2 = malloc(len + 3);
238 if (!file2)
239 return NULL;
240
241 sprintf(file2, "%s.1", file);
242
243 fd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
244 if (fd < 0)
245 goto out;
246
247 fd2 = open(file2, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
248 if (fd2 < 0)
249 goto err;
250
251 recorder = tracecmd_create_buffer_recorder_fd2(fd, fd2, cpu, flags, instance, maxkb);
252 if (!recorder)
253 goto err2;
254 out:
255 /* Unlink file2, we need to add everything to file at the end */
256 unlink(file2);
257 free(file2);
258
259 return recorder;
260 err2:
261 close(fd2);
262 err:
263 close(fd);
264 unlink(file);
265 goto out;
266 }
267
268 struct tracecmd_recorder *
tracecmd_create_buffer_recorder(const char * file,int cpu,unsigned flags,struct tracefs_instance * instance)269 tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags,
270 struct tracefs_instance *instance)
271 {
272 return __tracecmd_create_buffer_recorder(file, cpu, flags, instance, -1, 0);
273 }
274
275 /**
276 * tracecmd_create_recorder_virt - Create a recorder reading tracing data
277 * from the trace_fd file descriptor instead of from the local tracefs
278 * @file: output filename where tracing data will be written
279 * @cpu: which CPU is being traced
280 * @flags: flags configuring the recorder (see TRACECMD_RECORDER_* enums)
281 * @trace_fd: file descriptor from where tracing data will be read
282 */
283 struct tracecmd_recorder *
tracecmd_create_recorder_virt(const char * file,int cpu,unsigned flags,int trace_fd,int maxkb)284 tracecmd_create_recorder_virt(const char *file, int cpu, unsigned flags,
285 int trace_fd, int maxkb)
286 {
287 return __tracecmd_create_buffer_recorder(file, cpu, flags, NULL, trace_fd, maxkb);
288 }
289
tracecmd_create_recorder_fd(int fd,int cpu,unsigned flags)290 struct tracecmd_recorder *tracecmd_create_recorder_fd(int fd, int cpu, unsigned flags)
291 {
292 return tracecmd_create_buffer_recorder_fd(fd, cpu, flags, NULL);
293 }
294
tracecmd_create_recorder(const char * file,int cpu,unsigned flags)295 struct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu, unsigned flags)
296 {
297 return tracecmd_create_buffer_recorder(file, cpu, flags, NULL);
298 }
299
300 struct tracecmd_recorder *
tracecmd_create_recorder_maxkb(const char * file,int cpu,unsigned flags,int maxkb)301 tracecmd_create_recorder_maxkb(const char *file, int cpu, unsigned flags, int maxkb)
302 {
303 return tracecmd_create_buffer_recorder_maxkb(file, cpu, flags, NULL, maxkb);
304 }
305
update_fd(struct tracecmd_recorder * recorder,int size)306 static inline void update_fd(struct tracecmd_recorder *recorder, int size)
307 {
308 int fd;
309
310 if (!recorder->max)
311 return;
312
313 recorder->count += size;
314
315 if (recorder->count >= recorder->page_size) {
316 recorder->pages += recorder->count / recorder->page_size;
317 recorder->count = 0;
318 }
319
320 if (recorder->pages < recorder->max)
321 return;
322
323 recorder->pages = 0;
324
325 fd = recorder->fd;
326
327 /* Swap fd to next file. */
328 if (fd == recorder->fd1)
329 fd = recorder->fd2;
330 else
331 fd = recorder->fd1;
332
333 /* Zero out the new file we are writing to */
334 lseek(fd, 0, SEEK_SET);
335 ftruncate(fd, 0);
336
337 recorder->fd = fd;
338 }
339
340 /*
341 * Returns -1 on error.
342 * or bytes of data read.
343 */
read_data(struct tracecmd_recorder * recorder)344 static long read_data(struct tracecmd_recorder *recorder)
345 {
346 bool nonblock = recorder->stop;
347 char buf[recorder->subbuf_size];
348 long left;
349 long r, w;
350
351 r = tracefs_cpu_read(recorder->tcpu, buf, nonblock);
352 if (r < 0)
353 return r;
354
355 left = r;
356 do {
357 w = write(recorder->fd, buf + (r - left), left);
358 if (w > 0) {
359 left -= w;
360 update_fd(recorder, w);
361 }
362 } while (w >= 0 && left);
363
364 if (w < 0)
365 r = w;
366
367 return r;
368 }
369
370 /*
371 * Returns -1 on error.
372 * or bytes of data read.
373 */
direct_splice_data(struct tracecmd_recorder * recorder)374 static long direct_splice_data(struct tracecmd_recorder *recorder)
375 {
376 bool nonblock = recorder->stop;
377 return tracefs_cpu_pipe(recorder->tcpu, recorder->fd, nonblock);
378 }
379
move_data(struct tracecmd_recorder * recorder)380 static long move_data(struct tracecmd_recorder *recorder)
381 {
382 bool nonblock = recorder->stop;
383 long ret;
384
385 if (recorder->flags & TRACECMD_RECORD_NOSPLICE)
386 return read_data(recorder);
387
388 if (recorder->flags & TRACECMD_RECORD_NOBRASS)
389 return direct_splice_data(recorder);
390
391 ret = tracefs_cpu_write(recorder->tcpu, recorder->fd, nonblock);
392 if (ret > 0)
393 update_fd(recorder, ret);
394 return ret;
395 }
396
tracecmd_flush_recording(struct tracecmd_recorder * recorder,bool finish)397 long tracecmd_flush_recording(struct tracecmd_recorder *recorder, bool finish)
398 {
399 char buf[recorder->subbuf_size];
400 long total = 0;
401 long wrote = 0;
402 long ret;
403
404 if (!recorder)
405 return 0;
406
407 if (!finish)
408 return tracefs_cpu_flush_write(recorder->tcpu, recorder->fd);
409
410 set_nonblock(recorder);
411
412 do {
413 ret = tracefs_cpu_flush_write(recorder->tcpu, recorder->fd);
414 if (ret > 0)
415 wrote += ret;
416 } while (ret > 0);
417
418 /* Make sure we finish off with a page size boundary */
419 wrote &= recorder->subbuf_size - 1;
420 if (wrote) {
421 memset(buf, 0, recorder->subbuf_size);
422 write(recorder->fd, buf, recorder->subbuf_size - wrote);
423 total += recorder->subbuf_size;
424 }
425
426 return total;
427 }
428
tracecmd_start_recording(struct tracecmd_recorder * recorder,unsigned long sleep)429 int tracecmd_start_recording(struct tracecmd_recorder *recorder, unsigned long sleep)
430 {
431 struct timespec req = {
432 .tv_sec = sleep / 1000000,
433 .tv_nsec = (sleep % 1000000) * 1000,
434 };
435 long read = 1;
436 long ret;
437
438 recorder->stop = 0;
439
440 do {
441 /* Only sleep if we did not read anything last time */
442 if (!read && sleep)
443 nanosleep(&req, NULL);
444
445 read = 0;
446 do {
447 ret = move_data(recorder);
448 if (ret < 0) {
449 if (errno == EINTR)
450 continue;
451 if ((recorder->flags & TRACECMD_RECORD_POLL) &&
452 errno == EAGAIN)
453 continue;
454 return ret;
455 }
456 read += ret;
457 } while (ret > 0 && !recorder->stop);
458 } while (!recorder->stop);
459
460 /* Flush out the rest */
461 ret = tracecmd_flush_recording(recorder, true);
462
463 if (ret < 0)
464 return ret;
465
466 return 0;
467 }
468
tracecmd_stop_recording(struct tracecmd_recorder * recorder)469 int tracecmd_stop_recording(struct tracecmd_recorder *recorder)
470 {
471 if (!recorder)
472 return -1;
473
474 recorder->stop = 1;
475
476 return set_nonblock(recorder);
477 }
478