2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
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
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.
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.
26 #include <sys/types.h>
29 #include <urcu/list.h>
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
;
51 * Taken from the LTTng ABI
55 CONTEXT_PERF_COUNTER
= 1,
67 * Taken from the Perf ABI (all enum perf_*)
70 PERF_TYPE_HARDWARE
= 0,
71 PERF_TYPE_SOFTWARE
= 1,
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,
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,
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}
113 * Context type for command line option parsing.
117 struct cds_list_head list
;
123 static struct ctx_perf_type
{
124 enum perf_type value
;
126 } ctx_perf_type
[] = {
127 { PERF_TYPE_HARDWARE
, "hw" },
128 { PERF_TYPE_SOFTWARE
, "sw" },
134 static struct ctx_perf
{
137 enum perf_count_hard hard
;
138 enum perf_count_soft soft
;
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" },
161 { -1, .id
.hard
= -1 , NULL
},
167 static struct ctx_opts
{
168 enum context_type value
;
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 */
185 * List of context type. Use to enable multiple context on a single command
188 struct ctx_type_list
{
189 struct cds_list_head head
;
191 .head
= CDS_LIST_HEAD_INIT(ctx_type_list
.head
),
195 * Pretty print perf type.
197 static void print_perf_type(FILE *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
);
205 * Pretty print context type.
207 static void print_ctx_type(FILE *ofp
)
212 while (ctx_opts
[i
].symbol
!= NULL
) {
213 fprintf(ofp
, "%s = %d, ", ctx_opts
[i
].symbol
, ctx_opts
[i
].value
);
222 * Pretty print perf hardware counter.
224 static void print_perf_hw(FILE *ofp
)
226 int i
= 0, count
= 0;
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
);
243 * Pretty print perf software counter.
245 static void print_perf_sw(FILE *ofp
)
247 int i
= 0, count
= 0;
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
);
266 static void usage(FILE *ofp
)
268 fprintf(ofp
, "usage: lttng add-context -t TYPE [options] [context_options]\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");
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");
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
);
294 fprintf(ofp
, " Software IDs (%s: %d):\n", ctx_perf_type
[1].symbol
, ctx_perf_type
[1].value
);
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");
304 * Return perf hardware counter index.
306 static int find_perf_idx(const char *opt
)
310 while (ctx_perf
[i
].symbol
!= NULL
) {
311 if (strcmp(opt
, ctx_perf
[i
].symbol
) == 0) {
323 * Return perf type index in global array.
325 static int find_perf_type_idx(const char *opt
)
329 while (ctx_perf_type
[i
].symbol
!= NULL
) {
330 if (strcmp(opt
, ctx_perf_type
[i
].symbol
) == 0) {
342 * Return perf counter index
344 static int find_perf_symbol_idx(int type
, int id
)
348 while (ctx_perf
[i
].symbol
!= NULL
) {
349 if (ctx_perf
[i
].type
== type
) {
351 case PERF_TYPE_HARDWARE
:
352 if (ctx_perf
[i
].id
.hard
== id
) {
357 case PERF_TYPE_SOFTWARE
:
358 if (ctx_perf
[i
].id
.soft
== id
) {
373 * Find context numerical value from string.
375 static int find_ctx_type_idx(const char *opt
)
379 while (ctx_opts
[i
].symbol
!= NULL
) {
380 if (strcmp(opt
, ctx_opts
[i
].symbol
) == 0) {
392 * Return context symbol index
394 static int find_ctx_symbol_idx(int type
)
398 while (ctx_opts
[i
].symbol
!= NULL
) {
399 if (type
== ctx_opts
[i
].value
) {
411 * Add context to channel or event.
413 static int add_context(void)
415 int ret
= CMD_SUCCESS
, index
;
416 struct lttng_event_context context
;
417 struct lttng_domain dom
;
418 struct ctx_type
*type
;
420 if (set_session_name(opt_session_name
) < 0) {
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
);
433 index
= find_perf_type_idx(opt_perf_type
);
435 ERR("Bad event type given. Please use --perf-type TYPE.");
438 context
.u
.perf_counter
.type
= ctx_perf_type
[index
].value
;
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
);
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
;
452 case PERF_TYPE_SOFTWARE
:
453 context
.u
.perf_counter
.config
= ctx_perf
[index
].id
.soft
;
459 ERR("Bad perf event id given. Please use --perf-id ID.");
463 strncpy(context
.u
.perf_counter
.name
, ctx_perf
[index
].symbol
,
464 LTTNG_SYMBOL_NAME_LEN
);
468 /* Create kernel domain */
469 dom
.type
= LTTNG_DOMAIN_KERNEL
;
471 DBG("Adding kernel context");
472 ret
= lttng_add_context(&dom
, &context
, opt_event_name
,
477 if (type
->type
== LTTNG_KERNEL_CONTEXT_PERF_COUNTER
) {
478 MSG("Perf counter %s added", context
.u
.perf_counter
.name
);
480 index
= find_ctx_symbol_idx(type
->type
);
481 MSG("Kernel context %s added", ctx_opts
[index
].symbol
);
484 } else if (opt_userspace
) { /* User-space tracer action */
486 * TODO: Waiting on lttng UST 2.0
489 } else if (opt_pid
!= 0) {
491 ret
= CMD_NOT_IMPLEMENTED
;
494 ERR("Please specify a tracer (kernel or user-space)");
504 * Add context on channel or event.
506 int cmd_add_context(int argc
, const char **argv
)
508 int index
, opt
, ret
= CMD_SUCCESS
;
510 static poptContext pc
;
511 struct ctx_type
*type
;
518 pc
= poptGetContext(NULL
, argc
, argv
, long_options
, 0);
519 poptReadDefaultConfig(pc
, 0);
521 while ((opt
= poptGetNextOpt(pc
)) != -1) {
528 /* Mandatory field */
529 tmp
= poptGetOptArg(pc
);
536 type
= malloc(sizeof(struct ctx_type
));
538 perror("malloc ctx_type");
542 /* Numerical value are allowed also */
544 type
->type
= atoi(tmp
);
546 index
= find_ctx_type_idx(tmp
);
548 ERR("Unknown context type %s", tmp
);
551 type
->type
= ctx_opts
[index
].value
;
553 if (type
->type
== -1) {
554 ERR("Unknown context type %s", tmp
);
556 cds_list_add(&type
->list
, &ctx_type_list
.head
);
569 /* Cleanup allocated memory */
570 cds_list_for_each_entry(type
, &ctx_type_list
.head
, list
) {