6d8d91ffd73b70948c371cb69dbe0a9bc9e14210
[lttng-tools.git] / src / bin / lttng / commands / track-untrack.c
1 /*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Copyright (C) 2015 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
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.
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 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.
17 */
18
19 #define _LGPL_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 #include <assert.h>
29
30 #include <urcu/list.h>
31
32 #include <common/mi-lttng.h>
33
34 #include "../command.h"
35
36 enum cmd_type {
37 CMD_TRACK,
38 CMD_UNTRACK,
39 };
40
41 static char *opt_session_name;
42 static int opt_kernel;
43 static int opt_userspace;
44 static int opt_all;
45 static char *opt_pid_string;
46 static int opt_pid;
47
48 enum {
49 OPT_HELP = 1,
50 OPT_LIST_OPTIONS,
51 OPT_SESSION,
52 OPT_PID,
53 };
54
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, },
65 };
66
67 /*
68 * usage
69 */
70 static void usage(FILE *ofp, const char *cmd_str)
71 {
72 fprintf(ofp, "usage: lttng %s [-k|-u] [OPTIONS]\n", cmd_str);
73 fprintf(ofp, "\n");
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");
77 fprintf(ofp, "\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");
86 fprintf(ofp, "\n");
87 }
88
89 static
90 int parse_pid_string(const char *_pid_string,
91 int all, int **_pid_list, int *nr_pids)
92 {
93 const char *one_pid_str;
94 char *iter;
95 int retval = CMD_SUCCESS;
96 int count = 0;
97 int *pid_list = NULL;
98 char *pid_string = NULL;
99 char *endptr;
100
101 if (all && _pid_string) {
102 ERR("An empty PID string is expected with --all");
103 retval = CMD_ERROR;
104 goto error;
105 }
106 if (!all && !_pid_string) {
107 ERR("Please specify --all with an empty PID string");
108 retval = CMD_ERROR;
109 goto error;
110 }
111 if (all) {
112 pid_list = zmalloc(sizeof(*_pid_list));
113 if (!pid_list) {
114 ERR("Out of memory");
115 retval = CMD_ERROR;
116 goto error;
117 }
118 /* Empty PID string means all PIDs */
119 count = 1;
120 pid_list[0] = -1;
121 goto assign;
122 }
123
124 pid_string = strdup(_pid_string);
125 if (!pid_string) {
126 ERR("Out of memory");
127 retval = CMD_ERROR;
128 goto error;
129 }
130
131 /* Count */
132 one_pid_str = strtok_r(pid_string, ",", &iter);
133 while (one_pid_str != NULL) {
134 unsigned long v;
135
136 errno = 0;
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);
142 retval = CMD_ERROR;
143 goto error;
144 }
145
146 if ((long) v > INT_MAX || (int) v < 0) {
147 ERR("Invalid PID value %ld", (long) v);
148 retval = CMD_ERROR;
149 goto error;
150 }
151 count++;
152
153 /* For next loop */
154 one_pid_str = strtok_r(NULL, ",", &iter);
155 }
156
157 free(pid_string);
158 /* Identity of delimiter has been lost in first pass. */
159 pid_string = strdup(_pid_string);
160 if (!pid_string) {
161 ERR("Out of memory");
162 retval = CMD_ERROR;
163 goto error;
164 }
165
166 /* Allocate */
167 pid_list = zmalloc(count * sizeof(*pid_list));
168 if (!pid_list) {
169 ERR("Out of memory");
170 retval = CMD_ERROR;
171 goto error;
172 }
173
174 /* Copy */
175 count = 0;
176 one_pid_str = strtok_r(pid_string, ",", &iter);
177 while (one_pid_str != NULL) {
178 unsigned long v;
179
180 v = strtoul(one_pid_str, NULL, 10);
181 pid_list[count++] = (int) v;
182
183 /* For next loop */
184 one_pid_str = strtok_r(NULL, ",", &iter);
185 }
186
187 assign:
188 *nr_pids = count;
189 *_pid_list = pid_list;
190 goto end; /* SUCCESS */
191
192 /* ERROR */
193 error:
194 free(pid_list);
195 end:
196 free(pid_string);
197 return retval;
198 }
199
200 static
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)
204 {
205 int ret, success = 1 , i;
206 enum cmd_error_code retval = CMD_SUCCESS;
207 int *pid_list = NULL;
208 int nr_pids;
209 struct lttng_domain dom;
210 struct lttng_handle *handle = NULL;
211 int (*cmd_func)(struct lttng_handle *handle, int pid);
212
213 switch (cmd_type) {
214 case CMD_TRACK:
215 cmd_func = lttng_track_pid;
216 break;
217 case CMD_UNTRACK:
218 cmd_func = lttng_untrack_pid;
219 break;
220 default:
221 ERR("Unknown command");
222 retval = CMD_ERROR;
223 goto end;
224 }
225
226 memset(&dom, 0, sizeof(dom));
227 if (opt_kernel) {
228 dom.type = LTTNG_DOMAIN_KERNEL;
229 } else if (opt_userspace) {
230 dom.type = LTTNG_DOMAIN_UST;
231 } else {
232 /* Checked by the caller. */
233 assert(0);
234 }
235
236 ret = parse_pid_string(pid_string, all, &pid_list, &nr_pids);
237 if (ret != CMD_SUCCESS) {
238 ERR("Error parsing PID string");
239 usage(stderr, cmd_str);
240 retval = CMD_ERROR;
241 goto end;
242 }
243
244 handle = lttng_create_handle(session_name, &dom);
245 if (handle == NULL) {
246 retval = CMD_ERROR;
247 goto end;
248 }
249
250 if (writer) {
251 /* Open process element */
252 ret = mi_lttng_targets_open(writer);
253 if (ret) {
254 retval = CMD_ERROR;
255 goto end;
256 }
257 }
258
259 for (i = 0; i < nr_pids; i++) {
260 DBG("%s PID %d", cmd_str, pid_list[i]);
261 ret = cmd_func(handle, pid_list[i]);
262 if (ret) {
263 switch (-ret) {
264 case LTTNG_ERR_PID_TRACKED:
265 WARN("PID %i already tracked in session %s",
266 pid_list[i], session_name);
267 success = 1;
268 retval = CMD_SUCCESS;
269 break;
270 case LTTNG_ERR_PID_NOT_TRACKED:
271 WARN("PID %i not tracked in session %s",
272 pid_list[i], session_name);
273 success = 1;
274 retval = CMD_SUCCESS;
275 break;
276 default:
277 ERR("%s", lttng_strerror(ret));
278 success = 0;
279 retval = CMD_ERROR;
280 break;
281 }
282 } else {
283 MSG("PID %i %sed in session %s",
284 pid_list[i], cmd_str, session_name);
285 success = 1;
286 }
287
288 /* Mi */
289 if (writer) {
290 ret = mi_lttng_pid_target(writer, pid_list[i], 1);
291 if (ret) {
292 retval = CMD_ERROR;
293 goto end;
294 }
295
296 ret = mi_lttng_writer_write_element_bool(writer,
297 mi_lttng_element_success, success);
298 if (ret) {
299 retval = CMD_ERROR;
300 goto end;
301 }
302
303 ret = mi_lttng_writer_close_element(writer);
304 if (ret) {
305 retval = CMD_ERROR;
306 goto end;
307 }
308 }
309 }
310
311 if (writer) {
312 /* Close targets element */
313 ret = mi_lttng_writer_close_element(writer);
314 if (ret) {
315 retval = CMD_ERROR;
316 goto end;
317 }
318 }
319
320 end:
321 if (handle) {
322 lttng_destroy_handle(handle);
323 }
324 free(pid_list);
325 return retval;
326 }
327
328 static
329 const char *get_mi_element_command(enum cmd_type cmd_type)
330 {
331 switch (cmd_type) {
332 case CMD_TRACK:
333 return mi_lttng_element_command_track;
334 case CMD_UNTRACK:
335 return mi_lttng_element_command_untrack;
336 default:
337 return NULL;
338 }
339 }
340
341 /*
342 * Add/remove tracker to/from session.
343 */
344 static
345 int cmd_track_untrack(enum cmd_type cmd_type, const char *cmd_str,
346 int argc, const char **argv)
347 {
348 int opt, ret = 0;
349 enum cmd_error_code command_ret = CMD_SUCCESS;
350 int success = 1;
351 static poptContext pc;
352 char *session_name = NULL;
353 struct mi_writer *writer = NULL;
354
355 if (argc < 1) {
356 usage(stderr, cmd_str);
357 command_ret = CMD_ERROR;
358 goto end;
359 }
360
361 pc = poptGetContext(NULL, argc, argv, long_options, 0);
362 poptReadDefaultConfig(pc, 0);
363
364 while ((opt = poptGetNextOpt(pc)) != -1) {
365 switch (opt) {
366 case OPT_HELP:
367 SHOW_HELP();
368 goto end;
369 case OPT_LIST_OPTIONS:
370 list_cmd_options(stdout, long_options);
371 goto end;
372 case OPT_SESSION:
373 case OPT_PID:
374 opt_pid = 1;
375 break;
376 default:
377 usage(stderr, cmd_str);
378 command_ret = CMD_UNDEFINED;
379 goto end;
380 }
381 }
382
383 ret = print_missing_or_multiple_domains(opt_kernel + opt_userspace);
384 if (ret) {
385 ret = CMD_ERROR;
386 goto end;
387 }
388
389 if (!opt_session_name) {
390 session_name = get_session_name();
391 if (session_name == NULL) {
392 command_ret = CMD_ERROR;
393 goto end;
394 }
395 } else {
396 session_name = opt_session_name;
397 }
398
399 /* Currently only PID tracker is supported */
400 if (!opt_pid) {
401 ERR("Please specify at least one tracker with its expected arguments");
402 usage(stderr, cmd_str);
403 command_ret = CMD_ERROR;
404 goto end;
405 }
406
407 /* Mi check */
408 if (lttng_opt_mi) {
409 writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi);
410 if (!writer) {
411 command_ret = CMD_ERROR;
412 goto end;
413 }
414 }
415
416 if (writer) {
417 /* Open command element */
418 ret = mi_lttng_writer_command_open(writer,
419 get_mi_element_command(cmd_type));
420 if (ret) {
421 command_ret = CMD_ERROR;
422 goto end;
423 }
424
425 /* Open output element */
426 ret = mi_lttng_writer_open_element(writer,
427 mi_lttng_element_command_output);
428 if (ret) {
429 command_ret = CMD_ERROR;
430 goto end;
431 }
432 }
433
434 command_ret = track_untrack_pid(cmd_type,
435 cmd_str, session_name, opt_pid_string,
436 opt_all, writer);
437 if (command_ret != CMD_SUCCESS) {
438 success = 0;
439 }
440
441 /* Mi closing */
442 if (writer) {
443 /* Close output element */
444 ret = mi_lttng_writer_close_element(writer);
445 if (ret) {
446 command_ret = CMD_ERROR;
447 goto end;
448 }
449
450 /* Success ? */
451 ret = mi_lttng_writer_write_element_bool(writer,
452 mi_lttng_element_command_success, success);
453 if (ret) {
454 command_ret = CMD_ERROR;
455 goto end;
456 }
457
458 /* Command element close */
459 ret = mi_lttng_writer_command_close(writer);
460 if (ret) {
461 command_ret = CMD_ERROR;
462 goto end;
463 }
464 }
465
466 end:
467 if (!opt_session_name) {
468 free(session_name);
469 }
470
471 /* Mi clean-up */
472 if (writer && mi_lttng_writer_destroy(writer)) {
473 /* Preserve original error code */
474 command_ret = CMD_ERROR;
475 }
476
477 poptFreeContext(pc);
478 return (int) command_ret;
479 }
480
481 int cmd_track(int argc, const char **argv)
482 {
483 return cmd_track_untrack(CMD_TRACK, "track", argc, argv);
484 }
485
486 int cmd_untrack(int argc, const char **argv)
487 {
488 return cmd_track_untrack(CMD_UNTRACK, "untrack", argc, argv);
489 }
This page took 0.060852 seconds and 4 git commands to generate.