Change lttng API to use a lttng_handle
[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
26 #include "../cmd.h"
27
28 static int opt_pid;
29 static int opt_userspace;
30 static int opt_kernel;
31 static char *opt_channel;
32 static int opt_domain;
33
34 const char *indent4 = " ";
35 const char *indent6 = " ";
36 const char *indent8 = " ";
37
38 enum {
39 OPT_HELP = 1,
40 };
41
42 static struct lttng_handle *handle;
43
44 static struct poptOption long_options[] = {
45 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
46 {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0},
47 {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0},
48 {"userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0},
49 {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0},
50 {"channel", 'c', POPT_ARG_STRING, &opt_channel, 0, 0, 0},
51 {"domain", 'd', POPT_ARG_VAL, &opt_domain, 1, 0, 0},
52 {0, 0, 0, 0, 0, 0, 0}
53 };
54
55 /*
56 * usage
57 */
58 static void usage(FILE *ofp)
59 {
60 fprintf(ofp, "usage: lttng list [[-k] [-u] [-p PID] [SESSION [<options>]]]\n");
61 fprintf(ofp, "\n");
62 fprintf(ofp, "With no arguments, list available tracing session(s)\n");
63 fprintf(ofp, "\n");
64 fprintf(ofp, "With -k alone, list available kernel events\n");
65 fprintf(ofp, "With -u alone, list available userspace events\n");
66 fprintf(ofp, "\n");
67 fprintf(ofp, " -h, --help Show this help\n");
68 fprintf(ofp, " -k, --kernel Select kernel domain\n");
69 fprintf(ofp, " -u, --userspace Select user-space domain.\n");
70 fprintf(ofp, " -p, --pid PID List user-space events by PID\n");
71 fprintf(ofp, "\n");
72 fprintf(ofp, "Options:\n");
73 fprintf(ofp, " -c, --channel NAME List details of a channel\n");
74 fprintf(ofp, " -d, --domain List available domain(s)\n");
75 fprintf(ofp, "\n");
76 }
77
78 /*
79 * Get command line from /proc for a specific pid.
80 *
81 * On success, return an allocated string pointer to the proc cmdline.
82 * On error, return NULL.
83 */
84 #ifdef DISABLE
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 #endif /* DISABLE */
110
111 /*
112 * Ask for all trace events in the kernel and pretty print them.
113 */
114 static int list_kernel_events(void)
115 {
116 int i, size;
117 struct lttng_event *event_list;
118
119 DBG("Getting all tracing events");
120
121 size = lttng_list_tracepoints(handle, &event_list);
122 if (size < 0) {
123 ERR("Unable to list kernel events");
124 return size;
125 }
126
127 MSG("Kernel events:\n-------------");
128
129 for (i = 0; i < size; i++) {
130 MSG(" %s", event_list[i].name);
131 }
132
133 free(event_list);
134
135 return CMD_SUCCESS;
136 }
137
138 /*
139 * List events of channel of session and domain.
140 */
141 static int list_events(const char *channel_name)
142 {
143 int ret, count, i;
144 struct lttng_event *events = NULL;
145
146 count = lttng_list_events(handle, channel_name, &events);
147 if (count < 0) {
148 ret = count;
149 goto error;
150 }
151
152 MSG("\n%sEvents:", indent4);
153 if (count == 0) {
154 MSG("%sNone", indent6);
155 goto end;
156 }
157
158 for (i = 0; i < count; i++) {
159 switch (events[i].type) {
160 case LTTNG_EVENT_TRACEPOINT:
161 MSG("%s%s (type: tracepoint) [enabled: %d]", indent6,
162 events[i].name, events[i].enabled);
163 break;
164 case LTTNG_EVENT_PROBE:
165 MSG("%s%s (type: probe) [enabled: %d]", indent6,
166 events[i].name, events[i].enabled);
167 if (events[i].attr.probe.addr != 0) {
168 MSG("%saddr: 0x%" PRIx64, indent8, events[i].attr.probe.addr);
169 } else {
170 MSG("%soffset: 0x%" PRIx64, indent8, events[i].attr.probe.offset);
171 MSG("%ssymbol: %s", indent8, events[i].attr.probe.symbol_name);
172 }
173 break;
174 case LTTNG_EVENT_FUNCTION:
175 case LTTNG_EVENT_FUNCTION_ENTRY:
176 MSG("%s%s (type: function) [enabled: %d]", indent6,
177 events[i].name, events[i].enabled);
178 MSG("%ssymbol: \"%s\"", indent8, events[i].attr.ftrace.symbol_name);
179 break;
180 }
181 }
182
183 MSG("");
184
185 end:
186 if (events) {
187 free(events);
188 }
189 ret = CMD_SUCCESS;
190
191 error:
192 return ret;
193 }
194
195 /*
196 * Pretty print channel
197 */
198 static void print_channel(struct lttng_channel *channel)
199 {
200 MSG("- %s (enabled: %d):\n", channel->name, channel->enabled);
201
202 MSG("%sAttributes:", indent4);
203 MSG("%soverwrite mode: %d", indent6, channel->attr.overwrite);
204 MSG("%ssubbufers size: %" PRIu64, indent6, channel->attr.subbuf_size);
205 MSG("%snumber of subbufers: %" PRIu64, indent6, channel->attr.num_subbuf);
206 MSG("%sswitch timer interval: %u", indent6, channel->attr.switch_timer_interval);
207 MSG("%sread timer interval: %u", indent6, channel->attr.read_timer_interval);
208 switch (channel->attr.output) {
209 case LTTNG_EVENT_SPLICE:
210 MSG("%soutput: splice()", indent6);
211 break;
212 case LTTNG_EVENT_MMAP:
213 MSG("%soutput: mmap()", indent6);
214 break;
215 }
216 }
217
218 /*
219 * List channel(s) of session and domain.
220 *
221 * If channel_name is NULL, all channels are listed.
222 */
223 static int list_channels(const char *channel_name)
224 {
225 int count, i, ret = CMD_SUCCESS;
226 unsigned int chan_found = 0;
227 struct lttng_channel *channels = NULL;
228
229 DBG("Listing channel(s) (%s)", channel_name);
230
231 count = lttng_list_channels(handle, &channels);
232 if (count < 0) {
233 ret = count;
234 goto error;
235 } else if (count == 0) {
236 MSG("No channel found");
237 goto end;
238 }
239
240 if (channel_name == NULL) {
241 MSG("Channels:\n-------------");
242 }
243
244 for (i = 0; i < count; i++) {
245 if (channel_name != NULL) {
246 if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) {
247 chan_found = 1;
248 } else {
249 continue;
250 }
251 }
252 print_channel(&channels[i]);
253
254 /* Listing events per channel */
255 ret = list_events(channels[i].name);
256 if (ret < 0) {
257 MSG("%s", lttng_get_readable_code(ret));
258 }
259
260 if (chan_found) {
261 break;
262 }
263 }
264
265 if (!chan_found && channel_name != NULL) {
266 MSG("Channel %s not found", channel_name);
267 }
268
269 end:
270 free(channels);
271 ret = CMD_SUCCESS;
272
273 error:
274 return ret;
275 }
276
277 /*
278 * List available tracing session. List only basic information.
279 *
280 * If session_name is NULL, all sessions are listed.
281 */
282 static int list_sessions(const char *session_name)
283 {
284 int ret, count, i;
285 unsigned int session_found = 0;
286 struct lttng_session *sessions;
287
288 count = lttng_list_sessions(&sessions);
289 DBG("Session count %d", count);
290 if (count < 0) {
291 ret = count;
292 goto error;
293 }
294
295 if (session_name == NULL) {
296 MSG("Available tracing sessions:");
297 }
298
299 for (i = 0; i < count; i++) {
300 if (session_name != NULL) {
301 if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) {
302 session_found = 1;
303 MSG("Tracing session %s:", session_name);
304 MSG("%sTrace path: %s\n", indent4, sessions[i].path);
305 break;
306 }
307 continue;
308 }
309
310 MSG(" %d) %s (%s)", i + 1, sessions[i].name, sessions[i].path);
311
312 if (session_found) {
313 break;
314 }
315 }
316
317 free(sessions);
318
319 if (!session_found && session_name != NULL) {
320 MSG("Session %s not found", session_name);
321 }
322
323 if (session_name == NULL) {
324 MSG("\nUse lttng list <session_name> for more details");
325 }
326
327 return CMD_SUCCESS;
328
329 error:
330 return ret;
331 }
332
333 /*
334 * List available domain(s) for a session.
335 */
336 static int list_domains(void)
337 {
338 int i, count, ret = CMD_SUCCESS;
339 struct lttng_domain *domains = NULL;
340
341 MSG("Domains:\n-------------");
342
343 count = lttng_list_domains(handle, &domains);
344 if (count < 0) {
345 ret = count;
346 goto error;
347 } else if (count == 0) {
348 MSG(" None");
349 goto end;
350 }
351
352 for (i = 0; i < count; i++) {
353 switch (domains[i].type) {
354 case LTTNG_DOMAIN_KERNEL:
355 MSG(" - Kernel");
356 default:
357 break;
358 }
359 }
360
361 end:
362 free(domains);
363
364 error:
365 return ret;
366 }
367
368 /*
369 * The 'list <options>' first level command
370 */
371 int cmd_list(int argc, const char **argv)
372 {
373 int opt, i, ret = CMD_SUCCESS;
374 const char *session_name;
375 static poptContext pc;
376 struct lttng_domain domain;
377 struct lttng_domain *domains = NULL;
378
379 if (argc < 1) {
380 usage(stderr);
381 goto end;
382 }
383
384 pc = poptGetContext(NULL, argc, argv, long_options, 0);
385 poptReadDefaultConfig(pc, 0);
386
387 while ((opt = poptGetNextOpt(pc)) != -1) {
388 switch (opt) {
389 case OPT_HELP:
390 usage(stderr);
391 goto end;
392 default:
393 usage(stderr);
394 ret = CMD_UNDEFINED;
395 goto end;
396 }
397 }
398
399 if (opt_userspace || opt_pid != 0) {
400 MSG("*** Userspace tracing not implemented ***\n");
401 }
402
403 /* Get session name (trailing argument) */
404 session_name = poptGetArg(pc);
405 DBG("Session name: %s", session_name);
406
407 if (opt_kernel) {
408 domain.type = LTTNG_DOMAIN_KERNEL;
409 }
410
411 handle = lttng_create_handle(session_name, &domain);
412 if (handle == NULL) {
413 goto end;
414 }
415
416 if (session_name == NULL) {
417 if (opt_kernel) {
418 ret = list_kernel_events();
419 if (ret < 0) {
420 goto end;
421 }
422 } else {
423 ret = list_sessions(NULL);
424 if (ret < 0) {
425 goto end;
426 }
427 }
428 } else {
429 /* List session attributes */
430 ret = list_sessions(session_name);
431 if (ret < 0) {
432 goto end;
433 }
434
435 /* Domain listing */
436 if (opt_domain) {
437 ret = list_domains();
438 goto end;
439 }
440
441 if (opt_kernel) {
442 /* Channel listing */
443 ret = list_channels(opt_channel);
444 if (ret < 0) {
445 goto end;
446 }
447 } else if (opt_userspace) {
448 /* TODO: Userspace domain */
449 } else {
450 /* We want all domain(s) */
451 ret = lttng_list_domains(handle, &domains);
452 if (ret < 0) {
453 goto end;
454 }
455
456 for (i = 0; i < ret; i++) {
457 switch (domains[i].type) {
458 case LTTNG_DOMAIN_KERNEL:
459 MSG("=== Domain: Kernel ===\n");
460 break;
461 default:
462 MSG("=== Domain: Unimplemented ===\n");
463 break;
464 }
465
466 /* Clean handle before creating a new one */
467 lttng_destroy_handle(handle);
468
469 handle = lttng_create_handle(session_name, &domains[i]);
470 if (handle == NULL) {
471 goto end;
472 }
473
474 ret = list_channels(opt_channel);
475 if (ret < 0) {
476 goto end;
477 }
478 }
479 }
480 }
481
482 end:
483 if (domains) {
484 free(domains);
485 }
486 lttng_destroy_handle(handle);
487
488 return ret;
489 }
This page took 0.04172 seconds and 5 git commands to generate.