2727692a |
1 | /* libltt |
2 | * |
3 | * Linux Trace Toolkit Netlink Control Library |
4 | * |
c928825d |
5 | * Controls the ltt-control kernel module through debugfs. |
2727692a |
6 | * |
7 | * Copyright 2005 - |
8 | * Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> |
9 | * |
10 | * |
11 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License as published by |
13 | * the Free Software Foundation; either version 2 of the License, or |
14 | * (at your option) any later version. |
15 | * |
16 | * This program is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10e8a188 |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2727692a |
19 | * GNU General Public License for more details. |
c928825d |
20 | * |
2727692a |
21 | */ |
22 | |
23 | #ifdef HAVE_CONFIG_H |
24 | #include <config.h> |
25 | #endif |
26 | |
27 | #include <liblttctl/lttctl.h> |
28 | #include <errno.h> |
29 | #include <stdio.h> |
2727692a |
30 | #include <string.h> |
c928825d |
31 | #include <dirent.h> |
32 | #include <limits.h> |
33 | #include <fcntl.h> |
34 | #include <stdlib.h> |
2727692a |
35 | |
c928825d |
36 | #define MAX_CHANNEL (256) |
2727692a |
37 | |
c928825d |
38 | static char debugfsmntdir[PATH_MAX]; |
2727692a |
39 | |
c928825d |
40 | static int initdebugfsmntdir(void) |
2727692a |
41 | { |
c928825d |
42 | char mnt_dir[PATH_MAX]; |
43 | char mnt_type[PATH_MAX]; |
2727692a |
44 | |
c928825d |
45 | FILE *fp = fopen("/proc/mounts", "r"); |
46 | if (!fp) { |
47 | fprintf(stderr, "%s: Can't open /proc/mounts\n", __func__); |
48 | return 1; |
49 | } |
2727692a |
50 | |
c928825d |
51 | while (1) { |
52 | if (fscanf(fp, "%*s %s %s %*s %*s %*s", mnt_dir, mnt_type) |
53 | <= 0) { |
54 | fprintf(stderr, "%s: debugfs mountpoint not found\n", |
55 | __func__); |
56 | return 1; |
2727692a |
57 | } |
c928825d |
58 | if (!strcmp(mnt_type, "debugfs")) { |
59 | strcpy(debugfsmntdir, mnt_dir); |
2727692a |
60 | return 0; |
61 | } |
62 | } |
c928825d |
63 | } |
64 | |
65 | int lttctl_init(void) |
66 | { |
67 | int ret; |
68 | DIR *dir; |
69 | char controldirname[PATH_MAX]; |
70 | |
71 | ret = initdebugfsmntdir(); |
72 | if (ret) { |
73 | fprintf(stderr, "Debugfs mount point not found\n"); |
74 | return 1; |
2727692a |
75 | } |
2727692a |
76 | |
c928825d |
77 | /* check ltt control's debugfs dir */ |
78 | sprintf(controldirname, "%s/ltt/control/", debugfsmntdir); |
2727692a |
79 | |
c928825d |
80 | dir = opendir(controldirname); |
81 | if (!dir) { |
82 | fprintf(stderr, "ltt-trace-control's debugfs dir not found\n"); |
83 | closedir(dir); |
84 | return -errno; |
85 | } |
2727692a |
86 | |
c928825d |
87 | closedir(dir); |
2727692a |
88 | |
c928825d |
89 | return 0; |
90 | } |
2727692a |
91 | |
c928825d |
92 | int lttctl_destroy(void) |
2727692a |
93 | { |
c928825d |
94 | return 0; |
2727692a |
95 | } |
96 | |
c928825d |
97 | static int lttctl_sendop(const char *fname, const char *op) |
2727692a |
98 | { |
c928825d |
99 | int fd; |
100 | |
101 | if (!fname) { |
102 | fprintf(stderr, "%s: args invalid\n", __func__); |
103 | return 1; |
104 | } |
105 | |
106 | fd = open(fname, O_WRONLY); |
107 | if (fd == -1) { |
108 | fprintf(stderr, "%s: open %s failed: %s\n", __func__, fname, |
109 | strerror(errno)); |
110 | return errno; |
111 | } |
112 | |
113 | if (write(fd, op, strlen(op)) == -1) { |
114 | fprintf(stderr, "%s: write %s to %s failed: %s\n", __func__, op, |
115 | fname, strerror(errno)); |
116 | close(fd); |
117 | return 1; |
118 | } |
2727692a |
119 | |
c928825d |
120 | close(fd); |
121 | |
122 | return 0; |
123 | } |
2727692a |
124 | |
125 | /* |
c928825d |
126 | * check is trace exist(check debugfsmntdir too) |
127 | * expect: |
128 | * 0: expect that trace not exist |
129 | * !0: expect that trace exist |
130 | * |
131 | * ret: |
132 | * 0: check pass |
133 | * 1: check failed |
134 | * -ERRNO: error happened (no check) |
2727692a |
135 | */ |
c928825d |
136 | static int lttctl_check_trace(const char *name, int expect) |
2727692a |
137 | { |
c928825d |
138 | char tracedirname[PATH_MAX]; |
139 | DIR *dir; |
140 | int exist; |
2727692a |
141 | |
c928825d |
142 | if (!name) { |
143 | fprintf(stderr, "%s: args invalid\n", __func__); |
144 | return -EINVAL; |
2727692a |
145 | } |
c928825d |
146 | |
147 | if (!debugfsmntdir[0]) { |
148 | fprintf(stderr, "%s: debugfsmntdir not valid\n", __func__); |
149 | return -EINVAL; |
150 | } |
151 | |
152 | sprintf(tracedirname, "%s/ltt/control/%s", debugfsmntdir, name); |
153 | |
154 | dir = opendir(tracedirname); |
155 | if (dir) { |
156 | exist = 1; |
157 | } else { |
158 | if (errno != ENOENT) { |
159 | fprintf(stderr, "%s: %s\n", __func__, strerror(errno)); |
160 | return -EINVAL; |
161 | } |
162 | exist = 0; |
163 | } |
164 | |
165 | closedir(dir); |
166 | |
167 | if (!expect != !exist) { |
168 | if (exist) |
169 | fprintf(stderr, "Trace %s already exist\n", name); |
170 | else |
171 | fprintf(stderr, "Trace %s not exist\n", name); |
172 | return 1; |
173 | } |
174 | |
175 | return 0; |
2727692a |
176 | } |
177 | |
178 | /* |
c928825d |
179 | * get channel list of a trace |
180 | * don't include metadata channel when metadata is 0 |
181 | * |
182 | * return number of channel on success |
183 | * return negative number on fail |
184 | * Caller must free channellist. |
2727692a |
185 | */ |
c928825d |
186 | static int lttctl_get_channellist(const char *tracename, |
187 | char ***channellist, int metadata) |
2727692a |
188 | { |
c928825d |
189 | char tracedirname[PATH_MAX]; |
190 | struct dirent *dirent; |
191 | DIR *dir; |
192 | char **list = NULL, **old_list; |
193 | int nr_chan = 0; |
194 | |
195 | sprintf(tracedirname, "%s/ltt/control/%s/channel", debugfsmntdir, |
196 | tracename); |
197 | |
198 | dir = opendir(tracedirname); |
199 | if (!dir) { |
200 | nr_chan = -ENOENT; |
201 | goto error; |
202 | } |
203 | |
204 | for (;;) { |
205 | dirent = readdir(dir); |
206 | if (!dirent) |
207 | break; |
208 | if (!strcmp(dirent->d_name, ".") |
209 | || !strcmp(dirent->d_name, "..")) |
210 | continue; |
211 | if (!metadata && !strcmp(dirent->d_name, "metadata")) |
212 | continue; |
213 | old_list = list; |
214 | list = malloc(sizeof(char *) * ++nr_chan); |
215 | memcpy(list, old_list, sizeof(*list) * (nr_chan - 1)); |
216 | free(old_list); |
217 | list[nr_chan - 1] = strdup(dirent->d_name); |
218 | } |
219 | |
220 | closedir(dir); |
221 | |
222 | *channellist = list; |
223 | return nr_chan; |
224 | error: |
225 | free(list); |
226 | *channellist = NULL; |
227 | return nr_chan; |
228 | } |
229 | |
230 | int lttctl_setup_trace(const char *name) |
231 | { |
232 | int ret; |
233 | char ctlfname[PATH_MAX]; |
234 | |
235 | if (!name) { |
236 | fprintf(stderr, "%s: args invalid\n", __func__); |
237 | ret = -EINVAL; |
238 | goto arg_error; |
239 | } |
240 | |
241 | ret = lttctl_check_trace(name, 0); |
242 | if (ret) |
243 | goto arg_error; |
244 | |
245 | sprintf(ctlfname, "%s/ltt/setup_trace", debugfsmntdir); |
246 | |
247 | ret = lttctl_sendop(ctlfname, name); |
248 | if (ret) { |
249 | fprintf(stderr, "Setup trace failed\n"); |
250 | goto op_err; |
2727692a |
251 | } |
c928825d |
252 | |
2727692a |
253 | return 0; |
c928825d |
254 | |
255 | op_err: |
256 | arg_error: |
257 | return ret; |
2727692a |
258 | } |
259 | |
c928825d |
260 | int lttctl_destroy_trace(const char *name) |
261 | { |
262 | int ret; |
263 | char ctlfname[PATH_MAX]; |
264 | |
265 | if (!name) { |
266 | fprintf(stderr, "%s: args invalid\n", __func__); |
267 | ret = -EINVAL; |
268 | goto arg_error; |
269 | } |
270 | |
271 | ret = lttctl_check_trace(name, 1); |
272 | if (ret) |
273 | goto arg_error; |
274 | |
275 | sprintf(ctlfname, "%s/ltt/destroy_trace", debugfsmntdir); |
276 | |
277 | ret = lttctl_sendop(ctlfname, name); |
278 | if (ret) { |
279 | fprintf(stderr, "Destroy trace failed\n"); |
280 | goto op_err; |
281 | } |
282 | |
283 | return 0; |
284 | |
285 | op_err: |
286 | arg_error: |
287 | return ret; |
288 | } |
2727692a |
289 | |
c928825d |
290 | int lttctl_alloc_trace(const char *name) |
2727692a |
291 | { |
c928825d |
292 | int ret; |
293 | char ctlfname[PATH_MAX]; |
294 | |
295 | if (!name) { |
296 | fprintf(stderr, "%s: args invalid\n", __func__); |
297 | ret = -EINVAL; |
298 | goto arg_error; |
299 | } |
300 | |
301 | ret = lttctl_check_trace(name, 1); |
302 | if (ret) |
303 | goto arg_error; |
304 | |
305 | sprintf(ctlfname, "%s/ltt/control/%s/alloc", debugfsmntdir, name); |
306 | |
307 | ret = lttctl_sendop(ctlfname, "1"); |
308 | if (ret) { |
309 | fprintf(stderr, "Allocate trace failed\n"); |
310 | goto op_err; |
311 | } |
312 | |
313 | return 0; |
314 | |
315 | op_err: |
316 | arg_error: |
317 | return ret; |
318 | } |
319 | |
320 | int lttctl_start(const char *name) |
321 | { |
322 | int ret; |
323 | char ctlfname[PATH_MAX]; |
324 | |
325 | if (!name) { |
326 | fprintf(stderr, "%s: args invalid\n", __func__); |
327 | ret = -EINVAL; |
328 | goto arg_error; |
329 | } |
330 | |
331 | ret = lttctl_check_trace(name, 1); |
332 | if (ret) |
333 | goto arg_error; |
334 | |
335 | sprintf(ctlfname, "%s/ltt/control/%s/enabled", debugfsmntdir, name); |
336 | |
337 | ret = lttctl_sendop(ctlfname, "1"); |
338 | if (ret) { |
339 | fprintf(stderr, "Start trace failed\n"); |
340 | goto op_err; |
2727692a |
341 | } |
342 | |
343 | return 0; |
344 | |
c928825d |
345 | op_err: |
346 | arg_error: |
347 | return ret; |
2727692a |
348 | } |
349 | |
c928825d |
350 | int lttctl_pause(const char *name) |
2727692a |
351 | { |
c928825d |
352 | int ret; |
353 | char ctlfname[PATH_MAX]; |
354 | |
355 | if (!name) { |
356 | fprintf(stderr, "%s: args invalid\n", __func__); |
357 | ret = -EINVAL; |
358 | goto arg_error; |
359 | } |
360 | |
361 | ret = lttctl_check_trace(name, 1); |
362 | if (ret) |
363 | goto arg_error; |
364 | |
365 | sprintf(ctlfname, "%s/ltt/control/%s/enabled", debugfsmntdir, name); |
366 | |
367 | ret = lttctl_sendop(ctlfname, "0"); |
368 | if (ret) { |
369 | fprintf(stderr, "Pause trace failed\n"); |
370 | goto op_err; |
2727692a |
371 | } |
372 | |
373 | return 0; |
374 | |
c928825d |
375 | op_err: |
376 | arg_error: |
377 | return ret; |
378 | } |
379 | |
380 | int lttctl_set_trans(const char *name, const char *trans) |
381 | { |
382 | int ret; |
383 | char ctlfname[PATH_MAX]; |
384 | |
385 | if (!name) { |
386 | fprintf(stderr, "%s: args invalid\n", __func__); |
387 | ret = -EINVAL; |
388 | goto arg_error; |
389 | } |
390 | |
391 | ret = lttctl_check_trace(name, 1); |
392 | if (ret) |
393 | goto arg_error; |
394 | |
395 | sprintf(ctlfname, "%s/ltt/control/%s/trans", debugfsmntdir, name); |
2727692a |
396 | |
c928825d |
397 | ret = lttctl_sendop(ctlfname, trans); |
398 | if (ret) { |
399 | fprintf(stderr, "Set transport failed\n"); |
400 | goto op_err; |
401 | } |
402 | |
403 | return 0; |
404 | |
405 | op_err: |
406 | arg_error: |
407 | return ret; |
2727692a |
408 | } |
409 | |
c928825d |
410 | static int __lttctl_set_channel_enable(const char *name, const char *channel, |
411 | int enable) |
2727692a |
412 | { |
c928825d |
413 | int ret; |
414 | char ctlfname[PATH_MAX]; |
2727692a |
415 | |
c928825d |
416 | sprintf(ctlfname, "%s/ltt/control/%s/channel/%s/enable", debugfsmntdir, |
417 | name, channel); |
2727692a |
418 | |
c928825d |
419 | ret = lttctl_sendop(ctlfname, enable ? "1" : "0"); |
420 | if (ret) |
421 | fprintf(stderr, "Set channel's enable mode failed\n"); |
2727692a |
422 | |
c928825d |
423 | return ret; |
424 | } |
425 | int lttctl_set_channel_enable(const char *name, const char *channel, |
426 | int enable) |
427 | { |
428 | int ret; |
2727692a |
429 | |
c928825d |
430 | if (!name || !channel) { |
431 | fprintf(stderr, "%s: args invalid\n", __func__); |
432 | ret = -EINVAL; |
433 | goto arg_error; |
434 | } |
2727692a |
435 | |
c928825d |
436 | ret = lttctl_check_trace(name, 1); |
437 | if (ret) |
438 | goto arg_error; |
439 | |
440 | if (strcmp(channel, "all")) { |
441 | ret = __lttctl_set_channel_enable(name, channel, enable); |
442 | if (ret) |
443 | goto op_err; |
444 | } else { |
445 | char **channellist; |
446 | int n_channel; |
447 | |
448 | /* Don't allow set enable state for metadata channel */ |
449 | n_channel = lttctl_get_channellist(name, &channellist, 0); |
450 | if (n_channel < 0) { |
451 | fprintf(stderr, "%s: lttctl_get_channellist failed\n", |
452 | __func__); |
453 | ret = -ENOENT; |
454 | goto op_err; |
455 | } |
2727692a |
456 | |
c928825d |
457 | for (; n_channel > 0; n_channel--) { |
458 | ret = __lttctl_set_channel_enable(name, |
459 | channellist[n_channel - 1], enable); |
460 | if (ret) |
461 | goto op_err; |
462 | } |
463 | free(channellist); |
2727692a |
464 | } |
465 | |
466 | return 0; |
467 | |
c928825d |
468 | op_err: |
469 | arg_error: |
470 | return ret; |
471 | } |
472 | |
473 | static int __lttctl_set_channel_overwrite(const char *name, const char *channel, |
474 | int overwrite) |
475 | { |
476 | int ret; |
477 | char ctlfname[PATH_MAX]; |
478 | |
479 | sprintf(ctlfname, "%s/ltt/control/%s/channel/%s/overwrite", |
480 | debugfsmntdir, name, channel); |
481 | |
482 | ret = lttctl_sendop(ctlfname, overwrite ? "1" : "0"); |
483 | if (ret) |
484 | fprintf(stderr, "Set channel's overwrite mode failed\n"); |
485 | |
486 | return ret; |
487 | } |
488 | int lttctl_set_channel_overwrite(const char *name, const char *channel, |
489 | int overwrite) |
490 | { |
491 | int ret; |
492 | |
493 | if (!name || !channel) { |
494 | fprintf(stderr, "%s: args invalid\n", __func__); |
495 | ret = -EINVAL; |
496 | goto arg_error; |
497 | } |
498 | |
499 | ret = lttctl_check_trace(name, 1); |
500 | if (ret) |
501 | goto arg_error; |
502 | |
503 | if (strcmp(channel, "all")) { |
504 | ret = __lttctl_set_channel_overwrite(name, channel, overwrite); |
505 | if (ret) |
506 | goto op_err; |
507 | } else { |
508 | char **channellist; |
509 | int n_channel; |
510 | |
511 | /* Don't allow set overwrite for metadata channel */ |
512 | n_channel = lttctl_get_channellist(name, &channellist, 0); |
513 | if (n_channel < 0) { |
514 | fprintf(stderr, "%s: lttctl_get_channellist failed\n", |
515 | __func__); |
516 | ret = -ENOENT; |
517 | goto op_err; |
518 | } |
519 | |
520 | for (; n_channel > 0; n_channel--) { |
521 | ret = __lttctl_set_channel_overwrite(name, |
522 | channellist[n_channel - 1], overwrite); |
523 | if (ret) |
524 | goto op_err; |
525 | } |
526 | free(channellist); |
527 | } |
528 | |
529 | return 0; |
2727692a |
530 | |
c928825d |
531 | op_err: |
532 | arg_error: |
533 | return ret; |
2727692a |
534 | } |
535 | |
c928825d |
536 | static int __lttctl_set_channel_subbuf_num(const char *name, |
537 | const char *channel, unsigned subbuf_num) |
538 | { |
539 | int ret; |
540 | char ctlfname[PATH_MAX]; |
541 | char opstr[32]; |
542 | |
543 | sprintf(ctlfname, "%s/ltt/control/%s/channel/%s/subbuf_num", |
544 | debugfsmntdir, name, channel); |
545 | |
546 | sprintf(opstr, "%u", subbuf_num); |
547 | |
548 | ret = lttctl_sendop(ctlfname, opstr); |
549 | if (ret) |
550 | fprintf(stderr, "Set channel's subbuf number failed\n"); |
551 | |
552 | return ret; |
553 | } |
554 | int lttctl_set_channel_subbuf_num(const char *name, const char *channel, |
555 | unsigned subbuf_num) |
2727692a |
556 | { |
c928825d |
557 | int ret; |
558 | |
559 | if (!name || !channel) { |
560 | fprintf(stderr, "%s: args invalid\n", __func__); |
561 | ret = -EINVAL; |
562 | goto arg_error; |
563 | } |
564 | |
565 | ret = lttctl_check_trace(name, 1); |
566 | if (ret) |
567 | goto arg_error; |
568 | |
569 | if (strcmp(channel, "all")) { |
570 | ret = __lttctl_set_channel_subbuf_num(name, channel, |
571 | subbuf_num); |
572 | if (ret) |
573 | goto op_err; |
574 | } else { |
575 | char **channellist; |
576 | int n_channel; |
577 | |
578 | /* allow set subbuf_num for metadata channel */ |
579 | n_channel = lttctl_get_channellist(name, &channellist, 1); |
580 | if (n_channel < 0) { |
581 | fprintf(stderr, "%s: lttctl_get_channellist failed\n", |
582 | __func__); |
583 | ret = -ENOENT; |
584 | goto op_err; |
585 | } |
586 | |
587 | for (; n_channel > 0; n_channel--) { |
588 | ret = __lttctl_set_channel_subbuf_num(name, |
589 | channellist[n_channel - 1], subbuf_num); |
590 | if (ret) |
591 | goto op_err; |
592 | } |
593 | free(channellist); |
2727692a |
594 | } |
595 | |
596 | return 0; |
597 | |
c928825d |
598 | op_err: |
599 | arg_error: |
600 | return ret; |
601 | } |
602 | |
603 | static int __lttctl_set_channel_subbuf_size(const char *name, |
604 | const char *channel, unsigned subbuf_size) |
605 | { |
606 | int ret; |
607 | char ctlfname[PATH_MAX]; |
608 | char opstr[32]; |
609 | |
610 | sprintf(ctlfname, "%s/ltt/control/%s/channel/%s/subbuf_size", |
611 | debugfsmntdir, name, channel); |
612 | |
613 | sprintf(opstr, "%u", subbuf_size); |
614 | |
615 | ret = lttctl_sendop(ctlfname, opstr); |
616 | if (ret) |
617 | fprintf(stderr, "Set channel's subbuf size failed\n"); |
2727692a |
618 | } |
c928825d |
619 | int lttctl_set_channel_subbuf_size(const char *name, const char *channel, |
620 | unsigned subbuf_size) |
621 | { |
622 | int ret; |
2727692a |
623 | |
c928825d |
624 | if (!name || !channel) { |
625 | fprintf(stderr, "%s: args invalid\n", __func__); |
626 | ret = -EINVAL; |
627 | goto arg_error; |
628 | } |
629 | |
630 | ret = lttctl_check_trace(name, 1); |
631 | if (ret) |
632 | goto arg_error; |
633 | |
634 | if (strcmp(channel, "all")) { |
635 | ret = __lttctl_set_channel_subbuf_size(name, channel, |
636 | subbuf_size); |
637 | if (ret) |
638 | goto op_err; |
639 | } else { |
640 | char **channellist; |
641 | int n_channel; |
642 | |
643 | /* allow set subbuf_size for metadata channel */ |
644 | n_channel = lttctl_get_channellist(name, &channellist, 1); |
645 | if (n_channel < 0) { |
646 | fprintf(stderr, "%s: lttctl_get_channellist failed\n", |
647 | __func__); |
648 | ret = -ENOENT; |
649 | goto op_err; |
650 | } |
651 | |
652 | for (; n_channel > 0; n_channel--) { |
653 | ret = __lttctl_set_channel_subbuf_size(name, |
654 | channellist[n_channel - 1], subbuf_size); |
655 | if (ret) |
656 | goto op_err; |
657 | } |
658 | free(channellist); |
659 | } |
660 | |
661 | return 0; |
662 | |
663 | op_err: |
664 | arg_error: |
665 | return ret; |
666 | } |