Fix: Add vpid, vppid and vtid info in add-context help
[lttng-tools.git] / src / bin / 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 modify
5 * it under the terms of the GNU General Public License, version 2 only,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #define _GNU_SOURCE
19 #include <ctype.h>
20 #include <popt.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include <urcu/list.h>
29
30 #include "../command.h"
31
32 #define PRINT_LINE_LEN 80
33
34 static char *opt_channel_name;
35 static char *opt_session_name;
36 static int opt_kernel;
37 static int opt_userspace;
38 static char *opt_type;
39 #if 0
40 /* Not implemented yet */
41 static char *opt_cmd_name;
42 static pid_t opt_pid;
43 #endif
44
45 enum {
46 OPT_HELP = 1,
47 OPT_TYPE,
48 OPT_USERSPACE,
49 OPT_LIST_OPTIONS,
50 };
51
52 static struct lttng_handle *handle;
53
54 /*
55 * Taken from the LTTng ABI
56 */
57 enum context_type {
58 CONTEXT_PID = 0,
59 CONTEXT_PERF_COUNTER = 1,
60 CONTEXT_PROCNAME = 2,
61 CONTEXT_PRIO = 3,
62 CONTEXT_NICE = 4,
63 CONTEXT_VPID = 5,
64 CONTEXT_TID = 6,
65 CONTEXT_VTID = 7,
66 CONTEXT_PPID = 8,
67 CONTEXT_VPPID = 9,
68 CONTEXT_PTHREAD_ID = 10,
69 CONTEXT_HOSTNAME = 11,
70 CONTEXT_IP = 12,
71 };
72
73 /*
74 * Taken from the Perf ABI (all enum perf_*)
75 */
76 enum perf_type {
77 PERF_TYPE_HARDWARE = 0,
78 PERF_TYPE_SOFTWARE = 1,
79 PERF_TYPE_HW_CACHE = 3,
80 };
81
82 enum perf_count_hard {
83 PERF_COUNT_HW_CPU_CYCLES = 0,
84 PERF_COUNT_HW_INSTRUCTIONS = 1,
85 PERF_COUNT_HW_CACHE_REFERENCES = 2,
86 PERF_COUNT_HW_CACHE_MISSES = 3,
87 PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
88 PERF_COUNT_HW_BRANCH_MISSES = 5,
89 PERF_COUNT_HW_BUS_CYCLES = 6,
90 PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7,
91 PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8,
92 };
93
94 enum perf_count_soft {
95 PERF_COUNT_SW_CPU_CLOCK = 0,
96 PERF_COUNT_SW_TASK_CLOCK = 1,
97 PERF_COUNT_SW_PAGE_FAULTS = 2,
98 PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
99 PERF_COUNT_SW_CPU_MIGRATIONS = 4,
100 PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
101 PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
102 PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
103 PERF_COUNT_SW_EMULATION_FAULTS = 8,
104 };
105
106 /*
107 * Generalized hardware cache events:
108 *
109 * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x
110 * { read, write, prefetch } x
111 * { accesses, misses }
112 */
113 enum perf_hw_cache_id {
114 PERF_COUNT_HW_CACHE_L1D = 0,
115 PERF_COUNT_HW_CACHE_L1I = 1,
116 PERF_COUNT_HW_CACHE_LL = 2,
117 PERF_COUNT_HW_CACHE_DTLB = 3,
118 PERF_COUNT_HW_CACHE_ITLB = 4,
119 PERF_COUNT_HW_CACHE_BPU = 5,
120
121 PERF_COUNT_HW_CACHE_MAX, /* non-ABI */
122 };
123
124 enum perf_hw_cache_op_id {
125 PERF_COUNT_HW_CACHE_OP_READ = 0,
126 PERF_COUNT_HW_CACHE_OP_WRITE = 1,
127 PERF_COUNT_HW_CACHE_OP_PREFETCH = 2,
128
129 PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */
130 };
131
132 enum perf_hw_cache_op_result_id {
133 PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0,
134 PERF_COUNT_HW_CACHE_RESULT_MISS = 1,
135
136 PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */
137 };
138
139 static struct poptOption long_options[] = {
140 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
141 {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0},
142 {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0},
143 {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0},
144 {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0},
145 {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0},
146 {"type", 't', POPT_ARG_STRING, &opt_type, OPT_TYPE, 0, 0},
147 {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL},
148 {0, 0, 0, 0, 0, 0, 0}
149 };
150
151 /*
152 * Context options
153 */
154 #define PERF_HW(opt, name) \
155 { \
156 "perf:" #opt, CONTEXT_PERF_COUNTER, \
157 .u.perf = { PERF_TYPE_HARDWARE, PERF_COUNT_HW_##name, },\
158 }
159
160 #define PERF_SW(opt, name) \
161 { \
162 "perf:" #opt, CONTEXT_PERF_COUNTER, \
163 .u.perf = { PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##name, },\
164 }
165
166 #define _PERF_HW_CACHE(optstr, name, op, result) \
167 { \
168 "perf:" optstr, CONTEXT_PERF_COUNTER, \
169 .u.perf = { \
170 PERF_TYPE_HW_CACHE, \
171 (uint64_t) PERF_COUNT_HW_CACHE_##name \
172 | ((uint64_t) PERF_COUNT_HW_CACHE_OP_##op << 8) \
173 | ((uint64_t) PERF_COUNT_HW_CACHE_RESULT_##result << 16), \
174 }, \
175 }
176
177 #define PERF_HW_CACHE(opt, name) \
178 _PERF_HW_CACHE(#opt "-loads", name, READ, ACCESS), \
179 _PERF_HW_CACHE(#opt "-load-misses", name, READ, MISS), \
180 _PERF_HW_CACHE(#opt "-stores", name, WRITE, ACCESS), \
181 _PERF_HW_CACHE(#opt "-store-misses", name, WRITE, MISS), \
182 _PERF_HW_CACHE(#opt "-prefetches", name, PREFETCH, ACCESS), \
183 _PERF_HW_CACHE(#opt "-prefetch-misses", name, PREFETCH, MISS) \
184
185 static
186 const struct ctx_opts {
187 char *symbol;
188 enum context_type ctx_type;
189 union {
190 struct {
191 uint32_t type;
192 uint64_t config;
193 } perf;
194 } u;
195 } ctx_opts[] = {
196 { "pid", CONTEXT_PID },
197 { "procname", CONTEXT_PROCNAME },
198 { "prio", CONTEXT_PRIO },
199 { "nice", CONTEXT_NICE },
200 { "vpid", CONTEXT_VPID },
201 { "tid", CONTEXT_TID },
202 { "pthread_id", CONTEXT_PTHREAD_ID },
203 { "vtid", CONTEXT_VTID },
204 { "ppid", CONTEXT_PPID },
205 { "vppid", CONTEXT_VPPID },
206 { "hostname", CONTEXT_HOSTNAME },
207 { "ip", CONTEXT_IP },
208 /* Perf options */
209 PERF_HW(cpu-cycles, CPU_CYCLES),
210 PERF_HW(cycles, CPU_CYCLES),
211 PERF_HW(stalled-cycles-frontend, STALLED_CYCLES_FRONTEND),
212 PERF_HW(idle-cycles-frontend, STALLED_CYCLES_FRONTEND),
213 PERF_HW(stalled-cycles-backend, STALLED_CYCLES_BACKEND),
214 PERF_HW(idle-cycles-backend, STALLED_CYCLES_BACKEND),
215 PERF_HW(instructions, INSTRUCTIONS),
216 PERF_HW(cache-references, CACHE_REFERENCES),
217 PERF_HW(cache-misses, CACHE_MISSES),
218 PERF_HW(branch-instructions, BRANCH_INSTRUCTIONS),
219 PERF_HW(branches, BRANCH_INSTRUCTIONS),
220 PERF_HW(branch-misses, BRANCH_MISSES),
221 PERF_HW(bus-cycles, BUS_CYCLES),
222
223 PERF_HW_CACHE(L1-dcache, L1D),
224 PERF_HW_CACHE(L1-icache, L1I),
225 PERF_HW_CACHE(LLC, LL),
226 PERF_HW_CACHE(dTLB, DTLB),
227 _PERF_HW_CACHE("iTLB-loads", ITLB, READ, ACCESS),
228 _PERF_HW_CACHE("iTLB-load-misses", ITLB, READ, MISS),
229 _PERF_HW_CACHE("branch-loads", BPU, READ, ACCESS),
230 _PERF_HW_CACHE("branch-load-misses", BPU, READ, MISS),
231
232
233 PERF_SW(cpu-clock, CPU_CLOCK),
234 PERF_SW(task-clock, TASK_CLOCK),
235 PERF_SW(page-fault, PAGE_FAULTS),
236 PERF_SW(faults, PAGE_FAULTS),
237 PERF_SW(major-faults, PAGE_FAULTS_MAJ),
238 PERF_SW(minor-faults, PAGE_FAULTS_MIN),
239 PERF_SW(context-switches, CONTEXT_SWITCHES),
240 PERF_SW(cs, CONTEXT_SWITCHES),
241 PERF_SW(cpu-migrations, CPU_MIGRATIONS),
242 PERF_SW(migrations, CPU_MIGRATIONS),
243 PERF_SW(alignment-faults, ALIGNMENT_FAULTS),
244 PERF_SW(emulation-faults, EMULATION_FAULTS),
245 { NULL, -1 }, /* Closure */
246 };
247
248 #undef PERF_SW
249 #undef PERF_HW
250
251 /*
252 * Context type for command line option parsing.
253 */
254 struct ctx_type {
255 const struct ctx_opts *opt;
256 struct cds_list_head list;
257 };
258
259 /*
260 * List of context type. Use to enable multiple context on a single command
261 * line entry.
262 */
263 struct ctx_type_list {
264 struct cds_list_head head;
265 } ctx_type_list = {
266 .head = CDS_LIST_HEAD_INIT(ctx_type_list.head),
267 };
268
269 /*
270 * Pretty print context type.
271 */
272 static void print_ctx_type(FILE *ofp)
273 {
274 const char *indent = " ";
275 int indent_len = strlen(indent);
276 int len, i = 0;
277
278 fprintf(ofp, "%s", indent);
279 len = indent_len;
280 while (ctx_opts[i].symbol != NULL) {
281 if (len > indent_len) {
282 if (len + strlen(ctx_opts[i].symbol) + 2
283 >= PRINT_LINE_LEN) {
284 fprintf(ofp, ",\n");
285 fprintf(ofp, "%s", indent);
286 len = indent_len;
287 } else {
288 len += fprintf(ofp, ", ");
289 }
290 }
291 len += fprintf(ofp, "%s", ctx_opts[i].symbol);
292 i++;
293 }
294 }
295
296 /*
297 * usage
298 */
299 static void usage(FILE *ofp)
300 {
301 fprintf(ofp, "usage: lttng add-context -t TYPE [-k|-u] [OPTIONS]\n");
302 fprintf(ofp, "\n");
303 fprintf(ofp, "If no channel is given (-c), the context is added to\n");
304 fprintf(ofp, "all channels.\n");
305 fprintf(ofp, "\n");
306 fprintf(ofp, "Otherwise the context is added only to the channel (-c).\n");
307 fprintf(ofp, "\n");
308 fprintf(ofp, "Exactly one domain (-k or -u) must be specified.\n");
309 fprintf(ofp, "\n");
310 fprintf(ofp, "Options:\n");
311 fprintf(ofp, " -h, --help Show this help\n");
312 fprintf(ofp, " --list-options Simple listing of options\n");
313 fprintf(ofp, " -s, --session NAME Apply to session name\n");
314 fprintf(ofp, " -c, --channel NAME Apply to channel\n");
315 fprintf(ofp, " -k, --kernel Apply to the kernel tracer\n");
316 fprintf(ofp, " -u, --userspace Apply to the user-space tracer\n");
317 fprintf(ofp, "\n");
318 fprintf(ofp, "Context:\n");
319 fprintf(ofp, " -t, --type TYPE Context type. You can repeat that option on\n");
320 fprintf(ofp, " the command line to specify multiple contexts at once.\n");
321 fprintf(ofp, " (--kernel preempts --userspace)\n");
322 fprintf(ofp, " TYPE can be one of the strings below:\n");
323 print_ctx_type(ofp);
324 fprintf(ofp, "\n");
325 fprintf(ofp, "Note that the vpid, vppid and vtid context types represent the virtual process id,\n"
326 "virtual parent process id and virtual thread id as seen from the current execution context\n"
327 "as opposed to the pid, ppid and tid which are kernel internal data structures.\n\n");
328 fprintf(ofp, "Example:\n");
329 fprintf(ofp, "This command will add the context information 'prio' and two perf\n"
330 "counters (hardware branch misses and cache misses), to all channels\n"
331 "in the trace data output:\n");
332 fprintf(ofp, "# lttng add-context -k -t prio -t perf:branch-misses -t perf:cache-misses\n");
333 fprintf(ofp, "\n");
334 }
335
336 /*
337 * Find context numerical value from string.
338 */
339 static int find_ctx_type_idx(const char *opt)
340 {
341 int ret = -1, i = 0;
342
343 while (ctx_opts[i].symbol != NULL) {
344 if (strcmp(opt, ctx_opts[i].symbol) == 0) {
345 ret = i;
346 goto end;
347 }
348 i++;
349 }
350
351 end:
352 return ret;
353 }
354
355 /*
356 * Add context to channel or event.
357 */
358 static int add_context(char *session_name)
359 {
360 int ret = CMD_SUCCESS, warn = 0;
361 struct lttng_event_context context;
362 struct lttng_domain dom;
363 struct ctx_type *type;
364 char *ptr;
365
366 memset(&context, 0, sizeof(context));
367 memset(&dom, 0, sizeof(dom));
368
369 if (opt_kernel) {
370 dom.type = LTTNG_DOMAIN_KERNEL;
371 } else if (opt_userspace) {
372 dom.type = LTTNG_DOMAIN_UST;
373 } else {
374 print_missing_domain();
375 ret = CMD_ERROR;
376 goto error;
377 }
378
379 handle = lttng_create_handle(session_name, &dom);
380 if (handle == NULL) {
381 ret = CMD_ERROR;
382 goto error;
383 }
384
385 /* Iterate over all the context types given */
386 cds_list_for_each_entry(type, &ctx_type_list.head, list) {
387 context.ctx = (enum lttng_event_context_type) type->opt->ctx_type;
388 if (context.ctx == LTTNG_EVENT_CONTEXT_PERF_COUNTER) {
389 context.u.perf_counter.type = type->opt->u.perf.type;
390 context.u.perf_counter.config = type->opt->u.perf.config;
391 strncpy(context.u.perf_counter.name, type->opt->symbol,
392 LTTNG_SYMBOL_NAME_LEN);
393 context.u.perf_counter.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
394 /* Replace : and - by _ */
395 while ((ptr = strchr(context.u.perf_counter.name, '-')) != NULL) {
396 *ptr = '_';
397 }
398 while ((ptr = strchr(context.u.perf_counter.name, ':')) != NULL) {
399 *ptr = '_';
400 }
401 }
402 DBG("Adding context...");
403
404 ret = lttng_add_context(handle, &context, NULL, opt_channel_name);
405 if (ret < 0) {
406 ERR("%s: %s", type->opt->symbol, lttng_strerror(ret));
407 warn = 1;
408 continue;
409 } else {
410 if (opt_channel_name) {
411 MSG("%s context %s added to channel %s",
412 opt_kernel ? "kernel" : "UST", type->opt->symbol,
413 opt_channel_name);
414 } else {
415 MSG("%s context %s added to all channels",
416 opt_kernel ? "kernel" : "UST", type->opt->symbol)
417 }
418 }
419 }
420
421 ret = CMD_SUCCESS;
422
423 error:
424 lttng_destroy_handle(handle);
425
426 /*
427 * This means that at least one add_context failed and tells the user to
428 * look on stderr for error(s).
429 */
430 if (warn) {
431 ret = CMD_WARNING;
432 }
433 return ret;
434 }
435
436 /*
437 * Add context to channel or event.
438 */
439 int cmd_add_context(int argc, const char **argv)
440 {
441 int index, opt, ret = CMD_SUCCESS;
442 static poptContext pc;
443 struct ctx_type *type, *tmptype;
444 char *session_name = NULL;
445
446 if (argc < 2) {
447 usage(stderr);
448 ret = CMD_ERROR;
449 goto end;
450 }
451
452 pc = poptGetContext(NULL, argc, argv, long_options, 0);
453 poptReadDefaultConfig(pc, 0);
454
455 while ((opt = poptGetNextOpt(pc)) != -1) {
456 switch (opt) {
457 case OPT_HELP:
458 usage(stdout);
459 goto end;
460 case OPT_TYPE:
461 /*
462 * Look up the index of opt_type in ctx_opts[] first, so we don't
463 * have to free(type) on failure.
464 */
465 index = find_ctx_type_idx(opt_type);
466 if (index < 0) {
467 ERR("Unknown context type %s", opt_type);
468 ret = CMD_ERROR;
469 goto end;
470 }
471
472 type = malloc(sizeof(struct ctx_type));
473 if (type == NULL) {
474 perror("malloc ctx_type");
475 ret = CMD_FATAL;
476 goto end;
477 }
478
479 type->opt = &ctx_opts[index];
480 if (type->opt->symbol == NULL) {
481 ERR("Unknown context type %s", opt_type);
482 free(type);
483 ret = CMD_ERROR;
484 goto end;
485 } else {
486 cds_list_add_tail(&type->list, &ctx_type_list.head);
487 }
488 break;
489 case OPT_USERSPACE:
490 opt_userspace = 1;
491 #if 0
492 opt_cmd_name = poptGetOptArg(pc);
493 #endif
494 break;
495 case OPT_LIST_OPTIONS:
496 list_cmd_options(stdout, long_options);
497 goto end;
498 default:
499 usage(stderr);
500 ret = CMD_UNDEFINED;
501 goto end;
502 }
503 }
504
505 if (!opt_type) {
506 ERR("Missing mandatory -t TYPE");
507 usage(stderr);
508 ret = CMD_ERROR;
509 goto end;
510 }
511
512 if (!opt_session_name) {
513 session_name = get_session_name();
514 if (session_name == NULL) {
515 ret = CMD_ERROR;
516 goto end;
517 }
518 } else {
519 session_name = opt_session_name;
520 }
521
522 ret = add_context(session_name);
523
524 if (!opt_session_name) {
525 free(session_name);
526 }
527
528 end:
529 /* Cleanup allocated memory */
530 cds_list_for_each_entry_safe(type, tmptype, &ctx_type_list.head, list) {
531 free(type);
532 }
533
534 poptFreeContext(pc);
535 return ret;
536 }
This page took 0.039456 seconds and 4 git commands to generate.