2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Copyright (C) 2015 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2 only,
7 * as published by the Free Software Foundation.
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 along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include <sys/types.h>
30 #include <urcu/list.h>
32 #include <common/mi-lttng.h>
34 #include "../command.h"
41 static char *opt_session_name
;
42 static int opt_kernel
;
43 static int opt_userspace
;
45 static char *opt_pid_string
;
55 static struct poptOption long_options
[] = {
56 /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */
57 { "help", 'h', POPT_ARG_NONE
, 0, OPT_HELP
, 0, 0, },
58 { "session", 's', POPT_ARG_STRING
, &opt_session_name
, OPT_SESSION
, 0, 0, },
59 { "kernel", 'k', POPT_ARG_VAL
, &opt_kernel
, 1, 0, 0, },
60 { "userspace", 'u', POPT_ARG_VAL
, &opt_userspace
, 1, 0, 0, },
61 { "pid", 'p', POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_pid_string
, OPT_PID
, 0, 0, },
62 { "all", 'a', POPT_ARG_VAL
, &opt_all
, 1, 0, 0, },
63 { "list-options", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_OPTIONS
, 0, 0, },
64 { 0, 0, 0, 0, 0, 0, 0, },
70 static void usage(FILE *ofp
, const char *cmd_str
)
72 fprintf(ofp
, "usage: lttng %s [-k|-u] [OPTIONS]\n", cmd_str
);
74 fprintf(ofp
, "If no session is given (-s), the context is added to\n");
75 fprintf(ofp
, "the current sesssion. Exactly one domain (-k or -u)\n");
76 fprintf(ofp
, "must be specified.\n");
78 fprintf(ofp
, "Options:\n");
79 fprintf(ofp
, " -h, --help Show this help.\n");
80 fprintf(ofp
, " --list-options Simple listing of options.\n");
81 fprintf(ofp
, " -s, --session NAME Apply to session name.\n");
82 fprintf(ofp
, " -k, --kernel Apply to the kernel tracer.\n");
83 fprintf(ofp
, " -u, --userspace Apply to the user-space tracer.\n");
84 fprintf(ofp
, " -p, --pid [PID] Process ID tracker. Leave PID empty when used with --all.\n");
85 fprintf(ofp
, " -a, --all All PIDs (use with --pid).\n");
90 int parse_pid_string(const char *_pid_string
,
91 int all
, int **_pid_list
, int *nr_pids
)
93 const char *one_pid_str
;
95 int retval
= CMD_SUCCESS
;
98 char *pid_string
= NULL
;
101 if (all
&& _pid_string
) {
102 ERR("An empty PID string is expected with --all");
106 if (!all
&& !_pid_string
) {
107 ERR("Please specify --all with an empty PID string");
112 pid_list
= zmalloc(sizeof(*_pid_list
));
114 ERR("Out of memory");
118 /* Empty PID string means all PIDs */
124 pid_string
= strdup(_pid_string
);
126 ERR("Out of memory");
132 one_pid_str
= strtok_r(pid_string
, ",", &iter
);
133 while (one_pid_str
!= NULL
) {
137 v
= strtoul(one_pid_str
, &endptr
, 10);
138 if ((v
== 0 && errno
== EINVAL
)
139 || (v
== ULONG_MAX
&& errno
== ERANGE
)
140 || (*one_pid_str
!= '\0' && *endptr
!= '\0')){
141 ERR("Error parsing PID %s", one_pid_str
);
146 if ((long) v
> INT_MAX
|| (int) v
< 0) {
147 ERR("Invalid PID value %ld", (long) v
);
154 one_pid_str
= strtok_r(NULL
, ",", &iter
);
158 /* Identity of delimiter has been lost in first pass. */
159 pid_string
= strdup(_pid_string
);
161 ERR("Out of memory");
167 pid_list
= zmalloc(count
* sizeof(*pid_list
));
169 ERR("Out of memory");
176 one_pid_str
= strtok_r(pid_string
, ",", &iter
);
177 while (one_pid_str
!= NULL
) {
180 v
= strtoul(one_pid_str
, NULL
, 10);
181 pid_list
[count
++] = (int) v
;
184 one_pid_str
= strtok_r(NULL
, ",", &iter
);
189 *_pid_list
= pid_list
;
190 goto end
; /* SUCCESS */
201 enum cmd_error_code
track_untrack_pid(enum cmd_type cmd_type
, const char *cmd_str
,
202 const char *session_name
, const char *pid_string
,
203 int all
, struct mi_writer
*writer
)
205 int ret
, success
= 1 , i
;
206 enum cmd_error_code retval
= CMD_SUCCESS
;
207 int *pid_list
= NULL
;
209 struct lttng_domain dom
;
210 struct lttng_handle
*handle
= NULL
;
211 int (*lib_func
)(struct lttng_handle
*handle
, int pid
);
215 lib_func
= lttng_track_pid
;
218 lib_func
= lttng_untrack_pid
;
221 ERR("Unknown command");
226 memset(&dom
, 0, sizeof(dom
));
228 dom
.type
= LTTNG_DOMAIN_KERNEL
;
229 } else if (opt_userspace
) {
230 dom
.type
= LTTNG_DOMAIN_UST
;
232 print_missing_domain();
237 ret
= parse_pid_string(pid_string
, all
, &pid_list
, &nr_pids
);
238 if (ret
!= CMD_SUCCESS
) {
239 ERR("Error parsing PID string");
240 usage(stderr
, cmd_str
);
245 handle
= lttng_create_handle(session_name
, &dom
);
246 if (handle
== NULL
) {
252 /* Open process element */
253 ret
= mi_lttng_targets_open(writer
);
260 for (i
= 0; i
< nr_pids
; i
++) {
261 DBG("%s PID %d", cmd_str
, pid_list
[i
]);
262 ret
= lib_func(handle
, pid_list
[i
]);
265 case LTTNG_ERR_PID_TRACKED
:
266 WARN("PID %i already tracked in session %s",
267 pid_list
[i
], session_name
);
269 retval
= CMD_SUCCESS
;
271 case LTTNG_ERR_PID_NOT_TRACKED
:
272 WARN("PID %i not tracked in session %s",
273 pid_list
[i
], session_name
);
275 retval
= CMD_SUCCESS
;
278 ERR("%s", lttng_strerror(ret
));
284 MSG("PID %i %sed in session %s",
285 pid_list
[i
], cmd_str
, session_name
);
291 ret
= mi_lttng_pid_target(writer
, pid_list
[i
], 1);
297 ret
= mi_lttng_writer_write_element_bool(writer
,
298 mi_lttng_element_success
, success
);
304 ret
= mi_lttng_writer_close_element(writer
);
313 /* Close targets element */
314 ret
= mi_lttng_writer_close_element(writer
);
323 lttng_destroy_handle(handle
);
330 const char *get_mi_element_command(enum cmd_type cmd_type
)
334 return mi_lttng_element_command_track
;
336 return mi_lttng_element_command_untrack
;
343 * Add/remove tracker to/from session.
346 int cmd_track_untrack(enum cmd_type cmd_type
, const char *cmd_str
,
347 int argc
, const char **argv
)
350 enum cmd_error_code command_ret
= CMD_SUCCESS
;
352 static poptContext pc
;
353 char *session_name
= NULL
;
354 struct mi_writer
*writer
= NULL
;
357 usage(stderr
, cmd_str
);
358 command_ret
= CMD_ERROR
;
362 pc
= poptGetContext(NULL
, argc
, argv
, long_options
, 0);
363 poptReadDefaultConfig(pc
, 0);
365 while ((opt
= poptGetNextOpt(pc
)) != -1) {
368 usage(stdout
, cmd_str
);
370 case OPT_LIST_OPTIONS
:
371 list_cmd_options(stdout
, long_options
);
378 usage(stderr
, cmd_str
);
379 command_ret
= CMD_UNDEFINED
;
384 if (!(opt_userspace
^ opt_kernel
)) {
385 ERR("Exactly one of -u or -k needs to be specified.");
386 usage(stderr
, cmd_str
);
387 command_ret
= CMD_ERROR
;
391 if (!opt_session_name
) {
392 session_name
= get_session_name();
393 if (session_name
== NULL
) {
394 command_ret
= CMD_ERROR
;
398 session_name
= opt_session_name
;
401 /* Currently only PID tracker is supported */
403 ERR("Please specify at least one tracker with its expected arguments");
404 usage(stderr
, cmd_str
);
405 command_ret
= CMD_ERROR
;
411 writer
= mi_lttng_writer_create(fileno(stdout
), lttng_opt_mi
);
413 command_ret
= CMD_ERROR
;
419 /* Open command element */
420 ret
= mi_lttng_writer_command_open(writer
,
421 get_mi_element_command(cmd_type
));
423 command_ret
= CMD_ERROR
;
427 /* Open output element */
428 ret
= mi_lttng_writer_open_element(writer
,
429 mi_lttng_element_command_output
);
431 command_ret
= CMD_ERROR
;
436 command_ret
= track_untrack_pid(cmd_type
,
437 cmd_str
, session_name
, opt_pid_string
,
439 if (command_ret
!= CMD_SUCCESS
) {
445 /* Close output element */
446 ret
= mi_lttng_writer_close_element(writer
);
448 command_ret
= CMD_ERROR
;
453 ret
= mi_lttng_writer_write_element_bool(writer
,
454 mi_lttng_element_command_success
, success
);
456 command_ret
= CMD_ERROR
;
460 /* Command element close */
461 ret
= mi_lttng_writer_command_close(writer
);
463 command_ret
= CMD_ERROR
;
469 if (!opt_session_name
) {
474 if (writer
&& mi_lttng_writer_destroy(writer
)) {
475 /* Preserve original error code */
476 command_ret
= CMD_ERROR
;
480 return (int) command_ret
;
483 int cmd_track(int argc
, const char **argv
)
485 return cmd_track_untrack(CMD_TRACK
, "track", argc
, argv
);
488 int cmd_untrack(int argc
, const char **argv
)
490 return cmd_track_untrack(CMD_UNTRACK
, "untrack", argc
, argv
);