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.
26 #include <sys/types.h>
30 #include <urcu/list.h>
32 #include <common/mi-lttng.h>
34 #include "../command.h"
41 enum tracker_type_state
{
59 struct lttng_tracker_id
*array
;
62 static char *opt_session_name
;
63 static int opt_kernel
;
64 static int opt_userspace
;
66 static struct opt_type opt_pid
, opt_vpid
, opt_uid
, opt_vuid
, opt_gid
, opt_vgid
;
68 static enum tracker_type_state type_state
;
83 static struct poptOption long_options
[] = {
84 /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */
85 { "help", 'h', POPT_ARG_NONE
, 0, OPT_HELP
, 0, 0, },
86 { "session", 's', POPT_ARG_STRING
, &opt_session_name
, OPT_SESSION
, 0, 0, },
87 { "kernel", 'k', POPT_ARG_VAL
, &opt_kernel
, 1, 0, 0, },
88 { "userspace", 'u', POPT_ARG_VAL
, &opt_userspace
, 1, 0, 0, },
89 { "pid", 'p', POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_pid
.string
, OPT_PID
, 0, 0, },
90 { "vpid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_vpid
.string
, OPT_VPID
, 0, 0, },
91 { "uid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_uid
.string
, OPT_UID
, 0, 0, },
92 { "vuid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_vuid
.string
, OPT_VUID
, 0, 0, },
93 { "gid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_gid
.string
, OPT_GID
, 0, 0, },
94 { "vgid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_vgid
.string
, OPT_VGID
, 0, 0, },
95 { "all", 'a', POPT_ARG_NONE
, 0, OPT_ALL
, 0, 0, },
96 { "list-options", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_OPTIONS
, 0, 0, },
97 { 0, 0, 0, 0, 0, 0, 0, },
101 struct id_list
*alloc_id_list(size_t nr_items
)
103 struct id_list
*id_list
;
104 struct lttng_tracker_id
*items
;
106 id_list
= zmalloc(sizeof(*id_list
));
110 items
= zmalloc(nr_items
* sizeof(*items
));
114 id_list
->nr
= nr_items
;
115 id_list
->array
= items
;
123 void free_id_list(struct id_list
*list
)
131 for (i
= 0; i
< nr_items
; i
++) {
132 struct lttng_tracker_id
*item
= &list
->array
[i
];
140 int parse_id_string(const char *_id_string
,
141 int all
, struct id_list
**_id_list
)
143 const char *one_id_str
;
145 int retval
= CMD_SUCCESS
;
147 struct id_list
*id_list
= NULL
;
148 char *id_string
= NULL
;
151 if (all
&& _id_string
) {
152 ERR("An empty ID string is expected with --all");
156 if (!all
&& !_id_string
) {
157 ERR("Please specify --all with an empty ID string");
162 /* Empty ID string means all IDs */
163 id_list
= alloc_id_list(1);
165 ERR("Out of memory");
169 id_list
->array
[0].type
= LTTNG_ID_ALL
;
173 id_string
= strdup(_id_string
);
175 ERR("Out of memory");
181 one_id_str
= strtok_r(id_string
, ",", &iter
);
182 while (one_id_str
!= NULL
) {
185 if (isdigit(one_id_str
[0])) {
187 v
= strtoul(one_id_str
, &endptr
, 10);
188 if ((v
== 0 && errno
== EINVAL
)
189 || (v
== ULONG_MAX
&& errno
== ERANGE
)
190 || (*one_id_str
!= '\0' && *endptr
!= '\0')){
191 ERR("Error parsing ID %s", one_id_str
);
196 if ((long) v
> INT_MAX
|| (int) v
< 0) {
197 ERR("Invalid ID value %ld", (long) v
);
205 one_id_str
= strtok_r(NULL
, ",", &iter
);
209 /* Identity of delimiter has been lost in first pass. */
210 id_string
= strdup(_id_string
);
212 ERR("Out of memory");
218 id_list
= alloc_id_list(count
);
220 ERR("Out of memory");
225 /* Reparse string and populate the id list. */
227 one_id_str
= strtok_r(id_string
, ",", &iter
);
228 while (one_id_str
!= NULL
) {
229 struct lttng_tracker_id
*item
;
231 item
= &id_list
->array
[count
++];
232 if (isdigit(one_id_str
[0])) {
235 v
= strtoul(one_id_str
, NULL
, 10);
236 item
->type
= LTTNG_ID_VALUE
;
237 item
->value
= (int) v
;
239 item
->type
= LTTNG_ID_STRING
;
240 item
->string
= strdup(one_id_str
);
242 ERR("Out of memory");
249 one_id_str
= strtok_r(NULL
, ",", &iter
);
254 goto end
; /* SUCCESS */
258 free_id_list(id_list
);
265 const char *get_tracker_str(enum lttng_tracker_type tracker_type
)
267 switch (tracker_type
) {
268 case LTTNG_TRACKER_PID
:
270 case LTTNG_TRACKER_VPID
:
272 case LTTNG_TRACKER_UID
:
274 case LTTNG_TRACKER_VUID
:
276 case LTTNG_TRACKER_GID
:
278 case LTTNG_TRACKER_VGID
:
285 enum cmd_error_code
track_untrack_id(enum cmd_type cmd_type
, const char *cmd_str
,
286 const char *session_name
, const char *id_string
,
287 int all
, struct mi_writer
*writer
,
288 enum lttng_tracker_type tracker_type
)
290 int ret
, success
= 1 , i
;
291 enum cmd_error_code retval
= CMD_SUCCESS
;
292 struct id_list
*id_list
= NULL
;
293 struct lttng_domain dom
;
294 struct lttng_handle
*handle
= NULL
;
295 int (*cmd_func
)(struct lttng_handle
*handle
,
296 enum lttng_tracker_type tracker_type
,
297 struct lttng_tracker_id
*id
);
298 const char *tracker_str
;
302 cmd_func
= lttng_track_id
;
305 cmd_func
= lttng_untrack_id
;
308 ERR("Unknown command");
312 memset(&dom
, 0, sizeof(dom
));
314 dom
.type
= LTTNG_DOMAIN_KERNEL
;
315 } else if (opt_userspace
) {
316 dom
.type
= LTTNG_DOMAIN_UST
;
317 if (tracker_type
== LTTNG_TRACKER_PID
) {
318 MSG("For UST domain, the PID tracker is actually tracking VPID.");
319 tracker_type
= LTTNG_TRACKER_VPID
;
322 /* Checked by the caller. */
325 tracker_str
= get_tracker_str(tracker_type
);
327 ERR("Unknown tracker type");
331 ret
= parse_id_string(id_string
, all
, &id_list
);
332 if (ret
!= CMD_SUCCESS
) {
333 ERR("Error parsing %s string", tracker_str
);
338 handle
= lttng_create_handle(session_name
, &dom
);
339 if (handle
== NULL
) {
345 /* Open tracker_id and targets elements */
346 ret
= mi_lttng_id_tracker_open(writer
, tracker_type
);
352 for (i
= 0; i
< id_list
->nr
; i
++) {
353 struct lttng_tracker_id
*item
= &id_list
->array
[i
];
355 switch (item
->type
) {
357 DBG("%s all IDs", cmd_str
);
360 DBG("%s ID %d", cmd_str
, item
->value
);
362 case LTTNG_ID_STRING
:
363 DBG("%s ID '%s'", cmd_str
, item
->string
);
369 ret
= cmd_func(handle
, tracker_type
, item
);
374 case LTTNG_ERR_ID_TRACKED
:
375 msg
= "already tracked";
377 retval
= CMD_SUCCESS
;
379 case LTTNG_ERR_ID_NOT_TRACKED
:
382 retval
= CMD_SUCCESS
;
385 ERR("%s", lttng_strerror(ret
));
391 switch (item
->type
) {
393 WARN("All %s %s in session %s",
394 tracker_str
, msg
, session_name
);
397 WARN("%s %i %s in session %s",
398 tracker_str
, item
->value
,
401 case LTTNG_ID_STRING
:
402 WARN("%s '%s' %s in session %s",
403 tracker_str
, item
->string
,
412 switch (item
->type
) {
414 MSG("All %ss %sed in session %s",
415 tracker_str
, cmd_str
, session_name
);
418 MSG("%s %i %sed in session %s",
419 tracker_str
, item
->value
, cmd_str
,
422 case LTTNG_ID_STRING
:
423 MSG("%s '%s' %sed in session %s",
424 tracker_str
, item
->string
, cmd_str
,
436 ret
= mi_lttng_id_target(writer
,
437 tracker_type
, item
, 1);
443 ret
= mi_lttng_writer_write_element_bool(writer
,
444 mi_lttng_element_success
, success
);
450 ret
= mi_lttng_writer_close_element(writer
);
459 /* Close targets and tracker_id elements */
460 ret
= mi_lttng_close_multi_element(writer
, 2);
469 lttng_destroy_handle(handle
);
471 free_id_list(id_list
);
476 const char *get_mi_element_command(enum cmd_type cmd_type
)
480 return mi_lttng_element_command_track
;
482 return mi_lttng_element_command_untrack
;
489 * Add/remove tracker to/from session.
492 int cmd_track_untrack(enum cmd_type cmd_type
, const char *cmd_str
,
493 int argc
, const char **argv
)
496 enum cmd_error_code command_ret
= CMD_SUCCESS
;
498 static poptContext pc
;
499 char *session_name
= NULL
;
500 struct mi_writer
*writer
= NULL
;
503 command_ret
= CMD_ERROR
;
507 pc
= poptGetContext(NULL
, argc
, argv
, long_options
, 0);
508 poptReadDefaultConfig(pc
, 0);
510 while ((opt
= poptGetNextOpt(pc
)) != -1) {
515 case OPT_LIST_OPTIONS
:
516 list_cmd_options(stdout
, long_options
);
522 ERR("Duplicate --pid option is forbidden.\n");
523 command_ret
= CMD_ERROR
;
527 type_state
= STATE_PID
;
531 ERR("Duplicate --vpid option is forbidden.\n");
532 command_ret
= CMD_ERROR
;
536 type_state
= STATE_VPID
;
540 ERR("Duplicate --uid option is forbidden.\n");
541 command_ret
= CMD_ERROR
;
545 type_state
= STATE_UID
;
549 ERR("Duplicate --vuid option is forbidden.\n");
550 command_ret
= CMD_ERROR
;
554 type_state
= STATE_VUID
;
558 ERR("Duplicate --gid option is forbidden.\n");
559 command_ret
= CMD_ERROR
;
563 type_state
= STATE_GID
;
567 ERR("Duplicate --vgid option is forbidden.\n");
568 command_ret
= CMD_ERROR
;
572 type_state
= STATE_VGID
;
575 switch (type_state
) {
595 command_ret
= CMD_ERROR
;
600 command_ret
= CMD_UNDEFINED
;
605 ret
= print_missing_or_multiple_domains(opt_kernel
+ opt_userspace
);
607 command_ret
= CMD_ERROR
;
611 if (!opt_session_name
) {
612 session_name
= get_session_name();
613 if (session_name
== NULL
) {
614 command_ret
= CMD_ERROR
;
618 session_name
= opt_session_name
;
623 writer
= mi_lttng_writer_create(fileno(stdout
), lttng_opt_mi
);
625 command_ret
= CMD_ERROR
;
631 /* Open command element */
632 ret
= mi_lttng_writer_command_open(writer
,
633 get_mi_element_command(cmd_type
));
635 command_ret
= CMD_ERROR
;
639 /* Open output element */
640 ret
= mi_lttng_writer_open_element(writer
,
641 mi_lttng_element_command_output
);
643 command_ret
= CMD_ERROR
;
647 ret
= mi_lttng_trackers_open(writer
);
654 command_ret
= track_untrack_id(cmd_type
,
655 cmd_str
, session_name
, opt_pid
.string
,
656 opt_pid
.all
, writer
, LTTNG_TRACKER_PID
);
657 if (command_ret
!= CMD_SUCCESS
) {
662 command_ret
= track_untrack_id(cmd_type
,
663 cmd_str
, session_name
, opt_vpid
.string
,
664 opt_vpid
.all
, writer
, LTTNG_TRACKER_VPID
);
665 if (command_ret
!= CMD_SUCCESS
) {
670 command_ret
= track_untrack_id(cmd_type
,
671 cmd_str
, session_name
, opt_uid
.string
,
672 opt_uid
.all
, writer
, LTTNG_TRACKER_UID
);
673 if (command_ret
!= CMD_SUCCESS
) {
678 command_ret
= track_untrack_id(cmd_type
,
679 cmd_str
, session_name
, opt_vuid
.string
,
680 opt_vuid
.all
, writer
, LTTNG_TRACKER_VUID
);
681 if (command_ret
!= CMD_SUCCESS
) {
686 command_ret
= track_untrack_id(cmd_type
,
687 cmd_str
, session_name
, opt_gid
.string
,
688 opt_gid
.all
, writer
, LTTNG_TRACKER_GID
);
689 if (command_ret
!= CMD_SUCCESS
) {
694 command_ret
= track_untrack_id(cmd_type
,
695 cmd_str
, session_name
, opt_vgid
.string
,
696 opt_vgid
.all
, writer
, LTTNG_TRACKER_VGID
);
697 if (command_ret
!= CMD_SUCCESS
) {
704 /* Close trackers and output elements */
705 ret
= mi_lttng_close_multi_element(writer
, 2);
707 command_ret
= CMD_ERROR
;
712 ret
= mi_lttng_writer_write_element_bool(writer
,
713 mi_lttng_element_command_success
, success
);
715 command_ret
= CMD_ERROR
;
719 /* Command element close */
720 ret
= mi_lttng_writer_command_close(writer
);
722 command_ret
= CMD_ERROR
;
728 if (!opt_session_name
) {
733 if (writer
&& mi_lttng_writer_destroy(writer
)) {
734 /* Preserve original error code */
735 command_ret
= CMD_ERROR
;
739 return (int) command_ret
;
742 int cmd_track(int argc
, const char **argv
)
744 return cmd_track_untrack(CMD_TRACK
, "track", argc
, argv
);
747 int cmd_untrack(int argc
, const char **argv
)
749 return cmd_track_untrack(CMD_UNTRACK
, "untrack", argc
, argv
);