2 * Copyright (C) 2009 Pierre-Marc Fournier, Philippe Proulx-Barrette
3 * Copyright (C) 2011 Ericsson AB
4 * David Goulet <david.goulet@polymtl.ca>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 #include "libustcomm.h"
30 #include "libustctl.h"
36 * Execute command using ustcomm API.
38 static int do_cmd(int sock
, const struct ustcomm_header
*req_header
,
39 const char *req_data
, struct ustcomm_header
*res_header
, char **res_data
)
41 int result
, saved_errno
= 0;
44 recv_buf
= malloc(USTCOMM_BUFFER_SIZE
);
50 result
= ustcomm_req(sock
, req_header
, req_data
, res_header
, recv_buf
);
52 saved_errno
= -res_header
->result
;
53 if (res_header
->size
== 0 || saved_errno
> 0) {
63 ERR("ustcomm req failed");
65 saved_errno
= ENOTCONN
;
67 saved_errno
= -result
;
84 static int realloc_pid_list(pid_t
**pid_list
, unsigned int *pid_list_size
)
87 unsigned int new_pid_list_size
= 2 * (*pid_list_size
);
89 new_pid_list
= realloc(*pid_list
, new_pid_list_size
* sizeof(pid_t
));
94 *pid_list
= new_pid_list
;
95 *pid_list_size
= new_pid_list_size
;
103 * Set pid_list with all pid in the dir.
105 static int get_pids_in_dir(DIR *dir
, pid_t
**pid_list
,
106 unsigned int *pid_list_index
, unsigned int *pid_list_size
)
108 struct dirent
*dirent
;
111 while ((dirent
= readdir(dir
))) {
112 if (!strcmp(dirent
->d_name
, ".") ||
113 !strcmp(dirent
->d_name
, "..") ||
114 !strcmp(dirent
->d_name
, "ust-consumer") ||
115 dirent
->d_type
== DT_DIR
) {
119 if (ustcomm_is_socket_live(dirent
->d_name
, &read_pid
)) {
120 (*pid_list
)[(*pid_list_index
)++] = (long) read_pid
;
121 if (*pid_list_index
== *pid_list_size
) {
122 if (realloc_pid_list(pid_list
, pid_list_size
)) {
129 (*pid_list
)[*pid_list_index
] = 0; /* Array end */
137 static pid_t
*get_pids_non_root(void)
141 unsigned int pid_list_index
= 0, pid_list_size
= 1;
142 pid_t
*pid_list
= NULL
;
144 dir_name
= ustcomm_user_sock_dir();
149 dir
= opendir(dir_name
);
154 pid_list
= malloc(pid_list_size
* sizeof(pid_t
));
159 if (get_pids_in_dir(dir
, &pid_list
, &pid_list_index
, &pid_list_size
)) {
160 /* if any errors are encountered, force freeing of the list */
176 static pid_t
*get_pids_root(void)
180 unsigned int pid_list_index
= 0, pid_list_size
= 1;
181 pid_t
*pid_list
= NULL
;
182 struct dirent
*dirent
;
185 tmp_dir
= opendir(USER_TMP_DIR
);
190 pid_list
= malloc(pid_list_size
* sizeof(pid_t
));
195 while ((dirent
= readdir(tmp_dir
))) {
196 /* Compare the dir to check for the USER_SOCK_DIR_BASE prefix */
197 if (!strncmp(dirent
->d_name
, USER_SOCK_DIR_BASE
,
198 strlen(USER_SOCK_DIR_BASE
))) {
199 if (asprintf(&dir_name
, USER_TMP_DIR
"/%s", dirent
->d_name
) < 0) {
203 dir
= opendir(dir_name
);
209 result
= get_pids_in_dir(dir
, &pid_list
, &pid_list_index
, &pid_list_size
);
213 * if any errors are encountered,
214 * force freeing of the list
228 * ustctl_get_subbuf_num_size
230 static int ustctl_get_subbuf_num_size(int sock
, const char *trace
,
231 const char *channel
, int *num
, int *size
)
233 struct ustcomm_header req_header
, res_header
;
234 struct ustcomm_channel_info ch_inf
, *ch_inf_res
;
238 result
= ustcomm_pack_channel_info(&req_header
,
239 &ch_inf
, trace
, channel
);
245 req_header
.command
= GET_SUBBUF_NUM_SIZE
;
247 result
= do_cmd(sock
, &req_header
, (char *)&ch_inf
,
248 &res_header
, (char **)&ch_inf_res
);
253 *num
= ch_inf_res
->subbuf_num
;
254 *size
= ch_inf_res
->subbuf_size
;
264 * Do all trace action command
266 static int do_trace_cmd(int sock
, const char *trace
, int command
)
268 struct ustcomm_header req_header
, res_header
;
269 struct ustcomm_single_field trace_inf
;
272 result
= ustcomm_pack_single_field(&req_header
, &trace_inf
, trace
);
278 req_header
.command
= command
;
280 return do_cmd(sock
, &req_header
, (char *)&trace_inf
, &res_header
, NULL
);
288 int ustctl_connect_pid(pid_t pid
)
292 if (ustcomm_connect_app(pid
, &sock
)) {
293 ERR("could not connect to PID %u", (unsigned int) pid
);
302 * ustctl_get_online_pids
304 * Return list of online pids.
306 pid_t
*ustctl_get_online_pids(void)
311 pid_list
= get_pids_non_root();
313 pid_list
= get_pids_root();
316 if (pid_list
&& pid_list
[0] == 0) {
326 * Sets ust_marker state (USTCTL_MS_ON or USTCTL_MS_OFF).
328 * @param mn Marker name
329 * @param state Marker's new state
330 * @param pid Traced process ID
331 * @return 0 if successful, or errors {USTCTL_ERR_GEN, USTCTL_ERR_ARG}
333 int ustctl_set_marker_state(int sock
, const char *trace
, const char *channel
,
334 const char *ust_marker
, int state
)
336 struct ustcomm_header req_header
, res_header
;
337 struct ustcomm_ust_marker_info ust_marker_inf
;
340 result
= ustcomm_pack_ust_marker_info(&req_header
, &ust_marker_inf
,
341 trace
, channel
, ust_marker
);
347 req_header
.command
= state
? ENABLE_MARKER
: DISABLE_MARKER
;
349 return do_cmd(sock
, &req_header
, (char *)&ust_marker_inf
, &res_header
, NULL
);
353 * Set subbuffer size.
355 * @param channel_size Channel name and size
356 * @param pid Traced process ID
357 * @return 0 if successful, or error
359 int ustctl_set_subbuf_size(int sock
, const char *trace
, const char *channel
,
360 unsigned int subbuf_size
)
362 struct ustcomm_header req_header
, res_header
;
363 struct ustcomm_channel_info ch_inf
;
366 result
= ustcomm_pack_channel_info(&req_header
, &ch_inf
,
373 req_header
.command
= SET_SUBBUF_SIZE
;
374 ch_inf
.subbuf_size
= subbuf_size
;
376 return do_cmd(sock
, &req_header
, (char *)&ch_inf
, &res_header
, NULL
);
382 * @param channel_num Channel name and num
383 * @param pid Traced process ID
384 * @return 0 if successful, or error
386 int ustctl_set_subbuf_num(int sock
, const char *trace
, const char *channel
,
389 struct ustcomm_header req_header
, res_header
;
390 struct ustcomm_channel_info ch_inf
;
393 result
= ustcomm_pack_channel_info(&req_header
,
394 &ch_inf
, trace
, channel
);
400 req_header
.command
= SET_SUBBUF_NUM
;
401 ch_inf
.subbuf_num
= num
;
403 return do_cmd(sock
, &req_header
, (char *)&ch_inf
, &res_header
, NULL
);
410 * @param channel Channel name
411 * @param pid Traced process ID
412 * @return subbuf cnf if successful, or error
414 int ustctl_get_subbuf_num(int sock
, const char *trace
, const char *channel
)
416 int num
, size
, result
;
418 result
= ustctl_get_subbuf_num_size(sock
, trace
, channel
, &num
, &size
);
428 * Get subbuffer size.
430 * @param channel Channel name
431 * @param pid Traced process ID
432 * @return subbuf size if successful, or error
434 int ustctl_get_subbuf_size(int sock
, const char *trace
, const char *channel
)
436 int num
, size
, result
;
438 result
= ustctl_get_subbuf_num_size(sock
, trace
, channel
, &num
, &size
);
448 * Destroys an UST trace according to a PID.
450 * @param pid Traced process ID
451 * @return 0 if successful, or error USTCTL_ERR_GEN
453 int ustctl_destroy_trace(int sock
, const char *trace
)
455 return do_trace_cmd(sock
, trace
, DESTROY_TRACE
);
459 * Starts an UST trace (and setups it) according to a PID.
461 * @param pid Traced process ID
462 * @return 0 if successful, or error USTCTL_ERR_GEN
464 int ustctl_setup_and_start(int sock
, const char *trace
)
466 return do_trace_cmd(sock
, trace
, START
);
470 * Creates an UST trace according to a PID.
472 * @param pid Traced process ID
473 * @return 0 if successful, or error USTCTL_ERR_GEN
475 int ustctl_create_trace(int sock
, const char *trace
)
477 return do_trace_cmd(sock
, trace
, CREATE_TRACE
);
481 * Starts an UST trace according to a PID.
483 * @param pid Traced process ID
484 * @return 0 if successful, or error USTCTL_ERR_GEN
486 int ustctl_start_trace(int sock
, const char *trace
)
488 return do_trace_cmd(sock
, trace
, START_TRACE
);
492 * Alloc an UST trace according to a PID.
494 * @param pid Traced process ID
495 * @return 0 if successful, or error USTCTL_ERR_GEN
497 int ustctl_alloc_trace(int sock
, const char *trace
)
499 return do_trace_cmd(sock
, trace
, ALLOC_TRACE
);
503 * ustctl_force_switch
505 * Force switch buffer in libust
507 int ustctl_force_switch(int sock
, const char *trace
)
509 return do_trace_cmd(sock
, trace
, FORCE_SUBBUF_SWITCH
);
513 * Stops an UST trace according to a PID.
515 * @param pid Traced process ID
516 * @return 0 if successful, or error USTCTL_ERR_GEN
518 int ustctl_stop_trace(int sock
, const char *trace
)
520 return do_trace_cmd(sock
, trace
, STOP_TRACE
);
524 * Counts newlines ('\n') in a string.
526 * @param str String to search in
527 * @return Total newlines count
529 unsigned int ustctl_count_nl(const char *str
)
531 unsigned int i
= 0, tot
= 0;
533 while (str
[i
] != '\0') {
534 if (str
[i
] == '\n') {
544 * Frees a CMSF array.
546 * @param cmsf CMSF array to free
547 * @return 0 if successful, or error USTCTL_ERR_ARG
549 int ustctl_free_cmsf(struct ustctl_marker_status
*cmsf
)
556 while (cmsf
[i
].channel
!= NULL
) {
557 free(cmsf
[i
].channel
);
558 free(cmsf
[i
].ust_marker
);
568 * Gets channel/ust_marker/state/format string for a given PID.
570 * @param cmsf Pointer to CMSF array to be filled (callee allocates, caller
571 * frees with `ustctl_free_cmsf')
572 * @param pid Targeted PID
573 * @return 0 if successful, or -1 on error
575 int ustctl_get_cmsf(int sock
, struct ustctl_marker_status
**cmsf
)
577 struct ustcomm_header req_header
, res_header
;
578 char *big_str
= NULL
;
580 struct ustctl_marker_status
*tmp_cmsf
= NULL
;
581 unsigned int i
= 0, cmsf_ind
= 0;
587 req_header
.command
= LIST_MARKERS
;
590 result
= ustcomm_send(sock
, &req_header
, NULL
);
592 ERR("error while requesting ust_marker list");
596 result
= ustcomm_recv_alloc(sock
, &res_header
, &big_str
);
598 ERR("error while receiving ust_marker list");
602 tmp_cmsf
= (struct ustctl_marker_status
*) malloc(sizeof(struct ustctl_marker_status
) *
603 (ustctl_count_nl(big_str
) + 1));
604 if (tmp_cmsf
== NULL
) {
605 ERR("Failed to allocate CMSF array");
609 /* Parse received reply string (format: "[chan]/[mark] [st] [fs]"): */
610 while (big_str
[i
] != '\0') {
613 sscanf(big_str
+ i
, "ust_marker: %a[^/]/%a[^ ] %c %a[^\n]",
614 &tmp_cmsf
[cmsf_ind
].channel
,
615 &tmp_cmsf
[cmsf_ind
].ust_marker
,
617 &tmp_cmsf
[cmsf_ind
].fs
);
618 tmp_cmsf
[cmsf_ind
].state
= (state
== USTCTL_MS_CHR_ON
?
619 USTCTL_MS_ON
: USTCTL_MS_OFF
); /* Marker state */
621 while (big_str
[i
] != '\n') {
622 ++i
; /* Go to next '\n' */
624 ++i
; /* Skip current pointed '\n' */
627 tmp_cmsf
[cmsf_ind
].channel
= NULL
;
628 tmp_cmsf
[cmsf_ind
].ust_marker
= NULL
;
629 tmp_cmsf
[cmsf_ind
].fs
= NULL
;
640 * @param tes TES array to free
641 * @return 0 if successful, or error USTCTL_ERR_ARG
643 int ustctl_free_tes(struct ustctl_trace_event_status
*tes
)
646 return USTCTL_ERR_ARG
;
650 while (tes
[i
].name
!= NULL
) {
660 * Gets trace_events string for a given PID.
662 * @param tes Pointer to TES array to be filled (callee allocates, caller
663 * frees with `ustctl_free_tes')
664 * @param pid Targeted PID
665 * @return 0 if successful, or -1 on error
667 int ustctl_get_tes(int sock
, struct ustctl_trace_event_status
**tes
)
669 struct ustcomm_header req_header
, res_header
;
670 char *big_str
= NULL
;
672 struct ustctl_trace_event_status
*tmp_tes
= NULL
;
673 unsigned int i
= 0, tes_ind
= 0;
679 req_header
.command
= LIST_TRACE_EVENTS
;
682 result
= ustcomm_send(sock
, &req_header
, NULL
);
684 ERR("error while requesting trace_event list");
688 result
= ustcomm_recv_alloc(sock
, &res_header
, &big_str
);
690 ERR("error while receiving ust_marker list");
694 tmp_tes
= (struct ustctl_trace_event_status
*)
695 malloc(sizeof(struct ustctl_trace_event_status
) *
696 (ustctl_count_nl(big_str
) + 1));
697 if (tmp_tes
== NULL
) {
698 ERR("Failed to allocate TES array");
702 /* Parse received reply string (format: "[name]"): */
703 while (big_str
[i
] != '\0') {
704 sscanf(big_str
+ i
, "trace_event: %a[^\n]", &tmp_tes
[tes_ind
].name
);
705 while (big_str
[i
] != '\n') {
706 ++i
; /* Go to next '\n' */
708 ++i
; /* Skip current pointed '\n' */
711 tmp_tes
[tes_ind
].name
= NULL
;
722 * @param sock_path Sock path
723 * @param pid Traced process ID
724 * @return 0 if successful, or error
726 int ustctl_set_sock_path(int sock
, const char *sock_path
)
729 struct ustcomm_header req_header
, res_header
;
730 struct ustcomm_single_field sock_path_msg
;
732 result
= ustcomm_pack_single_field(&req_header
, &sock_path_msg
, sock_path
);
738 req_header
.command
= SET_SOCK_PATH
;
740 return do_cmd(sock
, &req_header
, (char *)&sock_path_msg
, &res_header
, NULL
);
746 * @param sock_path Pointer to where the sock path will be returned
747 * @param pid Traced process ID
748 * @return 0 if successful, or error
750 int ustctl_get_sock_path(int sock
, char **sock_path
)
753 struct ustcomm_header req_header
, res_header
;
754 struct ustcomm_single_field
*sock_path_msg
;
756 req_header
.command
= GET_SOCK_PATH
;
759 result
= do_cmd(sock
, &req_header
, NULL
, &res_header
,
760 (char **)&sock_path_msg
);
765 result
= ustcomm_unpack_single_field(sock_path_msg
);
770 *sock_path
= strdup(sock_path_msg
->field
);