Add string options for add-context
[lttng-tools.git] / lttng / commands / add_context.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 <ctype.h>
21 #include <popt.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28
29 #include <urcu/list.h>
30
31 #include "../cmd.h"
32 #include "../conf.h"
33 #include "../utils.h"
34
35 static char *opt_event_name;
36 static char *opt_channel_name;
37 static char *opt_session_name;
38 static int *opt_kernel;
39 static int opt_pid_all;
40 static int opt_userspace;
41 static char *opt_perf_type;
42 static char *opt_perf_id;
43 static pid_t opt_pid;
44
45 enum {
46 OPT_HELP = 1,
47 OPT_TYPE,
48 };
49
50 /*
51 * Taken from the LTTng ABI
52 */
53 enum context_type {
54 CONTEXT_PID = 0,
55 CONTEXT_PERF_COUNTER = 1,
56 CONTEXT_COMM = 2,
57 CONTEXT_PRIO = 3,
58 CONTEXT_NICE = 4,
59 CONTEXT_VPID = 5,
60 CONTEXT_TID = 6,
61 CONTEXT_VTID = 7,
62 CONTEXT_PPID = 8,
63 CONTEXT_VPPID = 9,
64 };
65
66 /*
67 * Taken from the Perf ABI (all enum perf_*)
68 */
69 enum perf_type {
70 PERF_TYPE_HARDWARE = 0,
71 PERF_TYPE_SOFTWARE = 1,
72 };
73
74 enum perf_count_hard {
75 PERF_COUNT_HW_CPU_CYCLES = 0,
76 PERF_COUNT_HW_INSTRUCTIONS = 1,
77 PERF_COUNT_HW_CACHE_REFERENCES = 2,
78 PERF_COUNT_HW_CACHE_MISSES = 3,
79 PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
80 PERF_COUNT_HW_BRANCH_MISSES = 5,
81 PERF_COUNT_HW_BUS_CYCLES = 6,
82 };
83
84 enum perf_count_soft {
85 PERF_COUNT_SW_CPU_CLOCK = 0,
86 PERF_COUNT_SW_TASK_CLOCK = 1,
87 PERF_COUNT_SW_PAGE_FAULTS = 2,
88 PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
89 PERF_COUNT_SW_CPU_MIGRATIONS = 4,
90 PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
91 PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
92 PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
93 PERF_COUNT_SW_EMULATION_FAULTS = 8,
94 };
95
96 static struct poptOption long_options[] = {
97 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
98 {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0},
99 {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0},
100 {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0},
101 {"event", 'e', POPT_ARG_STRING, &opt_event_name, 0, 0, 0},
102 {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0},
103 {"userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0},
104 {"all", 0, POPT_ARG_VAL, &opt_pid_all, 1, 0, 0},
105 {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0},
106 {"type", 't', POPT_ARG_STRING, 0, OPT_TYPE, 0, 0},
107 {"perf-type", 0, POPT_ARG_STRING, &opt_perf_type, 0, 0, 0},
108 {"perf-id", 0, POPT_ARG_STRING, &opt_perf_id, 0, 0, 0},
109 {0, 0, 0, 0, 0, 0, 0}
110 };
111
112 /*
113 * Context type for command line option parsing.
114 */
115 struct ctx_type {
116 int type;
117 struct cds_list_head list;
118 };
119
120 /*
121 * Perf counter type
122 */
123 static struct ctx_perf_type {
124 enum perf_type value;
125 char *symbol;
126 } ctx_perf_type[] = {
127 { PERF_TYPE_HARDWARE, "hw" },
128 { PERF_TYPE_SOFTWARE, "sw" },
129 };
130
131 /*
132 * Perf counter IDs
133 */
134 static struct ctx_perf {
135 enum perf_type type;
136 union {
137 enum perf_count_hard hard;
138 enum perf_count_soft soft;
139 } id;
140 char *symbol;
141 } ctx_perf[] = {
142 /* Hardware counter */
143 { PERF_TYPE_HARDWARE, .id.hard = PERF_COUNT_HW_CPU_CYCLES, "cpu_cycles" },
144 { PERF_TYPE_HARDWARE, .id.hard = PERF_COUNT_HW_INSTRUCTIONS, "instr" },
145 { PERF_TYPE_HARDWARE, .id.hard = PERF_COUNT_HW_CACHE_REFERENCES, "cache_refs" },
146 { PERF_TYPE_HARDWARE, .id.hard = PERF_COUNT_HW_CACHE_MISSES, "cache_miss" },
147 { PERF_TYPE_HARDWARE, .id.hard = PERF_COUNT_HW_BRANCH_INSTRUCTIONS, "branch_instr" },
148 { PERF_TYPE_HARDWARE, .id.hard = PERF_COUNT_HW_BRANCH_MISSES, "branch_miss" },
149 { PERF_TYPE_HARDWARE, .id.hard = PERF_COUNT_HW_BUS_CYCLES, "bus_cycles" },
150 /* Sofware counter */
151 { PERF_TYPE_SOFTWARE, .id.soft = PERF_COUNT_SW_CPU_CLOCK, "cpu_clock" },
152 { PERF_TYPE_SOFTWARE, .id.soft = PERF_COUNT_SW_TASK_CLOCK, "task_clock" },
153 { PERF_TYPE_SOFTWARE, .id.soft = PERF_COUNT_SW_PAGE_FAULTS, "page_faults" },
154 { PERF_TYPE_SOFTWARE, .id.soft = PERF_COUNT_SW_CONTEXT_SWITCHES, "ctx_switches" },
155 { PERF_TYPE_SOFTWARE, .id.soft = PERF_COUNT_SW_CPU_MIGRATIONS, "cpu_migration" },
156 { PERF_TYPE_SOFTWARE, .id.soft = PERF_COUNT_SW_PAGE_FAULTS_MIN, "page_faults_minor" },
157 { PERF_TYPE_SOFTWARE, .id.soft = PERF_COUNT_SW_PAGE_FAULTS_MAJ, "page_faults_major" },
158 { PERF_TYPE_SOFTWARE, .id.soft = PERF_COUNT_SW_ALIGNMENT_FAULTS, "align_faults" },
159 { PERF_TYPE_SOFTWARE, .id.soft = PERF_COUNT_SW_EMULATION_FAULTS, "emu_faults" },
160 /* Closure */
161 { -1, .id.hard = -1 , NULL },
162 };
163
164 /*
165 * Context options
166 */
167 static struct ctx_opts {
168 enum context_type value;
169 char *symbol;
170 } ctx_opts[] = {
171 { CONTEXT_PID, "pid" },
172 { CONTEXT_PERF_COUNTER, "perf" },
173 { CONTEXT_COMM, "comm" },
174 { CONTEXT_PRIO, "prio" },
175 { CONTEXT_NICE, "nice" },
176 { CONTEXT_VPID, "vpid" },
177 { CONTEXT_TID, "tid" },
178 { CONTEXT_VTID, "vtid" },
179 { CONTEXT_PPID, "ppid" },
180 { CONTEXT_VPPID, "vppid" },
181 { -1, NULL }, /* Closure */
182 };
183
184 /*
185 * List of context type. Use to enable multiple context on a single command
186 * line entry.
187 */
188 struct ctx_type_list {
189 struct cds_list_head head;
190 } ctx_type_list = {
191 .head = CDS_LIST_HEAD_INIT(ctx_type_list.head),
192 };
193
194 /*
195 * Pretty print perf type.
196 */
197 static void print_perf_type(FILE *ofp)
198 {
199 fprintf(ofp, " ");
200 fprintf(ofp, "%s = %d, ", ctx_perf_type[0].symbol, ctx_perf_type[0].value);
201 fprintf(ofp, "%s = %d\n", ctx_perf_type[1].symbol, ctx_perf_type[1].value);
202 }
203
204 /*
205 * Pretty print context type.
206 */
207 static void print_ctx_type(FILE *ofp)
208 {
209 int i = 0;
210
211 fprintf(ofp, " ");
212 while (ctx_opts[i].symbol != NULL) {
213 fprintf(ofp, "%s = %d, ", ctx_opts[i].symbol, ctx_opts[i].value);
214 i++;
215 if (!(i%3)) {
216 fprintf(ofp, "\n ");
217 }
218 }
219 }
220
221 /*
222 * Pretty print perf hardware counter.
223 */
224 static void print_perf_hw(FILE *ofp)
225 {
226 int i = 0, count = 0;
227
228 fprintf(ofp, " ");
229 while (ctx_perf[i].symbol != NULL) {
230 if (ctx_perf[i].type == PERF_TYPE_HARDWARE) {
231 fprintf(ofp, "%s = %d, ", ctx_perf[i].symbol, ctx_perf[i].id.hard);
232 count++;
233 if (!(count % 3)) {
234 fprintf(ofp, "\n ");
235 }
236 }
237 i++;
238 }
239 fprintf(ofp, "\n");
240 }
241
242 /*
243 * Pretty print perf software counter.
244 */
245 static void print_perf_sw(FILE *ofp)
246 {
247 int i = 0, count = 0;
248
249 fprintf(ofp, " ");
250 while (ctx_perf[i].symbol != NULL) {
251 if (ctx_perf[i].type == PERF_TYPE_SOFTWARE) {
252 fprintf(ofp, "%s = %d, ", ctx_perf[i].symbol, ctx_perf[i].id.soft);
253 count++;
254 if (!(count % 3)) {
255 fprintf(ofp, "\n ");
256 }
257 }
258 i++;
259 }
260 fprintf(ofp, "\n");
261 }
262
263 /*
264 * usage
265 */
266 static void usage(FILE *ofp)
267 {
268 fprintf(ofp, "usage: lttng add-context -t TYPE [options] [context_options]\n");
269 fprintf(ofp, "\n");
270 fprintf(ofp, "If no event name is given (-e), the context will be added to "
271 "all events in the channel.\n");
272 fprintf(ofp, "If no channel and no event is given (-c/-e), the context "
273 "will be added to all events in all channels\n");
274 fprintf(ofp, "\n");
275 fprintf(ofp, "Options:\n");
276 fprintf(ofp, " -h, --help Show this help\n");
277 fprintf(ofp, " -s, --session Apply on session name\n");
278 fprintf(ofp, " -c, --channel NAME Apply on channel\n");
279 fprintf(ofp, " -e, --event NAME Apply on event\n");
280 fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n");
281 fprintf(ofp, " -u, --userspace Apply for the user-space tracer\n");
282 fprintf(ofp, " --all If -u, apply on all traceable apps\n");
283 fprintf(ofp, " -p, --pid PID If -u, apply on a specific PID\n");
284 fprintf(ofp, " -t, --type TYPE Context type. You can repeat that option on the command line.\n");
285 fprintf(ofp, " TYPE can be a digit or a string below:\n");
286 print_ctx_type(ofp);
287 fprintf(ofp, "\n");
288 fprintf(ofp, "Context options:\n");
289 fprintf(ofp, " --perf-type TYPE Perf event type. TYPE can be a digit or a string below:\n");
290 print_perf_type(ofp);
291 fprintf(ofp, " --perf-id ID Perf event id. ID can be a digit or a string below:\n");
292 fprintf(ofp, " Hardware IDs (%s: %d):\n", ctx_perf_type[0].symbol, ctx_perf_type[0].value);
293 print_perf_hw(ofp);
294 fprintf(ofp, " Software IDs (%s: %d):\n", ctx_perf_type[1].symbol, ctx_perf_type[1].value);
295 print_perf_sw(ofp);
296 fprintf(ofp, "Example:\n");
297 fprintf(ofp, "This command will add the context information 'prio' and a perf counter hardware branch miss to\n"
298 "the 'sys_enter' event in the trace data output.\n");
299 fprintf(ofp, "# lttng add-context -k -e sys_enter -t prio -t perf --perf-type hw --perf-id branch_miss\n");
300 fprintf(ofp, "\n");
301 }
302
303 /*
304 * Return perf hardware counter index.
305 */
306 static int find_perf_idx(const char *opt)
307 {
308 int ret = -1, i = 0;
309
310 while (ctx_perf[i].symbol != NULL) {
311 if (strcmp(opt, ctx_perf[i].symbol) == 0) {
312 ret = i;
313 goto end;
314 }
315 i++;
316 }
317
318 end:
319 return ret;
320 }
321
322 /*
323 * Return perf type index in global array.
324 */
325 static int find_perf_type_idx(const char *opt)
326 {
327 int ret = -1, i = 0;
328
329 while (ctx_perf_type[i].symbol != NULL) {
330 if (strcmp(opt, ctx_perf_type[i].symbol) == 0) {
331 ret = i;
332 goto end;
333 }
334 i++;
335 }
336
337 end:
338 return ret;
339 }
340
341 /*
342 * Return perf counter index
343 */
344 static int find_perf_symbol_idx(int type, int id)
345 {
346 int ret = -1, i = 0;
347
348 while (ctx_perf[i].symbol != NULL) {
349 if (ctx_perf[i].type == type) {
350 switch (type) {
351 case PERF_TYPE_HARDWARE:
352 if (ctx_perf[i].id.hard == id) {
353 ret = i;
354 goto end;
355 }
356 break;
357 case PERF_TYPE_SOFTWARE:
358 if (ctx_perf[i].id.soft == id) {
359 ret = i;
360 goto end;
361 }
362 break;
363 }
364 }
365 i++;
366 }
367
368 end:
369 return ret;
370 }
371
372 /*
373 * Find context numerical value from string.
374 */
375 static int find_ctx_type_idx(const char *opt)
376 {
377 int ret = -1, i = 0;
378
379 while (ctx_opts[i].symbol != NULL) {
380 if (strcmp(opt, ctx_opts[i].symbol) == 0) {
381 ret = i;
382 goto end;
383 }
384 i++;
385 }
386
387 end:
388 return ret;
389 }
390
391 /*
392 * Return context symbol index
393 */
394 static int find_ctx_symbol_idx(int type)
395 {
396 int ret = -1, i = 0;
397
398 while (ctx_opts[i].symbol != NULL) {
399 if (type == ctx_opts[i].value) {
400 ret = i;
401 goto end;
402 }
403 i++;
404 }
405
406 end:
407 return ret;
408 }
409
410 /*
411 * Add context to channel or event.
412 */
413 static int add_context(void)
414 {
415 int ret = CMD_SUCCESS, index;
416 struct lttng_event_context context;
417 struct lttng_domain dom;
418 struct ctx_type *type;
419
420 if (set_session_name(opt_session_name) < 0) {
421 ret = CMD_ERROR;
422 goto error;
423 }
424
425 /* Iterate over all context type given */
426 cds_list_for_each_entry(type, &ctx_type_list.head, list) {
427 context.ctx = type->type;
428 if (type->type == LTTNG_KERNEL_CONTEXT_PERF_COUNTER) {
429 /* Check perf type */
430 if (isdigit(*opt_perf_type)) {
431 context.u.perf_counter.type = atoi(opt_perf_type);
432 } else {
433 index = find_perf_type_idx(opt_perf_type);
434 if (index == -1) {
435 ERR("Bad event type given. Please use --perf-type TYPE.");
436 goto error;
437 }
438 context.u.perf_counter.type = ctx_perf_type[index].value;
439 }
440
441 /* Check perf counter ID */
442 if (isdigit(*opt_perf_id)) {
443 context.u.perf_counter.config = atoi(opt_perf_id);
444 index = find_perf_symbol_idx(context.u.perf_counter.type,
445 context.u.perf_counter.config);
446 } else {
447 index = find_perf_idx(opt_perf_id);
448 switch (context.u.perf_counter.type) {
449 case PERF_TYPE_HARDWARE:
450 context.u.perf_counter.config = ctx_perf[index].id.hard;
451 break;
452 case PERF_TYPE_SOFTWARE:
453 context.u.perf_counter.config = ctx_perf[index].id.soft;
454 break;
455 }
456 }
457
458 if (index == -1) {
459 ERR("Bad perf event id given. Please use --perf-id ID.");
460 goto error;
461 }
462
463 strncpy(context.u.perf_counter.name, ctx_perf[index].symbol,
464 LTTNG_SYMBOL_NAME_LEN);
465 }
466
467 if (opt_kernel) {
468 /* Create kernel domain */
469 dom.type = LTTNG_DOMAIN_KERNEL;
470
471 DBG("Adding kernel context");
472 ret = lttng_add_context(&dom, &context, opt_event_name,
473 opt_channel_name);
474 if (ret < 0) {
475 goto error;
476 } else {
477 if (type->type == LTTNG_KERNEL_CONTEXT_PERF_COUNTER) {
478 MSG("Perf counter %s added", context.u.perf_counter.name);
479 } else {
480 index = find_ctx_symbol_idx(type->type);
481 MSG("Kernel context %s added", ctx_opts[index].symbol);
482 }
483 }
484 } else if (opt_userspace) { /* User-space tracer action */
485 /*
486 * TODO: Waiting on lttng UST 2.0
487 */
488 if (opt_pid_all) {
489 } else if (opt_pid != 0) {
490 }
491 ret = CMD_NOT_IMPLEMENTED;
492 goto error;
493 } else {
494 ERR("Please specify a tracer (kernel or user-space)");
495 goto error;
496 }
497 }
498
499 error:
500 return ret;
501 }
502
503 /*
504 * Add context on channel or event.
505 */
506 int cmd_add_context(int argc, const char **argv)
507 {
508 int index, opt, ret = CMD_SUCCESS;
509 char *tmp;
510 static poptContext pc;
511 struct ctx_type *type;
512
513 if (argc < 2) {
514 usage(stderr);
515 goto end;
516 }
517
518 pc = poptGetContext(NULL, argc, argv, long_options, 0);
519 poptReadDefaultConfig(pc, 0);
520
521 while ((opt = poptGetNextOpt(pc)) != -1) {
522 switch (opt) {
523 case OPT_HELP:
524 usage(stderr);
525 ret = CMD_SUCCESS;
526 goto end;
527 case OPT_TYPE:
528 /* Mandatory field */
529 tmp = poptGetOptArg(pc);
530 if (tmp == NULL) {
531 usage(stderr);
532 ret = CMD_ERROR;
533 free(tmp);
534 goto end;
535 }
536 type = malloc(sizeof(struct ctx_type));
537 if (type == NULL) {
538 perror("malloc ctx_type");
539 ret = -1;
540 goto end;
541 }
542 /* Numerical value are allowed also */
543 if (isdigit(*tmp)) {
544 type->type = atoi(tmp);
545 } else {
546 index = find_ctx_type_idx(tmp);
547 if (index < 0) {
548 ERR("Unknown context type %s", tmp);
549 goto end;
550 }
551 type->type = ctx_opts[index].value;
552 }
553 if (type->type == -1) {
554 ERR("Unknown context type %s", tmp);
555 } else {
556 cds_list_add(&type->list, &ctx_type_list.head);
557 }
558 free(tmp);
559 break;
560 default:
561 usage(stderr);
562 ret = CMD_UNDEFINED;
563 goto end;
564 }
565 }
566
567 ret = add_context();
568
569 /* Cleanup allocated memory */
570 cds_list_for_each_entry(type, &ctx_type_list.head, list) {
571 free(type);
572 }
573
574 end:
575 return ret;
576 }
This page took 0.040344 seconds and 4 git commands to generate.