Implement PID tracking for kernel tracing
[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 _GNU_SOURCE
20 #define _LGPL_SOURCE
21 #include <ctype.h>
22 #include <popt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.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
100 if (all && _pid_string) {
101 ERR("An empty PID string is expected with --all");
102 retval = CMD_ERROR;
103 goto error;
104 }
105 if (!all && !_pid_string) {
106 ERR("Please specify --all with an empty PID string");
107 retval = CMD_ERROR;
108 goto error;
109 }
110 if (all) {
111 pid_list = zmalloc(sizeof(*_pid_list));
112 if (!pid_list) {
113 ERR("Out of memory");
114 retval = CMD_ERROR;
115 goto error;
116 }
117 /* Empty PID string means all PIDs */
118 count = 1;
119 pid_list[0] = -1;
120 goto assign;
121 }
122
123 pid_string = strdup(_pid_string);
124 if (!pid_string) {
125 ERR("Out of memory");
126 retval = CMD_ERROR;
127 goto error;
128 }
129
130 /* Count */
131 one_pid_str = strtok_r(pid_string, ",", &iter);
132 while (one_pid_str != NULL) {
133 unsigned long v;
134
135 v = strtoul(one_pid_str, NULL, 10);
136 if ((v == 0 && errno == EINVAL)
137 || (v == ULONG_MAX && errno == ERANGE)) {
138 ERR("Error parsing PID %s", one_pid_str);
139 retval = CMD_ERROR;
140 goto error;
141 }
142 if ((long) v > INT_MAX || (int) v < 0) {
143 ERR("Invalid PID value %ld", (long) v);
144 retval = CMD_ERROR;
145 goto error;
146 }
147 count++;
148
149 /* For next loop */
150 one_pid_str = strtok_r(NULL, ",", &iter);
151 }
152
153 free(pid_string);
154 /* Identity of delimiter has been lost in first pass. */
155 pid_string = strdup(_pid_string);
156 if (!pid_string) {
157 ERR("Out of memory");
158 retval = CMD_ERROR;
159 goto error;
160 }
161
162 /* Allocate */
163 pid_list = zmalloc(count * sizeof(*pid_list));
164 if (!pid_list) {
165 ERR("Out of memory");
166 retval = CMD_ERROR;
167 goto error;
168 }
169
170 /* Copy */
171 count = 0;
172 one_pid_str = strtok_r(pid_string, ",", &iter);
173 while (one_pid_str != NULL) {
174 unsigned long v;
175
176 v = strtoul(one_pid_str, NULL, 10);
177 pid_list[count++] = (int) v;
178
179 /* For next loop */
180 one_pid_str = strtok_r(NULL, ",", &iter);
181 }
182
183 assign:
184 *nr_pids = count;
185 *_pid_list = pid_list;
186 goto end; /* SUCCESS */
187
188 /* ERROR */
189 error:
190 free(pid_list);
191 end:
192 free(pid_string);
193 return retval;
194 }
195
196 static
197 int track_untrack_pid(enum cmd_type cmd_type, const char *cmd_str,
198 const char *session_name, const char *pid_string,
199 int all, struct mi_writer *writer)
200 {
201 int ret, retval = CMD_SUCCESS, i;
202 int *pid_list = NULL;
203 int nr_pids;
204 struct lttng_domain dom;
205 struct lttng_handle *handle = NULL;
206 int (*lib_func)(struct lttng_handle *handle, int pid);
207
208 switch (cmd_type) {
209 case CMD_TRACK:
210 lib_func = lttng_track_pid;
211 break;
212 case CMD_UNTRACK:
213 lib_func = lttng_untrack_pid;
214 break;
215 default:
216 ERR("Unknown command");
217 retval = CMD_ERROR;
218 goto end;
219 }
220
221 memset(&dom, 0, sizeof(dom));
222 if (opt_kernel) {
223 dom.type = LTTNG_DOMAIN_KERNEL;
224 } else if (opt_userspace) {
225 dom.type = LTTNG_DOMAIN_UST;
226 } else {
227 print_missing_domain();
228 ret = CMD_ERROR;
229 goto end;
230 }
231
232 ret = parse_pid_string(pid_string, all, &pid_list, &nr_pids);
233 if (ret != CMD_SUCCESS) {
234 ERR("Error parsing PID string");
235 usage(stderr, cmd_str);
236 retval = CMD_ERROR;
237 goto end;
238 }
239
240 handle = lttng_create_handle(session_name, &dom);
241 if (handle == NULL) {
242 retval = CMD_ERROR;
243 goto end;
244 }
245
246 if (writer) {
247 /* Open pids element */
248 ret = mi_lttng_writer_open_element(writer, config_element_pids);
249 if (ret) {
250 retval = CMD_ERROR;
251 goto end;
252 }
253 }
254
255 /* TODO: MI */
256 for (i = 0; i < nr_pids; i++) {
257 DBG("%s PID %d", cmd_str, pid_list[i]);
258 ret = lib_func(handle, pid_list[i]);
259 if (ret) {
260 retval = CMD_ERROR;
261 goto end;
262 }
263 }
264
265 if (writer) {
266 /* Close pids element */
267 ret = mi_lttng_writer_close_element(writer);
268 if (ret) {
269 retval = CMD_ERROR;
270 goto end;
271 }
272 }
273
274 /* SUCCESS */
275 end:
276 if (handle) {
277 lttng_destroy_handle(handle);
278 }
279 free(pid_list);
280 return retval;
281 }
282
283 static
284 const char *get_mi_element_command(enum cmd_type cmd_type)
285 {
286 switch (cmd_type) {
287 case CMD_TRACK:
288 return mi_lttng_element_command_track;
289 case CMD_UNTRACK:
290 return mi_lttng_element_command_untrack;
291 default:
292 return NULL;
293 }
294 }
295
296 /*
297 * Add/remove tracker to/from session.
298 */
299 static
300 int cmd_track_untrack(enum cmd_type cmd_type, const char *cmd_str,
301 int argc, const char **argv)
302 {
303 int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS;
304 int success = 1;
305 static poptContext pc;
306 char *session_name = NULL;
307 struct mi_writer *writer = NULL;
308
309 if (argc < 1) {
310 usage(stderr, cmd_str);
311 ret = CMD_ERROR;
312 goto end;
313 }
314
315 pc = poptGetContext(NULL, argc, argv, long_options, 0);
316 poptReadDefaultConfig(pc, 0);
317
318 while ((opt = poptGetNextOpt(pc)) != -1) {
319 switch (opt) {
320 case OPT_HELP:
321 usage(stdout, cmd_str);
322 goto end;
323 case OPT_LIST_OPTIONS:
324 list_cmd_options(stdout, long_options);
325 goto end;
326 case OPT_SESSION:
327 case OPT_PID:
328 opt_pid = 1;
329 break;
330 default:
331 usage(stderr, cmd_str);
332 ret = CMD_UNDEFINED;
333 goto end;
334 }
335 }
336
337 if (!(opt_userspace ^ opt_kernel)) {
338 ERR("Exactly one of -u or -k needs to be specified.");
339 usage(stderr, cmd_str);
340 ret = CMD_ERROR;
341 goto end;
342 }
343
344 if (!opt_session_name) {
345 session_name = get_session_name();
346 if (session_name == NULL) {
347 ret = CMD_ERROR;
348 goto end;
349 }
350 } else {
351 session_name = opt_session_name;
352 }
353
354 /* Currently only PID tracker is supported */
355 if (!opt_pid) {
356 ERR("Please specify at least one tracker with its expected arguments");
357 usage(stderr, cmd_str);
358 ret = CMD_ERROR;
359 goto end;
360 }
361
362 /* Mi check */
363 if (lttng_opt_mi) {
364 writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi);
365 if (!writer) {
366 ret = CMD_ERROR;
367 goto end;
368 }
369 }
370
371 if (writer) {
372 /* Open command element */
373 ret = mi_lttng_writer_command_open(writer,
374 get_mi_element_command(cmd_type));
375 if (ret) {
376 ret = CMD_ERROR;
377 goto end;
378 }
379
380 /* Open output element */
381 ret = mi_lttng_writer_open_element(writer,
382 mi_lttng_element_command_output);
383 if (ret) {
384 ret = CMD_ERROR;
385 goto end;
386 }
387 }
388
389 command_ret = track_untrack_pid(cmd_type,
390 cmd_str, session_name, opt_pid_string,
391 opt_all, writer);
392 if (command_ret) {
393 success = 0;
394 }
395
396 /* Mi closing */
397 if (writer) {
398 /* Close output element */
399 ret = mi_lttng_writer_close_element(writer);
400 if (ret) {
401 ret = CMD_ERROR;
402 goto end;
403 }
404
405 /* Success ? */
406 ret = mi_lttng_writer_write_element_bool(writer,
407 mi_lttng_element_command_success, success);
408 if (ret) {
409 ret = CMD_ERROR;
410 goto end;
411 }
412
413 /* Command element close */
414 ret = mi_lttng_writer_command_close(writer);
415 if (ret) {
416 ret = CMD_ERROR;
417 goto end;
418 }
419 }
420
421 end:
422 if (!opt_session_name) {
423 free(session_name);
424 }
425
426 /* Mi clean-up */
427 if (writer && mi_lttng_writer_destroy(writer)) {
428 /* Preserve original error code */
429 ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL;
430 }
431
432 /* Overwrite ret if an error occurred during track() */
433 ret = command_ret ? command_ret : ret;
434
435 poptFreeContext(pc);
436 return ret;
437 }
438
439 int cmd_track(int argc, const char **argv)
440 {
441 return cmd_track_untrack(CMD_TRACK, "track", argc, argv);
442 }
443
444 int cmd_untrack(int argc, const char **argv)
445 {
446 return cmd_track_untrack(CMD_UNTRACK, "untrack", argc, argv);
447 }
This page took 0.057739 seconds and 5 git commands to generate.