Add listing kernel event support
[lttng-tools.git] / lttng / lttng.c
1 /*
2 * Copyright (c) 2011 David Goulet <david.goulet@polymtl.ca>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #define _GNU_SOURCE
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <grp.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <unistd.h>
32
33 #include <lttng/lttng.h>
34
35 #include "lttngerr.h"
36 #include "options.h"
37
38 /* Variables */
39 static char *progname;
40 static char *session_name;
41 static uuid_t current_uuid;
42 static int auto_session;
43 static int auto_trace;
44
45 /* Prototypes */
46 static int process_client_opt(void);
47 static int process_opt_list_apps(void);
48 static int process_opt_list_sessions(void);
49 static int process_opt_list_traces(void);
50 static int process_opt_kernel_list_events(void);
51 static int process_opt_create_session(void);
52 static int process_kernel_create_trace(void);
53 static int process_opt_kernel_event(void);
54 static int process_kernel_start_trace(void);
55 static int set_session_uuid(void);
56 static void sighandler(int sig);
57 static int set_signal_handler(void);
58 static int validate_options(void);
59 static char *get_cmdline_by_pid(pid_t pid);
60 static void set_opt_session_info(void);
61
62 /*
63 * start_client
64 *
65 * Process client request from the command line
66 * options. Every tracing action is done by the
67 * liblttngctl API.
68 */
69 static int process_client_opt(void)
70 {
71 int ret;
72
73 set_opt_session_info();
74
75 if (opt_list_apps) {
76 ret = process_opt_list_apps();
77 if (ret < 0) {
78 goto end;
79 }
80 goto error;
81 }
82
83 if (opt_list_session) {
84 ret = process_opt_list_sessions();
85 if (ret < 0) {
86 goto end;
87 }
88 goto error;
89 }
90
91 if (opt_list_events) {
92 if (opt_trace_kernel) {
93 ret = process_opt_kernel_list_events();
94 if (ret < 0) {
95 goto end;
96 }
97 } else if (opt_trace_pid != 0) {
98 // TODO
99 }
100 goto error;
101 }
102
103 /* Session creation or auto session set on */
104 if (auto_session || opt_create_session) {
105 DBG("Creating a new session");
106 ret = process_opt_create_session();
107 if (ret < 0) {
108 goto end;
109 }
110 }
111
112 ret = set_session_uuid();
113 if (ret < 0) {
114 ERR("Session %s not found", opt_session_name);
115 goto error;
116 }
117
118 if (opt_destroy_session) {
119 ret = lttng_destroy_session(&current_uuid);
120 if (ret < 0) {
121 goto end;
122 }
123 MSG("Session %s destroyed.", opt_session_name);
124 }
125
126 if (opt_list_traces) {
127 ret = process_opt_list_traces();
128 if (ret < 0) {
129 goto end;
130 }
131 }
132
133 /*
134 * Action on traces (kernel or/and userspace).
135 */
136
137 if (opt_trace_kernel) {
138 if (auto_trace || opt_create_trace) {
139 DBG("Creating a kernel trace");
140 ret = process_kernel_create_trace();
141 if (ret < 0) {
142 goto end;
143 }
144 }
145
146 if (opt_event_list != NULL) {
147 ret = process_opt_kernel_event();
148 if (ret < 0) {
149 goto end;
150 }
151 } else {
152 // Enable all events
153 }
154
155 if (auto_trace || opt_start_trace) {
156 DBG("Starting kernel tracing");
157 ret = process_kernel_start_trace();
158 if (ret < 0) {
159 goto end;
160 }
161 }
162
163 if (opt_stop_trace) {
164 DBG("Stopping kernel tracing");
165 ret = lttng_kernel_stop_tracing();
166 if (ret < 0) {
167 goto end;
168 }
169 }
170 }
171
172 if (opt_trace_pid != 0) {
173 if (auto_trace || opt_create_trace) {
174 DBG("Create a userspace trace for pid %d", opt_trace_pid);
175 ret = lttng_ust_create_trace(opt_trace_pid);
176 if (ret < 0) {
177 goto end;
178 }
179 MSG("Trace created successfully!");
180 }
181
182 if (auto_trace || opt_start_trace) {
183 DBG("Start trace for pid %d", opt_trace_pid);
184 ret = lttng_ust_start_trace(opt_trace_pid);
185 if (ret < 0) {
186 goto end;
187 }
188 MSG("Trace started successfully!");
189 } else if (opt_stop_trace) {
190 DBG("Stop trace for pid %d", opt_trace_pid);
191 ret = lttng_ust_stop_trace(opt_trace_pid);
192 if (ret < 0) {
193 goto end;
194 }
195 MSG("Trace stopped successfully!");
196 }
197
198 }
199
200 return 0;
201
202 end:
203 ERR("%s", lttng_get_readable_code(ret));
204 error: /* fall through */
205 return ret;
206 }
207
208 /*
209 * process_kernel_start_trace
210 *
211 * Start a kernel trace.
212 */
213 static int process_kernel_start_trace(void)
214 {
215 int ret;
216
217 ret = lttng_kernel_create_stream();
218 if (ret < 0) {
219 goto error;
220 }
221
222 ret = lttng_kernel_start_tracing();
223 if (ret < 0) {
224 goto error;
225 }
226
227 MSG("Kernel tracing started");
228
229 return 0;
230
231 error:
232 return ret;
233 }
234
235 /*
236 * process_kernel_create_trace
237 *
238 * Create a kernel trace.
239 */
240 static int process_kernel_create_trace(void)
241 {
242 int ret;
243
244 /* Setup kernel session */
245 ret = lttng_kernel_create_session();
246 if (ret < 0) {
247 goto error;
248 }
249
250 /* Create an empty channel (with no event) */
251 ret = lttng_kernel_create_channel();
252 if (ret < 0) {
253 goto error;
254 }
255
256 /* Opening metadata for session */
257 ret = lttng_kernel_open_metadata();
258 if (ret < 0) {
259 goto error;
260 }
261
262 return 0;
263
264 error:
265 return ret;
266 }
267
268 /*
269 * process_opt_kernel_list_events
270 *
271 * Ask for all trace events in the kernel and pretty print them.
272 */
273 static int process_opt_kernel_list_events(void)
274 {
275 int ret, pos, size;
276 char *event_list, *event, *ptr;
277
278 DBG("Getting all tracing events");
279
280 ret = lttng_kernel_list_events(&event_list);
281 if (ret < 0) {
282 ERR("Unable to list events.");
283 return ret;
284 }
285
286 MSG("Kernel tracepoints:\n-------------");
287
288 ptr = event_list;
289 while ((size = sscanf(ptr, "event { name = %m[^;]; };%n\n", &event, &pos)) == 1) {
290 MSG(" - %s", event);
291 /* Move pointer to the next line */
292 ptr += pos + 1;
293 free(event);
294 }
295
296 free(event_list);
297
298 return 0;
299 }
300
301 /*
302 * process_opt_kernel_event
303 *
304 * Enable kernel event from the command line list given.
305 */
306 static int process_opt_kernel_event(void)
307 {
308 int ret;
309 char *event_name;
310
311 event_name = strtok(opt_event_list, ",");
312 while (event_name != NULL) {
313 DBG("Enabling kernel event %s", event_name);
314 ret = lttng_kernel_enable_event(event_name);
315 if (ret < 0) {
316 ERR("%s %s", lttng_get_readable_code(ret), event_name);
317 } else {
318 MSG("Kernel event %s enabled.", event_name);
319 }
320 /* Next event */
321 event_name = strtok(NULL, ",");
322 }
323
324 return 0;
325 }
326
327 /*
328 * set_opt_session_info
329 *
330 * Setup session_name, current_uuid, short_str_uuid and
331 * long_str_uuid using the command line options.
332 */
333 static void set_opt_session_info(void)
334 {
335 if (opt_session_name != NULL) {
336 session_name = strndup(opt_session_name, NAME_MAX);
337 DBG("Session name set to %s", session_name);
338 }
339 }
340
341 /*
342 * set_session_uuid
343 *
344 * Set current session uuid to the current flow of command(s) using the
345 * session_name.
346 */
347 static int set_session_uuid(void)
348 {
349 int ret, count, i, found = 0;
350 struct lttng_session *sessions;
351
352 if (!uuid_is_null(current_uuid)) {
353 lttng_set_current_session_uuid(&current_uuid);
354 goto end;
355 }
356
357 count = lttng_list_sessions(&sessions);
358 if (count < 0) {
359 ret = count;
360 goto error;
361 }
362
363 for (i = 0; i < count; i++) {
364 if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) {
365 lttng_set_current_session_uuid(&sessions[i].uuid);
366 uuid_copy(current_uuid, sessions[i].uuid);
367 found = 1;
368 break;
369 }
370 }
371
372 free(sessions);
373
374 if (!found) {
375 return -1;
376 }
377
378 end:
379 DBG("Session UUID set");
380 return 0;
381
382 error:
383 return ret;
384 }
385
386 /*
387 * process_opt_list_traces
388 *
389 * Get list of all traces for a specific session uuid.
390 */
391 static int process_opt_list_traces(void)
392 {
393 int ret, i;
394 struct lttng_trace *traces;
395
396 ret = lttng_list_traces(&current_uuid, &traces);
397 DBG("Number of traces to list %d", ret);
398 if (ret < 0) {
399 goto error;
400 }
401
402 /* No traces */
403 if (ret == 0) {
404 MSG("No traces found.");
405 goto error;
406 }
407
408 MSG("Userspace traces:");
409 for (i = 0; i < ret; i++) {
410 if (traces[i].type == USERSPACE) {
411 MSG("\t%d) %s (pid: %d): %s",
412 i, traces[i].name, traces[i].pid,
413 get_cmdline_by_pid(traces[i].pid));
414 } else {
415 break;
416 }
417 }
418
419 MSG("Kernel traces:");
420 for (;i < ret; i++) {
421 if (traces[i].type == KERNEL) {
422 MSG("\t%d) %s", i, traces[i].name);
423 }
424 }
425
426 free(traces);
427
428 error:
429 return ret;
430 }
431
432 /*
433 * process_opt_create_session
434 *
435 * Create a new session using the name pass
436 * to the command line.
437 */
438 static int process_opt_create_session(void)
439 {
440 int ret;
441 char name[NAME_MAX];
442 time_t rawtime;
443 struct tm *timeinfo;
444
445 /* Auto session name creation */
446 if (opt_session_name == NULL) {
447 time(&rawtime);
448 timeinfo = localtime(&rawtime);
449 strftime(name, sizeof(name), "auto-%Y%m%d-%H%M%S", timeinfo);
450 session_name = strndup(name, sizeof(name));
451 DBG("Auto session name set to %s", session_name);
452 }
453
454 ret = lttng_create_session(session_name);
455 if (ret < 0) {
456 goto error;
457 }
458
459 MSG("Session created: %s", session_name);
460
461 error:
462 return ret;
463 }
464
465 /*
466 * process_opt_list_sessions
467 *
468 * Get the list of available sessions from
469 * the session daemon and print it to user.
470 */
471 static int process_opt_list_sessions(void)
472 {
473 int ret, count, i;
474 struct lttng_session *sessions;
475
476 count = lttng_list_sessions(&sessions);
477 DBG("Session count %d", count);
478 if (count < 0) {
479 ret = count;
480 goto error;
481 }
482
483 MSG("Available sessions (UUIDs):");
484 for (i = 0; i < count; i++) {
485 MSG(" %d) %s", i+1, sessions[i].name);
486 }
487
488 free(sessions);
489 MSG("\nTo select a session, use -s, --session UUID.");
490
491 return 0;
492
493 error:
494 return ret;
495 }
496
497 /*
498 * process_opt_list_apps
499 *
500 * Get the UST traceable pid list and print
501 * them to the user.
502 */
503 static int process_opt_list_apps(void)
504 {
505 int i, ret, count;
506 pid_t *pids;
507 char *cmdline;
508
509 count = lttng_ust_list_apps(&pids);
510 if (count < 0) {
511 ret = count;
512 goto error;
513 }
514
515 MSG("LTTng UST traceable application [name (pid)]:");
516 for (i=0; i < count; i++) {
517 cmdline = get_cmdline_by_pid(pids[i]);
518 if (cmdline == NULL) {
519 MSG("\t(not running) (%d)", pids[i]);
520 continue;
521 }
522 MSG("\t%s (%d)", cmdline, pids[i]);
523 free(cmdline);
524 }
525
526 /* Allocated by lttng_ust_list_apps() */
527 free(pids);
528
529 return 0;
530
531 error:
532 return ret;
533 }
534
535 /*
536 * get_cmdline_by_pid
537 *
538 * Get command line from /proc for a specific pid.
539 *
540 * On success, return an allocated string pointer pointing to
541 * the proc cmdline.
542 * On error, return NULL.
543 */
544 static char *get_cmdline_by_pid(pid_t pid)
545 {
546 int ret;
547 FILE *fp;
548 char *cmdline = NULL;
549 char path[24]; /* Can't go bigger than /proc/65535/cmdline */
550
551 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
552 fp = fopen(path, "r");
553 if (fp == NULL) {
554 goto end;
555 }
556
557 /* Caller must free() *cmdline */
558 cmdline = malloc(PATH_MAX);
559 ret = fread(cmdline, 1, PATH_MAX, fp);
560 fclose(fp);
561
562 end:
563 return cmdline;
564 }
565
566 /*
567 * validate_options
568 *
569 * Make sure that all options passed to the command line are compatible with
570 * each others.
571 *
572 * On error, return -1
573 * On success, return 0
574 */
575 static int validate_options(void)
576 {
577 /* If listing options, jump validation */
578 if (opt_list_apps || opt_list_session) {
579 goto end;
580 }
581 /* Conflicting command */
582 if (opt_start_trace && opt_stop_trace) {
583 ERR("Can't use --start and --stop together.");
584 goto error;
585 /* If no PID specified and trace_kernel is off */
586 } else if ((opt_trace_pid == 0 && !opt_trace_kernel) &&
587 (opt_create_trace || opt_start_trace || opt_stop_trace || opt_destroy_trace)) {
588 ERR("Please specify for which tracer (-k or -p PID).");
589 goto error;
590 /* List traces, we need a session name */
591 } else if (opt_list_traces && opt_session_name == NULL) {
592 ERR("Can't use -t without -s, --session option.");
593 goto error;
594 /* Can't set event for both kernel and userspace at the same time */
595 } else if (opt_event_list != NULL && (opt_trace_kernel && opt_trace_pid)) {
596 ERR("Please don't use --event for both kernel and userspace.\nOne at a time to enable events.");
597 goto error;
598 /* Don't need a trace name for kernel tracig */
599 } else if (opt_trace_name != NULL && opt_trace_kernel) {
600 ERR("For action on a kernel trace, please don't specify a trace name.");
601 goto error;
602 } else if (opt_destroy_trace && opt_session_name == NULL) {
603 ERR("Please specify a session in order to destroy a trace");
604 goto error;
605 } else if (opt_create_trace || opt_destroy_trace) {
606 /* Both kernel and user-space are denied for these options */
607 if (opt_trace_pid != 0 && opt_trace_kernel) {
608 ERR("Kernel and user-space trace creation and destruction can't be used together.");
609 goto error;
610 /* Need a trace name for user-space tracing */
611 } else if (opt_trace_name == NULL && opt_trace_pid != 0) {
612 ERR("Please specify a trace name for user-space tracing");
613 goto error;
614 }
615 } else if (opt_stop_trace && opt_trace_pid != 0 && opt_trace_name == NULL) {
616 ERR("Please specify a trace name for user-space tracing");
617 goto error;
618 } else if (opt_stop_trace && opt_session_name == NULL) {
619 ERR("Please specify a session to stop tracing");
620 goto error;
621 }
622
623 /* If start trace, auto start tracing */
624 if (opt_start_trace || opt_event_list != NULL) {
625 DBG("Requesting auto tracing");
626 auto_trace = 1;
627 }
628
629 /* If no session, auto create one */
630 if (opt_session_name == NULL) {
631 DBG("Requesting an auto session creation");
632 auto_session = 1;
633 }
634
635 end:
636 return 0;
637
638 error:
639 return -1;
640 }
641
642 /*
643 * spawn_sessiond
644 *
645 * Spawn a session daemon by forking and execv.
646 */
647 static int spawn_sessiond(char *pathname)
648 {
649 int ret = 0;
650 pid_t pid;
651
652 MSG("Spawning session daemon");
653 pid = fork();
654 if (pid == 0) {
655 /*
656 * Spawn session daemon and tell
657 * it to signal us when ready.
658 */
659 execlp(pathname, "ltt-sessiond", "--sig-parent", "--quiet", NULL);
660 /* execlp only returns if error happened */
661 if (errno == ENOENT) {
662 ERR("No session daemon found. Use --sessiond-path.");
663 } else {
664 perror("execlp");
665 }
666 kill(getppid(), SIGTERM); /* unpause parent */
667 exit(EXIT_FAILURE);
668 } else if (pid > 0) {
669 /* Wait for ltt-sessiond to start */
670 pause();
671 goto end;
672 } else {
673 perror("fork");
674 ret = -1;
675 goto end;
676 }
677
678 end:
679 return ret;
680 }
681
682 /*
683 * check_ltt_sessiond
684 *
685 * Check if the session daemon is available using
686 * the liblttngctl API for the check. If not, try to
687 * spawn a daemon.
688 */
689 static int check_ltt_sessiond(void)
690 {
691 int ret;
692 char *pathname = NULL, *alloc_pathname = NULL;
693
694 ret = lttng_session_daemon_alive();
695 if (ret == 0) { /* not alive */
696 /* Try command line option path */
697 if (opt_sessiond_path != NULL) {
698 ret = access(opt_sessiond_path, F_OK | X_OK);
699 if (ret < 0) {
700 ERR("No such file: %s", opt_sessiond_path);
701 goto end;
702 }
703 pathname = opt_sessiond_path;
704 } else {
705 /* Try LTTNG_SESSIOND_PATH env variable */
706 pathname = getenv(LTTNG_SESSIOND_PATH_ENV);
707 }
708
709 /* Let's rock and roll */
710 if (pathname == NULL) {
711 ret = asprintf(&alloc_pathname, "ltt-sessiond");
712 if (ret < 0) {
713 goto end;
714 }
715 pathname = alloc_pathname;
716 }
717
718 ret = spawn_sessiond(pathname);
719 free(alloc_pathname);
720 if (ret < 0) {
721 ERR("Problem occurs when starting %s", pathname);
722 goto end;
723 }
724 }
725
726 end:
727 return ret;
728 }
729
730 /*
731 * set_signal_handler
732 *
733 * Setup signal handler for SIGCHLD and SIGTERM.
734 */
735 static int set_signal_handler(void)
736 {
737 int ret = 0;
738 struct sigaction sa;
739 sigset_t sigset;
740
741 if ((ret = sigemptyset(&sigset)) < 0) {
742 perror("sigemptyset");
743 goto end;
744 }
745
746 sa.sa_handler = sighandler;
747 sa.sa_mask = sigset;
748 sa.sa_flags = 0;
749 if ((ret = sigaction(SIGCHLD, &sa, NULL)) < 0) {
750 perror("sigaction");
751 goto end;
752 }
753
754 if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
755 perror("sigaction");
756 goto end;
757 }
758
759 end:
760 return ret;
761 }
762
763 /*
764 * sighandler
765 *
766 * Signal handler for the daemon
767 */
768 static void sighandler(int sig)
769 {
770 switch (sig) {
771 case SIGTERM:
772 DBG("SIGTERM catched");
773 clean_exit(EXIT_FAILURE);
774 break;
775 case SIGCHLD:
776 /* Notify is done */
777 DBG("SIGCHLD catched");
778 break;
779 default:
780 DBG("Unknown signal %d catched", sig);
781 break;
782 }
783
784 return;
785 }
786
787 /*
788 * clean_exit
789 */
790 void clean_exit(int code)
791 {
792 DBG("Clean exit");
793 if (session_name) {
794 free(session_name);
795 }
796
797 exit(code);
798 }
799
800 /*
801 * main
802 */
803 int main(int argc, char *argv[])
804 {
805 int ret;
806
807 progname = argv[0] ? argv[0] : "lttng";
808
809 /* For Mathieu Desnoyers aka Dr Tracing */
810 if (strncmp(progname, "drtrace", 7) == 0) {
811 MSG("%c[%d;%dmWelcome back Dr Tracing!%c[%dm\n\n", 27,1,33,27,0);
812 }
813
814 ret = parse_args(argc, (const char **) argv);
815 if (ret < 0) {
816 clean_exit(EXIT_FAILURE);
817 }
818
819 ret = validate_options();
820 if (ret < 0) {
821 return EXIT_FAILURE;
822 }
823
824 ret = set_signal_handler();
825 if (ret < 0) {
826 clean_exit(ret);
827 }
828
829 if (opt_tracing_group != NULL) {
830 DBG("Set tracing group to '%s'", opt_tracing_group);
831 lttng_set_tracing_group(opt_tracing_group);
832 }
833
834 /* If ask for kernel tracing, need root perms */
835 if (opt_trace_kernel) {
836 DBG("Kernel tracing activated");
837 if (getuid() != 0) {
838 ERR("%s must be setuid root", progname);
839 clean_exit(-EPERM);
840 }
841 }
842
843 /* Check if the lttng session daemon is running.
844 * If no, a daemon will be spawned.
845 */
846 if (opt_no_sessiond == 0 && (check_ltt_sessiond() < 0)) {
847 clean_exit(EXIT_FAILURE);
848 }
849
850 ret = process_client_opt();
851 if (ret < 0) {
852 clean_exit(ret);
853 }
854
855 clean_exit(0);
856
857 return 0;
858 }
This page took 0.045397 seconds and 4 git commands to generate.