808c2a549edbe9519a3b90387fbff845547020bb
[lttng-tools.git] / lttng / commands / list.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
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; only version 2
7 * of the License.
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
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #define _GNU_SOURCE
20 #include <inttypes.h>
21 #include <popt.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26
27 #include "../cmd.h"
28
29 static int opt_pid;
30 static int opt_userspace;
31 static int opt_kernel;
32 static char *opt_channel;
33 static int opt_domain;
34
35 const char *indent4 = " ";
36 const char *indent6 = " ";
37 const char *indent8 = " ";
38
39 enum {
40 OPT_HELP = 1,
41 };
42
43 static struct lttng_handle *handle;
44
45 static struct poptOption long_options[] = {
46 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
47 {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0},
48 {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0},
49 {"userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0},
50 {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0},
51 {"channel", 'c', POPT_ARG_STRING, &opt_channel, 0, 0, 0},
52 {"domain", 'd', POPT_ARG_VAL, &opt_domain, 1, 0, 0},
53 {0, 0, 0, 0, 0, 0, 0}
54 };
55
56 /*
57 * usage
58 */
59 static void usage(FILE *ofp)
60 {
61 fprintf(ofp, "usage: lttng list [[-k] [-u] [-p PID] [SESSION [<options>]]]\n");
62 fprintf(ofp, "\n");
63 fprintf(ofp, "With no arguments, list available tracing session(s)\n");
64 fprintf(ofp, "\n");
65 fprintf(ofp, "With -k alone, list available kernel events\n");
66 fprintf(ofp, "With -u alone, list available userspace events\n");
67 fprintf(ofp, "\n");
68 fprintf(ofp, " -h, --help Show this help\n");
69 fprintf(ofp, " -k, --kernel Select kernel domain\n");
70 fprintf(ofp, " -u, --userspace Select user-space domain.\n");
71 fprintf(ofp, " -p, --pid PID List user-space events by PID\n");
72 fprintf(ofp, "\n");
73 fprintf(ofp, "Options:\n");
74 fprintf(ofp, " -c, --channel NAME List details of a channel\n");
75 fprintf(ofp, " -d, --domain List available domain(s)\n");
76 fprintf(ofp, "\n");
77 }
78
79 /*
80 * Get command line from /proc for a specific pid.
81 *
82 * On success, return an allocated string pointer to the proc cmdline.
83 * On error, return NULL.
84 */
85 static char *get_cmdline_by_pid(pid_t pid)
86 {
87 int ret;
88 FILE *fp;
89 char *cmdline = NULL;
90 char path[24]; /* Can't go bigger than /proc/65535/cmdline */
91
92 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
93 fp = fopen(path, "r");
94 if (fp == NULL) {
95 goto end;
96 }
97
98 /* Caller must free() *cmdline */
99 cmdline = malloc(PATH_MAX);
100 ret = fread(cmdline, 1, PATH_MAX, fp);
101 if (ret < 0) {
102 perror("fread proc list");
103 }
104 fclose(fp);
105
106 end:
107 return cmdline;
108 }
109
110 /*
111 * Pretty print single event.
112 */
113 static void print_events(struct lttng_event *event)
114 {
115 switch (event->type) {
116 case LTTNG_EVENT_TRACEPOINT:
117 MSG("%s%s (type: tracepoint) [enabled: %d]", indent6,
118 event->name, event->enabled);
119 break;
120 case LTTNG_EVENT_PROBE:
121 MSG("%s%s (type: probe) [enabled: %d]", indent6,
122 event->name, event->enabled);
123 if (event->attr.probe.addr != 0) {
124 MSG("%saddr: 0x%" PRIx64, indent8, event->attr.probe.addr);
125 } else {
126 MSG("%soffset: 0x%" PRIx64, indent8, event->attr.probe.offset);
127 MSG("%ssymbol: %s", indent8, event->attr.probe.symbol_name);
128 }
129 break;
130 case LTTNG_EVENT_FUNCTION:
131 case LTTNG_EVENT_FUNCTION_ENTRY:
132 MSG("%s%s (type: function) [enabled: %d]", indent6,
133 event->name, event->enabled);
134 MSG("%ssymbol: \"%s\"", indent8, event->attr.ftrace.symbol_name);
135 break;
136 case LTTNG_EVENT_SYSCALL:
137 MSG("%s (type: syscall) [enabled: %d]", indent6,
138 event->enabled);
139 break;
140 case LTTNG_EVENT_NOOP:
141 MSG("%s (type: noop) [enabled: %d]", indent6,
142 event->enabled);
143 break;
144 case LTTNG_EVENT_ALL:
145 /* We should never have "all" events in list. */
146 assert(0);
147 break;
148 }
149 }
150
151 /*
152 * Ask session daemon for all user space tracepoints available.
153 */
154 static int list_ust_events(void)
155 {
156 int i, size;
157 struct lttng_domain domain;
158 struct lttng_handle *handle;
159 struct lttng_event *event_list;
160 pid_t cur_pid = 0;
161
162 DBG("Getting UST tracing events");
163
164 domain.type = LTTNG_DOMAIN_UST;
165
166 handle = lttng_create_handle(NULL, &domain);
167 if (handle == NULL) {
168 goto error;
169 }
170
171 size = lttng_list_tracepoints(handle, &event_list);
172 if (size < 0) {
173 ERR("Unable to list UST events");
174 return size;
175 }
176
177 MSG("UST events:\n-------------");
178
179 if (size == 0) {
180 MSG("None");
181 }
182
183 for (i = 0; i < size; i++) {
184 if (cur_pid != event_list[i].pid) {
185 cur_pid = event_list[i].pid;
186 MSG("\nPID: %d - Name: %s", cur_pid, get_cmdline_by_pid(cur_pid));
187 }
188 print_events(&event_list[i]);
189 }
190
191 MSG("");
192
193 free(event_list);
194
195 return CMD_SUCCESS;
196
197 error:
198 return -1;
199 }
200
201 /*
202 * Ask for all trace events in the kernel and pretty print them.
203 */
204 static int list_kernel_events(void)
205 {
206 int i, size;
207 struct lttng_domain domain;
208 struct lttng_handle *handle;
209 struct lttng_event *event_list;
210
211 DBG("Getting kernel tracing events");
212
213 domain.type = LTTNG_DOMAIN_KERNEL;
214
215 handle = lttng_create_handle(NULL, &domain);
216 if (handle == NULL) {
217 goto error;
218 }
219
220 size = lttng_list_tracepoints(handle, &event_list);
221 if (size < 0) {
222 ERR("Unable to list kernel events");
223 return size;
224 }
225
226 MSG("Kernel events:\n-------------");
227
228 for (i = 0; i < size; i++) {
229 print_events(&event_list[i]);
230 }
231
232 MSG("");
233
234 free(event_list);
235
236 return CMD_SUCCESS;
237
238 error:
239 return -1;
240 }
241
242 /*
243 * List events of channel of session and domain.
244 */
245 static int list_events(const char *channel_name)
246 {
247 int ret, count, i;
248 struct lttng_event *events = NULL;
249
250 count = lttng_list_events(handle, channel_name, &events);
251 if (count < 0) {
252 ret = count;
253 goto error;
254 }
255
256 MSG("\n%sEvents:", indent4);
257 if (count == 0) {
258 MSG("%sNone", indent6);
259 goto end;
260 }
261
262 for (i = 0; i < count; i++) {
263 print_events(&events[i]);
264 }
265
266 MSG("");
267
268 end:
269 if (events) {
270 free(events);
271 }
272 ret = CMD_SUCCESS;
273
274 error:
275 return ret;
276 }
277
278 /*
279 * Pretty print channel
280 */
281 static void print_channel(struct lttng_channel *channel)
282 {
283 MSG("- %s (enabled: %d):\n", channel->name, channel->enabled);
284
285 MSG("%sAttributes:", indent4);
286 MSG("%soverwrite mode: %d", indent6, channel->attr.overwrite);
287 MSG("%ssubbufers size: %" PRIu64, indent6, channel->attr.subbuf_size);
288 MSG("%snumber of subbufers: %" PRIu64, indent6, channel->attr.num_subbuf);
289 MSG("%sswitch timer interval: %u", indent6, channel->attr.switch_timer_interval);
290 MSG("%sread timer interval: %u", indent6, channel->attr.read_timer_interval);
291 switch (channel->attr.output) {
292 case LTTNG_EVENT_SPLICE:
293 MSG("%soutput: splice()", indent6);
294 break;
295 case LTTNG_EVENT_MMAP:
296 MSG("%soutput: mmap()", indent6);
297 break;
298 }
299 }
300
301 /*
302 * List channel(s) of session and domain.
303 *
304 * If channel_name is NULL, all channels are listed.
305 */
306 static int list_channels(const char *channel_name)
307 {
308 int count, i, ret = CMD_SUCCESS;
309 unsigned int chan_found = 0;
310 struct lttng_channel *channels = NULL;
311
312 DBG("Listing channel(s) (%s)", channel_name);
313
314 count = lttng_list_channels(handle, &channels);
315 if (count < 0) {
316 ret = count;
317 goto error;
318 } else if (count == 0) {
319 MSG("No channel found");
320 goto end;
321 }
322
323 if (channel_name == NULL) {
324 MSG("Channels:\n-------------");
325 }
326
327 for (i = 0; i < count; i++) {
328 if (channel_name != NULL) {
329 if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) {
330 chan_found = 1;
331 } else {
332 continue;
333 }
334 }
335 print_channel(&channels[i]);
336
337 /* Listing events per channel */
338 ret = list_events(channels[i].name);
339 if (ret < 0) {
340 MSG("%s", lttng_strerror(ret));
341 }
342
343 if (chan_found) {
344 break;
345 }
346 }
347
348 if (!chan_found && channel_name != NULL) {
349 MSG("Channel %s not found", channel_name);
350 }
351
352 end:
353 free(channels);
354 ret = CMD_SUCCESS;
355
356 error:
357 return ret;
358 }
359
360 /*
361 * List available tracing session. List only basic information.
362 *
363 * If session_name is NULL, all sessions are listed.
364 */
365 static int list_sessions(const char *session_name)
366 {
367 int ret, count, i;
368 unsigned int session_found = 0;
369 struct lttng_session *sessions;
370
371 count = lttng_list_sessions(&sessions);
372 DBG("Session count %d", count);
373 if (count < 0) {
374 ret = count;
375 goto error;
376 }
377
378 if (session_name == NULL) {
379 MSG("Available tracing sessions:");
380 }
381
382 for (i = 0; i < count; i++) {
383 if (session_name != NULL) {
384 if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) {
385 session_found = 1;
386 MSG("Tracing session %s:", session_name);
387 MSG("%sTrace path: %s\n", indent4, sessions[i].path);
388 break;
389 }
390 continue;
391 }
392
393 MSG(" %d) %s (%s)", i + 1, sessions[i].name, sessions[i].path);
394
395 if (session_found) {
396 break;
397 }
398 }
399
400 free(sessions);
401
402 if (!session_found && session_name != NULL) {
403 MSG("Session %s not found", session_name);
404 }
405
406 if (session_name == NULL) {
407 MSG("\nUse lttng list <session_name> for more details");
408 }
409
410 return CMD_SUCCESS;
411
412 error:
413 return ret;
414 }
415
416 /*
417 * List available domain(s) for a session.
418 */
419 static int list_domains(void)
420 {
421 int i, count, ret = CMD_SUCCESS;
422 struct lttng_domain *domains = NULL;
423
424 MSG("Domains:\n-------------");
425
426 count = lttng_list_domains(handle, &domains);
427 if (count < 0) {
428 ret = count;
429 goto error;
430 } else if (count == 0) {
431 MSG(" None");
432 goto end;
433 }
434
435 for (i = 0; i < count; i++) {
436 switch (domains[i].type) {
437 case LTTNG_DOMAIN_KERNEL:
438 MSG(" - Kernel");
439 break;
440 case LTTNG_DOMAIN_UST:
441 MSG(" - UST global");
442 break;
443 default:
444 break;
445 }
446 }
447
448 end:
449 free(domains);
450
451 error:
452 return ret;
453 }
454
455 /*
456 * The 'list <options>' first level command
457 */
458 int cmd_list(int argc, const char **argv)
459 {
460 int opt, i, ret = CMD_SUCCESS;
461 unsigned int nb_domain;
462 const char *session_name;
463 static poptContext pc;
464 struct lttng_domain domain;
465 struct lttng_domain *domains = NULL;
466
467 if (argc < 1) {
468 usage(stderr);
469 goto end;
470 }
471
472 pc = poptGetContext(NULL, argc, argv, long_options, 0);
473 poptReadDefaultConfig(pc, 0);
474
475 while ((opt = poptGetNextOpt(pc)) != -1) {
476 switch (opt) {
477 case OPT_HELP:
478 usage(stderr);
479 goto end;
480 default:
481 usage(stderr);
482 ret = CMD_UNDEFINED;
483 goto end;
484 }
485 }
486
487 if (opt_pid != 0) {
488 MSG("*** Userspace tracing not implemented for PID ***\n");
489 }
490
491 /* Get session name (trailing argument) */
492 session_name = poptGetArg(pc);
493 DBG2("Session name: %s", session_name);
494
495 if (opt_kernel) {
496 domain.type = LTTNG_DOMAIN_KERNEL;
497 } else if (opt_userspace) {
498 DBG2("Listing userspace global domain");
499 domain.type = LTTNG_DOMAIN_UST;
500 }
501
502 handle = lttng_create_handle(session_name, &domain);
503 if (handle == NULL) {
504 goto end;
505 }
506
507 if (session_name == NULL) {
508 if (!opt_kernel && !opt_userspace) {
509 ret = list_sessions(NULL);
510 if (ret < 0) {
511 goto end;
512 }
513 }
514 if (opt_kernel) {
515 ret = list_kernel_events();
516 if (ret < 0) {
517 goto end;
518 }
519 }
520 if (opt_userspace) {
521 ret = list_ust_events();
522 if (ret < 0) {
523 goto end;
524 }
525 }
526 } else {
527 /* List session attributes */
528 ret = list_sessions(session_name);
529 if (ret < 0) {
530 goto end;
531 }
532
533 /* Domain listing */
534 if (opt_domain) {
535 ret = list_domains();
536 goto end;
537 }
538
539 if (opt_kernel) {
540 /* Channel listing */
541 ret = list_channels(opt_channel);
542 if (ret < 0) {
543 goto end;
544 }
545 } else {
546 /* We want all domain(s) */
547 nb_domain = lttng_list_domains(handle, &domains);
548 if (nb_domain < 0) {
549 ret = nb_domain;
550 goto end;
551 }
552
553 for (i = 0; i < nb_domain; i++) {
554 switch (domains[i].type) {
555 case LTTNG_DOMAIN_KERNEL:
556 MSG("=== Domain: Kernel ===\n");
557 break;
558 case LTTNG_DOMAIN_UST:
559 MSG("=== Domain: UST global ===\n");
560 break;
561 default:
562 MSG("=== Domain: Unimplemented ===\n");
563 break;
564 }
565
566 /* Clean handle before creating a new one */
567 lttng_destroy_handle(handle);
568
569 handle = lttng_create_handle(session_name, &domains[i]);
570 if (handle == NULL) {
571 goto end;
572 }
573
574 ret = list_channels(opt_channel);
575 if (ret < 0) {
576 goto end;
577 }
578 }
579 }
580 }
581
582 end:
583 if (domains) {
584 free(domains);
585 }
586 lttng_destroy_handle(handle);
587
588 return ret;
589 }
This page took 0.039737 seconds and 3 git commands to generate.