--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include
+AM_CFLAGS = -fno-strict-aliasing
+
+noinst_LTLIBRARIES = libustcomm.la
+libustcomm_la_SOURCES = \
+ ustcomm.h \
+ ustcomm.c
+
+libustcomm_la_LDFLAGS = -no-undefined -static
+libustcomm_la_CFLAGS = -DUST_COMPONENT="libustcomm" -fPIC -fno-strict-aliasing
--- /dev/null
+/* Copyright (C) 2009 Pierre-Marc Fournier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* API used by UST components to communicate with each other via sockets. */
+
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <poll.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ustcomm.h"
+#include "usterr.h"
+#include "share.h"
+
+static int mkdir_p(const char *path, mode_t mode)
+{
+ const char *path_p;
+ char *tmp;
+
+ int retval = 0;
+ int result;
+ mode_t old_umask;
+
+ tmp = zmalloc(strlen(path) + 1);
+ if (tmp == NULL)
+ return -1;
+
+ /* skip first / */
+ path_p = path+1;
+
+ old_umask = umask(0);
+ for(;;) {
+ while (*path_p != '/') {
+ if(*path_p == 0)
+ break;
+ ++path_p;
+ }
+ if (*path_p == '/') {
+ strncpy(tmp, path, path_p - path);
+ tmp[path_p-path] = '\0';
+ if (tmp[path_p - path - 1] != '/') {
+ result = mkdir(tmp, mode);
+ if(result == -1) {
+ if (!(errno == EEXIST || errno == EACCES || errno == EROFS)) {
+ /* Then this is a real error */
+ retval = -1;
+ break;
+ }
+ }
+ }
+ /* pass / */
+ path_p++;
+ } else {
+ /* last component */
+ result = mkdir(path, mode);
+ if (result == -1)
+ retval = -1;
+ break;
+ }
+ }
+
+ free(tmp);
+ umask(old_umask);
+ return retval;
+}
+
+static struct sockaddr_un * create_sock_addr(const char *name,
+ size_t *sock_addr_size)
+{
+ struct sockaddr_un * addr;
+ size_t alloc_size;
+
+ alloc_size = (size_t) (((struct sockaddr_un *) 0)->sun_path) +
+ strlen(name) + 1;
+
+ addr = malloc(alloc_size);
+ if (addr < 0) {
+ ERR("allocating addr failed");
+ return NULL;
+ }
+
+ addr->sun_family = AF_UNIX;
+ strcpy(addr->sun_path, name);
+
+ *sock_addr_size = alloc_size;
+
+ return addr;
+}
+
+struct ustcomm_sock * ustcomm_init_sock(int fd, int epoll_fd,
+ struct cds_list_head *list)
+{
+ struct epoll_event ev;
+ struct ustcomm_sock *sock;
+
+ sock = malloc(sizeof(struct ustcomm_sock));
+ if (!sock) {
+ perror("malloc: couldn't allocate ustcomm_sock");
+ return NULL;
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = sock;
+ sock->fd = fd;
+
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock->fd, &ev) == -1) {
+ perror("epoll_ctl: failed to add socket\n");
+ free(sock);
+ return NULL;
+ }
+
+ sock->epoll_fd = epoll_fd;
+ if (list) {
+ cds_list_add(&sock->list, list);
+ } else {
+ CDS_INIT_LIST_HEAD(&sock->list);
+ }
+
+ return sock;
+}
+
+void ustcomm_del_sock(struct ustcomm_sock *sock, int keep_in_epoll)
+{
+ cds_list_del(&sock->list);
+ if (!keep_in_epoll) {
+ if (epoll_ctl(sock->epoll_fd, EPOLL_CTL_DEL, sock->fd, NULL) == -1) {
+ PERROR("epoll_ctl: failed to delete socket");
+ }
+ }
+ close(sock->fd);
+ free(sock);
+}
+
+struct ustcomm_sock * ustcomm_init_named_socket(const char *name,
+ int epoll_fd)
+{
+ int result;
+ int fd;
+ size_t sock_addr_size;
+ struct sockaddr_un * addr;
+ struct ustcomm_sock *sock;
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if(fd == -1) {
+ PERROR("socket");
+ return NULL;
+ }
+
+ addr = create_sock_addr(name, &sock_addr_size);
+ if (addr == NULL) {
+ ERR("allocating addr, UST thread bailing");
+ goto close_sock;
+ }
+
+ result = access(name, F_OK);
+ if(result == 0) {
+ /* file exists */
+ result = unlink(name);
+ if(result == -1) {
+ PERROR("unlink of socket file");
+ goto free_addr;
+ }
+ DBG("socket already exists; overwriting");
+ }
+
+ result = bind(fd, (struct sockaddr *)addr, sock_addr_size);
+ if(result == -1) {
+ PERROR("bind");
+ goto free_addr;
+ }
+
+ result = listen(fd, 1);
+ if(result == -1) {
+ PERROR("listen");
+ goto free_addr;
+ }
+
+ sock = ustcomm_init_sock(fd, epoll_fd,
+ NULL);
+ if (!sock) {
+ ERR("failed to create ustcomm_sock");
+ goto free_addr;
+ }
+
+ free(addr);
+
+ return sock;
+
+free_addr:
+ free(addr);
+close_sock:
+ close(fd);
+
+ return NULL;
+}
+
+void ustcomm_del_named_sock(struct ustcomm_sock *sock,
+ int keep_socket_file)
+{
+ int result, fd;
+ struct stat st;
+ struct sockaddr dummy;
+ struct sockaddr_un *sockaddr = NULL;
+ int alloc_size;
+
+ fd = sock->fd;
+
+ if(!keep_socket_file) {
+
+ /* Get the socket name */
+ alloc_size = sizeof(dummy);
+ if (getsockname(fd, &dummy, (socklen_t *)&alloc_size) < 0) {
+ PERROR("getsockname failed");
+ goto del_sock;
+ }
+
+ sockaddr = zmalloc(alloc_size);
+ if (!sockaddr) {
+ ERR("failed to allocate sockaddr");
+ goto del_sock;
+ }
+
+ if (getsockname(fd, sockaddr, (socklen_t *)&alloc_size) < 0) {
+ PERROR("getsockname failed");
+ goto free_sockaddr;
+ }
+
+ /* Destroy socket */
+ result = stat(sockaddr->sun_path, &st);
+ if(result < 0) {
+ PERROR("stat (%s)", sockaddr->sun_path);
+ goto free_sockaddr;
+ }
+
+ /* Paranoid check before deleting. */
+ result = S_ISSOCK(st.st_mode);
+ if(!result) {
+ ERR("The socket we are about to delete is not a socket.");
+ goto free_sockaddr;
+ }
+
+ result = unlink(sockaddr->sun_path);
+ if(result < 0) {
+ PERROR("unlink");
+ }
+ }
+
+free_sockaddr:
+ free(sockaddr);
+
+del_sock:
+ ustcomm_del_sock(sock, keep_socket_file);
+}
+
+int ustcomm_recv_alloc(int sock,
+ struct ustcomm_header *header,
+ char **data) {
+ int result;
+ struct ustcomm_header peek_header;
+ struct iovec iov[2];
+ struct msghdr msg;
+
+ /* Just to make the caller fail hard */
+ *data = NULL;
+
+ result = recv(sock, &peek_header, sizeof(peek_header),
+ MSG_PEEK | MSG_WAITALL);
+ if (result <= 0) {
+ if(errno == ECONNRESET) {
+ return 0;
+ } else if (errno == EINTR) {
+ return -1;
+ } else if (result < 0) {
+ PERROR("recv");
+ return -1;
+ }
+ return 0;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+
+ iov[0].iov_base = (char *)header;
+ iov[0].iov_len = sizeof(struct ustcomm_header);
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ if (peek_header.size) {
+ *data = zmalloc(peek_header.size);
+ if (!*data) {
+ return -ENOMEM;
+ }
+
+ iov[1].iov_base = *data;
+ iov[1].iov_len = peek_header.size;
+
+ msg.msg_iovlen++;
+ }
+
+ result = recvmsg(sock, &msg, MSG_WAITALL);
+ if (result < 0) {
+ free(*data);
+ PERROR("recvmsg failed");
+ }
+
+ return result;
+}
+
+/* returns 1 to indicate a message was received
+ * returns 0 to indicate no message was received (end of stream)
+ * returns -1 to indicate an error
+ */
+int ustcomm_recv_fd(int sock,
+ struct ustcomm_header *header,
+ char *data, int *fd)
+{
+ int result;
+ struct ustcomm_header peek_header;
+ struct iovec iov[2];
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char buf[CMSG_SPACE(sizeof(int))];
+
+ result = recv(sock, &peek_header, sizeof(peek_header),
+ MSG_PEEK | MSG_WAITALL);
+ if (result <= 0) {
+ if(errno == ECONNRESET) {
+ return 0;
+ } else if (errno == EINTR) {
+ return -1;
+ } else if (result < 0) {
+ PERROR("recv");
+ return -1;
+ }
+ return 0;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+
+ iov[0].iov_base = (char *)header;
+ iov[0].iov_len = sizeof(struct ustcomm_header);
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ if (peek_header.size && data) {
+ if (peek_header.size < 0 ||
+ peek_header.size > USTCOMM_DATA_SIZE) {
+ ERR("big peek header! %ld", peek_header.size);
+ return 0;
+ }
+
+ iov[1].iov_base = data;
+ iov[1].iov_len = peek_header.size;
+
+ msg.msg_iovlen++;
+ }
+
+ if (fd && peek_header.fd_included) {
+ msg.msg_control = buf;
+ msg.msg_controllen = sizeof(buf);
+ }
+
+ result = recvmsg(sock, &msg, MSG_WAITALL);
+ if (result <= 0) {
+ if (result < 0) {
+ PERROR("recvmsg failed");
+ }
+ return result;
+ }
+
+ if (fd && peek_header.fd_included) {
+ cmsg = CMSG_FIRSTHDR(&msg);
+ result = 0;
+ while (cmsg != NULL) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS) {
+ *fd = *(int *) CMSG_DATA(cmsg);
+ result = 1;
+ break;
+ }
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+ if (!result) {
+ ERR("Failed to receive file descriptor\n");
+ }
+ }
+
+ return 1;
+}
+
+int ustcomm_recv(int sock,
+ struct ustcomm_header *header,
+ char *data)
+{
+ return ustcomm_recv_fd(sock, header, data, NULL);
+}
+
+
+int ustcomm_send_fd(int sock,
+ const struct ustcomm_header *header,
+ const char *data,
+ int *fd)
+{
+ struct iovec iov[2];
+ struct msghdr msg;
+ int result;
+ struct cmsghdr *cmsg;
+ char buf[CMSG_SPACE(sizeof(int))];
+
+ memset(&msg, 0, sizeof(msg));
+
+ iov[0].iov_base = (char *)header;
+ iov[0].iov_len = sizeof(struct ustcomm_header);
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ if (header->size && data) {
+ iov[1].iov_base = (char *)data;
+ iov[1].iov_len = header->size;
+
+ msg.msg_iovlen++;
+
+ }
+
+ if (fd && header->fd_included) {
+ msg.msg_control = buf;
+ msg.msg_controllen = sizeof(buf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ *(int *) CMSG_DATA(cmsg) = *fd;
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+
+ result = sendmsg(sock, &msg, MSG_NOSIGNAL);
+ if (result < 0 && errno != EPIPE) {
+ PERROR("sendmsg failed");
+ }
+ return result;
+}
+
+int ustcomm_send(int sock,
+ const struct ustcomm_header *header,
+ const char *data)
+{
+ return ustcomm_send_fd(sock, header, data, NULL);
+}
+
+int ustcomm_req(int sock,
+ const struct ustcomm_header *req_header,
+ const char *req_data,
+ struct ustcomm_header *res_header,
+ char *res_data)
+{
+ int result;
+
+ result = ustcomm_send(sock, req_header, req_data);
+ if ( result <= 0) {
+ return result;
+ }
+
+ return ustcomm_recv(sock, res_header, res_data);
+}
+
+/* Return value:
+ * 0: success
+ * -1: error
+ */
+
+int ustcomm_connect_path(const char *name, int *connection_fd)
+{
+ int result, fd;
+ size_t sock_addr_size;
+ struct sockaddr_un *addr;
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if(fd == -1) {
+ PERROR("socket");
+ return -1;
+ }
+
+ addr = create_sock_addr(name, &sock_addr_size);
+ if (addr == NULL) {
+ ERR("allocating addr failed");
+ goto close_sock;
+ }
+
+ result = connect(fd, (struct sockaddr *)addr, sock_addr_size);
+ if(result == -1) {
+ PERROR("connect (path=%s)", name);
+ goto free_sock_addr;
+ }
+
+ *connection_fd = fd;
+
+ free(addr);
+
+ return 0;
+
+free_sock_addr:
+ free(addr);
+close_sock:
+ close(fd);
+
+ return -1;
+}
+
+/* Returns the current users socket directory, must be freed */
+char *ustcomm_user_sock_dir(void)
+{
+ int result;
+ char *sock_dir = NULL;
+
+ result = asprintf(&sock_dir, "%s%s", USER_SOCK_DIR,
+ cuserid(NULL));
+ if (result < 0) {
+ ERR("string overflow allocating directory name");
+ return NULL;
+ }
+
+ return sock_dir;
+}
+
+static int time_and_pid_from_socket_name(char *sock_name, unsigned long *time,
+ pid_t *pid)
+{
+ char *saveptr, *pid_m_time_str;
+ char *sock_basename = strdup(basename(sock_name));
+
+ if (!sock_basename) {
+ return -1;
+ }
+
+ /* This is the pid */
+ pid_m_time_str = strtok_r(sock_basename, ".", &saveptr);
+ if (!pid_m_time_str) {
+ goto out_err;
+ }
+
+ errno = 0;
+ *pid = (pid_t)strtoul(pid_m_time_str, NULL, 10);
+ if (errno) {
+ goto out_err;
+ }
+
+ /* This should be the time-stamp */
+ pid_m_time_str = strtok_r(NULL, ".", &saveptr);
+ if (!pid_m_time_str) {
+ goto out_err;
+ }
+
+ errno = 0;
+ *time = strtoul(pid_m_time_str, NULL, 10);
+ if (errno) {
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ free(sock_basename);
+ return -1;
+}
+
+time_t ustcomm_pid_st_mtime(pid_t pid)
+{
+ struct stat proc_stat;
+ char proc_name[PATH_MAX];
+
+ if (snprintf(proc_name, PATH_MAX - 1, "/proc/%ld", (long) pid) < 0) {
+ return 0;
+ }
+
+ if (stat(proc_name, &proc_stat)) {
+ return 0;
+ }
+
+ return proc_stat.st_mtime;
+}
+
+int ustcomm_is_socket_live(char *sock_name, pid_t *read_pid)
+{
+ time_t time_from_pid;
+ unsigned long time_from_sock;
+ pid_t pid;
+
+ if (time_and_pid_from_socket_name(sock_name, &time_from_sock, &pid)) {
+ return 0;
+ }
+
+ if (read_pid) {
+ *read_pid = pid;
+ }
+
+ time_from_pid = ustcomm_pid_st_mtime(pid);
+ if (!time_from_pid) {
+ return 0;
+ }
+
+ if ((unsigned long) time_from_pid == time_from_sock) {
+ return 1;
+ }
+
+ return 0;
+}
+
+#define MAX_SOCK_PATH_BASE_LEN 100
+
+static int ustcomm_get_sock_name(char *dir_name, pid_t pid, char *sock_name)
+{
+ struct dirent *dirent;
+ char sock_path_base[MAX_SOCK_PATH_BASE_LEN];
+ int len;
+ DIR *dir = opendir(dir_name);
+
+ snprintf(sock_path_base, MAX_SOCK_PATH_BASE_LEN - 1,
+ "%ld.", (long) pid);
+ len = strlen(sock_path_base);
+
+ while ((dirent = readdir(dir))) {
+ if (!strcmp(dirent->d_name, ".") ||
+ !strcmp(dirent->d_name, "..") ||
+ !strcmp(dirent->d_name, "ust-consumer") ||
+ dirent->d_type == DT_DIR ||
+ strncmp(dirent->d_name, sock_path_base, len)) {
+ continue;
+ }
+
+ if (ustcomm_is_socket_live(dirent->d_name, NULL)) {
+ if (snprintf(sock_name, PATH_MAX - 1, "%s/%s",
+ dir_name, dirent->d_name) < 0) {
+ PERROR("path longer than PATH_MAX?");
+ goto out_err;
+ }
+ closedir(dir);
+ return 0;
+ }
+ }
+
+out_err:
+ closedir(dir);
+ return -1;
+}
+
+/* Open a connection to a traceable app.
+ *
+ * Return value:
+ * 0: success
+ * -1: error
+ */
+
+static int connect_app_non_root(pid_t pid, int *app_fd)
+{
+ int result;
+ int retval = 0;
+ char *dir_name;
+ char sock_name[PATH_MAX];
+
+ dir_name = ustcomm_user_sock_dir();
+ if (!dir_name)
+ return -ENOMEM;
+
+ if (ustcomm_get_sock_name(dir_name, pid, sock_name)) {
+ retval = -ENOENT;
+ goto free_dir_name;
+ }
+
+ result = ustcomm_connect_path(sock_name, app_fd);
+ if (result < 0) {
+ ERR("failed to connect to app");
+ retval = -1;
+ goto free_dir_name;
+ }
+
+free_dir_name:
+ free(dir_name);
+
+ return retval;
+}
+
+
+
+static int connect_app_root(pid_t pid, int *app_fd)
+{
+ DIR *tmp_dir;
+ struct dirent *dirent;
+ char dir_name[PATH_MAX], sock_name[PATH_MAX];
+ int result = -1;
+
+ tmp_dir = opendir(USER_TMP_DIR);
+ if (!tmp_dir) {
+ return -1;
+ }
+
+ while ((dirent = readdir(tmp_dir))) {
+ if (!strncmp(dirent->d_name, USER_SOCK_DIR_BASE,
+ strlen(USER_SOCK_DIR_BASE))) {
+
+ if (snprintf(dir_name, PATH_MAX - 1, "%s/%s", USER_TMP_DIR,
+ dirent->d_name) < 0) {
+ continue;
+ }
+
+ if (ustcomm_get_sock_name(dir_name, pid, sock_name)) {
+ continue;
+ }
+
+ result = ustcomm_connect_path(sock_name, app_fd);
+
+ if (result == 0) {
+ goto close_tmp_dir;
+ }
+ }
+ }
+
+close_tmp_dir:
+ closedir(tmp_dir);
+
+ return result;
+}
+
+int ustcomm_connect_app(pid_t pid, int *app_fd)
+{
+ *app_fd = 0;
+
+ if (geteuid()) {
+ return connect_app_non_root(pid, app_fd);
+ } else {
+ return connect_app_root(pid, app_fd);
+ }
+
+}
+
+int ensure_dir_exists(const char *dir, mode_t mode)
+{
+ struct stat st;
+ int result;
+
+ if (!strcmp(dir, ""))
+ return -1;
+
+ result = stat(dir, &st);
+ if (result < 0 && errno != ENOENT) {
+ return -1;
+ } else if (result < 0) {
+ /* ENOENT */
+ int result;
+
+ result = mkdir_p(dir, mode);
+ if(result != 0) {
+ ERR("executing in recursive creation of directory %s", dir);
+ return -1;
+ }
+ } else {
+ if (st.st_mode != mode) {
+ result = chmod(dir, mode);
+ if (result < 0) {
+ ERR("couldn't set directory mode on %s", dir);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+char * ustcomm_print_data(char *data_field, int field_size,
+ int *offset, const char *format, ...)
+{
+ va_list args;
+ int count, limit;
+ char *ptr = USTCOMM_POISON_PTR;
+
+ limit = field_size - *offset;
+ va_start(args, format);
+ count = vsnprintf(&data_field[*offset], limit, format, args);
+ va_end(args);
+
+ if (count < limit && count > -1) {
+ ptr = NULL + *offset;
+ *offset = *offset + count + 1;
+ }
+
+ return ptr;
+}
+
+char * ustcomm_restore_ptr(char *ptr, char *data_field, int data_field_size)
+{
+ if ((unsigned long)ptr > data_field_size ||
+ ptr == USTCOMM_POISON_PTR) {
+ return NULL;
+ }
+
+ return data_field + (long)ptr;
+}
+
+int ustcomm_pack_single_field(struct ustcomm_header *header,
+ struct ustcomm_single_field *single_field,
+ const char *string)
+{
+ int offset = 0;
+
+ single_field->field = ustcomm_print_data(single_field->data,
+ sizeof(single_field->data),
+ &offset,
+ string);
+
+ if (single_field->field == USTCOMM_POISON_PTR) {
+ return -ENOMEM;
+ }
+
+ header->size = COMPUTE_MSG_SIZE(single_field, offset);
+
+ return 0;
+}
+
+int ustcomm_unpack_single_field(struct ustcomm_single_field *single_field)
+{
+ single_field->field = ustcomm_restore_ptr(single_field->field,
+ single_field->data,
+ sizeof(single_field->data));
+ if (!single_field->field) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int ustcomm_pack_channel_info(struct ustcomm_header *header,
+ struct ustcomm_channel_info *ch_inf,
+ const char *trace,
+ const char *channel)
+{
+ int offset = 0;
+
+ ch_inf->trace = ustcomm_print_data(ch_inf->data,
+ sizeof(ch_inf->data),
+ &offset,
+ trace);
+
+ if (ch_inf->trace == USTCOMM_POISON_PTR) {
+ return -ENOMEM;
+ }
+
+ ch_inf->channel = ustcomm_print_data(ch_inf->data,
+ sizeof(ch_inf->data),
+ &offset,
+ channel);
+
+ if (ch_inf->channel == USTCOMM_POISON_PTR) {
+ return -ENOMEM;
+ }
+
+ header->size = COMPUTE_MSG_SIZE(ch_inf, offset);
+
+ return 0;
+}
+
+
+int ustcomm_unpack_channel_info(struct ustcomm_channel_info *ch_inf)
+{
+ ch_inf->trace = ustcomm_restore_ptr(ch_inf->trace,
+ ch_inf->data,
+ sizeof(ch_inf->data));
+ if (!ch_inf->trace) {
+ return -EINVAL;
+ }
+
+ ch_inf->channel = ustcomm_restore_ptr(ch_inf->channel,
+ ch_inf->data,
+ sizeof(ch_inf->data));
+ if (!ch_inf->channel) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int ustcomm_pack_buffer_info(struct ustcomm_header *header,
+ struct ustcomm_buffer_info *buf_inf,
+ const char *trace,
+ const char *channel,
+ int channel_cpu)
+{
+ int offset = 0;
+
+ buf_inf->trace = ustcomm_print_data(buf_inf->data,
+ sizeof(buf_inf->data),
+ &offset,
+ trace);
+
+ if (buf_inf->trace == USTCOMM_POISON_PTR) {
+ return -ENOMEM;
+ }
+
+ buf_inf->channel = ustcomm_print_data(buf_inf->data,
+ sizeof(buf_inf->data),
+ &offset,
+ channel);
+
+ if (buf_inf->channel == USTCOMM_POISON_PTR) {
+ return -ENOMEM;
+ }
+
+ buf_inf->ch_cpu = channel_cpu;
+
+ header->size = COMPUTE_MSG_SIZE(buf_inf, offset);
+
+ return 0;
+}
+
+
+int ustcomm_unpack_buffer_info(struct ustcomm_buffer_info *buf_inf)
+{
+ buf_inf->trace = ustcomm_restore_ptr(buf_inf->trace,
+ buf_inf->data,
+ sizeof(buf_inf->data));
+ if (!buf_inf->trace) {
+ return -EINVAL;
+ }
+
+ buf_inf->channel = ustcomm_restore_ptr(buf_inf->channel,
+ buf_inf->data,
+ sizeof(buf_inf->data));
+ if (!buf_inf->channel) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int ustcomm_pack_ust_marker_info(struct ustcomm_header *header,
+ struct ustcomm_ust_marker_info *ust_marker_inf,
+ const char *trace,
+ const char *channel,
+ const char *ust_marker)
+{
+ int offset = 0;
+
+ ust_marker_inf->trace = ustcomm_print_data(ust_marker_inf->data,
+ sizeof(ust_marker_inf->data),
+ &offset,
+ trace);
+
+ if (ust_marker_inf->trace == USTCOMM_POISON_PTR) {
+ return -ENOMEM;
+ }
+
+
+ ust_marker_inf->channel = ustcomm_print_data(ust_marker_inf->data,
+ sizeof(ust_marker_inf->data),
+ &offset,
+ channel);
+
+ if (ust_marker_inf->channel == USTCOMM_POISON_PTR) {
+ return -ENOMEM;
+ }
+
+
+ ust_marker_inf->ust_marker = ustcomm_print_data(ust_marker_inf->data,
+ sizeof(ust_marker_inf->data),
+ &offset,
+ ust_marker);
+
+ if (ust_marker_inf->ust_marker == USTCOMM_POISON_PTR) {
+ return -ENOMEM;
+ }
+
+ header->size = COMPUTE_MSG_SIZE(ust_marker_inf, offset);
+
+ return 0;
+}
+
+int ustcomm_unpack_ust_marker_info(struct ustcomm_ust_marker_info *ust_marker_inf)
+{
+ ust_marker_inf->trace = ustcomm_restore_ptr(ust_marker_inf->trace,
+ ust_marker_inf->data,
+ sizeof(ust_marker_inf->data));
+ if (!ust_marker_inf->trace) {
+ return -EINVAL;
+ }
+
+ ust_marker_inf->channel = ustcomm_restore_ptr(ust_marker_inf->channel,
+ ust_marker_inf->data,
+ sizeof(ust_marker_inf->data));
+ if (!ust_marker_inf->channel) {
+ return -EINVAL;
+ }
+
+ ust_marker_inf->ust_marker = ustcomm_restore_ptr(ust_marker_inf->ust_marker,
+ ust_marker_inf->data,
+ sizeof(ust_marker_inf->data));
+ if (!ust_marker_inf->ust_marker) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
--- /dev/null
+/* Copyright (C) 2009 Pierre-Marc Fournier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef USTCOMM_H
+#define USTCOMM_H
+
+#include <sys/types.h>
+#include <sys/un.h>
+#include <urcu/list.h>
+
+#include <ust/kcompat/kcompat.h>
+
+#define SOCK_DIR "/tmp/ust-app-socks"
+#define USER_TMP_DIR "/tmp"
+#define USER_SOCK_DIR_BASE "ust-socks-"
+#define USER_SOCK_DIR USER_TMP_DIR "/" USER_SOCK_DIR_BASE
+
+struct ustcomm_sock {
+ struct cds_list_head list;
+ int fd;
+ int epoll_fd;
+};
+
+struct ustcomm_header {
+ int command;
+ long size;
+ int result;
+ int fd_included;
+};
+
+#define USTCOMM_BUFFER_SIZE ((1 << 12) - sizeof(struct ustcomm_header))
+
+/* Specify a sata size that leaves margin at the end of a buffer
+ * in order to make sure that we never have more data than
+ * will fit in the buffer AND that the last chars (due to a
+ * pre-receive memset) will always be 0, terminating any string
+ */
+#define USTCOMM_DATA_SIZE (USTCOMM_BUFFER_SIZE - 20 * sizeof(void *))
+
+enum tracectl_commands {
+ ALLOC_TRACE,
+ CONSUME_BUFFER,
+ CREATE_TRACE,
+ DESTROY_TRACE,
+ DISABLE_MARKER,
+ ENABLE_MARKER,
+ EXIT,
+ FORCE_SUBBUF_SWITCH,
+ GET_BUF_SHMID_PIPE_FD,
+ GET_PIDUNIQUE,
+ GET_SOCK_PATH,
+ GET_SUBBUFFER,
+ GET_SUBBUF_NUM_SIZE,
+ LIST_MARKERS,
+ LIST_TRACE_EVENTS,
+ LOAD_PROBE_LIB,
+ NOTIFY_BUF_MAPPED,
+ PRINT_MARKERS,
+ PRINT_TRACE_EVENTS,
+ PUT_SUBBUFFER,
+ SETUP_TRACE,
+ SET_SOCK_PATH,
+ SET_SUBBUF_NUM,
+ SET_SUBBUF_SIZE,
+ START,
+ START_TRACE,
+ STOP_TRACE,
+};
+
+struct ustcomm_single_field {
+ char *field;
+ char data[USTCOMM_DATA_SIZE];
+};
+
+struct ustcomm_channel_info {
+ char *trace;
+ char *channel;
+ unsigned int subbuf_size;
+ unsigned int subbuf_num;
+ char data[USTCOMM_DATA_SIZE];
+};
+
+struct ustcomm_buffer_info {
+ char *trace;
+ char *channel;
+ int ch_cpu;
+ pid_t pid;
+ int buf_shmid;
+ int buf_struct_shmid;
+ long consumed_old;
+ char data[USTCOMM_DATA_SIZE];
+};
+
+struct ustcomm_ust_marker_info {
+ char *trace;
+ char *channel;
+ char *ust_marker;
+ char data[USTCOMM_DATA_SIZE];
+};
+
+struct ustcomm_pidunique {
+ s64 pidunique;
+};
+
+struct ustcomm_notify_buf_mapped {
+ char data[USTCOMM_DATA_SIZE];
+};
+
+/* Ensure directory existence, usefull for unix sockets */
+extern int ensure_dir_exists(const char *dir, mode_t mode);
+
+/* Create and delete sockets */
+extern struct ustcomm_sock * ustcomm_init_sock(int fd, int epoll_fd,
+ struct cds_list_head *list);
+extern void ustcomm_del_sock(struct ustcomm_sock *sock, int keep_in_epoll);
+
+/* Create and delete named sockets */
+extern struct ustcomm_sock * ustcomm_init_named_socket(const char *name,
+ int epoll_fd);
+extern void ustcomm_del_named_sock(struct ustcomm_sock *sock,
+ int keep_socket_file);
+
+/* Send and receive functions for file descriptors */
+extern int ustcomm_send_fd(int sock, const struct ustcomm_header *header,
+ const char *data, int *fd);
+extern int ustcomm_recv_fd(int sock, struct ustcomm_header *header,
+ char *data, int *fd);
+
+/* Normal send and receive functions */
+extern int ustcomm_send(int sock, const struct ustcomm_header *header,
+ const char *data);
+extern int ustcomm_recv(int sock, struct ustcomm_header *header,
+ char *data);
+
+/* Receive and allocate data, not to be used inside libust */
+extern int ustcomm_recv_alloc(int sock,
+ struct ustcomm_header *header,
+ char **data);
+
+/* Request function, send and receive */
+extern int ustcomm_req(int sock,
+ const struct ustcomm_header *req_header,
+ const char *req_data,
+ struct ustcomm_header *res_header,
+ char *res_data);
+
+extern int ustcomm_request_consumer(pid_t pid, const char *channel);
+
+/* Returns the current users socket directory, must be freed */
+extern char *ustcomm_user_sock_dir(void);
+
+/* Get the st_m_time from proc*/
+extern time_t ustcomm_pid_st_mtime(pid_t pid);
+
+/* Check that a socket is live */
+extern int ustcomm_is_socket_live(char *sock_name, pid_t *read_pid);
+
+extern int ustcomm_connect_app(pid_t pid, int *app_fd);
+extern int ustcomm_connect_path(const char *path, int *connection_fd);
+
+extern int nth_token_is(const char *str, const char *token, int tok_no);
+
+extern char *nth_token(const char *str, int tok_no);
+
+/* String serialising functions, printf straight into a buffer */
+#define USTCOMM_POISON_PTR (void *)0x19831018
+
+extern char * ustcomm_print_data(char *data_field, int field_size,
+ int *offset, const char *format, ...);
+extern char * ustcomm_restore_ptr(char *ptr, char *data_field,
+ int data_field_size);
+
+#define COMPUTE_MSG_SIZE(struct_ptr, offset) \
+ (size_t) (long)(struct_ptr)->data - (long)(struct_ptr) + (offset)
+
+/* Packing and unpacking functions, making life easier */
+extern int ustcomm_pack_single_field(struct ustcomm_header *header,
+ struct ustcomm_single_field *sf,
+ const char *trace);
+
+extern int ustcomm_unpack_single_field(struct ustcomm_single_field *sf);
+
+extern int ustcomm_pack_channel_info(struct ustcomm_header *header,
+ struct ustcomm_channel_info *ch_inf,
+ const char *trace,
+ const char *channel);
+
+extern int ustcomm_unpack_channel_info(struct ustcomm_channel_info *ch_inf);
+
+extern int ustcomm_pack_buffer_info(struct ustcomm_header *header,
+ struct ustcomm_buffer_info *buf_inf,
+ const char *trace,
+ const char *channel,
+ int channel_cpu);
+
+extern int ustcomm_unpack_buffer_info(struct ustcomm_buffer_info *buf_inf);
+
+extern int ustcomm_pack_ust_marker_info(struct ustcomm_header *header,
+ struct ustcomm_ust_marker_info *ust_marker_inf,
+ const char *trace,
+ const char *channel,
+ const char *ust_marker);
+
+extern int ustcomm_unpack_ust_marker_info(struct ustcomm_ust_marker_info *ust_marker_inf);
+
+#endif /* USTCOMM_H */
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/libust -I$(top_srcdir)/libustcomm \
+ -I$(top_srcdir)/include
+AM_CFLAGS = -fno-strict-aliasing
+
+lib_LTLIBRARIES = libustconsumer.la
+
+libustconsumer_la_SOURCES = libustconsumer.c lowlevel.c lowlevel.h
+
+libustconsumer_la_LDFLAGS = -no-undefined -version-info 0:0:0
+
+libustconsumer_la_LIBADD = \
+ -lpthread \
+ $(top_builddir)/snprintf/libustsnprintf.la \
+ $(top_builddir)/libustcomm/libustcomm.la
+
+libustconsumer_la_CFLAGS = -fno-strict-aliasing
--- /dev/null
+/* Copyright (C) 2009 Pierre-Marc Fournier
+ * 2010 Alexis Halle
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/epoll.h>
+#include <sys/shm.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <ust/ustconsumer.h>
+#include "lowlevel.h"
+#include "usterr_signal_safe.h"
+#include "ustcomm.h"
+
+#define GET_SUBBUF_OK 1
+#define GET_SUBBUF_DONE 0
+#define GET_SUBBUF_DIED 2
+
+#define PUT_SUBBUF_OK 1
+#define PUT_SUBBUF_DIED 0
+#define PUT_SUBBUF_PUSHED 2
+#define PUT_SUBBUF_DONE 3
+
+#define UNIX_PATH_MAX 108
+
+static int get_subbuffer(struct buffer_info *buf)
+{
+ struct ustcomm_header _send_hdr, *send_hdr;
+ struct ustcomm_header _recv_hdr, *recv_hdr;
+ struct ustcomm_buffer_info _send_msg, _recv_msg;
+ struct ustcomm_buffer_info *send_msg, *recv_msg;
+ int result;
+
+ send_hdr = &_send_hdr;
+ recv_hdr = &_recv_hdr;
+ send_msg = &_send_msg;
+ recv_msg = &_recv_msg;
+
+ result = ustcomm_pack_buffer_info(send_hdr, send_msg, buf->trace,
+ buf->channel, buf->channel_cpu);
+ if (result < 0) {
+ return result;
+ }
+
+ send_hdr->command = GET_SUBBUFFER;
+
+ result = ustcomm_req(buf->app_sock, send_hdr, (char *)send_msg,
+ recv_hdr, (char *)recv_msg);
+ if ((result < 0 && (errno == ECONNRESET || errno == EPIPE)) ||
+ result == 0) {
+ DBG("app died while being traced");
+ return GET_SUBBUF_DIED;
+ } else if (result < 0) {
+ ERR("get_subbuffer: ustcomm_req failed");
+ return result;
+ }
+
+ if (!recv_hdr->result) {
+ DBG("got subbuffer %s", buf->name);
+ buf->consumed_old = recv_msg->consumed_old;
+ return GET_SUBBUF_OK;
+ } else if (recv_hdr->result == -ENODATA) {
+ DBG("For buffer %s, the trace was not found. This likely means"
+ " it was destroyed by the user.", buf->name);
+ return GET_SUBBUF_DIED;
+ }
+
+ DBG("error getting subbuffer %s", buf->name);
+ return recv_hdr->result;
+}
+
+static int put_subbuffer(struct buffer_info *buf)
+{
+ struct ustcomm_header _send_hdr, *send_hdr;
+ struct ustcomm_header _recv_hdr, *recv_hdr;
+ struct ustcomm_buffer_info _send_msg, *send_msg;
+ int result;
+
+ send_hdr = &_send_hdr;
+ recv_hdr = &_recv_hdr;
+ send_msg = &_send_msg;
+
+ result = ustcomm_pack_buffer_info(send_hdr, send_msg, buf->trace,
+ buf->channel, buf->channel_cpu);
+ if (result < 0) {
+ return result;
+ }
+
+ send_hdr->command = PUT_SUBBUFFER;
+ send_msg->consumed_old = buf->consumed_old;
+
+ result = ustcomm_req(buf->app_sock, send_hdr, (char *)send_msg,
+ recv_hdr, NULL);
+ if ((result < 0 && (errno == ECONNRESET || errno == EPIPE)) ||
+ result == 0) {
+ DBG("app died while being traced");
+ return PUT_SUBBUF_DIED;
+ } else if (result < 0) {
+ ERR("put_subbuffer: ustcomm_req failed");
+ return result;
+ }
+
+ if (!recv_hdr->result) {
+ DBG("put subbuffer %s", buf->name);
+ return PUT_SUBBUF_OK;
+ } else if (recv_hdr->result == -ENODATA) {
+ DBG("For buffer %s, the trace was not found. This likely means"
+ " it was destroyed by the user.", buf->name);
+ return PUT_SUBBUF_DIED;
+ }
+
+ DBG("error getting subbuffer %s", buf->name);
+ return recv_hdr->result;
+}
+
+void decrement_active_buffers(void *arg)
+{
+ struct ustconsumer_instance *instance = arg;
+ pthread_mutex_lock(&instance->mutex);
+ instance->active_buffers--;
+ pthread_mutex_unlock(&instance->mutex);
+}
+
+static int get_pidunique(int sock, int64_t *pidunique)
+{
+ struct ustcomm_header _send_hdr, *send_hdr;
+ struct ustcomm_header _recv_hdr, *recv_hdr;
+ struct ustcomm_pidunique _recv_msg, *recv_msg;
+ int result;
+
+ send_hdr = &_send_hdr;
+ recv_hdr = &_recv_hdr;
+ recv_msg = &_recv_msg;
+
+ memset(send_hdr, 0, sizeof(*send_hdr));
+
+ send_hdr->command = GET_PIDUNIQUE;
+ result = ustcomm_req(sock, send_hdr, NULL, recv_hdr, (char *)recv_msg);
+ if (result < 1) {
+ return -ENOTCONN;
+ }
+ if (recv_hdr->result < 0) {
+ ERR("App responded with error: %s", strerror(recv_hdr->result));
+ return recv_hdr->result;
+ }
+
+ *pidunique = recv_msg->pidunique;
+
+ return 0;
+}
+
+static int get_buf_shmid_pipe_fd(int sock, struct buffer_info *buf,
+ int *buf_shmid, int *buf_struct_shmid,
+ int *buf_pipe_fd)
+{
+ struct ustcomm_header _send_hdr, *send_hdr;
+ struct ustcomm_header _recv_hdr, *recv_hdr;
+ struct ustcomm_buffer_info _send_msg, *send_msg;
+ struct ustcomm_buffer_info _recv_msg, *recv_msg;
+ int result, recv_pipe_fd;
+
+ send_hdr = &_send_hdr;
+ recv_hdr = &_recv_hdr;
+ send_msg = &_send_msg;
+ recv_msg = &_recv_msg;
+
+ result = ustcomm_pack_buffer_info(send_hdr, send_msg, buf->trace,
+ buf->channel, buf->channel_cpu);
+ if (result < 0) {
+ ERR("Failed to pack buffer info");
+ return result;
+ }
+
+ send_hdr->command = GET_BUF_SHMID_PIPE_FD;
+
+ result = ustcomm_send(sock, send_hdr, (char *)send_msg);
+ if (result < 1) {
+ ERR("Failed to send request");
+ return -ENOTCONN;
+ }
+ result = ustcomm_recv_fd(sock, recv_hdr, (char *)recv_msg, &recv_pipe_fd);
+ if (result < 1) {
+ ERR("Failed to receive message and fd");
+ return -ENOTCONN;
+ }
+ if (recv_hdr->result < 0) {
+ ERR("App responded with error %s", strerror(recv_hdr->result));
+ return recv_hdr->result;
+ }
+
+ *buf_shmid = recv_msg->buf_shmid;
+ *buf_struct_shmid = recv_msg->buf_struct_shmid;
+ *buf_pipe_fd = recv_pipe_fd;
+
+ return 0;
+}
+
+static int get_subbuf_num_size(int sock, struct buffer_info *buf,
+ int *subbuf_num, int *subbuf_size)
+{
+ struct ustcomm_header _send_hdr, *send_hdr;
+ struct ustcomm_header _recv_hdr, *recv_hdr;
+ struct ustcomm_channel_info _send_msg, *send_msg;
+ struct ustcomm_channel_info _recv_msg, *recv_msg;
+ int result;
+
+ send_hdr = &_send_hdr;
+ recv_hdr = &_recv_hdr;
+ send_msg = &_send_msg;
+ recv_msg = &_recv_msg;
+
+ result = ustcomm_pack_channel_info(send_hdr, send_msg, buf->trace,
+ buf->channel);
+ if (result < 0) {
+ return result;
+ }
+
+ send_hdr->command = GET_SUBBUF_NUM_SIZE;
+
+ result = ustcomm_req(sock, send_hdr, (char *)send_msg,
+ recv_hdr, (char *)recv_msg);
+ if (result < 1) {
+ return -ENOTCONN;
+ }
+
+ *subbuf_num = recv_msg->subbuf_num;
+ *subbuf_size = recv_msg->subbuf_size;
+
+ return recv_hdr->result;
+}
+
+
+static int notify_buffer_mapped(int sock, struct buffer_info *buf)
+{
+ struct ustcomm_header _send_hdr, *send_hdr;
+ struct ustcomm_header _recv_hdr, *recv_hdr;
+ struct ustcomm_buffer_info _send_msg, *send_msg;
+ int result;
+
+ send_hdr = &_send_hdr;
+ recv_hdr = &_recv_hdr;
+ send_msg = &_send_msg;
+
+ result = ustcomm_pack_buffer_info(send_hdr, send_msg, buf->trace,
+ buf->channel, buf->channel_cpu);
+ if (result < 0) {
+ return result;
+ }
+
+ send_hdr->command = NOTIFY_BUF_MAPPED;
+
+ result = ustcomm_req(sock, send_hdr, (char *)send_msg,
+ recv_hdr, NULL);
+ if (result < 1) {
+ return -ENOTCONN;
+ }
+
+ return recv_hdr->result;
+}
+
+
+struct buffer_info *connect_buffer(struct ustconsumer_instance *instance, pid_t pid,
+ const char *trace, const char *channel,
+ int channel_cpu)
+{
+ struct buffer_info *buf;
+ int result;
+ struct shmid_ds shmds;
+
+ buf = (struct buffer_info *) zmalloc(sizeof(struct buffer_info));
+ if(buf == NULL) {
+ ERR("add_buffer: insufficient memory");
+ return NULL;
+ }
+
+ buf->trace = strdup(trace);
+ if (!buf->trace) {
+ goto free_buf;
+ }
+
+ buf->channel = strdup(channel);
+ if (!buf->channel) {
+ goto free_buf_trace;
+ }
+
+ result = asprintf(&buf->name, "%s_%d", channel, channel_cpu);
+ if (result < 0 || buf->name == NULL) {
+ goto free_buf_channel;
+ }
+
+ buf->channel_cpu = channel_cpu;
+ buf->pid = pid;
+
+ result = ustcomm_connect_app(buf->pid, &buf->app_sock);
+ if(result) {
+ WARN("unable to connect to process, it probably died before we were able to connect");
+ goto free_buf_name;
+ }
+
+ /* get pidunique */
+ result = get_pidunique(buf->app_sock, &buf->pidunique);
+ if (result < 0) {
+ ERR("Failed to get pidunique");
+ goto close_app_sock;
+ }
+
+ /* get shmid and pipe fd */
+ result = get_buf_shmid_pipe_fd(buf->app_sock, buf, &buf->shmid,
+ &buf->bufstruct_shmid, &buf->pipe_fd);
+ if (result < 0) {
+ ERR("Failed to get buf_shmid and pipe_fd");
+ goto close_app_sock;
+ } else {
+ struct stat temp;
+ fstat(buf->pipe_fd, &temp);
+ if (!S_ISFIFO(temp.st_mode)) {
+ ERR("Didn't receive a fifo from the app");
+ goto close_app_sock;
+ }
+ }
+
+
+ /* get number of subbufs and subbuf size */
+ result = get_subbuf_num_size(buf->app_sock, buf, &buf->n_subbufs,
+ &buf->subbuf_size);
+ if (result < 0) {
+ ERR("Failed to get subbuf number and size");
+ goto close_fifo;
+ }
+
+ /* Set subbuffer's information */
+ buf->subbuf_size_order = get_count_order(buf->subbuf_size);
+ buf->alloc_size = buf->subbuf_size * buf->n_subbufs;
+
+ /* attach memory */
+ buf->mem = shmat(buf->shmid, NULL, 0);
+ if(buf->mem == (void *) 0) {
+ PERROR("shmat");
+ goto close_fifo;
+ }
+ DBG("successfully attached buffer memory");
+
+ buf->bufstruct_mem = shmat(buf->bufstruct_shmid, NULL, 0);
+ if(buf->bufstruct_mem == (void *) 0) {
+ PERROR("shmat");
+ goto shmdt_mem;
+ }
+ DBG("successfully attached buffer bufstruct memory");
+
+ /* obtain info on the memory segment */
+ result = shmctl(buf->shmid, IPC_STAT, &shmds);
+ if(result == -1) {
+ PERROR("shmctl");
+ goto shmdt_bufstruct_mem;
+ }
+ buf->memlen = shmds.shm_segsz;
+
+ /* Notify the application that we have mapped the buffer */
+ result = notify_buffer_mapped(buf->app_sock, buf);
+ if (result < 0) {
+ goto shmdt_bufstruct_mem;
+ }
+
+ if(instance->callbacks->on_open_buffer)
+ instance->callbacks->on_open_buffer(instance->callbacks, buf);
+
+ pthread_mutex_lock(&instance->mutex);
+ instance->active_buffers++;
+ pthread_mutex_unlock(&instance->mutex);
+
+ return buf;
+
+shmdt_bufstruct_mem:
+ shmdt(buf->bufstruct_mem);
+
+shmdt_mem:
+ shmdt(buf->mem);
+
+close_fifo:
+ close(buf->pipe_fd);
+
+close_app_sock:
+ close(buf->app_sock);
+
+free_buf_name:
+ free(buf->name);
+
+free_buf_channel:
+ free(buf->channel);
+
+free_buf_trace:
+ free(buf->trace);
+
+free_buf:
+ free(buf);
+ return NULL;
+}
+
+static void destroy_buffer(struct ustconsumer_callbacks *callbacks,
+ struct buffer_info *buf)
+{
+ int result;
+
+ result = close(buf->pipe_fd);
+ if(result == -1) {
+ WARN("problem closing the pipe fd");
+ }
+
+ result = close(buf->app_sock);
+ if(result == -1) {
+ WARN("problem calling ustcomm_close_app");
+ }
+
+ result = shmdt(buf->mem);
+ if(result == -1) {
+ PERROR("shmdt");
+ }
+
+ result = shmdt(buf->bufstruct_mem);
+ if(result == -1) {
+ PERROR("shmdt");
+ }
+
+ if(callbacks->on_close_buffer)
+ callbacks->on_close_buffer(callbacks, buf);
+
+ free(buf);
+}
+
+int consumer_loop(struct ustconsumer_instance *instance, struct buffer_info *buf)
+{
+ int result = 0;
+ int read_result;
+ char read_buf;
+
+ pthread_cleanup_push(decrement_active_buffers, instance);
+
+ for(;;) {
+ read_result = read(buf->pipe_fd, &read_buf, 1);
+ /* get the subbuffer */
+ if (read_result == 1) {
+ result = get_subbuffer(buf);
+ if (result < 0) {
+ ERR("error getting subbuffer");
+ continue;
+ } else if (result == GET_SUBBUF_DIED) {
+ finish_consuming_dead_subbuffer(instance->callbacks, buf);
+ break;
+ }
+ } else if ((read_result == -1 && (errno == ECONNRESET || errno == EPIPE)) ||
+ result == 0) {
+ DBG("App died while being traced");
+ finish_consuming_dead_subbuffer(instance->callbacks, buf);
+ break;
+ } else if (read_result == -1 && errno == EINTR) {
+ continue;
+ }
+
+ if(instance->callbacks->on_read_subbuffer)
+ instance->callbacks->on_read_subbuffer(instance->callbacks, buf);
+
+ /* put the subbuffer */
+ result = put_subbuffer(buf);
+ if(result == -1) {
+ ERR("unknown error putting subbuffer (channel=%s)", buf->name);
+ break;
+ }
+ else if(result == PUT_SUBBUF_PUSHED) {
+ ERR("Buffer overflow (channel=%s), reader pushed. This channel will not be usable passed this point.", buf->name);
+ break;
+ }
+ else if(result == PUT_SUBBUF_DIED) {
+ DBG("application died while putting subbuffer");
+ /* Skip the first subbuffer. We are not sure it is trustable
+ * because the put_subbuffer() did not complete.
+ */
+ /* TODO: check on_put_error return value */
+ if(instance->callbacks->on_put_error)
+ instance->callbacks->on_put_error(instance->callbacks, buf);
+
+ finish_consuming_dead_subbuffer(instance->callbacks, buf);
+ break;
+ }
+ else if(result == PUT_SUBBUF_DONE) {
+ /* Done with this subbuffer */
+ /* FIXME: add a case where this branch is used? Upon
+ * normal trace termination, at put_subbuf time, a
+ * special last-subbuffer code could be returned by
+ * the listener.
+ */
+ break;
+ }
+ else if(result == PUT_SUBBUF_OK) {
+ }
+ }
+
+ DBG("thread for buffer %s is stopping", buf->name);
+
+ /* FIXME: destroy, unalloc... */
+
+ pthread_cleanup_pop(1);
+
+ return 0;
+}
+
+struct consumer_thread_args {
+ pid_t pid;
+ const char *trace;
+ const char *channel;
+ int channel_cpu;
+ struct ustconsumer_instance *instance;
+};
+
+void *consumer_thread(void *arg)
+{
+ struct buffer_info *buf;
+ struct consumer_thread_args *args = (struct consumer_thread_args *) arg;
+ int result;
+ sigset_t sigset;
+
+ pthread_mutex_lock(&args->instance->mutex);
+ args->instance->active_threads++;
+ pthread_mutex_unlock(&args->instance->mutex);
+
+ if(args->instance->callbacks->on_new_thread)
+ args->instance->callbacks->on_new_thread(args->instance->callbacks);
+
+ /* Block signals that should be handled by the main thread. */
+ result = sigemptyset(&sigset);
+ if(result == -1) {
+ PERROR("sigemptyset");
+ goto end;
+ }
+ result = sigaddset(&sigset, SIGTERM);
+ if(result == -1) {
+ PERROR("sigaddset");
+ goto end;
+ }
+ result = sigaddset(&sigset, SIGINT);
+ if(result == -1) {
+ PERROR("sigaddset");
+ goto end;
+ }
+ result = sigprocmask(SIG_BLOCK, &sigset, NULL);
+ if(result == -1) {
+ PERROR("sigprocmask");
+ goto end;
+ }
+
+ buf = connect_buffer(args->instance, args->pid, args->trace,
+ args->channel, args->channel_cpu);
+ if(buf == NULL) {
+ ERR("failed to connect to buffer");
+ goto end;
+ }
+
+ consumer_loop(args->instance, buf);
+
+ destroy_buffer(args->instance->callbacks, buf);
+
+ end:
+
+ if(args->instance->callbacks->on_close_thread)
+ args->instance->callbacks->on_close_thread(args->instance->callbacks);
+
+ pthread_mutex_lock(&args->instance->mutex);
+ args->instance->active_threads--;
+ pthread_mutex_unlock(&args->instance->mutex);
+
+ free((void *)args->channel);
+ free(args);
+ return NULL;
+}
+
+int start_consuming_buffer(struct ustconsumer_instance *instance, pid_t pid,
+ const char *trace, const char *channel,
+ int channel_cpu)
+{
+ pthread_t thr;
+ struct consumer_thread_args *args;
+ int result;
+
+ DBG("beginning of start_consuming_buffer: args: pid %d bufname %s_%d", pid, channel,
+ channel_cpu);
+
+ args = (struct consumer_thread_args *) zmalloc(sizeof(struct consumer_thread_args));
+ if (!args) {
+ return -ENOMEM;
+ }
+
+ args->pid = pid;
+ args->trace = strdup(trace);
+ args->channel = strdup(channel);
+ args->channel_cpu = channel_cpu;
+ args->instance = instance;
+ DBG("beginning2 of start_consuming_buffer: args: pid %d trace %s"
+ " bufname %s_%d", args->pid, args->trace, args->channel, args->channel_cpu);
+
+ result = pthread_create(&thr, NULL, consumer_thread, args);
+ if(result == -1) {
+ ERR("pthread_create failed");
+ return -1;
+ }
+ result = pthread_detach(thr);
+ if(result == -1) {
+ ERR("pthread_detach failed");
+ return -1;
+ }
+ DBG("end of start_consuming_buffer: args: pid %d trace %s "
+ "bufname %s_%d", args->pid, args->channel, args->trace, args->channel_cpu);
+
+ return 0;
+}
+static void process_client_cmd(int sock, struct ustcomm_header *req_header,
+ char *recvbuf, struct ustconsumer_instance *instance)
+{
+ int result;
+ struct ustcomm_header _res_header = {0};
+ struct ustcomm_header *res_header = &_res_header;
+ struct ustcomm_buffer_info *buf_inf;
+
+ DBG("Processing client command");
+
+ switch (req_header->command) {
+ case CONSUME_BUFFER:
+
+ buf_inf = (struct ustcomm_buffer_info *)recvbuf;
+ result = ustcomm_unpack_buffer_info(buf_inf);
+ if (result < 0) {
+ ERR("Couldn't unpack buffer info");
+ return;
+ }
+
+ DBG("Going to consume trace %s buffer %s_%d in process %d",
+ buf_inf->trace, buf_inf->channel, buf_inf->ch_cpu,
+ buf_inf->pid);
+ result = start_consuming_buffer(instance, buf_inf->pid,
+ buf_inf->trace,
+ buf_inf->channel,
+ buf_inf->ch_cpu);
+ if (result < 0) {
+ ERR("error in add_buffer");
+ return;
+ }
+
+ res_header->result = 0;
+ break;
+ case EXIT:
+ res_header->result = 0;
+ /* Only there to force poll to return */
+ break;
+ default:
+ res_header->result = -EINVAL;
+ WARN("unknown command: %d", req_header->command);
+ }
+
+ if (ustcomm_send(sock, res_header, NULL) <= 0) {
+ ERR("couldn't send command response");
+ }
+}
+
+#define MAX_EVENTS 10
+
+int ustconsumer_start_instance(struct ustconsumer_instance *instance)
+{
+ struct ustcomm_header recv_hdr;
+ char recv_buf[USTCOMM_BUFFER_SIZE];
+ struct ustcomm_sock *epoll_sock;
+ struct epoll_event events[MAX_EVENTS];
+ struct sockaddr addr;
+ int result, epoll_fd, accept_fd, nfds, i, addr_size, timeout;
+
+ if(!instance->is_init) {
+ ERR("libustconsumer instance not initialized");
+ return 1;
+ }
+ epoll_fd = instance->epoll_fd;
+
+ timeout = -1;
+
+ /* app loop */
+ for(;;) {
+ nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, timeout);
+ if (nfds == -1 && errno == EINTR) {
+ /* Caught signal */
+ } else if (nfds == -1) {
+ PERROR("ustconsumer_start_instance: epoll_wait failed");
+ continue;
+ }
+
+ for (i = 0; i < nfds; ++i) {
+ epoll_sock = (struct ustcomm_sock *)events[i].data.ptr;
+ if (epoll_sock == instance->listen_sock) {
+ addr_size = sizeof(struct sockaddr);
+ accept_fd = accept(epoll_sock->fd,
+ &addr,
+ (socklen_t *)&addr_size);
+ if (accept_fd == -1) {
+ PERROR("ustconsumer_start_instance: "
+ "accept failed");
+ continue;
+ }
+ ustcomm_init_sock(accept_fd, epoll_fd,
+ &instance->connections);
+ } else {
+ result = ustcomm_recv(epoll_sock->fd, &recv_hdr,
+ recv_buf);
+ if (result < 1) {
+ ustcomm_del_sock(epoll_sock, 0);
+ } else {
+ process_client_cmd(epoll_sock->fd,
+ &recv_hdr, recv_buf,
+ instance);
+ }
+
+ }
+ }
+
+ if (instance->quit_program) {
+ pthread_mutex_lock(&instance->mutex);
+ if (instance->active_buffers == 0 && instance->active_threads == 0) {
+ pthread_mutex_unlock(&instance->mutex);
+ break;
+ }
+ pthread_mutex_unlock(&instance->mutex);
+ timeout = 100;
+ }
+ }
+
+ if(instance->callbacks->on_trace_end)
+ instance->callbacks->on_trace_end(instance);
+
+ ustconsumer_delete_instance(instance);
+
+ return 0;
+}
+
+/* FIXME: threads and connections !? */
+void ustconsumer_delete_instance(struct ustconsumer_instance *instance)
+{
+ if (instance->is_init) {
+ ustcomm_del_named_sock(instance->listen_sock, 0);
+ close(instance->epoll_fd);
+ }
+
+ pthread_mutex_destroy(&instance->mutex);
+ free(instance->sock_path);
+ free(instance);
+}
+
+/* FIXME: Do something about the fixed path length, maybe get rid
+ * of the whole concept and use a pipe?
+ */
+int ustconsumer_stop_instance(struct ustconsumer_instance *instance, int send_msg)
+{
+ int result;
+ int fd;
+ int bytes = 0;
+
+ char msg[] = "exit";
+
+ instance->quit_program = 1;
+
+ if(!send_msg)
+ return 0;
+
+ /* Send a message through the socket to force poll to return */
+
+ struct sockaddr_un addr;
+
+socket_again:
+ result = fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if(result == -1) {
+ if (errno == EINTR)
+ goto socket_again;
+ PERROR("socket");
+ return 1;
+ }
+
+ addr.sun_family = AF_UNIX;
+
+ strncpy(addr.sun_path, instance->sock_path, UNIX_PATH_MAX);
+ addr.sun_path[UNIX_PATH_MAX-1] = '\0';
+
+connect_again:
+ result = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if(result == -1) {
+ if (errno == EINTR)
+ goto connect_again;
+ PERROR("connect");
+ }
+
+ while(bytes != sizeof(msg)) {
+ int inc = send(fd, msg, sizeof(msg), 0);
+ if (inc < 0 && errno != EINTR)
+ break;
+ else
+ bytes += inc;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+struct ustconsumer_instance
+*ustconsumer_new_instance(struct ustconsumer_callbacks *callbacks,
+ char *sock_path)
+{
+ struct ustconsumer_instance *instance =
+ zmalloc(sizeof(struct ustconsumer_instance));
+ if(!instance) {
+ return NULL;
+ }
+
+ instance->callbacks = callbacks;
+ instance->quit_program = 0;
+ instance->is_init = 0;
+ instance->active_buffers = 0;
+ pthread_mutex_init(&instance->mutex, NULL);
+
+ if (sock_path) {
+ instance->sock_path = strdup(sock_path);
+ } else {
+ instance->sock_path = NULL;
+ }
+
+ return instance;
+}
+
+static int init_ustconsumer_socket(struct ustconsumer_instance *instance)
+{
+ char *name;
+
+ if (instance->sock_path) {
+ if (asprintf(&name, "%s", instance->sock_path) < 0) {
+ ERR("ustcomm_init_ustconsumer : asprintf failed (sock_path %s)",
+ instance->sock_path);
+ return -1;
+ }
+ } else {
+ int result;
+
+ /* Only check if socket dir exists if we are using the default directory */
+ result = ensure_dir_exists(SOCK_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (result == -1) {
+ ERR("Unable to create socket directory %s", SOCK_DIR);
+ return -1;
+ }
+
+ if (asprintf(&name, "%s/%s", SOCK_DIR, "ustconsumer") < 0) {
+ ERR("ustcomm_init_ustconsumer : asprintf failed (%s/ustconsumer)",
+ SOCK_DIR);
+ return -1;
+ }
+ }
+
+ /* Set up epoll */
+ instance->epoll_fd = epoll_create(MAX_EVENTS);
+ if (instance->epoll_fd == -1) {
+ ERR("epoll_create failed, start instance bailing");
+ goto free_name;
+ }
+
+ /* Create the named socket */
+ instance->listen_sock = ustcomm_init_named_socket(name,
+ instance->epoll_fd);
+ if(!instance->listen_sock) {
+ ERR("error initializing named socket at %s", name);
+ goto close_epoll;
+ }
+
+ CDS_INIT_LIST_HEAD(&instance->connections);
+
+ free(name);
+
+ return 0;
+
+close_epoll:
+ close(instance->epoll_fd);
+free_name:
+ free(name);
+
+ return -1;
+}
+
+int ustconsumer_init_instance(struct ustconsumer_instance *instance)
+{
+ int result;
+ result = init_ustconsumer_socket(instance);
+ if(result == -1) {
+ ERR("failed to initialize socket");
+ return 1;
+ }
+ instance->is_init = 1;
+ return 0;
+}
+
--- /dev/null
+/* Copyright (C) 2009 Pierre-Marc Fournier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <byteswap.h>
+
+#include "ust/ustconsumer.h"
+#include "buffers.h"
+#include "tracer.h"
+#include "usterr_signal_safe.h"
+
+/* This truncates to an offset in the buffer. */
+#define USTD_BUFFER_TRUNC(offset, bufinfo) \
+ ((offset) & (~(((bufinfo)->subbuf_size*(bufinfo)->n_subbufs)-1)))
+
+#define LTT_MAGIC_NUMBER 0x00D6B7ED
+#define LTT_REV_MAGIC_NUMBER 0xEDB7D600
+
+
+static void ltt_relay_print_subbuffer_errors(
+ struct buffer_info *buf,
+ long cons_off, int cpu)
+{
+ struct ust_buffer *ust_buf = buf->bufstruct_mem;
+ long cons_idx, commit_count, commit_count_mask, write_offset;
+
+ cons_idx = SUBBUF_INDEX(cons_off, buf);
+ commit_count = uatomic_read(&ust_buf->commit_seq[cons_idx]);
+ commit_count_mask = (~0UL >> get_count_order(buf->n_subbufs));
+
+ /*
+ * No need to order commit_count and write_offset reads because we
+ * execute after trace is stopped when there are no readers left.
+ */
+ write_offset = uatomic_read(&ust_buf->offset);
+ WARN( "LTT : unread channel %s offset is %ld "
+ "and cons_off : %ld (cpu %d)\n",
+ buf->channel, write_offset, cons_off, cpu);
+ /* Check each sub-buffer for non filled commit count */
+ if (((commit_count - buf->subbuf_size) & commit_count_mask)
+ - (BUFFER_TRUNC(cons_off, buf) >> get_count_order(buf->n_subbufs)) != 0) {
+ ERR("LTT : %s : subbuffer %lu has non filled "
+ "commit count [seq] [%lu].\n",
+ buf->channel, cons_idx, commit_count);
+ }
+ ERR("LTT : %s : commit count : %lu, subbuf size %d\n",
+ buf->channel, commit_count,
+ buf->subbuf_size);
+}
+
+static void ltt_relay_print_errors(struct buffer_info *buf, int cpu)
+{
+ struct ust_buffer *ust_buf = buf->bufstruct_mem;
+ long cons_off;
+
+ for (cons_off = uatomic_read(&ust_buf->consumed);
+ (SUBBUF_TRUNC(uatomic_read(&ust_buf->offset), buf)
+ - cons_off) > 0;
+ cons_off = SUBBUF_ALIGN(cons_off, buf))
+ ltt_relay_print_subbuffer_errors(buf, cons_off, cpu);
+}
+
+static void ltt_relay_print_buffer_errors(struct buffer_info *buf, int cpu)
+{
+ struct ust_buffer *ust_buf = buf->bufstruct_mem;
+
+ if (uatomic_read(&ust_buf->events_lost))
+ ERR("channel %s: %ld events lost (cpu %d)",
+ buf->channel,
+ uatomic_read(&ust_buf->events_lost), cpu);
+ if (uatomic_read(&ust_buf->corrupted_subbuffers))
+ ERR("channel %s : %ld corrupted subbuffers (cpu %d)",
+ buf->channel,
+ uatomic_read(&ust_buf->corrupted_subbuffers), cpu);
+
+ ltt_relay_print_errors(buf, cpu);
+}
+
+/* Returns the size of a subbuffer size. This is the size that
+ * will need to be written to disk.
+ *
+ * @subbuffer: pointer to the beginning of the subbuffer (the
+ * beginning of its header)
+ */
+
+size_t subbuffer_data_size(void *subbuf)
+{
+ struct ltt_subbuffer_header *header = subbuf;
+ int reverse;
+ u32 data_size;
+
+ if(header->magic_number == LTT_MAGIC_NUMBER) {
+ reverse = 0;
+ }
+ else if(header->magic_number == LTT_REV_MAGIC_NUMBER) {
+ reverse = 1;
+ }
+ else {
+ return -1;
+ }
+
+ data_size = header->sb_size;
+ if(reverse)
+ data_size = bswap_32(data_size);
+
+ return data_size;
+}
+
+
+void finish_consuming_dead_subbuffer(struct ustconsumer_callbacks *callbacks, struct buffer_info *buf)
+{
+ struct ust_buffer *ust_buf = buf->bufstruct_mem;
+ unsigned long n_subbufs_order = get_count_order(buf->n_subbufs);
+ unsigned long commit_seq_mask = (~0UL >> n_subbufs_order);
+ unsigned long cons_off;
+ int ret;
+
+ DBG("processing dead buffer (%s)", buf->name);
+ DBG("consumed offset is %ld (%s)", uatomic_read(&ust_buf->consumed),
+ buf->name);
+ DBG("write offset is %ld (%s)", uatomic_read(&ust_buf->offset),
+ buf->name);
+
+ /*
+ * Iterate on subbuffers to recover, including the one the writer
+ * just wrote data into. Using write position - 1 since the writer
+ * position points into the position that is going to be written.
+ */
+ for (cons_off = uatomic_read(&ust_buf->consumed);
+ (long) (SUBBUF_TRUNC(uatomic_read(&ust_buf->offset) - 1, buf)
+ - cons_off) >= 0;
+ cons_off = SUBBUF_ALIGN(cons_off, buf)) {
+ /*
+ * commit_seq is the offset in the buffer of the end of the last sequential commit.
+ * Bytes beyond this limit cannot be recovered. This is a free-running counter.
+ */
+ unsigned long commit_seq =
+ uatomic_read(&ust_buf->commit_seq[SUBBUF_INDEX(cons_off, buf)]);
+ struct ltt_subbuffer_header *header =
+ (struct ltt_subbuffer_header *)((char *) buf->mem
+ + SUBBUF_INDEX(cons_off, buf) * buf->subbuf_size);
+ unsigned long valid_length;
+
+ /* Check if subbuf was fully written. This is from Mathieu's algorithm/paper. */
+ if (((commit_seq - buf->subbuf_size) & commit_seq_mask)
+ - (USTD_BUFFER_TRUNC(uatomic_read(&ust_buf->consumed), buf) >> n_subbufs_order) == 0
+ && header->data_size != 0xffffffff) {
+ assert(header->sb_size != 0xffffffff);
+ /*
+ * If it was fully written, we only check the data_size.
+ * This is the amount of valid data at the beginning of
+ * the subbuffer.
+ */
+ valid_length = header->data_size;
+ DBG("writing full subbuffer (%ld) with valid_length = %ld",
+ SUBBUF_INDEX(cons_off, buf), valid_length);
+ } else {
+ /*
+ * If the subbuffer was not fully written, then we don't
+ * check data_size because it hasn't been written yet.
+ * Instead we check commit_seq and use it to choose a
+ * value for data_size. The viewer will need this value
+ * when parsing. Generally, this will happen only for
+ * the last subbuffer. However, if we have threads still
+ * holding reserved slots in the previous subbuffers,
+ * which could happen for other subbuffers prior to the
+ * last one. Note that when data_size is set, the
+ * commit_seq count is still at a value that shows the
+ * amount of valid data to read. It's only _after_
+ * writing data_size that commit_seq is updated to
+ * include the end-of-buffer padding.
+ */
+ valid_length = commit_seq & (buf->subbuf_size - 1);
+ DBG("writing unfull subbuffer (%ld) with valid_length = %ld",
+ SUBBUF_INDEX(cons_off, buf), valid_length);
+ header->data_size = valid_length;
+ header->sb_size = PAGE_ALIGN(valid_length);
+ }
+
+ if (callbacks->on_read_partial_subbuffer) {
+ ret = callbacks->on_read_partial_subbuffer(callbacks, buf,
+ SUBBUF_INDEX(cons_off, buf),
+ valid_length);
+ if (ret < 0)
+ break; /* Error happened */
+ }
+ }
+ /* Increment the consumed offset */
+ uatomic_set(&ust_buf->consumed, cons_off);
+ ltt_relay_print_buffer_errors(buf, buf->channel_cpu);
+}
+
--- /dev/null
+/*
+ * lowlevel libustd header file
+ *
+ * Copyright 2005-2010 -
+ * Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
+ * Copyright 2010-
+ * Oumarou Dicko <oumarou.dicko@polymtl.ca>
+ * Michael Sills-Lavoie <michael.sills-lavoie@polymtl.ca>
+ * Alexis Halle <alexis.halle@polymtl.ca>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LOWLEVEL_H
+#define LOWLEVEL_H
+
+#include "ust/ustconsumer.h"
+
+void finish_consuming_dead_subbuffer(struct ustconsumer_callbacks *callbacks, struct buffer_info *buf);
+size_t subbuffer_data_size(void *subbuf);
+
+#endif /* LOWLEVEL_H */
+
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libustcomm
+AM_CFLAGS = -fno-strict-aliasing
+
+lib_LTLIBRARIES = libustctl.la
+
+libustctl_la_SOURCES = \
+ libustctl.c
+
+libustctl_la_LDFLAGS = -no-undefined -version-info 0:0:0
+
+libustctl_la_LIBADD = \
+ $(top_builddir)/libustcomm/libustcomm.la
+
+libustctl_la_CFLAGS = -DUST_COMPONENT="libustctl" -fno-strict-aliasing
--- /dev/null
+libustctl is a library that provides an API and its implementation to send
+commands to traceable processes.
--- /dev/null
+/* Copyright (C) 2009 Pierre-Marc Fournier, Philippe Proulx-Barrette
+ * Copyright (C) 2011 Ericsson AB
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <dirent.h>
+
+#include "ustcomm.h"
+#include "ust/ustctl.h"
+#include "usterr.h"
+
+static int do_cmd(int sock,
+ const struct ustcomm_header *req_header,
+ const char *req_data,
+ struct ustcomm_header *res_header,
+ char **res_data)
+{
+ int result, saved_errno = 0;
+ char *recv_buf;
+
+ recv_buf = zmalloc(USTCOMM_BUFFER_SIZE);
+ if (!recv_buf) {
+ saved_errno = ENOMEM;
+ goto out;
+ }
+
+ result = ustcomm_req(sock, req_header, req_data, res_header, recv_buf);
+ if (result > 0) {
+ saved_errno = -res_header->result;
+ if (res_header->size == 0 || saved_errno > 0) {
+ free(recv_buf);
+ } else {
+ if (res_data) {
+ *res_data = recv_buf;
+ } else {
+ free(recv_buf);
+ }
+ }
+ } else {
+ ERR("ustcomm req failed");
+ if (result == 0) {
+ saved_errno = ENOTCONN;
+ } else {
+ saved_errno = -result;
+ }
+ free(recv_buf);
+ }
+
+out:
+ errno = saved_errno;
+ if (errno) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ustctl_connect_pid(pid_t pid)
+{
+ int sock;
+
+ if (ustcomm_connect_app(pid, &sock)) {
+ ERR("could not connect to PID %u", (unsigned int) pid);
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ return sock;
+}
+
+static int realloc_pid_list(pid_t **pid_list, unsigned int *pid_list_size)
+{
+ pid_t *new_pid_list;
+ unsigned int new_pid_list_size = 2 * *pid_list_size;
+
+ new_pid_list = realloc(*pid_list,
+ new_pid_list_size * sizeof(pid_t));
+ if (!*new_pid_list) {
+ return -1;
+ }
+
+ *pid_list = new_pid_list;
+ *pid_list_size = new_pid_list_size;
+
+ return 0;
+}
+
+static int get_pids_in_dir(DIR *dir, pid_t **pid_list,
+ unsigned int *pid_list_index,
+ unsigned int *pid_list_size)
+{
+ struct dirent *dirent;
+ pid_t read_pid;
+
+ while ((dirent = readdir(dir))) {
+ if (!strcmp(dirent->d_name, ".") ||
+ !strcmp(dirent->d_name, "..") ||
+ !strcmp(dirent->d_name, "ust-consumer") ||
+ dirent->d_type == DT_DIR) {
+
+ continue;
+ }
+
+ if (ustcomm_is_socket_live(dirent->d_name, &read_pid)) {
+
+ (*pid_list)[(*pid_list_index)++] = (long) read_pid;
+
+ if (*pid_list_index == *pid_list_size) {
+ if (realloc_pid_list(pid_list, pid_list_size)) {
+ return -1;
+ }
+ }
+ }
+ }
+
+ (*pid_list)[*pid_list_index] = 0; /* Array end */
+
+ return 0;
+}
+
+static pid_t *get_pids_non_root(void)
+{
+ char *dir_name;
+ DIR *dir;
+ unsigned int pid_list_index = 0, pid_list_size = 1;
+ pid_t *pid_list = NULL;
+
+ dir_name = ustcomm_user_sock_dir();
+ if (!dir_name) {
+ return NULL;
+ }
+
+ dir = opendir(dir_name);
+ if (!dir) {
+ goto free_dir_name;
+ }
+
+ pid_list = malloc(pid_list_size * sizeof(pid_t));
+ if (!pid_list) {
+ goto close_dir;
+ }
+
+ if (get_pids_in_dir(dir, &pid_list, &pid_list_index, &pid_list_size)) {
+ /* if any errors are encountered, force freeing of the list */
+ pid_list[0] = 0;
+ }
+
+close_dir:
+ closedir(dir);
+
+free_dir_name:
+ free(dir_name);
+
+ return pid_list;
+}
+
+static pid_t *get_pids_root(void)
+{
+ char *dir_name;
+ DIR *tmp_dir, *dir;
+ unsigned int pid_list_index = 0, pid_list_size = 1;
+ pid_t *pid_list = NULL;
+ struct dirent *dirent;
+ int result;
+
+ tmp_dir = opendir(USER_TMP_DIR);
+ if (!tmp_dir) {
+ return NULL;
+ }
+
+ pid_list = malloc(pid_list_size * sizeof(pid_t));
+ if (!pid_list) {
+ goto close_tmp_dir;
+ }
+
+ while ((dirent = readdir(tmp_dir))) {
+ /* Compare the dir to check for the USER_SOCK_DIR_BASE prefix */
+ if (!strncmp(dirent->d_name, USER_SOCK_DIR_BASE,
+ strlen(USER_SOCK_DIR_BASE))) {
+
+ if (asprintf(&dir_name, USER_TMP_DIR "/%s",
+ dirent->d_name) < 0) {
+ goto close_tmp_dir;
+ }
+
+ dir = opendir(dir_name);
+
+ free(dir_name);
+
+ if (!dir) {
+ continue;
+ }
+
+ result = get_pids_in_dir(dir, &pid_list, &pid_list_index,
+ &pid_list_size);
+
+ closedir(dir);
+
+ if (result) {
+ /*
+ * if any errors are encountered,
+ * force freeing of the list
+ */
+ pid_list[0] = 0;
+ break;
+ }
+ }
+ }
+
+close_tmp_dir:
+ closedir(tmp_dir);
+
+ return pid_list;
+}
+
+pid_t *ustctl_get_online_pids(void)
+{
+ pid_t *pid_list;
+
+ if (geteuid()) {
+ pid_list = get_pids_non_root();
+ } else {
+ pid_list = get_pids_root();
+ }
+
+ if (pid_list && pid_list[0] == 0) {
+ /* No PID at all */
+ free(pid_list);
+ pid_list = NULL;
+ }
+
+ return pid_list;
+}
+
+/**
+ * Sets ust_marker state (USTCTL_MS_ON or USTCTL_MS_OFF).
+ *
+ * @param mn Marker name
+ * @param state Marker's new state
+ * @param pid Traced process ID
+ * @return 0 if successful, or errors {USTCTL_ERR_GEN, USTCTL_ERR_ARG}
+ */
+int ustctl_set_ust_marker_state(int sock, const char *trace, const char *channel,
+ const char *ust_marker, int state)
+{
+ struct ustcomm_header req_header, res_header;
+ struct ustcomm_ust_marker_info ust_marker_inf;
+ int result;
+
+ result = ustcomm_pack_ust_marker_info(&req_header,
+ &ust_marker_inf,
+ trace,
+ channel,
+ ust_marker);
+ if (result < 0) {
+ errno = -result;
+ return -1;
+ }
+
+ req_header.command = state ? ENABLE_MARKER : DISABLE_MARKER;
+
+ return do_cmd(sock, &req_header, (char *)&ust_marker_inf,
+ &res_header, NULL);
+}
+
+/**
+ * Set subbuffer size.
+ *
+ * @param channel_size Channel name and size
+ * @param pid Traced process ID
+ * @return 0 if successful, or error
+ */
+int ustctl_set_subbuf_size(int sock, const char *trace, const char *channel,
+ unsigned int subbuf_size)
+{
+ struct ustcomm_header req_header, res_header;
+ struct ustcomm_channel_info ch_inf;
+ int result;
+
+ result = ustcomm_pack_channel_info(&req_header,
+ &ch_inf,
+ trace,
+ channel);
+ if (result < 0) {
+ errno = -result;
+ return -1;
+ }
+
+ req_header.command = SET_SUBBUF_SIZE;
+ ch_inf.subbuf_size = subbuf_size;
+
+ return do_cmd(sock, &req_header, (char *)&ch_inf,
+ &res_header, NULL);
+}
+
+/**
+ * Set subbuffer num.
+ *
+ * @param channel_num Channel name and num
+ * @param pid Traced process ID
+ * @return 0 if successful, or error
+ */
+int ustctl_set_subbuf_num(int sock, const char *trace, const char *channel,
+ unsigned int num)
+{
+ struct ustcomm_header req_header, res_header;
+ struct ustcomm_channel_info ch_inf;
+ int result;
+
+ result = ustcomm_pack_channel_info(&req_header,
+ &ch_inf,
+ trace,
+ channel);
+ if (result < 0) {
+ errno = -result;
+ return -1;
+ }
+
+ req_header.command = SET_SUBBUF_NUM;
+ ch_inf.subbuf_num = num;
+
+ return do_cmd(sock, &req_header, (char *)&ch_inf,
+ &res_header, NULL);
+
+}
+
+
+static int ustctl_get_subbuf_num_size(int sock, const char *trace, const char *channel,
+ int *num, int *size)
+{
+ struct ustcomm_header req_header, res_header;
+ struct ustcomm_channel_info ch_inf, *ch_inf_res;
+ int result;
+
+
+ result = ustcomm_pack_channel_info(&req_header,
+ &ch_inf,
+ trace,
+ channel);
+ if (result < 0) {
+ errno = -result;
+ return -1;
+ }
+
+ req_header.command = GET_SUBBUF_NUM_SIZE;
+
+ result = do_cmd(sock, &req_header, (char *)&ch_inf,
+ &res_header, (char **)&ch_inf_res);
+ if (result < 0) {
+ return -1;
+ }
+
+ *num = ch_inf_res->subbuf_num;
+ *size = ch_inf_res->subbuf_size;
+
+ free(ch_inf_res);
+
+ return 0;
+}
+
+/**
+ * Get subbuffer num.
+ *
+ * @param channel Channel name
+ * @param pid Traced process ID
+ * @return subbuf cnf if successful, or error
+ */
+int ustctl_get_subbuf_num(int sock, const char *trace, const char *channel)
+{
+ int num, size, result;
+
+ result = ustctl_get_subbuf_num_size(sock, trace, channel,
+ &num, &size);
+ if (result < 0) {
+ errno = -result;
+ return -1;
+ }
+
+ return num;
+}
+
+/**
+ * Get subbuffer size.
+ *
+ * @param channel Channel name
+ * @param pid Traced process ID
+ * @return subbuf size if successful, or error
+ */
+int ustctl_get_subbuf_size(int sock, const char *trace, const char *channel)
+{
+ int num, size, result;
+
+ result = ustctl_get_subbuf_num_size(sock, trace, channel,
+ &num, &size);
+ if (result < 0) {
+ errno = -result;
+ return -1;
+ }
+
+ return size;
+}
+
+static int do_trace_cmd(int sock, const char *trace, int command)
+{
+ struct ustcomm_header req_header, res_header;
+ struct ustcomm_single_field trace_inf;
+ int result;
+
+ result = ustcomm_pack_single_field(&req_header,
+ &trace_inf,
+ trace);
+ if (result < 0) {
+ errno = -result;
+ return -1;
+ }
+
+ req_header.command = command;
+
+ return do_cmd(sock, &req_header, (char *)&trace_inf, &res_header, NULL);
+}
+
+/**
+ * Destroys an UST trace according to a PID.
+ *
+ * @param pid Traced process ID
+ * @return 0 if successful, or error USTCTL_ERR_GEN
+ */
+int ustctl_destroy_trace(int sock, const char *trace)
+{
+ return do_trace_cmd(sock, trace, DESTROY_TRACE);
+}
+
+/**
+ * Starts an UST trace (and setups it) according to a PID.
+ *
+ * @param pid Traced process ID
+ * @return 0 if successful, or error USTCTL_ERR_GEN
+ */
+int ustctl_setup_and_start(int sock, const char *trace)
+{
+ return do_trace_cmd(sock, trace, START);
+}
+
+/**
+ * Creates an UST trace according to a PID.
+ *
+ * @param pid Traced process ID
+ * @return 0 if successful, or error USTCTL_ERR_GEN
+ */
+int ustctl_create_trace(int sock, const char *trace)
+{
+ return do_trace_cmd(sock, trace, CREATE_TRACE);
+}
+
+/**
+ * Starts an UST trace according to a PID.
+ *
+ * @param pid Traced process ID
+ * @return 0 if successful, or error USTCTL_ERR_GEN
+ */
+int ustctl_start_trace(int sock, const char *trace)
+{
+ return do_trace_cmd(sock, trace, START_TRACE);
+}
+
+/**
+ * Alloc an UST trace according to a PID.
+ *
+ * @param pid Traced process ID
+ * @return 0 if successful, or error USTCTL_ERR_GEN
+ */
+int ustctl_alloc_trace(int sock, const char *trace)
+{
+ return do_trace_cmd(sock, trace, ALLOC_TRACE);
+}
+
+
+int ustctl_force_switch(int sock, const char *trace)
+{
+ return do_trace_cmd(sock, trace, FORCE_SUBBUF_SWITCH);
+}
+
+/**
+ * Stops an UST trace according to a PID.
+ *
+ * @param pid Traced process ID
+ * @return 0 if successful, or error USTCTL_ERR_GEN
+ */
+int ustctl_stop_trace(int sock, const char *trace)
+{
+ return do_trace_cmd(sock, trace, STOP_TRACE);
+}
+
+/**
+ * Counts newlines ('\n') in a string.
+ *
+ * @param str String to search in
+ * @return Total newlines count
+ */
+unsigned int ustctl_count_nl(const char *str)
+{
+ unsigned int i = 0, tot = 0;
+
+ while (str[i] != '\0') {
+ if (str[i] == '\n') {
+ ++tot;
+ }
+ ++i;
+ }
+
+ return tot;
+}
+
+/**
+ * Frees a CMSF array.
+ *
+ * @param cmsf CMSF array to free
+ * @return 0 if successful, or error USTCTL_ERR_ARG
+ */
+int ustctl_free_cmsf(struct ust_marker_status *cmsf)
+{
+ if (cmsf == NULL) {
+ return USTCTL_ERR_ARG;
+ }
+
+ unsigned int i = 0;
+ while (cmsf[i].channel != NULL) {
+ free(cmsf[i].channel);
+ free(cmsf[i].ust_marker);
+ free(cmsf[i].fs);
+ ++i;
+ }
+ free(cmsf);
+
+ return 0;
+}
+
+/**
+ * Gets channel/ust_marker/state/format string for a given PID.
+ *
+ * @param cmsf Pointer to CMSF array to be filled (callee allocates, caller
+ * frees with `ustctl_free_cmsf')
+ * @param pid Targeted PID
+ * @return 0 if successful, or -1 on error
+ */
+int ustctl_get_cmsf(int sock, struct ust_marker_status **cmsf)
+{
+ struct ustcomm_header req_header, res_header;
+ char *big_str = NULL;
+ int result;
+ struct ust_marker_status *tmp_cmsf = NULL;
+ unsigned int i = 0, cmsf_ind = 0;
+
+ if (cmsf == NULL) {
+ return -1;
+ }
+
+ req_header.command = LIST_MARKERS;
+ req_header.size = 0;
+
+ result = ustcomm_send(sock, &req_header, NULL);
+ if (result <= 0) {
+ PERROR("error while requesting ust_marker list");
+ return -1;
+ }
+
+ result = ustcomm_recv_alloc(sock, &res_header, &big_str);
+ if (result <= 0) {
+ ERR("error while receiving ust_marker list");
+ return -1;
+ }
+
+ tmp_cmsf = (struct ust_marker_status *) zmalloc(sizeof(struct ust_marker_status) *
+ (ustctl_count_nl(big_str) + 1));
+ if (tmp_cmsf == NULL) {
+ ERR("Failed to allocate CMSF array");
+ return -1;
+ }
+
+ /* Parse received reply string (format: "[chan]/[mark] [st] [fs]"): */
+ while (big_str[i] != '\0') {
+ char state;
+
+ sscanf(big_str + i, "ust_marker: %a[^/]/%a[^ ] %c %a[^\n]",
+ &tmp_cmsf[cmsf_ind].channel,
+ &tmp_cmsf[cmsf_ind].ust_marker,
+ &state,
+ &tmp_cmsf[cmsf_ind].fs);
+ tmp_cmsf[cmsf_ind].state = (state == USTCTL_MS_CHR_ON ?
+ USTCTL_MS_ON : USTCTL_MS_OFF); /* Marker state */
+
+ while (big_str[i] != '\n') {
+ ++i; /* Go to next '\n' */
+ }
+ ++i; /* Skip current pointed '\n' */
+ ++cmsf_ind;
+ }
+ tmp_cmsf[cmsf_ind].channel = NULL;
+ tmp_cmsf[cmsf_ind].ust_marker = NULL;
+ tmp_cmsf[cmsf_ind].fs = NULL;
+
+ *cmsf = tmp_cmsf;
+
+ free(big_str);
+ return 0;
+}
+
+/**
+ * Frees a TES array.
+ *
+ * @param tes TES array to free
+ * @return 0 if successful, or error USTCTL_ERR_ARG
+ */
+int ustctl_free_tes(struct trace_event_status *tes)
+{
+ if (tes == NULL) {
+ return USTCTL_ERR_ARG;
+ }
+
+ unsigned int i = 0;
+ while (tes[i].name != NULL) {
+ free(tes[i].name);
+ ++i;
+ }
+ free(tes);
+
+ return 0;
+}
+
+/**
+ * Gets trace_events string for a given PID.
+ *
+ * @param tes Pointer to TES array to be filled (callee allocates, caller
+ * frees with `ustctl_free_tes')
+ * @param pid Targeted PID
+ * @return 0 if successful, or -1 on error
+ */
+int ustctl_get_tes(int sock, struct trace_event_status **tes)
+{
+ struct ustcomm_header req_header, res_header;
+ char *big_str = NULL;
+ int result;
+ struct trace_event_status *tmp_tes = NULL;
+ unsigned int i = 0, tes_ind = 0;
+
+ if (tes == NULL) {
+ return -1;
+ }
+
+ req_header.command = LIST_TRACE_EVENTS;
+ req_header.size = 0;
+
+ result = ustcomm_send(sock, &req_header, NULL);
+ if (result != 1) {
+ ERR("error while requesting trace_event list");
+ return -1;
+ }
+
+ result = ustcomm_recv_alloc(sock, &res_header, &big_str);
+ if (result != 1) {
+ ERR("error while receiving ust_marker list");
+ return -1;
+ }
+
+ tmp_tes = (struct trace_event_status *)
+ zmalloc(sizeof(struct trace_event_status) *
+ (ustctl_count_nl(big_str) + 1));
+ if (tmp_tes == NULL) {
+ ERR("Failed to allocate TES array");
+ return -1;
+ }
+
+ /* Parse received reply string (format: "[name]"): */
+ while (big_str[i] != '\0') {
+ sscanf(big_str + i, "trace_event: %a[^\n]",
+ &tmp_tes[tes_ind].name);
+ while (big_str[i] != '\n') {
+ ++i; /* Go to next '\n' */
+ }
+ ++i; /* Skip current pointed '\n' */
+ ++tes_ind;
+ }
+ tmp_tes[tes_ind].name = NULL;
+
+ *tes = tmp_tes;
+
+ free(big_str);
+ return 0;
+}
+
+/**
+ * Set sock path
+ *
+ * @param sock_path Sock path
+ * @param pid Traced process ID
+ * @return 0 if successful, or error
+ */
+int ustctl_set_sock_path(int sock, const char *sock_path)
+{
+ int result;
+ struct ustcomm_header req_header, res_header;
+ struct ustcomm_single_field sock_path_msg;
+
+ result = ustcomm_pack_single_field(&req_header,
+ &sock_path_msg,
+ sock_path);
+ if (result < 0) {
+ errno = -result;
+ return -1;
+ }
+
+ req_header.command = SET_SOCK_PATH;
+
+ return do_cmd(sock, &req_header, (char *)&sock_path_msg,
+ &res_header, NULL);
+}
+
+/**
+ * Get sock path
+ *
+ * @param sock_path Pointer to where the sock path will be returned
+ * @param pid Traced process ID
+ * @return 0 if successful, or error
+ */
+int ustctl_get_sock_path(int sock, char **sock_path)
+{
+ int result;
+ struct ustcomm_header req_header, res_header;
+ struct ustcomm_single_field *sock_path_msg;
+
+ req_header.command = GET_SOCK_PATH;
+ req_header.size = 0;
+
+ result = do_cmd(sock, &req_header, NULL, &res_header,
+ (char **)&sock_path_msg);
+ if (result < 0) {
+ return -1;
+ }
+
+ result = ustcomm_unpack_single_field(sock_path_msg);
+ if (result < 0) {
+ return result;
+ }
+
+ *sock_path = strdup(sock_path_msg->field);
+
+ free(sock_path_msg);
+
+ return 0;
+}
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/libust -I$(top_srcdir)/libustcomm \
+ -I$(top_srcdir)/include -I$(top_srcdir)/libustconsumer
+AM_CFLAGS = -fno-strict-aliasing
+
+bin_PROGRAMS = ust-consumerd
+
+ust_consumerd_SOURCES = ust-consumerd.c
+
+ust_consumerd_LDADD = \
+ $(top_builddir)/snprintf/libustsnprintf.la \
+ $(top_builddir)/libustcomm/libustcomm.la \
+ $(top_builddir)/libustconsumer/libustconsumer.la
+
+ust_consumerd_CFLAGS = -DUST_COMPONENT=ust-consumerd -fno-strict-aliasing
--- /dev/null
+This is ust-consumerd, the UST consumer daemon.
+
+This daemon is used to collect the traces for the traced programs and write them to disk.
--- /dev/null
+/* Copyright (C) 2009 Pierre-Marc Fournier
+ * 2010 Alexis Halle
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/shm.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <getopt.h>
+
+#include "ust/ustconsumer.h"
+#include "../libustconsumer/lowlevel.h"
+#include "usterr.h"
+
+char *sock_path=NULL;
+char *trace_path=NULL;
+int daemon_mode = 0;
+char *pidfile = NULL;
+
+struct ustconsumer_instance *instance;
+
+struct buffer_info_local {
+ /* output file */
+ int file_fd;
+ /* the offset we must truncate to, to unput the last subbuffer */
+ off_t previous_offset;
+};
+
+static int write_pidfile(const char *file_name, pid_t pid)
+{
+ FILE *pidfp;
+
+ pidfp = fopen(file_name, "w");
+ if(!pidfp) {
+ PERROR("fopen (%s)", file_name);
+ WARN("killing child process");
+ return -1;
+ }
+
+ fprintf(pidfp, "%d\n", pid);
+
+ fclose(pidfp);
+
+ return 0;
+}
+
+int create_dir_if_needed(char *dir)
+{
+ int result;
+ result = mkdir(dir, 0777);
+ if(result == -1) {
+ if(errno != EEXIST) {
+ PERROR("mkdir");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int unwrite_last_subbuffer(struct buffer_info *buf)
+{
+ int result;
+ struct buffer_info_local *buf_local = buf->user_data;
+
+ result = ftruncate(buf_local->file_fd, buf_local->previous_offset);
+ if(result == -1) {
+ PERROR("ftruncate");
+ return -1;
+ }
+
+ result = lseek(buf_local->file_fd, buf_local->previous_offset, SEEK_SET);
+ if(result == (int)(off_t)-1) {
+ PERROR("lseek");
+ return -1;
+ }
+
+ return 0;
+}
+
+int write_current_subbuffer(struct buffer_info *buf)
+{
+ int result;
+ struct buffer_info_local *buf_local = buf->user_data;
+
+ void *subbuf_mem = buf->mem + (buf->consumed_old & (buf->n_subbufs * buf->subbuf_size-1));
+
+ size_t cur_sb_size = subbuffer_data_size(subbuf_mem);
+
+ off_t cur_offset = lseek(buf_local->file_fd, 0, SEEK_CUR);
+ if(cur_offset == (off_t)-1) {
+ PERROR("lseek");
+ return -1;
+ }
+
+ buf_local->previous_offset = cur_offset;
+ DBG("previous_offset: %ld", cur_offset);
+
+ result = patient_write(buf_local->file_fd, subbuf_mem, cur_sb_size);
+ if(result == -1) {
+ PERROR("write");
+ return -1;
+ }
+
+ return 0;
+}
+
+int on_read_subbuffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
+{
+ return write_current_subbuffer(buf);
+}
+
+int on_read_partial_subbuffer(struct ustconsumer_callbacks *data, struct buffer_info *buf,
+ long subbuf_index, unsigned long valid_length)
+{
+ struct buffer_info_local *buf_local = buf->user_data;
+ char *tmp;
+ int result;
+ unsigned long pad_size;
+
+ result = patient_write(buf_local->file_fd, buf->mem + subbuf_index * buf->subbuf_size, valid_length);
+ if(result == -1) {
+ ERR("Error writing to buffer file");
+ return result;
+ }
+
+ /* pad with empty bytes */
+ pad_size = PAGE_ALIGN(valid_length)-valid_length;
+ if(pad_size) {
+ tmp = zmalloc(pad_size);
+ result = patient_write(buf_local->file_fd, tmp, pad_size);
+ if(result == -1) {
+ ERR("Error writing to buffer file");
+ return result;
+ }
+ free(tmp);
+ }
+ return result;
+}
+
+int on_open_buffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
+{
+ char *tmp;
+ int result;
+ int fd;
+ struct buffer_info_local *buf_local =
+ zmalloc(sizeof(struct buffer_info_local));
+
+ if(!buf_local) {
+ ERR("could not allocate buffer_info_local struct");
+ return 1;
+ }
+
+ buf->user_data = buf_local;
+
+ /* open file for output */
+ if(!trace_path) {
+ /* Only create the directory if using the default path, because
+ * of the risk of typo when using trace path override. We don't
+ * want to risk creating plenty of useless directories in that case.
+ */
+ result = create_dir_if_needed(USTCONSUMER_DEFAULT_TRACE_PATH);
+ if(result == -1) {
+ ERR("could not create directory %s", USTCONSUMER_DEFAULT_TRACE_PATH);
+ return 1;
+ }
+
+ trace_path = USTCONSUMER_DEFAULT_TRACE_PATH;
+ }
+
+ if (asprintf(&tmp, "%s/%u_%" PRId64 "", trace_path, buf->pid, buf->pidunique) < 0) {
+ ERR("on_open_buffer : asprintf failed (%s/%u_%" PRId64 ")",
+ trace_path, buf->pid, buf->pidunique);
+ return 1;
+ }
+ result = create_dir_if_needed(tmp);
+ if(result == -1) {
+ ERR("could not create directory %s", tmp);
+ free(tmp);
+ return 1;
+ }
+ free(tmp);
+
+ if (asprintf(&tmp, "%s/%u_%" PRId64 "/%s", trace_path, buf->pid, buf->pidunique, buf->name) < 0) {
+ ERR("on_open_buffer : asprintf failed (%s/%u_%" PRId64 "/%s)",
+ trace_path, buf->pid, buf->pidunique, buf->name);
+ return 1;
+ }
+again:
+ result = fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 00600);
+ if (result == -1 && errno == EINTR)
+ goto again;
+
+ if(result == -1) {
+ PERROR("open");
+ ERR("failed opening trace file %s", tmp);
+ return 1;
+ }
+ buf_local->file_fd = fd;
+ free(tmp);
+
+ return 0;
+}
+
+int on_close_buffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
+{
+ struct buffer_info_local *buf_local = buf->user_data;
+ int result;
+
+again:
+ result = close(buf_local->file_fd);
+ if (result == -1 && errno == EINTR)
+ goto again;
+ free(buf_local);
+ if(result == -1) {
+ PERROR("close");
+ }
+ return 0;
+}
+
+int on_put_error(struct ustconsumer_callbacks *data, struct buffer_info *buf)
+{
+ return unwrite_last_subbuffer(buf);
+}
+
+struct ustconsumer_callbacks *new_callbacks()
+{
+ struct ustconsumer_callbacks *callbacks =
+ zmalloc(sizeof(struct ustconsumer_callbacks));
+
+ if(!callbacks)
+ return NULL;
+
+ callbacks->on_open_buffer = on_open_buffer;
+ callbacks->on_close_buffer = on_close_buffer;
+ callbacks->on_read_subbuffer = on_read_subbuffer;
+ callbacks->on_read_partial_subbuffer = on_read_partial_subbuffer;
+ callbacks->on_put_error = on_put_error;
+ callbacks->on_new_thread = NULL;
+ callbacks->on_close_thread = NULL;
+ callbacks->on_trace_end = NULL;
+
+ return callbacks;
+
+}
+
+int is_directory(const char *dir)
+{
+ int result;
+ struct stat st;
+
+ result = stat(dir, &st);
+ if(result == -1) {
+ PERROR("stat");
+ return 0;
+ }
+
+ if(!S_ISDIR(st.st_mode)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+void usage(void)
+{
+ fprintf(stderr, "Usage:\nust-consumerd OPTIONS\n\nOptions:\n"
+ "\t-h\t\tDisplay this usage.\n"
+ "\t-o DIR\t\tSpecify the directory where to output the traces.\n"
+ "\t-s PATH\t\tSpecify the path to use for the daemon socket.\n"
+ "\t-d\t\tStart as a daemon.\n"
+ "\t--pidfile FILE\tWrite the PID in this file (when using -d).\n");
+}
+
+int parse_args(int argc, char **argv)
+{
+ int c;
+
+ while (1) {
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"pidfile", 1, 0, 'p'},
+ {"help", 0, 0, 'h'},
+ {"version", 0, 0, 'V'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "hs:o:d", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 0:
+ printf("option %s", long_options[option_index].name);
+ if (optarg)
+ printf(" with arg %s", optarg);
+ printf("\n");
+ break;
+ case 's':
+ sock_path = optarg;
+ break;
+ case 'o':
+ trace_path = optarg;
+ if(!is_directory(trace_path)) {
+ ERR("Not a valid directory. (%s)", trace_path);
+ return -1;
+ }
+ break;
+ case 'd':
+ daemon_mode = 1;
+ break;
+ case 'p':
+ pidfile = strdup(optarg);
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ case 'V':
+ printf("Version 0.0\n");
+ break;
+
+ default:
+ /* unknown option or other error; error is
+ printed by getopt, just return */
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void sigterm_handler(int sig)
+{
+ ustconsumer_stop_instance(instance, 0);
+}
+
+int start_ustconsumer(int fd)
+{
+ int result;
+ sigset_t sigset;
+ struct sigaction sa;
+
+ struct ustconsumer_callbacks *callbacks = new_callbacks();
+ if(!callbacks) {
+ PERROR("new_callbacks");
+ return 1;
+ }
+
+ result = sigemptyset(&sigset);
+ if(result == -1) {
+ PERROR("sigemptyset");
+ return 1;
+ }
+ sa.sa_handler = sigterm_handler;
+ sa.sa_mask = sigset;
+ sa.sa_flags = 0;
+ result = sigaction(SIGTERM, &sa, NULL);
+ if(result == -1) {
+ PERROR("sigaction");
+ return 1;
+ }
+ result = sigaction(SIGINT, &sa, NULL);
+ if(result == -1) {
+ PERROR("sigaction");
+ return 1;
+ }
+
+ instance = ustconsumer_new_instance(callbacks, sock_path);
+ if(!instance) {
+ ERR("failed to create ustconsumer instance");
+ return 1;
+ }
+
+ result = ustconsumer_init_instance(instance);
+ if(result) {
+ ERR("failed to initialize ustconsumer instance");
+ return 1;
+ }
+
+ /* setup handler for SIGPIPE */
+ result = sigemptyset(&sigset);
+ if(result == -1) {
+ PERROR("sigemptyset");
+ return 1;
+ }
+ result = sigaddset(&sigset, SIGPIPE);
+ if(result == -1) {
+ PERROR("sigaddset");
+ return 1;
+ }
+ result = sigprocmask(SIG_BLOCK, &sigset, NULL);
+ if(result == -1) {
+ PERROR("sigprocmask");
+ return 1;
+ }
+
+ /* Write pidfile */
+ if(pidfile) {
+ result = write_pidfile(pidfile, getpid());
+ if(result == -1) {
+ ERR("failed to write pidfile");
+ return 1;
+ }
+ }
+
+ /* Notify parent that we are successfully started. */
+ if(fd != -1) {
+ /* write any one character */
+ result = write(fd, "!", 1);
+ if(result == -1) {
+ PERROR("write");
+ return -1;
+ }
+ if(result != 1) {
+ ERR("Problem sending confirmation of daemon start to parent");
+ return -1;
+ }
+ result = close(fd);
+ if(result == -1) {
+ PERROR("close");
+ }
+ }
+
+ ustconsumer_start_instance(instance);
+
+ free(callbacks);
+
+ return 0;
+}
+
+int start_ustconsumer_daemon()
+{
+ int result;
+ int fd[2];
+ pid_t child_pid;
+
+ result = pipe(fd);
+
+ result = child_pid = fork();
+ if(result == -1) {
+ PERROR("fork");
+ return -1;
+ }
+ else if(result == 0) {
+ return start_ustconsumer(fd[1]);
+ }
+ else {
+ char buf;
+
+ result = read(fd[0], &buf, 1);
+ if(result == -1) {
+ PERROR("read");
+ return -1;
+ }
+ if(result != 1) {
+ ERR("did not receive valid confirmation that the daemon is started");
+ return -1;
+ }
+
+ result = close(fd[0]);
+ if(result == -1) {
+ PERROR("close");
+ }
+
+ DBG("The daemon is now successfully started");
+ }
+
+ /* Wait for confirmation that the server is ready. */
+
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int result;
+
+ result = parse_args(argc, argv);
+ if(result == -1) {
+ exit(1);
+ }
+
+ if(daemon_mode) {
+ result = start_ustconsumer_daemon();
+ }
+ else {
+ result = start_ustconsumer(-1);
+ }
+
+ return result;
+}
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libustcomm \
+ -I$(top_srcdir)/libustctl $(KCOMPAT_CFLAGS)
+AM_CFLAGS = -fno-strict-aliasing
+
+bin_PROGRAMS = ustctl
+
+ustctl_SOURCES = \
+ ustctl.c marker_cmds.c trace_cmds.c channel_cmds.c cli.c cli.h scanning_functions.c scanning_functions.h
+
+ustctl_CFLAGS = -DUST_COMPONENT=ustctl -fno-strict-aliasing
+
+ustctl_LDADD = \
+ $(top_builddir)/snprintf/libustsnprintf.la \
+ $(top_builddir)/libustcomm/libustcomm.la \
+ $(top_builddir)/libustctl/libustctl.la
--- /dev/null
+ustctl is a helper application used to control tracing on programs that support tracing.
+
+It can list markers, enable/disable markers, start tracing and stop tracing.
--- /dev/null
+/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ust/ustctl.h>
+#include "scanning_functions.h"
+#include "usterr.h"
+#include "cli.h"
+
+static int set_subbuf_size(int argc, char *argv[])
+{
+ int sock, result = 0;
+ char *channel = NULL;
+ unsigned int size;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (scan_ch_and_num(argv[3], &channel, &size)) {
+ fprintf(stderr, "Failed to scan channel and size from"
+ " %s\n", argv[3]);
+ if (channel)
+ free(channel);
+ return -1;
+ }
+ if (ustctl_set_subbuf_size(sock, argv[2], channel, size)) {
+ ERR("error while trying to set the size of subbuffers "
+ "for PID %s\n",
+ argv[1]);
+ result = -1;
+ }
+
+ free(channel);
+
+ return result;
+}
+
+static int set_subbuf_num(int argc, char *argv[])
+{
+ int sock, result = 0;
+ char *channel = NULL;
+ unsigned int num;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (scan_ch_and_num(argv[3], &channel, &num)) {
+ fprintf(stderr, "Failed to scan channel and number from"
+ " %s\n", argv[3]);
+ if (channel)
+ free(channel);
+ return -1;
+ }
+ if (ustctl_set_subbuf_num(sock, argv[2], channel, num)) {
+ ERR("error while trying to set the number of subbuffers for PID %s\n",
+ argv[1]);
+ result = -1;
+ }
+
+ free(channel);
+
+ return result;
+}
+
+static int get_subbuf_size(int argc, char *argv[])
+{
+ int sock;
+ unsigned int size;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if ((size = ustctl_get_subbuf_size(sock, argv[2], argv[3])) < 0) {
+ ERR("error while trying to get the subbuffer size from PID %s\n",
+ argv[1]);
+ return -1;
+ }
+
+ printf("The subbufer size is %d bytes\n", size);
+
+ return 0;
+}
+
+static int get_subbuf_num(int argc, char *argv[])
+{
+ int sock;
+ unsigned int num;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if ((num = ustctl_get_subbuf_num(sock, argv[2], argv[3])) < 0) {
+ ERR("error while trying to get the subbuffer size from PID %s\n",
+ argv[1]);
+ return -1;
+ }
+
+ printf("There are %u subbufers in each buffer\n", num);
+
+ return 0;
+}
+
+struct cli_cmd __cli_cmds channel_cmds[] = {
+ {
+ .name = "set-subbuf-size",
+ .description = "Set the subbuffer size for a channel",
+ .help_text = "set-subbuf-size <pid> <trace> <channel>/<size> \n"
+ "Set the subbuffer size for a channel\n",
+ .function = set_subbuf_size,
+ .desired_args = 3,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "set-subbuf-num",
+ .description = "Set the number of subbuffers for a channel",
+ .help_text = "set-subbuf-num <pid> <trace> <channel>/<num> \n"
+ "Set the number of subbuffers for a channel\n",
+ .function = set_subbuf_num,
+ .desired_args = 3,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "get-subbuf-size",
+ .description = "Get the subbuffer size for a channel",
+ .help_text = "get-subbuf-size <pid> <trace> <channel>\n"
+ "Get the subbuffer size for a channel\n",
+ .function = get_subbuf_size,
+ .desired_args = 3,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "get-subbuf-num",
+ .description = "Get the number of subbuffers for a channel",
+ .help_text = "get-subbuf-num <pid> <trace> <channel>\n"
+ "Get the number of subbuffers for a channel\n",
+ .function = get_subbuf_num,
+ .desired_args = 3,
+ .desired_args_op = CLI_EQ,
+ },
+};
--- /dev/null
+/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cli.h"
+
+/* This dummy command is needed to create the sections in cli.o before
+ * other .o files have these sections, usefull for development.
+ */
+static int _dummy(int argc, char *argv[]) {
+ return 0;
+}
+
+/* Define a dummy cmd to guarantee existence of the builtin variables */
+struct cli_cmd __cli_cmds __dummy_cli_cmd[] = {
+ {
+ .name = "_dummy",
+ .description = NULL,
+ .help_text = NULL,
+ .function = _dummy,
+ .desired_args = 0,
+ .desired_args_op = 0,
+ },
+};
+
+extern struct cli_cmd __start___cli_cmds[] __attribute__((visibility("hidden")));
+extern struct cli_cmd __stop___cli_cmds[] __attribute__((visibility("hidden")));
+
+static struct cli_cmd **cli_cmd_list;
+static int cli_cmd_list_size;
+
+static char *process_name;
+
+static int compute_cli_cmds_size(void)
+{
+ long cli_cmds_start, cli_cmds_end;
+
+ cli_cmds_start = (long)__start___cli_cmds;
+ cli_cmds_end = (long)__stop___cli_cmds;
+
+ return (cli_cmds_end - cli_cmds_start) / sizeof(struct cli_cmd);
+}
+
+static void __attribute__((constructor)) generate_cli_cmd_list(int argc, char *argv[])
+{
+ struct cli_cmd *cli_cmd;
+ int section_size, i;
+
+ process_name = basename(argv[0]);
+
+ section_size = compute_cli_cmds_size();
+
+ cli_cmd_list = malloc(section_size * sizeof(void *));
+ if (!cli_cmd_list) {
+ fprintf(stderr, "Failed to allocate command list!");
+ exit(EXIT_FAILURE);
+ }
+
+ cli_cmd_list_size = 0;
+
+ cli_cmd = __start___cli_cmds;
+ for (i = 0; i < section_size; i++) {
+ if (&cli_cmd[i] == &__dummy_cli_cmd[0]) {
+ continue;
+ }
+
+ if (cli_cmd[i].name) {
+ cli_cmd_list[cli_cmd_list_size++] = &cli_cmd[i];
+ }
+ }
+}
+
+struct cli_cmd *find_cli_cmd(const char *command)
+{
+ int i;
+
+ for (i = 0; i < cli_cmd_list_size; i++) {
+ if (!strcmp(cli_cmd_list[i]->name, command)) {
+ return cli_cmd_list[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int cmpcli_cmds(const void *p1, const void *p2)
+{
+ return strcmp(* (char * const *) ((struct cli_cmd *)p1)->name,
+ * (char * const *) ((struct cli_cmd *)p2)->name);
+}
+
+#define HELP_BUFFER_SIZE 4096
+
+static void print_cmd_help(const char *prefix, const char *infix,
+ struct cli_cmd *cli_cmd)
+{
+ if (cli_cmd->help_text) {
+ fprintf(stderr, "%s%s%s",
+ prefix,
+ infix,
+ cli_cmd->help_text);
+ } else if (cli_cmd->description) {
+ fprintf(stderr, "%s%s%s\n%s\n",
+ prefix,
+ infix,
+ cli_cmd->name,
+ cli_cmd->description);
+ } else {
+ fprintf(stderr, "No help available for %s\n",
+ cli_cmd->name);
+ }
+}
+
+void list_cli_cmds(int option)
+{
+ int i;
+
+ qsort(cli_cmd_list, cli_cmd_list_size, sizeof(void *), cmpcli_cmds);
+
+ for (i = 0; i < cli_cmd_list_size; i++) {
+ switch (option) {
+ case CLI_SIMPLE_LIST:
+ fprintf(stderr, "%s ", cli_cmd_list[i]->name);
+ break;
+ case CLI_DESCRIPTIVE_LIST:
+ fprintf(stderr, " %-25s%s\n", cli_cmd_list[i]->name,
+ cli_cmd_list[i]->description);
+ break;
+ case CLI_EXTENDED_LIST:
+ print_cmd_help("", "", cli_cmd_list[i]);
+ fprintf(stderr, "\n");
+ break;
+ }
+ }
+
+ if (option == CLI_SIMPLE_LIST) {
+ fprintf(stderr, "\n");
+ }
+}
+
+int cli_print_help(const char *command)
+{
+ struct cli_cmd *cli_cmd;
+
+ cli_cmd = find_cli_cmd(command);
+ if (!cli_cmd) {
+ return -1;
+ }
+
+ print_cmd_help(process_name, " ", cli_cmd);
+
+ return 0;
+}
+
+static void cli_check_argc(const char *command, int args,
+ int operator, int desired_args)
+{
+ switch(operator) {
+ case CLI_EQ:
+ if (args != desired_args)
+ goto print_error;
+ break;
+ case CLI_GE:
+ if (args < desired_args)
+ goto print_error;
+ break;
+ }
+
+ return;
+
+print_error:
+ fprintf(stderr, "%s %s requires %s%d argument%s, see usage.\n",
+ process_name, command, operator == CLI_EQ ? "" : "at least ",
+ desired_args, desired_args > 1 ? "s" : "");
+ cli_print_help(command);
+ exit(EXIT_FAILURE);
+}
+
+
+void cli_dispatch_cmd(struct cli_cmd *cmd, int argc, char *argv[])
+{
+ cli_check_argc(cmd->name, argc - 1, cmd->desired_args_op,
+ cmd->desired_args);
+
+ if (cmd->function(argc, argv)) {
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+}
--- /dev/null
+/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef _CLI_H
+#define _CLI_H
+
+struct cli_cmd {
+ const char *name;
+ const char *description;
+ const char *help_text;
+ int (*function)(int, char **);
+ int desired_args;
+ int desired_args_op;
+} __attribute__((aligned(8)));
+
+#define __cli_cmds __attribute__((section("__cli_cmds"), aligned(8), used))
+
+struct cli_cmd *find_cli_cmd(const char *command);
+
+enum cli_list_opts {
+ CLI_SIMPLE_LIST,
+ CLI_DESCRIPTIVE_LIST,
+ CLI_EXTENDED_LIST,
+};
+
+void list_cli_cmds(int option);
+
+int cli_print_help(const char *command);
+
+enum cli_arg_ops {
+ CLI_EQ,
+ CLI_GE,
+};
+
+void cli_dispatch_cmd(struct cli_cmd *cmd, int argc, char *argv[]);
+
+#endif /* _CLI_H */
--- /dev/null
+/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ust/ustctl.h>
+#include "scanning_functions.h"
+#include "usterr.h"
+#include "cli.h"
+
+static int list_markers(int argc, char *argv[])
+{
+ struct ust_marker_status *cmsf = NULL;
+ int i, sock;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_get_cmsf(sock, &cmsf)) {
+ ERR("error while trying to list markers for PID %s\n", argv[1]);
+ return -1;
+ }
+ for (i = 0; cmsf[i].channel; i++) {
+ printf("{PID: %s, channel/marker: %s/%s, "
+ "state: %u, fmt: %s}\n",
+ argv[1],
+ cmsf[i].channel,
+ cmsf[i].ust_marker,
+ cmsf[i].state,
+ cmsf[i].fs);
+ }
+ ustctl_free_cmsf(cmsf);
+ return 0;
+}
+
+static int enable_marker(int argc, char *argv[])
+{
+ int i, sock, result = 0;
+ char *channel, *marker;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ for (i = 3; i < argc; i++) {
+ channel = NULL;
+ marker = NULL;
+ if (scan_ch_marker(argv[i],
+ &channel, &marker)) {
+ result = -1;
+ fprintf(stderr, "Failed to scan channel and marker from"
+ " %s\n", argv[i]);
+ if (channel)
+ free(channel);
+ if (marker)
+ free(marker);
+ }
+ if (ustctl_set_ust_marker_state(sock, argv[2], channel, marker, 1)) {
+ PERROR("error while trying to enable marker %s with PID %s",
+ argv[i], argv[1]);
+ result = -1;
+ }
+ free(channel);
+ free(marker);
+ }
+
+ return result;
+}
+
+static int disable_marker(int argc, char *argv[])
+{
+ int i, sock, result = 0;
+ char *channel, *marker;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ for (i = 3; i < argc; i++) {
+ channel = NULL;
+ marker = NULL;
+ if (scan_ch_marker(argv[i],
+ &channel, &marker)) {
+ fprintf(stderr, "Failed to scan channel and marker from"
+ " %s\n", argv[i]);
+ if (channel)
+ free(channel);
+ if (marker)
+ free(marker);
+ return -1;
+ }
+ if (ustctl_set_ust_marker_state(sock, argv[2], channel, marker, 0)) {
+ PERROR("error while trying to disable marker %s with PID %s",
+ argv[i], argv[1]);
+ result = -1;
+ }
+ free(channel);
+ free(marker);
+ }
+
+ return result;
+}
+
+struct cli_cmd __cli_cmds ust_marker_cmds[] = {
+ {
+ .name = "list-markers",
+ .description = "List markers for a given pid",
+ .help_text = "list-markers <pid>\n"
+ "List the markers in a process\n",
+ .function = list_markers,
+ .desired_args = 1,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "enable-marker",
+ .description = "Enable markers for a given pid",
+ .help_text = "enable-marker <pid> <trace> <channel>/<marker>... \n"
+ "Enable the listed markers for the trace in process pid\n",
+ .function = enable_marker,
+ .desired_args = 3,
+ .desired_args_op = CLI_GE,
+ },
+ {
+ .name = "disable-marker",
+ .description = "Disable markers for a given pid",
+ .help_text = "disable-marker <pid> <trace> <channel>/<marker>... \n"
+ "Disable the listed markers for the trace in process pid\n",
+ .function = disable_marker,
+ .desired_args = 3,
+ .desired_args_op = CLI_GE,
+ }
+};
--- /dev/null
+/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <ust/ustctl.h>
+#include "usterr.h"
+
+
+int parse_and_connect_pid(const char *pid_string)
+{
+ pid_t pid;
+ int sock;
+
+ errno = 0;
+ pid = strtoull(pid_string, NULL, 10);
+ if (errno) {
+ perror("Failed to parse pid");
+ exit(EXIT_FAILURE);
+ }
+
+ sock = ustctl_connect_pid(pid);
+ if (sock < 0) {
+ perror("Failed to connect to process");
+ exit(EXIT_FAILURE);
+ }
+
+ return sock;
+}
+
+int scan_ch_marker(const char *channel_marker, char **channel, char **marker)
+{
+ int result;
+
+ *channel = NULL;
+ *marker = NULL;
+
+ result = sscanf(channel_marker, "%a[^/]/%as", channel, marker);
+ if (result != 2) {
+ if (errno) {
+ PERROR("Failed to read channel and marker names");
+ } else {
+ ERR("Failed to parse marker and channel names");
+ }
+ if (*channel) {
+ free(*channel);
+ }
+ if (*marker) {
+ free(*marker);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+int scan_ch_and_num(const char *ch_num, char **channel, unsigned int *num)
+{
+ int result;
+
+ *channel = NULL;
+
+ result = sscanf(ch_num, "%a[^/]/%u", channel, num);
+ if (result != 2) {
+ if (errno) {
+ PERROR("Failed to parse channel and number");
+ } else {
+ ERR("Failed to parse channel and number");
+ }
+ if (*channel) {
+ free(*channel);
+ }
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __SCANNING_FUNCTIONS_H
+#define __SCANNING_FUNCTIONS_H
+
+int parse_and_connect_pid(const char *pid_string);
+
+int scan_ch_marker(const char *channel_marker, char **channel, char **marker);
+
+int scan_ch_and_num(const char *ch_num, char **channel, unsigned int *num);
+
+#endif /* __SCANNING_FUNCTIONS_H */
--- /dev/null
+/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ust/ustctl.h>
+#include "scanning_functions.h"
+#include "usterr.h"
+#include "cli.h"
+
+
+static int create_trace(int argc, char *argv[])
+{
+ int sock;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_create_trace(sock, argv[2])) {
+ ERR("Failed to create trace %s for PID %s\n", argv[2], argv[1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int alloc_trace(int argc, char *argv[])
+{
+ int sock;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_alloc_trace(sock, argv[2])) {
+ ERR("Failed to allocate trace %s for PID %s\n", argv[2], argv[1]);
+ return -1;
+ }
+ return 0;
+}
+
+static int start_trace(int argc, char *argv[])
+{
+ int sock;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_start_trace(sock, argv[2])) {
+ ERR("Failed to start trace %s for PID %s\n", argv[2], argv[1]);
+ return -1;
+ }
+ return 0;
+}
+
+static int stop_trace(int argc, char *argv[])
+{
+ int sock;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_stop_trace(sock, argv[2])) {
+ ERR("Failed to stop trace %s for PID %s\n", argv[2], argv[1]);
+ return -1;
+ }
+ return 0;
+}
+
+static int destroy_trace(int argc, char *argv[])
+{
+ int sock;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_destroy_trace(sock, argv[2])) {
+ ERR("Failed to destroy trace %s for PID %s\n", argv[2], argv[1]);
+ return -1;
+ }
+ return 0;
+}
+
+static int force_subbuf_switch(int argc, char *argv[])
+{
+ int sock;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_force_switch(sock, argv[2])) {
+ ERR("error while trying to force switch for PID %s\n", argv[1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct cli_cmd __cli_cmds trace_cmds[] = {
+ {
+ .name = "create-trace",
+ .description = "Create a trace for a process",
+ .help_text = "create-trace <pid> <trace>\n"
+ "Create a trace for a process\n",
+ .function = create_trace,
+ .desired_args = 2,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "alloc-trace",
+ .description = "Allocate a trace for a process",
+ .help_text = "alloc-trace <pid> <trace>\n"
+ "Allocate a trace for a process\n",
+ .function = alloc_trace,
+ .desired_args = 2,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "start-trace",
+ .description = "Start a trace for a process",
+ .help_text = "start-trace <pid> <trace>\n"
+ "Start a trace for a process\n",
+ .function = start_trace,
+ .desired_args = 2,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "stop-trace",
+ .description = "Stop a trace for a process",
+ .help_text = "stop-trace <pid> <trace>\n"
+ "Stop a trace for a process\n",
+ .function = stop_trace,
+ .desired_args = 2,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "destroy-trace",
+ .description = "Destroy a trace for a process",
+ .help_text = "destroy-trace <pid> <trace>\n"
+ "Destroy a trace for a process\n",
+ .function = destroy_trace,
+ .desired_args = 2,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "force-subbuf-switch",
+ .description = "Force a subbuffer switch",
+ .help_text = "force-subbuf-switch <pid> <trace>\n"
+ "Force a subbuffer switch for a trace, currently this forces\n"
+ "a subbuffer switch for all traces in a process\n",
+ .function = force_subbuf_switch,
+ .desired_args = 2,
+ .desired_args_op = CLI_EQ,
+ },
+};
--- /dev/null
+/* Copyright (C) 2009 Pierre-Marc Fournier
+ * Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "ust/ustctl.h"
+#include "usterr.h"
+#include "cli.h"
+#include "scanning_functions.h"
+
+void usage(const char *process_name)
+{
+ fprintf(stderr, "Usage: %s COMMAND [ARGS]...\n", process_name);
+ fprintf(stderr,
+ "Control tracing within a process that supports UST,\n"
+ " the Userspace Tracing libary\n"
+ "Options:\n"
+ " -h[<cmd>], --help[=<cmd>] "
+ "help, for a command if provided\n"
+ " -l, --list "
+ "short list of commands\n"
+ " -e, --extended-list "
+ "extented list of commands with help\n"
+ "Commands:\n");
+ list_cli_cmds(CLI_DESCRIPTIVE_LIST);
+}
+
+/*
+ * Provide backward compatibility for scripts that make use of the
+ * --commands in ustctl version <= 0.11
+ */
+enum command {
+ CREATE_TRACE=1000,
+ ALLOC_TRACE,
+ START_TRACE,
+ STOP_TRACE,
+ DESTROY_TRACE,
+ LIST_MARKERS,
+ LIST_TRACE_EVENTS,
+ ENABLE_MARKER,
+ DISABLE_MARKER,
+};
+
+struct option options[] =
+{
+ { "create-trace", 0, 0, CREATE_TRACE },
+ { "alloc-trace", 0, 0, ALLOC_TRACE },
+ { "start-trace", 0, 0, START_TRACE },
+ { "stop-trace", 0, 0, STOP_TRACE },
+ { "destroy-trace", 0, 0, DESTROY_TRACE },
+ { "list-markers", 0, 0, LIST_MARKERS },
+ { "list-trace-events", 0, 0, LIST_TRACE_EVENTS},
+ { "enable-marker", 0, 0, ENABLE_MARKER },
+ { "disable-marker", 0, 0, DISABLE_MARKER },
+ {"help", 2, NULL, 'h'},
+ {"list", 0, NULL, 'l'},
+ {"extended-list", 0, NULL, 'e'},
+ {NULL, 0, NULL, 0},
+};
+
+int main(int argc, char *argv[])
+{
+ struct cli_cmd *cli_cmd;
+ char **args = argv;
+ int opt;
+ int i;
+
+ if(argc <= 1) {
+ fprintf(stderr, "No operation specified.\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ while ((opt = getopt_long(argc, argv, "+h::le",
+ options, NULL)) != -1) {
+ switch (opt) {
+ case 'h':
+ if (!optarg) {
+ usage(argv[0]);
+ } else {
+ if (cli_print_help(optarg)) {
+ fprintf(stderr, "No such command %s\n",
+ optarg);
+ }
+ }
+ exit(EXIT_FAILURE);
+ break;
+ case 'l':
+ list_cli_cmds(CLI_SIMPLE_LIST);
+ exit(EXIT_FAILURE);
+ break;
+ case 'e':
+ list_cli_cmds(CLI_EXTENDED_LIST);
+ exit(EXIT_FAILURE);
+ case LIST_MARKERS:
+ case LIST_TRACE_EVENTS:
+ case CREATE_TRACE:
+ case ALLOC_TRACE:
+ case START_TRACE:
+ case STOP_TRACE:
+ case DESTROY_TRACE:
+ case ENABLE_MARKER:
+ case DISABLE_MARKER:
+ args = (char **)malloc(sizeof(char *) * (argc + 3));
+ optind--;
+ args[optind] = strdup(&argv[optind][2]);
+ for (i = optind + 1; i < argc; i++) {
+ args[i] = argv[i];
+ }
+ if (opt >= CREATE_TRACE && opt <= DESTROY_TRACE) {
+ args[argc] = strdup("auto");
+ argc++;
+ }
+ if (opt >= ENABLE_MARKER && opt <= DISABLE_MARKER) {
+ args[argc] = args[argc - 2];
+ args[argc - 2] = args[argc - 1];
+ args[argc - 1] = strdup("auto");
+ argc++;
+ }
+ args[argc] = NULL;
+ goto do_cli;
+ default:
+ fprintf(stderr, "Unknown option\n");
+ break;
+ }
+ }
+
+do_cli:
+ cli_cmd = find_cli_cmd(args[optind]);
+ if (!cli_cmd) {
+ fprintf(stderr, "No such command %s\n",
+ args[optind]);
+ exit(EXIT_FAILURE);
+ }
+
+ cli_dispatch_cmd(cli_cmd, argc - optind, &args[optind]);
+
+ return 0;
+}
+
+static int list_trace_events(int argc, char *argv[])
+{
+ struct trace_event_status *tes = NULL;
+ int i, sock;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_get_tes(sock, &tes)) {
+ ERR("error while trying to list "
+ "trace_events for PID %s\n",
+ argv[1]);
+ return -1;
+ }
+ i = 0;
+ for (i = 0; tes[i].name; i++) {
+ printf("{PID: %s, trace_event: %s}\n",
+ argv[1],
+ tes[i].name);
+ }
+ ustctl_free_tes(tes);
+
+ return 0;
+}
+
+static int set_sock_path(int argc, char *argv[])
+{
+ int sock;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_set_sock_path(sock, argv[2])) {
+ ERR("error while trying to set sock path for PID %s\n", argv[1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_sock_path(int argc, char *argv[])
+{
+ int sock;
+ char *sock_path;
+
+ sock = parse_and_connect_pid(argv[1]);
+
+ if (ustctl_get_sock_path(sock, &sock_path)) {
+ ERR("error while trying to get sock path for PID %s\n", argv[1]);
+ return -1;
+ }
+ printf("The socket path is %s\n", sock_path);
+ free(sock_path);
+
+ return 0;
+}
+
+static int list_pids(int argc, char *argv[])
+{
+ pid_t *pid_list;
+ int i;
+
+ pid_list = ustctl_get_online_pids();
+ if (!pid_list) {
+ return -1;
+ }
+
+ for (i = 0; pid_list[i]; i++) {
+ printf("%ld\n", (long)pid_list[i]);
+ }
+
+ free(pid_list);
+
+ return 0;
+}
+
+struct cli_cmd __cli_cmds general_cmds[] = {
+ {
+ .name = "list-trace-events",
+ .description = "List trace-events for a given pid",
+ .help_text = "list-trace-events <pid>\n"
+ "List the trace-events in a process\n",
+ .function = list_trace_events,
+ .desired_args = 1,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "set-sock-path",
+ .description = "Set the path to the consumer daemon socket",
+ .help_text = "set-sock-path <pid> <sock-path>\n"
+ "Set the path to the consumer daemon socket\n",
+ .function = set_sock_path,
+ .desired_args = 2,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "get-sock-path",
+ .description = "Get the path to the consumer daemon socket",
+ .help_text = "get-sock-path <pid>\n"
+ "Get the path to the consumer daemon socket\n",
+ .function = get_sock_path,
+ .desired_args = 1,
+ .desired_args_op = CLI_EQ,
+ },
+ {
+ .name = "list-pids",
+ .description = "List traceable pids",
+ .help_text = "list-pids\n"
+ "List the traceable pids for the current user\n",
+ .function = list_pids,
+ .desired_args = 0,
+ .desired_args_op = CLI_EQ,
+ },
+};
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include
-AM_CFLAGS = -fno-strict-aliasing
-
-noinst_LTLIBRARIES = libustcomm.la
-libustcomm_la_SOURCES = \
- ustcomm.h \
- ustcomm.c
-
-libustcomm_la_LDFLAGS = -no-undefined -static
-libustcomm_la_CFLAGS = -DUST_COMPONENT="libustcomm" -fPIC -fno-strict-aliasing
+++ /dev/null
-/* Copyright (C) 2009 Pierre-Marc Fournier
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/* API used by UST components to communicate with each other via sockets. */
-
-#define _GNU_SOURCE
-#include <dirent.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-#include <poll.h>
-#include <sys/epoll.h>
-#include <sys/stat.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "ustcomm.h"
-#include "usterr.h"
-#include "share.h"
-
-static int mkdir_p(const char *path, mode_t mode)
-{
- const char *path_p;
- char *tmp;
-
- int retval = 0;
- int result;
- mode_t old_umask;
-
- tmp = zmalloc(strlen(path) + 1);
- if (tmp == NULL)
- return -1;
-
- /* skip first / */
- path_p = path+1;
-
- old_umask = umask(0);
- for(;;) {
- while (*path_p != '/') {
- if(*path_p == 0)
- break;
- ++path_p;
- }
- if (*path_p == '/') {
- strncpy(tmp, path, path_p - path);
- tmp[path_p-path] = '\0';
- if (tmp[path_p - path - 1] != '/') {
- result = mkdir(tmp, mode);
- if(result == -1) {
- if (!(errno == EEXIST || errno == EACCES || errno == EROFS)) {
- /* Then this is a real error */
- retval = -1;
- break;
- }
- }
- }
- /* pass / */
- path_p++;
- } else {
- /* last component */
- result = mkdir(path, mode);
- if (result == -1)
- retval = -1;
- break;
- }
- }
-
- free(tmp);
- umask(old_umask);
- return retval;
-}
-
-static struct sockaddr_un * create_sock_addr(const char *name,
- size_t *sock_addr_size)
-{
- struct sockaddr_un * addr;
- size_t alloc_size;
-
- alloc_size = (size_t) (((struct sockaddr_un *) 0)->sun_path) +
- strlen(name) + 1;
-
- addr = malloc(alloc_size);
- if (addr < 0) {
- ERR("allocating addr failed");
- return NULL;
- }
-
- addr->sun_family = AF_UNIX;
- strcpy(addr->sun_path, name);
-
- *sock_addr_size = alloc_size;
-
- return addr;
-}
-
-struct ustcomm_sock * ustcomm_init_sock(int fd, int epoll_fd,
- struct cds_list_head *list)
-{
- struct epoll_event ev;
- struct ustcomm_sock *sock;
-
- sock = malloc(sizeof(struct ustcomm_sock));
- if (!sock) {
- perror("malloc: couldn't allocate ustcomm_sock");
- return NULL;
- }
-
- ev.events = EPOLLIN;
- ev.data.ptr = sock;
- sock->fd = fd;
-
- if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock->fd, &ev) == -1) {
- perror("epoll_ctl: failed to add socket\n");
- free(sock);
- return NULL;
- }
-
- sock->epoll_fd = epoll_fd;
- if (list) {
- cds_list_add(&sock->list, list);
- } else {
- CDS_INIT_LIST_HEAD(&sock->list);
- }
-
- return sock;
-}
-
-void ustcomm_del_sock(struct ustcomm_sock *sock, int keep_in_epoll)
-{
- cds_list_del(&sock->list);
- if (!keep_in_epoll) {
- if (epoll_ctl(sock->epoll_fd, EPOLL_CTL_DEL, sock->fd, NULL) == -1) {
- PERROR("epoll_ctl: failed to delete socket");
- }
- }
- close(sock->fd);
- free(sock);
-}
-
-struct ustcomm_sock * ustcomm_init_named_socket(const char *name,
- int epoll_fd)
-{
- int result;
- int fd;
- size_t sock_addr_size;
- struct sockaddr_un * addr;
- struct ustcomm_sock *sock;
-
- fd = socket(PF_UNIX, SOCK_STREAM, 0);
- if(fd == -1) {
- PERROR("socket");
- return NULL;
- }
-
- addr = create_sock_addr(name, &sock_addr_size);
- if (addr == NULL) {
- ERR("allocating addr, UST thread bailing");
- goto close_sock;
- }
-
- result = access(name, F_OK);
- if(result == 0) {
- /* file exists */
- result = unlink(name);
- if(result == -1) {
- PERROR("unlink of socket file");
- goto free_addr;
- }
- DBG("socket already exists; overwriting");
- }
-
- result = bind(fd, (struct sockaddr *)addr, sock_addr_size);
- if(result == -1) {
- PERROR("bind");
- goto free_addr;
- }
-
- result = listen(fd, 1);
- if(result == -1) {
- PERROR("listen");
- goto free_addr;
- }
-
- sock = ustcomm_init_sock(fd, epoll_fd,
- NULL);
- if (!sock) {
- ERR("failed to create ustcomm_sock");
- goto free_addr;
- }
-
- free(addr);
-
- return sock;
-
-free_addr:
- free(addr);
-close_sock:
- close(fd);
-
- return NULL;
-}
-
-void ustcomm_del_named_sock(struct ustcomm_sock *sock,
- int keep_socket_file)
-{
- int result, fd;
- struct stat st;
- struct sockaddr dummy;
- struct sockaddr_un *sockaddr = NULL;
- int alloc_size;
-
- fd = sock->fd;
-
- if(!keep_socket_file) {
-
- /* Get the socket name */
- alloc_size = sizeof(dummy);
- if (getsockname(fd, &dummy, (socklen_t *)&alloc_size) < 0) {
- PERROR("getsockname failed");
- goto del_sock;
- }
-
- sockaddr = zmalloc(alloc_size);
- if (!sockaddr) {
- ERR("failed to allocate sockaddr");
- goto del_sock;
- }
-
- if (getsockname(fd, sockaddr, (socklen_t *)&alloc_size) < 0) {
- PERROR("getsockname failed");
- goto free_sockaddr;
- }
-
- /* Destroy socket */
- result = stat(sockaddr->sun_path, &st);
- if(result < 0) {
- PERROR("stat (%s)", sockaddr->sun_path);
- goto free_sockaddr;
- }
-
- /* Paranoid check before deleting. */
- result = S_ISSOCK(st.st_mode);
- if(!result) {
- ERR("The socket we are about to delete is not a socket.");
- goto free_sockaddr;
- }
-
- result = unlink(sockaddr->sun_path);
- if(result < 0) {
- PERROR("unlink");
- }
- }
-
-free_sockaddr:
- free(sockaddr);
-
-del_sock:
- ustcomm_del_sock(sock, keep_socket_file);
-}
-
-int ustcomm_recv_alloc(int sock,
- struct ustcomm_header *header,
- char **data) {
- int result;
- struct ustcomm_header peek_header;
- struct iovec iov[2];
- struct msghdr msg;
-
- /* Just to make the caller fail hard */
- *data = NULL;
-
- result = recv(sock, &peek_header, sizeof(peek_header),
- MSG_PEEK | MSG_WAITALL);
- if (result <= 0) {
- if(errno == ECONNRESET) {
- return 0;
- } else if (errno == EINTR) {
- return -1;
- } else if (result < 0) {
- PERROR("recv");
- return -1;
- }
- return 0;
- }
-
- memset(&msg, 0, sizeof(msg));
-
- iov[0].iov_base = (char *)header;
- iov[0].iov_len = sizeof(struct ustcomm_header);
-
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
-
- if (peek_header.size) {
- *data = zmalloc(peek_header.size);
- if (!*data) {
- return -ENOMEM;
- }
-
- iov[1].iov_base = *data;
- iov[1].iov_len = peek_header.size;
-
- msg.msg_iovlen++;
- }
-
- result = recvmsg(sock, &msg, MSG_WAITALL);
- if (result < 0) {
- free(*data);
- PERROR("recvmsg failed");
- }
-
- return result;
-}
-
-/* returns 1 to indicate a message was received
- * returns 0 to indicate no message was received (end of stream)
- * returns -1 to indicate an error
- */
-int ustcomm_recv_fd(int sock,
- struct ustcomm_header *header,
- char *data, int *fd)
-{
- int result;
- struct ustcomm_header peek_header;
- struct iovec iov[2];
- struct msghdr msg;
- struct cmsghdr *cmsg;
- char buf[CMSG_SPACE(sizeof(int))];
-
- result = recv(sock, &peek_header, sizeof(peek_header),
- MSG_PEEK | MSG_WAITALL);
- if (result <= 0) {
- if(errno == ECONNRESET) {
- return 0;
- } else if (errno == EINTR) {
- return -1;
- } else if (result < 0) {
- PERROR("recv");
- return -1;
- }
- return 0;
- }
-
- memset(&msg, 0, sizeof(msg));
-
- iov[0].iov_base = (char *)header;
- iov[0].iov_len = sizeof(struct ustcomm_header);
-
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
-
- if (peek_header.size && data) {
- if (peek_header.size < 0 ||
- peek_header.size > USTCOMM_DATA_SIZE) {
- ERR("big peek header! %ld", peek_header.size);
- return 0;
- }
-
- iov[1].iov_base = data;
- iov[1].iov_len = peek_header.size;
-
- msg.msg_iovlen++;
- }
-
- if (fd && peek_header.fd_included) {
- msg.msg_control = buf;
- msg.msg_controllen = sizeof(buf);
- }
-
- result = recvmsg(sock, &msg, MSG_WAITALL);
- if (result <= 0) {
- if (result < 0) {
- PERROR("recvmsg failed");
- }
- return result;
- }
-
- if (fd && peek_header.fd_included) {
- cmsg = CMSG_FIRSTHDR(&msg);
- result = 0;
- while (cmsg != NULL) {
- if (cmsg->cmsg_level == SOL_SOCKET
- && cmsg->cmsg_type == SCM_RIGHTS) {
- *fd = *(int *) CMSG_DATA(cmsg);
- result = 1;
- break;
- }
- cmsg = CMSG_NXTHDR(&msg, cmsg);
- }
- if (!result) {
- ERR("Failed to receive file descriptor\n");
- }
- }
-
- return 1;
-}
-
-int ustcomm_recv(int sock,
- struct ustcomm_header *header,
- char *data)
-{
- return ustcomm_recv_fd(sock, header, data, NULL);
-}
-
-
-int ustcomm_send_fd(int sock,
- const struct ustcomm_header *header,
- const char *data,
- int *fd)
-{
- struct iovec iov[2];
- struct msghdr msg;
- int result;
- struct cmsghdr *cmsg;
- char buf[CMSG_SPACE(sizeof(int))];
-
- memset(&msg, 0, sizeof(msg));
-
- iov[0].iov_base = (char *)header;
- iov[0].iov_len = sizeof(struct ustcomm_header);
-
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
-
- if (header->size && data) {
- iov[1].iov_base = (char *)data;
- iov[1].iov_len = header->size;
-
- msg.msg_iovlen++;
-
- }
-
- if (fd && header->fd_included) {
- msg.msg_control = buf;
- msg.msg_controllen = sizeof(buf);
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- *(int *) CMSG_DATA(cmsg) = *fd;
- msg.msg_controllen = cmsg->cmsg_len;
- }
-
- result = sendmsg(sock, &msg, MSG_NOSIGNAL);
- if (result < 0 && errno != EPIPE) {
- PERROR("sendmsg failed");
- }
- return result;
-}
-
-int ustcomm_send(int sock,
- const struct ustcomm_header *header,
- const char *data)
-{
- return ustcomm_send_fd(sock, header, data, NULL);
-}
-
-int ustcomm_req(int sock,
- const struct ustcomm_header *req_header,
- const char *req_data,
- struct ustcomm_header *res_header,
- char *res_data)
-{
- int result;
-
- result = ustcomm_send(sock, req_header, req_data);
- if ( result <= 0) {
- return result;
- }
-
- return ustcomm_recv(sock, res_header, res_data);
-}
-
-/* Return value:
- * 0: success
- * -1: error
- */
-
-int ustcomm_connect_path(const char *name, int *connection_fd)
-{
- int result, fd;
- size_t sock_addr_size;
- struct sockaddr_un *addr;
-
- fd = socket(PF_UNIX, SOCK_STREAM, 0);
- if(fd == -1) {
- PERROR("socket");
- return -1;
- }
-
- addr = create_sock_addr(name, &sock_addr_size);
- if (addr == NULL) {
- ERR("allocating addr failed");
- goto close_sock;
- }
-
- result = connect(fd, (struct sockaddr *)addr, sock_addr_size);
- if(result == -1) {
- PERROR("connect (path=%s)", name);
- goto free_sock_addr;
- }
-
- *connection_fd = fd;
-
- free(addr);
-
- return 0;
-
-free_sock_addr:
- free(addr);
-close_sock:
- close(fd);
-
- return -1;
-}
-
-/* Returns the current users socket directory, must be freed */
-char *ustcomm_user_sock_dir(void)
-{
- int result;
- char *sock_dir = NULL;
-
- result = asprintf(&sock_dir, "%s%s", USER_SOCK_DIR,
- cuserid(NULL));
- if (result < 0) {
- ERR("string overflow allocating directory name");
- return NULL;
- }
-
- return sock_dir;
-}
-
-static int time_and_pid_from_socket_name(char *sock_name, unsigned long *time,
- pid_t *pid)
-{
- char *saveptr, *pid_m_time_str;
- char *sock_basename = strdup(basename(sock_name));
-
- if (!sock_basename) {
- return -1;
- }
-
- /* This is the pid */
- pid_m_time_str = strtok_r(sock_basename, ".", &saveptr);
- if (!pid_m_time_str) {
- goto out_err;
- }
-
- errno = 0;
- *pid = (pid_t)strtoul(pid_m_time_str, NULL, 10);
- if (errno) {
- goto out_err;
- }
-
- /* This should be the time-stamp */
- pid_m_time_str = strtok_r(NULL, ".", &saveptr);
- if (!pid_m_time_str) {
- goto out_err;
- }
-
- errno = 0;
- *time = strtoul(pid_m_time_str, NULL, 10);
- if (errno) {
- goto out_err;
- }
-
- return 0;
-
-out_err:
- free(sock_basename);
- return -1;
-}
-
-time_t ustcomm_pid_st_mtime(pid_t pid)
-{
- struct stat proc_stat;
- char proc_name[PATH_MAX];
-
- if (snprintf(proc_name, PATH_MAX - 1, "/proc/%ld", (long) pid) < 0) {
- return 0;
- }
-
- if (stat(proc_name, &proc_stat)) {
- return 0;
- }
-
- return proc_stat.st_mtime;
-}
-
-int ustcomm_is_socket_live(char *sock_name, pid_t *read_pid)
-{
- time_t time_from_pid;
- unsigned long time_from_sock;
- pid_t pid;
-
- if (time_and_pid_from_socket_name(sock_name, &time_from_sock, &pid)) {
- return 0;
- }
-
- if (read_pid) {
- *read_pid = pid;
- }
-
- time_from_pid = ustcomm_pid_st_mtime(pid);
- if (!time_from_pid) {
- return 0;
- }
-
- if ((unsigned long) time_from_pid == time_from_sock) {
- return 1;
- }
-
- return 0;
-}
-
-#define MAX_SOCK_PATH_BASE_LEN 100
-
-static int ustcomm_get_sock_name(char *dir_name, pid_t pid, char *sock_name)
-{
- struct dirent *dirent;
- char sock_path_base[MAX_SOCK_PATH_BASE_LEN];
- int len;
- DIR *dir = opendir(dir_name);
-
- snprintf(sock_path_base, MAX_SOCK_PATH_BASE_LEN - 1,
- "%ld.", (long) pid);
- len = strlen(sock_path_base);
-
- while ((dirent = readdir(dir))) {
- if (!strcmp(dirent->d_name, ".") ||
- !strcmp(dirent->d_name, "..") ||
- !strcmp(dirent->d_name, "ust-consumer") ||
- dirent->d_type == DT_DIR ||
- strncmp(dirent->d_name, sock_path_base, len)) {
- continue;
- }
-
- if (ustcomm_is_socket_live(dirent->d_name, NULL)) {
- if (snprintf(sock_name, PATH_MAX - 1, "%s/%s",
- dir_name, dirent->d_name) < 0) {
- PERROR("path longer than PATH_MAX?");
- goto out_err;
- }
- closedir(dir);
- return 0;
- }
- }
-
-out_err:
- closedir(dir);
- return -1;
-}
-
-/* Open a connection to a traceable app.
- *
- * Return value:
- * 0: success
- * -1: error
- */
-
-static int connect_app_non_root(pid_t pid, int *app_fd)
-{
- int result;
- int retval = 0;
- char *dir_name;
- char sock_name[PATH_MAX];
-
- dir_name = ustcomm_user_sock_dir();
- if (!dir_name)
- return -ENOMEM;
-
- if (ustcomm_get_sock_name(dir_name, pid, sock_name)) {
- retval = -ENOENT;
- goto free_dir_name;
- }
-
- result = ustcomm_connect_path(sock_name, app_fd);
- if (result < 0) {
- ERR("failed to connect to app");
- retval = -1;
- goto free_dir_name;
- }
-
-free_dir_name:
- free(dir_name);
-
- return retval;
-}
-
-
-
-static int connect_app_root(pid_t pid, int *app_fd)
-{
- DIR *tmp_dir;
- struct dirent *dirent;
- char dir_name[PATH_MAX], sock_name[PATH_MAX];
- int result = -1;
-
- tmp_dir = opendir(USER_TMP_DIR);
- if (!tmp_dir) {
- return -1;
- }
-
- while ((dirent = readdir(tmp_dir))) {
- if (!strncmp(dirent->d_name, USER_SOCK_DIR_BASE,
- strlen(USER_SOCK_DIR_BASE))) {
-
- if (snprintf(dir_name, PATH_MAX - 1, "%s/%s", USER_TMP_DIR,
- dirent->d_name) < 0) {
- continue;
- }
-
- if (ustcomm_get_sock_name(dir_name, pid, sock_name)) {
- continue;
- }
-
- result = ustcomm_connect_path(sock_name, app_fd);
-
- if (result == 0) {
- goto close_tmp_dir;
- }
- }
- }
-
-close_tmp_dir:
- closedir(tmp_dir);
-
- return result;
-}
-
-int ustcomm_connect_app(pid_t pid, int *app_fd)
-{
- *app_fd = 0;
-
- if (geteuid()) {
- return connect_app_non_root(pid, app_fd);
- } else {
- return connect_app_root(pid, app_fd);
- }
-
-}
-
-int ensure_dir_exists(const char *dir, mode_t mode)
-{
- struct stat st;
- int result;
-
- if (!strcmp(dir, ""))
- return -1;
-
- result = stat(dir, &st);
- if (result < 0 && errno != ENOENT) {
- return -1;
- } else if (result < 0) {
- /* ENOENT */
- int result;
-
- result = mkdir_p(dir, mode);
- if(result != 0) {
- ERR("executing in recursive creation of directory %s", dir);
- return -1;
- }
- } else {
- if (st.st_mode != mode) {
- result = chmod(dir, mode);
- if (result < 0) {
- ERR("couldn't set directory mode on %s", dir);
- return -1;
- }
- }
- }
-
- return 0;
-}
-
-char * ustcomm_print_data(char *data_field, int field_size,
- int *offset, const char *format, ...)
-{
- va_list args;
- int count, limit;
- char *ptr = USTCOMM_POISON_PTR;
-
- limit = field_size - *offset;
- va_start(args, format);
- count = vsnprintf(&data_field[*offset], limit, format, args);
- va_end(args);
-
- if (count < limit && count > -1) {
- ptr = NULL + *offset;
- *offset = *offset + count + 1;
- }
-
- return ptr;
-}
-
-char * ustcomm_restore_ptr(char *ptr, char *data_field, int data_field_size)
-{
- if ((unsigned long)ptr > data_field_size ||
- ptr == USTCOMM_POISON_PTR) {
- return NULL;
- }
-
- return data_field + (long)ptr;
-}
-
-int ustcomm_pack_single_field(struct ustcomm_header *header,
- struct ustcomm_single_field *single_field,
- const char *string)
-{
- int offset = 0;
-
- single_field->field = ustcomm_print_data(single_field->data,
- sizeof(single_field->data),
- &offset,
- string);
-
- if (single_field->field == USTCOMM_POISON_PTR) {
- return -ENOMEM;
- }
-
- header->size = COMPUTE_MSG_SIZE(single_field, offset);
-
- return 0;
-}
-
-int ustcomm_unpack_single_field(struct ustcomm_single_field *single_field)
-{
- single_field->field = ustcomm_restore_ptr(single_field->field,
- single_field->data,
- sizeof(single_field->data));
- if (!single_field->field) {
- return -EINVAL;
- }
-
- return 0;
-}
-
-int ustcomm_pack_channel_info(struct ustcomm_header *header,
- struct ustcomm_channel_info *ch_inf,
- const char *trace,
- const char *channel)
-{
- int offset = 0;
-
- ch_inf->trace = ustcomm_print_data(ch_inf->data,
- sizeof(ch_inf->data),
- &offset,
- trace);
-
- if (ch_inf->trace == USTCOMM_POISON_PTR) {
- return -ENOMEM;
- }
-
- ch_inf->channel = ustcomm_print_data(ch_inf->data,
- sizeof(ch_inf->data),
- &offset,
- channel);
-
- if (ch_inf->channel == USTCOMM_POISON_PTR) {
- return -ENOMEM;
- }
-
- header->size = COMPUTE_MSG_SIZE(ch_inf, offset);
-
- return 0;
-}
-
-
-int ustcomm_unpack_channel_info(struct ustcomm_channel_info *ch_inf)
-{
- ch_inf->trace = ustcomm_restore_ptr(ch_inf->trace,
- ch_inf->data,
- sizeof(ch_inf->data));
- if (!ch_inf->trace) {
- return -EINVAL;
- }
-
- ch_inf->channel = ustcomm_restore_ptr(ch_inf->channel,
- ch_inf->data,
- sizeof(ch_inf->data));
- if (!ch_inf->channel) {
- return -EINVAL;
- }
-
- return 0;
-}
-
-int ustcomm_pack_buffer_info(struct ustcomm_header *header,
- struct ustcomm_buffer_info *buf_inf,
- const char *trace,
- const char *channel,
- int channel_cpu)
-{
- int offset = 0;
-
- buf_inf->trace = ustcomm_print_data(buf_inf->data,
- sizeof(buf_inf->data),
- &offset,
- trace);
-
- if (buf_inf->trace == USTCOMM_POISON_PTR) {
- return -ENOMEM;
- }
-
- buf_inf->channel = ustcomm_print_data(buf_inf->data,
- sizeof(buf_inf->data),
- &offset,
- channel);
-
- if (buf_inf->channel == USTCOMM_POISON_PTR) {
- return -ENOMEM;
- }
-
- buf_inf->ch_cpu = channel_cpu;
-
- header->size = COMPUTE_MSG_SIZE(buf_inf, offset);
-
- return 0;
-}
-
-
-int ustcomm_unpack_buffer_info(struct ustcomm_buffer_info *buf_inf)
-{
- buf_inf->trace = ustcomm_restore_ptr(buf_inf->trace,
- buf_inf->data,
- sizeof(buf_inf->data));
- if (!buf_inf->trace) {
- return -EINVAL;
- }
-
- buf_inf->channel = ustcomm_restore_ptr(buf_inf->channel,
- buf_inf->data,
- sizeof(buf_inf->data));
- if (!buf_inf->channel) {
- return -EINVAL;
- }
-
- return 0;
-}
-
-int ustcomm_pack_ust_marker_info(struct ustcomm_header *header,
- struct ustcomm_ust_marker_info *ust_marker_inf,
- const char *trace,
- const char *channel,
- const char *ust_marker)
-{
- int offset = 0;
-
- ust_marker_inf->trace = ustcomm_print_data(ust_marker_inf->data,
- sizeof(ust_marker_inf->data),
- &offset,
- trace);
-
- if (ust_marker_inf->trace == USTCOMM_POISON_PTR) {
- return -ENOMEM;
- }
-
-
- ust_marker_inf->channel = ustcomm_print_data(ust_marker_inf->data,
- sizeof(ust_marker_inf->data),
- &offset,
- channel);
-
- if (ust_marker_inf->channel == USTCOMM_POISON_PTR) {
- return -ENOMEM;
- }
-
-
- ust_marker_inf->ust_marker = ustcomm_print_data(ust_marker_inf->data,
- sizeof(ust_marker_inf->data),
- &offset,
- ust_marker);
-
- if (ust_marker_inf->ust_marker == USTCOMM_POISON_PTR) {
- return -ENOMEM;
- }
-
- header->size = COMPUTE_MSG_SIZE(ust_marker_inf, offset);
-
- return 0;
-}
-
-int ustcomm_unpack_ust_marker_info(struct ustcomm_ust_marker_info *ust_marker_inf)
-{
- ust_marker_inf->trace = ustcomm_restore_ptr(ust_marker_inf->trace,
- ust_marker_inf->data,
- sizeof(ust_marker_inf->data));
- if (!ust_marker_inf->trace) {
- return -EINVAL;
- }
-
- ust_marker_inf->channel = ustcomm_restore_ptr(ust_marker_inf->channel,
- ust_marker_inf->data,
- sizeof(ust_marker_inf->data));
- if (!ust_marker_inf->channel) {
- return -EINVAL;
- }
-
- ust_marker_inf->ust_marker = ustcomm_restore_ptr(ust_marker_inf->ust_marker,
- ust_marker_inf->data,
- sizeof(ust_marker_inf->data));
- if (!ust_marker_inf->ust_marker) {
- return -EINVAL;
- }
-
- return 0;
-}
-
+++ /dev/null
-/* Copyright (C) 2009 Pierre-Marc Fournier
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef USTCOMM_H
-#define USTCOMM_H
-
-#include <sys/types.h>
-#include <sys/un.h>
-#include <urcu/list.h>
-
-#include <ust/kcompat/kcompat.h>
-
-#define SOCK_DIR "/tmp/ust-app-socks"
-#define USER_TMP_DIR "/tmp"
-#define USER_SOCK_DIR_BASE "ust-socks-"
-#define USER_SOCK_DIR USER_TMP_DIR "/" USER_SOCK_DIR_BASE
-
-struct ustcomm_sock {
- struct cds_list_head list;
- int fd;
- int epoll_fd;
-};
-
-struct ustcomm_header {
- int command;
- long size;
- int result;
- int fd_included;
-};
-
-#define USTCOMM_BUFFER_SIZE ((1 << 12) - sizeof(struct ustcomm_header))
-
-/* Specify a sata size that leaves margin at the end of a buffer
- * in order to make sure that we never have more data than
- * will fit in the buffer AND that the last chars (due to a
- * pre-receive memset) will always be 0, terminating any string
- */
-#define USTCOMM_DATA_SIZE (USTCOMM_BUFFER_SIZE - 20 * sizeof(void *))
-
-enum tracectl_commands {
- ALLOC_TRACE,
- CONSUME_BUFFER,
- CREATE_TRACE,
- DESTROY_TRACE,
- DISABLE_MARKER,
- ENABLE_MARKER,
- EXIT,
- FORCE_SUBBUF_SWITCH,
- GET_BUF_SHMID_PIPE_FD,
- GET_PIDUNIQUE,
- GET_SOCK_PATH,
- GET_SUBBUFFER,
- GET_SUBBUF_NUM_SIZE,
- LIST_MARKERS,
- LIST_TRACE_EVENTS,
- LOAD_PROBE_LIB,
- NOTIFY_BUF_MAPPED,
- PRINT_MARKERS,
- PRINT_TRACE_EVENTS,
- PUT_SUBBUFFER,
- SETUP_TRACE,
- SET_SOCK_PATH,
- SET_SUBBUF_NUM,
- SET_SUBBUF_SIZE,
- START,
- START_TRACE,
- STOP_TRACE,
-};
-
-struct ustcomm_single_field {
- char *field;
- char data[USTCOMM_DATA_SIZE];
-};
-
-struct ustcomm_channel_info {
- char *trace;
- char *channel;
- unsigned int subbuf_size;
- unsigned int subbuf_num;
- char data[USTCOMM_DATA_SIZE];
-};
-
-struct ustcomm_buffer_info {
- char *trace;
- char *channel;
- int ch_cpu;
- pid_t pid;
- int buf_shmid;
- int buf_struct_shmid;
- long consumed_old;
- char data[USTCOMM_DATA_SIZE];
-};
-
-struct ustcomm_ust_marker_info {
- char *trace;
- char *channel;
- char *ust_marker;
- char data[USTCOMM_DATA_SIZE];
-};
-
-struct ustcomm_pidunique {
- s64 pidunique;
-};
-
-struct ustcomm_notify_buf_mapped {
- char data[USTCOMM_DATA_SIZE];
-};
-
-/* Ensure directory existence, usefull for unix sockets */
-extern int ensure_dir_exists(const char *dir, mode_t mode);
-
-/* Create and delete sockets */
-extern struct ustcomm_sock * ustcomm_init_sock(int fd, int epoll_fd,
- struct cds_list_head *list);
-extern void ustcomm_del_sock(struct ustcomm_sock *sock, int keep_in_epoll);
-
-/* Create and delete named sockets */
-extern struct ustcomm_sock * ustcomm_init_named_socket(const char *name,
- int epoll_fd);
-extern void ustcomm_del_named_sock(struct ustcomm_sock *sock,
- int keep_socket_file);
-
-/* Send and receive functions for file descriptors */
-extern int ustcomm_send_fd(int sock, const struct ustcomm_header *header,
- const char *data, int *fd);
-extern int ustcomm_recv_fd(int sock, struct ustcomm_header *header,
- char *data, int *fd);
-
-/* Normal send and receive functions */
-extern int ustcomm_send(int sock, const struct ustcomm_header *header,
- const char *data);
-extern int ustcomm_recv(int sock, struct ustcomm_header *header,
- char *data);
-
-/* Receive and allocate data, not to be used inside libust */
-extern int ustcomm_recv_alloc(int sock,
- struct ustcomm_header *header,
- char **data);
-
-/* Request function, send and receive */
-extern int ustcomm_req(int sock,
- const struct ustcomm_header *req_header,
- const char *req_data,
- struct ustcomm_header *res_header,
- char *res_data);
-
-extern int ustcomm_request_consumer(pid_t pid, const char *channel);
-
-/* Returns the current users socket directory, must be freed */
-extern char *ustcomm_user_sock_dir(void);
-
-/* Get the st_m_time from proc*/
-extern time_t ustcomm_pid_st_mtime(pid_t pid);
-
-/* Check that a socket is live */
-extern int ustcomm_is_socket_live(char *sock_name, pid_t *read_pid);
-
-extern int ustcomm_connect_app(pid_t pid, int *app_fd);
-extern int ustcomm_connect_path(const char *path, int *connection_fd);
-
-extern int nth_token_is(const char *str, const char *token, int tok_no);
-
-extern char *nth_token(const char *str, int tok_no);
-
-/* String serialising functions, printf straight into a buffer */
-#define USTCOMM_POISON_PTR (void *)0x19831018
-
-extern char * ustcomm_print_data(char *data_field, int field_size,
- int *offset, const char *format, ...);
-extern char * ustcomm_restore_ptr(char *ptr, char *data_field,
- int data_field_size);
-
-#define COMPUTE_MSG_SIZE(struct_ptr, offset) \
- (size_t) (long)(struct_ptr)->data - (long)(struct_ptr) + (offset)
-
-/* Packing and unpacking functions, making life easier */
-extern int ustcomm_pack_single_field(struct ustcomm_header *header,
- struct ustcomm_single_field *sf,
- const char *trace);
-
-extern int ustcomm_unpack_single_field(struct ustcomm_single_field *sf);
-
-extern int ustcomm_pack_channel_info(struct ustcomm_header *header,
- struct ustcomm_channel_info *ch_inf,
- const char *trace,
- const char *channel);
-
-extern int ustcomm_unpack_channel_info(struct ustcomm_channel_info *ch_inf);
-
-extern int ustcomm_pack_buffer_info(struct ustcomm_header *header,
- struct ustcomm_buffer_info *buf_inf,
- const char *trace,
- const char *channel,
- int channel_cpu);
-
-extern int ustcomm_unpack_buffer_info(struct ustcomm_buffer_info *buf_inf);
-
-extern int ustcomm_pack_ust_marker_info(struct ustcomm_header *header,
- struct ustcomm_ust_marker_info *ust_marker_inf,
- const char *trace,
- const char *channel,
- const char *ust_marker);
-
-extern int ustcomm_unpack_ust_marker_info(struct ustcomm_ust_marker_info *ust_marker_inf);
-
-#endif /* USTCOMM_H */
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/libust -I$(top_srcdir)/libustcomm \
- -I$(top_srcdir)/include
-AM_CFLAGS = -fno-strict-aliasing
-
-lib_LTLIBRARIES = libustconsumer.la
-
-libustconsumer_la_SOURCES = libustconsumer.c lowlevel.c lowlevel.h
-
-libustconsumer_la_LDFLAGS = -no-undefined -version-info 0:0:0
-
-libustconsumer_la_LIBADD = \
- -lpthread \
- $(top_builddir)/snprintf/libustsnprintf.la \
- $(top_builddir)/libustcomm/libustcomm.la
-
-libustconsumer_la_CFLAGS = -fno-strict-aliasing
+++ /dev/null
-/* Copyright (C) 2009 Pierre-Marc Fournier
- * 2010 Alexis Halle
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define _GNU_SOURCE
-
-#include <sys/epoll.h>
-#include <sys/shm.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <signal.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#include <ust/ustconsumer.h>
-#include "lowlevel.h"
-#include "usterr_signal_safe.h"
-#include "ustcomm.h"
-
-#define GET_SUBBUF_OK 1
-#define GET_SUBBUF_DONE 0
-#define GET_SUBBUF_DIED 2
-
-#define PUT_SUBBUF_OK 1
-#define PUT_SUBBUF_DIED 0
-#define PUT_SUBBUF_PUSHED 2
-#define PUT_SUBBUF_DONE 3
-
-#define UNIX_PATH_MAX 108
-
-static int get_subbuffer(struct buffer_info *buf)
-{
- struct ustcomm_header _send_hdr, *send_hdr;
- struct ustcomm_header _recv_hdr, *recv_hdr;
- struct ustcomm_buffer_info _send_msg, _recv_msg;
- struct ustcomm_buffer_info *send_msg, *recv_msg;
- int result;
-
- send_hdr = &_send_hdr;
- recv_hdr = &_recv_hdr;
- send_msg = &_send_msg;
- recv_msg = &_recv_msg;
-
- result = ustcomm_pack_buffer_info(send_hdr, send_msg, buf->trace,
- buf->channel, buf->channel_cpu);
- if (result < 0) {
- return result;
- }
-
- send_hdr->command = GET_SUBBUFFER;
-
- result = ustcomm_req(buf->app_sock, send_hdr, (char *)send_msg,
- recv_hdr, (char *)recv_msg);
- if ((result < 0 && (errno == ECONNRESET || errno == EPIPE)) ||
- result == 0) {
- DBG("app died while being traced");
- return GET_SUBBUF_DIED;
- } else if (result < 0) {
- ERR("get_subbuffer: ustcomm_req failed");
- return result;
- }
-
- if (!recv_hdr->result) {
- DBG("got subbuffer %s", buf->name);
- buf->consumed_old = recv_msg->consumed_old;
- return GET_SUBBUF_OK;
- } else if (recv_hdr->result == -ENODATA) {
- DBG("For buffer %s, the trace was not found. This likely means"
- " it was destroyed by the user.", buf->name);
- return GET_SUBBUF_DIED;
- }
-
- DBG("error getting subbuffer %s", buf->name);
- return recv_hdr->result;
-}
-
-static int put_subbuffer(struct buffer_info *buf)
-{
- struct ustcomm_header _send_hdr, *send_hdr;
- struct ustcomm_header _recv_hdr, *recv_hdr;
- struct ustcomm_buffer_info _send_msg, *send_msg;
- int result;
-
- send_hdr = &_send_hdr;
- recv_hdr = &_recv_hdr;
- send_msg = &_send_msg;
-
- result = ustcomm_pack_buffer_info(send_hdr, send_msg, buf->trace,
- buf->channel, buf->channel_cpu);
- if (result < 0) {
- return result;
- }
-
- send_hdr->command = PUT_SUBBUFFER;
- send_msg->consumed_old = buf->consumed_old;
-
- result = ustcomm_req(buf->app_sock, send_hdr, (char *)send_msg,
- recv_hdr, NULL);
- if ((result < 0 && (errno == ECONNRESET || errno == EPIPE)) ||
- result == 0) {
- DBG("app died while being traced");
- return PUT_SUBBUF_DIED;
- } else if (result < 0) {
- ERR("put_subbuffer: ustcomm_req failed");
- return result;
- }
-
- if (!recv_hdr->result) {
- DBG("put subbuffer %s", buf->name);
- return PUT_SUBBUF_OK;
- } else if (recv_hdr->result == -ENODATA) {
- DBG("For buffer %s, the trace was not found. This likely means"
- " it was destroyed by the user.", buf->name);
- return PUT_SUBBUF_DIED;
- }
-
- DBG("error getting subbuffer %s", buf->name);
- return recv_hdr->result;
-}
-
-void decrement_active_buffers(void *arg)
-{
- struct ustconsumer_instance *instance = arg;
- pthread_mutex_lock(&instance->mutex);
- instance->active_buffers--;
- pthread_mutex_unlock(&instance->mutex);
-}
-
-static int get_pidunique(int sock, int64_t *pidunique)
-{
- struct ustcomm_header _send_hdr, *send_hdr;
- struct ustcomm_header _recv_hdr, *recv_hdr;
- struct ustcomm_pidunique _recv_msg, *recv_msg;
- int result;
-
- send_hdr = &_send_hdr;
- recv_hdr = &_recv_hdr;
- recv_msg = &_recv_msg;
-
- memset(send_hdr, 0, sizeof(*send_hdr));
-
- send_hdr->command = GET_PIDUNIQUE;
- result = ustcomm_req(sock, send_hdr, NULL, recv_hdr, (char *)recv_msg);
- if (result < 1) {
- return -ENOTCONN;
- }
- if (recv_hdr->result < 0) {
- ERR("App responded with error: %s", strerror(recv_hdr->result));
- return recv_hdr->result;
- }
-
- *pidunique = recv_msg->pidunique;
-
- return 0;
-}
-
-static int get_buf_shmid_pipe_fd(int sock, struct buffer_info *buf,
- int *buf_shmid, int *buf_struct_shmid,
- int *buf_pipe_fd)
-{
- struct ustcomm_header _send_hdr, *send_hdr;
- struct ustcomm_header _recv_hdr, *recv_hdr;
- struct ustcomm_buffer_info _send_msg, *send_msg;
- struct ustcomm_buffer_info _recv_msg, *recv_msg;
- int result, recv_pipe_fd;
-
- send_hdr = &_send_hdr;
- recv_hdr = &_recv_hdr;
- send_msg = &_send_msg;
- recv_msg = &_recv_msg;
-
- result = ustcomm_pack_buffer_info(send_hdr, send_msg, buf->trace,
- buf->channel, buf->channel_cpu);
- if (result < 0) {
- ERR("Failed to pack buffer info");
- return result;
- }
-
- send_hdr->command = GET_BUF_SHMID_PIPE_FD;
-
- result = ustcomm_send(sock, send_hdr, (char *)send_msg);
- if (result < 1) {
- ERR("Failed to send request");
- return -ENOTCONN;
- }
- result = ustcomm_recv_fd(sock, recv_hdr, (char *)recv_msg, &recv_pipe_fd);
- if (result < 1) {
- ERR("Failed to receive message and fd");
- return -ENOTCONN;
- }
- if (recv_hdr->result < 0) {
- ERR("App responded with error %s", strerror(recv_hdr->result));
- return recv_hdr->result;
- }
-
- *buf_shmid = recv_msg->buf_shmid;
- *buf_struct_shmid = recv_msg->buf_struct_shmid;
- *buf_pipe_fd = recv_pipe_fd;
-
- return 0;
-}
-
-static int get_subbuf_num_size(int sock, struct buffer_info *buf,
- int *subbuf_num, int *subbuf_size)
-{
- struct ustcomm_header _send_hdr, *send_hdr;
- struct ustcomm_header _recv_hdr, *recv_hdr;
- struct ustcomm_channel_info _send_msg, *send_msg;
- struct ustcomm_channel_info _recv_msg, *recv_msg;
- int result;
-
- send_hdr = &_send_hdr;
- recv_hdr = &_recv_hdr;
- send_msg = &_send_msg;
- recv_msg = &_recv_msg;
-
- result = ustcomm_pack_channel_info(send_hdr, send_msg, buf->trace,
- buf->channel);
- if (result < 0) {
- return result;
- }
-
- send_hdr->command = GET_SUBBUF_NUM_SIZE;
-
- result = ustcomm_req(sock, send_hdr, (char *)send_msg,
- recv_hdr, (char *)recv_msg);
- if (result < 1) {
- return -ENOTCONN;
- }
-
- *subbuf_num = recv_msg->subbuf_num;
- *subbuf_size = recv_msg->subbuf_size;
-
- return recv_hdr->result;
-}
-
-
-static int notify_buffer_mapped(int sock, struct buffer_info *buf)
-{
- struct ustcomm_header _send_hdr, *send_hdr;
- struct ustcomm_header _recv_hdr, *recv_hdr;
- struct ustcomm_buffer_info _send_msg, *send_msg;
- int result;
-
- send_hdr = &_send_hdr;
- recv_hdr = &_recv_hdr;
- send_msg = &_send_msg;
-
- result = ustcomm_pack_buffer_info(send_hdr, send_msg, buf->trace,
- buf->channel, buf->channel_cpu);
- if (result < 0) {
- return result;
- }
-
- send_hdr->command = NOTIFY_BUF_MAPPED;
-
- result = ustcomm_req(sock, send_hdr, (char *)send_msg,
- recv_hdr, NULL);
- if (result < 1) {
- return -ENOTCONN;
- }
-
- return recv_hdr->result;
-}
-
-
-struct buffer_info *connect_buffer(struct ustconsumer_instance *instance, pid_t pid,
- const char *trace, const char *channel,
- int channel_cpu)
-{
- struct buffer_info *buf;
- int result;
- struct shmid_ds shmds;
-
- buf = (struct buffer_info *) zmalloc(sizeof(struct buffer_info));
- if(buf == NULL) {
- ERR("add_buffer: insufficient memory");
- return NULL;
- }
-
- buf->trace = strdup(trace);
- if (!buf->trace) {
- goto free_buf;
- }
-
- buf->channel = strdup(channel);
- if (!buf->channel) {
- goto free_buf_trace;
- }
-
- result = asprintf(&buf->name, "%s_%d", channel, channel_cpu);
- if (result < 0 || buf->name == NULL) {
- goto free_buf_channel;
- }
-
- buf->channel_cpu = channel_cpu;
- buf->pid = pid;
-
- result = ustcomm_connect_app(buf->pid, &buf->app_sock);
- if(result) {
- WARN("unable to connect to process, it probably died before we were able to connect");
- goto free_buf_name;
- }
-
- /* get pidunique */
- result = get_pidunique(buf->app_sock, &buf->pidunique);
- if (result < 0) {
- ERR("Failed to get pidunique");
- goto close_app_sock;
- }
-
- /* get shmid and pipe fd */
- result = get_buf_shmid_pipe_fd(buf->app_sock, buf, &buf->shmid,
- &buf->bufstruct_shmid, &buf->pipe_fd);
- if (result < 0) {
- ERR("Failed to get buf_shmid and pipe_fd");
- goto close_app_sock;
- } else {
- struct stat temp;
- fstat(buf->pipe_fd, &temp);
- if (!S_ISFIFO(temp.st_mode)) {
- ERR("Didn't receive a fifo from the app");
- goto close_app_sock;
- }
- }
-
-
- /* get number of subbufs and subbuf size */
- result = get_subbuf_num_size(buf->app_sock, buf, &buf->n_subbufs,
- &buf->subbuf_size);
- if (result < 0) {
- ERR("Failed to get subbuf number and size");
- goto close_fifo;
- }
-
- /* Set subbuffer's information */
- buf->subbuf_size_order = get_count_order(buf->subbuf_size);
- buf->alloc_size = buf->subbuf_size * buf->n_subbufs;
-
- /* attach memory */
- buf->mem = shmat(buf->shmid, NULL, 0);
- if(buf->mem == (void *) 0) {
- PERROR("shmat");
- goto close_fifo;
- }
- DBG("successfully attached buffer memory");
-
- buf->bufstruct_mem = shmat(buf->bufstruct_shmid, NULL, 0);
- if(buf->bufstruct_mem == (void *) 0) {
- PERROR("shmat");
- goto shmdt_mem;
- }
- DBG("successfully attached buffer bufstruct memory");
-
- /* obtain info on the memory segment */
- result = shmctl(buf->shmid, IPC_STAT, &shmds);
- if(result == -1) {
- PERROR("shmctl");
- goto shmdt_bufstruct_mem;
- }
- buf->memlen = shmds.shm_segsz;
-
- /* Notify the application that we have mapped the buffer */
- result = notify_buffer_mapped(buf->app_sock, buf);
- if (result < 0) {
- goto shmdt_bufstruct_mem;
- }
-
- if(instance->callbacks->on_open_buffer)
- instance->callbacks->on_open_buffer(instance->callbacks, buf);
-
- pthread_mutex_lock(&instance->mutex);
- instance->active_buffers++;
- pthread_mutex_unlock(&instance->mutex);
-
- return buf;
-
-shmdt_bufstruct_mem:
- shmdt(buf->bufstruct_mem);
-
-shmdt_mem:
- shmdt(buf->mem);
-
-close_fifo:
- close(buf->pipe_fd);
-
-close_app_sock:
- close(buf->app_sock);
-
-free_buf_name:
- free(buf->name);
-
-free_buf_channel:
- free(buf->channel);
-
-free_buf_trace:
- free(buf->trace);
-
-free_buf:
- free(buf);
- return NULL;
-}
-
-static void destroy_buffer(struct ustconsumer_callbacks *callbacks,
- struct buffer_info *buf)
-{
- int result;
-
- result = close(buf->pipe_fd);
- if(result == -1) {
- WARN("problem closing the pipe fd");
- }
-
- result = close(buf->app_sock);
- if(result == -1) {
- WARN("problem calling ustcomm_close_app");
- }
-
- result = shmdt(buf->mem);
- if(result == -1) {
- PERROR("shmdt");
- }
-
- result = shmdt(buf->bufstruct_mem);
- if(result == -1) {
- PERROR("shmdt");
- }
-
- if(callbacks->on_close_buffer)
- callbacks->on_close_buffer(callbacks, buf);
-
- free(buf);
-}
-
-int consumer_loop(struct ustconsumer_instance *instance, struct buffer_info *buf)
-{
- int result = 0;
- int read_result;
- char read_buf;
-
- pthread_cleanup_push(decrement_active_buffers, instance);
-
- for(;;) {
- read_result = read(buf->pipe_fd, &read_buf, 1);
- /* get the subbuffer */
- if (read_result == 1) {
- result = get_subbuffer(buf);
- if (result < 0) {
- ERR("error getting subbuffer");
- continue;
- } else if (result == GET_SUBBUF_DIED) {
- finish_consuming_dead_subbuffer(instance->callbacks, buf);
- break;
- }
- } else if ((read_result == -1 && (errno == ECONNRESET || errno == EPIPE)) ||
- result == 0) {
- DBG("App died while being traced");
- finish_consuming_dead_subbuffer(instance->callbacks, buf);
- break;
- } else if (read_result == -1 && errno == EINTR) {
- continue;
- }
-
- if(instance->callbacks->on_read_subbuffer)
- instance->callbacks->on_read_subbuffer(instance->callbacks, buf);
-
- /* put the subbuffer */
- result = put_subbuffer(buf);
- if(result == -1) {
- ERR("unknown error putting subbuffer (channel=%s)", buf->name);
- break;
- }
- else if(result == PUT_SUBBUF_PUSHED) {
- ERR("Buffer overflow (channel=%s), reader pushed. This channel will not be usable passed this point.", buf->name);
- break;
- }
- else if(result == PUT_SUBBUF_DIED) {
- DBG("application died while putting subbuffer");
- /* Skip the first subbuffer. We are not sure it is trustable
- * because the put_subbuffer() did not complete.
- */
- /* TODO: check on_put_error return value */
- if(instance->callbacks->on_put_error)
- instance->callbacks->on_put_error(instance->callbacks, buf);
-
- finish_consuming_dead_subbuffer(instance->callbacks, buf);
- break;
- }
- else if(result == PUT_SUBBUF_DONE) {
- /* Done with this subbuffer */
- /* FIXME: add a case where this branch is used? Upon
- * normal trace termination, at put_subbuf time, a
- * special last-subbuffer code could be returned by
- * the listener.
- */
- break;
- }
- else if(result == PUT_SUBBUF_OK) {
- }
- }
-
- DBG("thread for buffer %s is stopping", buf->name);
-
- /* FIXME: destroy, unalloc... */
-
- pthread_cleanup_pop(1);
-
- return 0;
-}
-
-struct consumer_thread_args {
- pid_t pid;
- const char *trace;
- const char *channel;
- int channel_cpu;
- struct ustconsumer_instance *instance;
-};
-
-void *consumer_thread(void *arg)
-{
- struct buffer_info *buf;
- struct consumer_thread_args *args = (struct consumer_thread_args *) arg;
- int result;
- sigset_t sigset;
-
- pthread_mutex_lock(&args->instance->mutex);
- args->instance->active_threads++;
- pthread_mutex_unlock(&args->instance->mutex);
-
- if(args->instance->callbacks->on_new_thread)
- args->instance->callbacks->on_new_thread(args->instance->callbacks);
-
- /* Block signals that should be handled by the main thread. */
- result = sigemptyset(&sigset);
- if(result == -1) {
- PERROR("sigemptyset");
- goto end;
- }
- result = sigaddset(&sigset, SIGTERM);
- if(result == -1) {
- PERROR("sigaddset");
- goto end;
- }
- result = sigaddset(&sigset, SIGINT);
- if(result == -1) {
- PERROR("sigaddset");
- goto end;
- }
- result = sigprocmask(SIG_BLOCK, &sigset, NULL);
- if(result == -1) {
- PERROR("sigprocmask");
- goto end;
- }
-
- buf = connect_buffer(args->instance, args->pid, args->trace,
- args->channel, args->channel_cpu);
- if(buf == NULL) {
- ERR("failed to connect to buffer");
- goto end;
- }
-
- consumer_loop(args->instance, buf);
-
- destroy_buffer(args->instance->callbacks, buf);
-
- end:
-
- if(args->instance->callbacks->on_close_thread)
- args->instance->callbacks->on_close_thread(args->instance->callbacks);
-
- pthread_mutex_lock(&args->instance->mutex);
- args->instance->active_threads--;
- pthread_mutex_unlock(&args->instance->mutex);
-
- free((void *)args->channel);
- free(args);
- return NULL;
-}
-
-int start_consuming_buffer(struct ustconsumer_instance *instance, pid_t pid,
- const char *trace, const char *channel,
- int channel_cpu)
-{
- pthread_t thr;
- struct consumer_thread_args *args;
- int result;
-
- DBG("beginning of start_consuming_buffer: args: pid %d bufname %s_%d", pid, channel,
- channel_cpu);
-
- args = (struct consumer_thread_args *) zmalloc(sizeof(struct consumer_thread_args));
- if (!args) {
- return -ENOMEM;
- }
-
- args->pid = pid;
- args->trace = strdup(trace);
- args->channel = strdup(channel);
- args->channel_cpu = channel_cpu;
- args->instance = instance;
- DBG("beginning2 of start_consuming_buffer: args: pid %d trace %s"
- " bufname %s_%d", args->pid, args->trace, args->channel, args->channel_cpu);
-
- result = pthread_create(&thr, NULL, consumer_thread, args);
- if(result == -1) {
- ERR("pthread_create failed");
- return -1;
- }
- result = pthread_detach(thr);
- if(result == -1) {
- ERR("pthread_detach failed");
- return -1;
- }
- DBG("end of start_consuming_buffer: args: pid %d trace %s "
- "bufname %s_%d", args->pid, args->channel, args->trace, args->channel_cpu);
-
- return 0;
-}
-static void process_client_cmd(int sock, struct ustcomm_header *req_header,
- char *recvbuf, struct ustconsumer_instance *instance)
-{
- int result;
- struct ustcomm_header _res_header = {0};
- struct ustcomm_header *res_header = &_res_header;
- struct ustcomm_buffer_info *buf_inf;
-
- DBG("Processing client command");
-
- switch (req_header->command) {
- case CONSUME_BUFFER:
-
- buf_inf = (struct ustcomm_buffer_info *)recvbuf;
- result = ustcomm_unpack_buffer_info(buf_inf);
- if (result < 0) {
- ERR("Couldn't unpack buffer info");
- return;
- }
-
- DBG("Going to consume trace %s buffer %s_%d in process %d",
- buf_inf->trace, buf_inf->channel, buf_inf->ch_cpu,
- buf_inf->pid);
- result = start_consuming_buffer(instance, buf_inf->pid,
- buf_inf->trace,
- buf_inf->channel,
- buf_inf->ch_cpu);
- if (result < 0) {
- ERR("error in add_buffer");
- return;
- }
-
- res_header->result = 0;
- break;
- case EXIT:
- res_header->result = 0;
- /* Only there to force poll to return */
- break;
- default:
- res_header->result = -EINVAL;
- WARN("unknown command: %d", req_header->command);
- }
-
- if (ustcomm_send(sock, res_header, NULL) <= 0) {
- ERR("couldn't send command response");
- }
-}
-
-#define MAX_EVENTS 10
-
-int ustconsumer_start_instance(struct ustconsumer_instance *instance)
-{
- struct ustcomm_header recv_hdr;
- char recv_buf[USTCOMM_BUFFER_SIZE];
- struct ustcomm_sock *epoll_sock;
- struct epoll_event events[MAX_EVENTS];
- struct sockaddr addr;
- int result, epoll_fd, accept_fd, nfds, i, addr_size, timeout;
-
- if(!instance->is_init) {
- ERR("libustconsumer instance not initialized");
- return 1;
- }
- epoll_fd = instance->epoll_fd;
-
- timeout = -1;
-
- /* app loop */
- for(;;) {
- nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, timeout);
- if (nfds == -1 && errno == EINTR) {
- /* Caught signal */
- } else if (nfds == -1) {
- PERROR("ustconsumer_start_instance: epoll_wait failed");
- continue;
- }
-
- for (i = 0; i < nfds; ++i) {
- epoll_sock = (struct ustcomm_sock *)events[i].data.ptr;
- if (epoll_sock == instance->listen_sock) {
- addr_size = sizeof(struct sockaddr);
- accept_fd = accept(epoll_sock->fd,
- &addr,
- (socklen_t *)&addr_size);
- if (accept_fd == -1) {
- PERROR("ustconsumer_start_instance: "
- "accept failed");
- continue;
- }
- ustcomm_init_sock(accept_fd, epoll_fd,
- &instance->connections);
- } else {
- result = ustcomm_recv(epoll_sock->fd, &recv_hdr,
- recv_buf);
- if (result < 1) {
- ustcomm_del_sock(epoll_sock, 0);
- } else {
- process_client_cmd(epoll_sock->fd,
- &recv_hdr, recv_buf,
- instance);
- }
-
- }
- }
-
- if (instance->quit_program) {
- pthread_mutex_lock(&instance->mutex);
- if (instance->active_buffers == 0 && instance->active_threads == 0) {
- pthread_mutex_unlock(&instance->mutex);
- break;
- }
- pthread_mutex_unlock(&instance->mutex);
- timeout = 100;
- }
- }
-
- if(instance->callbacks->on_trace_end)
- instance->callbacks->on_trace_end(instance);
-
- ustconsumer_delete_instance(instance);
-
- return 0;
-}
-
-/* FIXME: threads and connections !? */
-void ustconsumer_delete_instance(struct ustconsumer_instance *instance)
-{
- if (instance->is_init) {
- ustcomm_del_named_sock(instance->listen_sock, 0);
- close(instance->epoll_fd);
- }
-
- pthread_mutex_destroy(&instance->mutex);
- free(instance->sock_path);
- free(instance);
-}
-
-/* FIXME: Do something about the fixed path length, maybe get rid
- * of the whole concept and use a pipe?
- */
-int ustconsumer_stop_instance(struct ustconsumer_instance *instance, int send_msg)
-{
- int result;
- int fd;
- int bytes = 0;
-
- char msg[] = "exit";
-
- instance->quit_program = 1;
-
- if(!send_msg)
- return 0;
-
- /* Send a message through the socket to force poll to return */
-
- struct sockaddr_un addr;
-
-socket_again:
- result = fd = socket(PF_UNIX, SOCK_STREAM, 0);
- if(result == -1) {
- if (errno == EINTR)
- goto socket_again;
- PERROR("socket");
- return 1;
- }
-
- addr.sun_family = AF_UNIX;
-
- strncpy(addr.sun_path, instance->sock_path, UNIX_PATH_MAX);
- addr.sun_path[UNIX_PATH_MAX-1] = '\0';
-
-connect_again:
- result = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
- if(result == -1) {
- if (errno == EINTR)
- goto connect_again;
- PERROR("connect");
- }
-
- while(bytes != sizeof(msg)) {
- int inc = send(fd, msg, sizeof(msg), 0);
- if (inc < 0 && errno != EINTR)
- break;
- else
- bytes += inc;
- }
-
- close(fd);
-
- return 0;
-}
-
-struct ustconsumer_instance
-*ustconsumer_new_instance(struct ustconsumer_callbacks *callbacks,
- char *sock_path)
-{
- struct ustconsumer_instance *instance =
- zmalloc(sizeof(struct ustconsumer_instance));
- if(!instance) {
- return NULL;
- }
-
- instance->callbacks = callbacks;
- instance->quit_program = 0;
- instance->is_init = 0;
- instance->active_buffers = 0;
- pthread_mutex_init(&instance->mutex, NULL);
-
- if (sock_path) {
- instance->sock_path = strdup(sock_path);
- } else {
- instance->sock_path = NULL;
- }
-
- return instance;
-}
-
-static int init_ustconsumer_socket(struct ustconsumer_instance *instance)
-{
- char *name;
-
- if (instance->sock_path) {
- if (asprintf(&name, "%s", instance->sock_path) < 0) {
- ERR("ustcomm_init_ustconsumer : asprintf failed (sock_path %s)",
- instance->sock_path);
- return -1;
- }
- } else {
- int result;
-
- /* Only check if socket dir exists if we are using the default directory */
- result = ensure_dir_exists(SOCK_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
- if (result == -1) {
- ERR("Unable to create socket directory %s", SOCK_DIR);
- return -1;
- }
-
- if (asprintf(&name, "%s/%s", SOCK_DIR, "ustconsumer") < 0) {
- ERR("ustcomm_init_ustconsumer : asprintf failed (%s/ustconsumer)",
- SOCK_DIR);
- return -1;
- }
- }
-
- /* Set up epoll */
- instance->epoll_fd = epoll_create(MAX_EVENTS);
- if (instance->epoll_fd == -1) {
- ERR("epoll_create failed, start instance bailing");
- goto free_name;
- }
-
- /* Create the named socket */
- instance->listen_sock = ustcomm_init_named_socket(name,
- instance->epoll_fd);
- if(!instance->listen_sock) {
- ERR("error initializing named socket at %s", name);
- goto close_epoll;
- }
-
- CDS_INIT_LIST_HEAD(&instance->connections);
-
- free(name);
-
- return 0;
-
-close_epoll:
- close(instance->epoll_fd);
-free_name:
- free(name);
-
- return -1;
-}
-
-int ustconsumer_init_instance(struct ustconsumer_instance *instance)
-{
- int result;
- result = init_ustconsumer_socket(instance);
- if(result == -1) {
- ERR("failed to initialize socket");
- return 1;
- }
- instance->is_init = 1;
- return 0;
-}
-
+++ /dev/null
-/* Copyright (C) 2009 Pierre-Marc Fournier
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <assert.h>
-#include <byteswap.h>
-
-#include "ust/ustconsumer.h"
-#include "buffers.h"
-#include "tracer.h"
-#include "usterr_signal_safe.h"
-
-/* This truncates to an offset in the buffer. */
-#define USTD_BUFFER_TRUNC(offset, bufinfo) \
- ((offset) & (~(((bufinfo)->subbuf_size*(bufinfo)->n_subbufs)-1)))
-
-#define LTT_MAGIC_NUMBER 0x00D6B7ED
-#define LTT_REV_MAGIC_NUMBER 0xEDB7D600
-
-
-static void ltt_relay_print_subbuffer_errors(
- struct buffer_info *buf,
- long cons_off, int cpu)
-{
- struct ust_buffer *ust_buf = buf->bufstruct_mem;
- long cons_idx, commit_count, commit_count_mask, write_offset;
-
- cons_idx = SUBBUF_INDEX(cons_off, buf);
- commit_count = uatomic_read(&ust_buf->commit_seq[cons_idx]);
- commit_count_mask = (~0UL >> get_count_order(buf->n_subbufs));
-
- /*
- * No need to order commit_count and write_offset reads because we
- * execute after trace is stopped when there are no readers left.
- */
- write_offset = uatomic_read(&ust_buf->offset);
- WARN( "LTT : unread channel %s offset is %ld "
- "and cons_off : %ld (cpu %d)\n",
- buf->channel, write_offset, cons_off, cpu);
- /* Check each sub-buffer for non filled commit count */
- if (((commit_count - buf->subbuf_size) & commit_count_mask)
- - (BUFFER_TRUNC(cons_off, buf) >> get_count_order(buf->n_subbufs)) != 0) {
- ERR("LTT : %s : subbuffer %lu has non filled "
- "commit count [seq] [%lu].\n",
- buf->channel, cons_idx, commit_count);
- }
- ERR("LTT : %s : commit count : %lu, subbuf size %d\n",
- buf->channel, commit_count,
- buf->subbuf_size);
-}
-
-static void ltt_relay_print_errors(struct buffer_info *buf, int cpu)
-{
- struct ust_buffer *ust_buf = buf->bufstruct_mem;
- long cons_off;
-
- for (cons_off = uatomic_read(&ust_buf->consumed);
- (SUBBUF_TRUNC(uatomic_read(&ust_buf->offset), buf)
- - cons_off) > 0;
- cons_off = SUBBUF_ALIGN(cons_off, buf))
- ltt_relay_print_subbuffer_errors(buf, cons_off, cpu);
-}
-
-static void ltt_relay_print_buffer_errors(struct buffer_info *buf, int cpu)
-{
- struct ust_buffer *ust_buf = buf->bufstruct_mem;
-
- if (uatomic_read(&ust_buf->events_lost))
- ERR("channel %s: %ld events lost (cpu %d)",
- buf->channel,
- uatomic_read(&ust_buf->events_lost), cpu);
- if (uatomic_read(&ust_buf->corrupted_subbuffers))
- ERR("channel %s : %ld corrupted subbuffers (cpu %d)",
- buf->channel,
- uatomic_read(&ust_buf->corrupted_subbuffers), cpu);
-
- ltt_relay_print_errors(buf, cpu);
-}
-
-/* Returns the size of a subbuffer size. This is the size that
- * will need to be written to disk.
- *
- * @subbuffer: pointer to the beginning of the subbuffer (the
- * beginning of its header)
- */
-
-size_t subbuffer_data_size(void *subbuf)
-{
- struct ltt_subbuffer_header *header = subbuf;
- int reverse;
- u32 data_size;
-
- if(header->magic_number == LTT_MAGIC_NUMBER) {
- reverse = 0;
- }
- else if(header->magic_number == LTT_REV_MAGIC_NUMBER) {
- reverse = 1;
- }
- else {
- return -1;
- }
-
- data_size = header->sb_size;
- if(reverse)
- data_size = bswap_32(data_size);
-
- return data_size;
-}
-
-
-void finish_consuming_dead_subbuffer(struct ustconsumer_callbacks *callbacks, struct buffer_info *buf)
-{
- struct ust_buffer *ust_buf = buf->bufstruct_mem;
- unsigned long n_subbufs_order = get_count_order(buf->n_subbufs);
- unsigned long commit_seq_mask = (~0UL >> n_subbufs_order);
- unsigned long cons_off;
- int ret;
-
- DBG("processing dead buffer (%s)", buf->name);
- DBG("consumed offset is %ld (%s)", uatomic_read(&ust_buf->consumed),
- buf->name);
- DBG("write offset is %ld (%s)", uatomic_read(&ust_buf->offset),
- buf->name);
-
- /*
- * Iterate on subbuffers to recover, including the one the writer
- * just wrote data into. Using write position - 1 since the writer
- * position points into the position that is going to be written.
- */
- for (cons_off = uatomic_read(&ust_buf->consumed);
- (long) (SUBBUF_TRUNC(uatomic_read(&ust_buf->offset) - 1, buf)
- - cons_off) >= 0;
- cons_off = SUBBUF_ALIGN(cons_off, buf)) {
- /*
- * commit_seq is the offset in the buffer of the end of the last sequential commit.
- * Bytes beyond this limit cannot be recovered. This is a free-running counter.
- */
- unsigned long commit_seq =
- uatomic_read(&ust_buf->commit_seq[SUBBUF_INDEX(cons_off, buf)]);
- struct ltt_subbuffer_header *header =
- (struct ltt_subbuffer_header *)((char *) buf->mem
- + SUBBUF_INDEX(cons_off, buf) * buf->subbuf_size);
- unsigned long valid_length;
-
- /* Check if subbuf was fully written. This is from Mathieu's algorithm/paper. */
- if (((commit_seq - buf->subbuf_size) & commit_seq_mask)
- - (USTD_BUFFER_TRUNC(uatomic_read(&ust_buf->consumed), buf) >> n_subbufs_order) == 0
- && header->data_size != 0xffffffff) {
- assert(header->sb_size != 0xffffffff);
- /*
- * If it was fully written, we only check the data_size.
- * This is the amount of valid data at the beginning of
- * the subbuffer.
- */
- valid_length = header->data_size;
- DBG("writing full subbuffer (%ld) with valid_length = %ld",
- SUBBUF_INDEX(cons_off, buf), valid_length);
- } else {
- /*
- * If the subbuffer was not fully written, then we don't
- * check data_size because it hasn't been written yet.
- * Instead we check commit_seq and use it to choose a
- * value for data_size. The viewer will need this value
- * when parsing. Generally, this will happen only for
- * the last subbuffer. However, if we have threads still
- * holding reserved slots in the previous subbuffers,
- * which could happen for other subbuffers prior to the
- * last one. Note that when data_size is set, the
- * commit_seq count is still at a value that shows the
- * amount of valid data to read. It's only _after_
- * writing data_size that commit_seq is updated to
- * include the end-of-buffer padding.
- */
- valid_length = commit_seq & (buf->subbuf_size - 1);
- DBG("writing unfull subbuffer (%ld) with valid_length = %ld",
- SUBBUF_INDEX(cons_off, buf), valid_length);
- header->data_size = valid_length;
- header->sb_size = PAGE_ALIGN(valid_length);
- }
-
- if (callbacks->on_read_partial_subbuffer) {
- ret = callbacks->on_read_partial_subbuffer(callbacks, buf,
- SUBBUF_INDEX(cons_off, buf),
- valid_length);
- if (ret < 0)
- break; /* Error happened */
- }
- }
- /* Increment the consumed offset */
- uatomic_set(&ust_buf->consumed, cons_off);
- ltt_relay_print_buffer_errors(buf, buf->channel_cpu);
-}
-
+++ /dev/null
-/*
- * lowlevel libustd header file
- *
- * Copyright 2005-2010 -
- * Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
- * Copyright 2010-
- * Oumarou Dicko <oumarou.dicko@polymtl.ca>
- * Michael Sills-Lavoie <michael.sills-lavoie@polymtl.ca>
- * Alexis Halle <alexis.halle@polymtl.ca>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef LOWLEVEL_H
-#define LOWLEVEL_H
-
-#include "ust/ustconsumer.h"
-
-void finish_consuming_dead_subbuffer(struct ustconsumer_callbacks *callbacks, struct buffer_info *buf);
-size_t subbuffer_data_size(void *subbuf);
-
-#endif /* LOWLEVEL_H */
-
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libustcomm
-AM_CFLAGS = -fno-strict-aliasing
-
-lib_LTLIBRARIES = libustctl.la
-
-libustctl_la_SOURCES = \
- libustctl.c
-
-libustctl_la_LDFLAGS = -no-undefined -version-info 0:0:0
-
-libustctl_la_LIBADD = \
- $(top_builddir)/libustcomm/libustcomm.la
-
-libustctl_la_CFLAGS = -DUST_COMPONENT="libustctl" -fno-strict-aliasing
+++ /dev/null
-libustctl is a library that provides an API and its implementation to send
-commands to traceable processes.
+++ /dev/null
-/* Copyright (C) 2009 Pierre-Marc Fournier, Philippe Proulx-Barrette
- * Copyright (C) 2011 Ericsson AB
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <string.h>
-#include <dirent.h>
-
-#include "ustcomm.h"
-#include "ust/ustctl.h"
-#include "usterr.h"
-
-static int do_cmd(int sock,
- const struct ustcomm_header *req_header,
- const char *req_data,
- struct ustcomm_header *res_header,
- char **res_data)
-{
- int result, saved_errno = 0;
- char *recv_buf;
-
- recv_buf = zmalloc(USTCOMM_BUFFER_SIZE);
- if (!recv_buf) {
- saved_errno = ENOMEM;
- goto out;
- }
-
- result = ustcomm_req(sock, req_header, req_data, res_header, recv_buf);
- if (result > 0) {
- saved_errno = -res_header->result;
- if (res_header->size == 0 || saved_errno > 0) {
- free(recv_buf);
- } else {
- if (res_data) {
- *res_data = recv_buf;
- } else {
- free(recv_buf);
- }
- }
- } else {
- ERR("ustcomm req failed");
- if (result == 0) {
- saved_errno = ENOTCONN;
- } else {
- saved_errno = -result;
- }
- free(recv_buf);
- }
-
-out:
- errno = saved_errno;
- if (errno) {
- return -1;
- }
-
- return 0;
-}
-
-int ustctl_connect_pid(pid_t pid)
-{
- int sock;
-
- if (ustcomm_connect_app(pid, &sock)) {
- ERR("could not connect to PID %u", (unsigned int) pid);
- errno = ENOTCONN;
- return -1;
- }
-
- return sock;
-}
-
-static int realloc_pid_list(pid_t **pid_list, unsigned int *pid_list_size)
-{
- pid_t *new_pid_list;
- unsigned int new_pid_list_size = 2 * *pid_list_size;
-
- new_pid_list = realloc(*pid_list,
- new_pid_list_size * sizeof(pid_t));
- if (!*new_pid_list) {
- return -1;
- }
-
- *pid_list = new_pid_list;
- *pid_list_size = new_pid_list_size;
-
- return 0;
-}
-
-static int get_pids_in_dir(DIR *dir, pid_t **pid_list,
- unsigned int *pid_list_index,
- unsigned int *pid_list_size)
-{
- struct dirent *dirent;
- pid_t read_pid;
-
- while ((dirent = readdir(dir))) {
- if (!strcmp(dirent->d_name, ".") ||
- !strcmp(dirent->d_name, "..") ||
- !strcmp(dirent->d_name, "ust-consumer") ||
- dirent->d_type == DT_DIR) {
-
- continue;
- }
-
- if (ustcomm_is_socket_live(dirent->d_name, &read_pid)) {
-
- (*pid_list)[(*pid_list_index)++] = (long) read_pid;
-
- if (*pid_list_index == *pid_list_size) {
- if (realloc_pid_list(pid_list, pid_list_size)) {
- return -1;
- }
- }
- }
- }
-
- (*pid_list)[*pid_list_index] = 0; /* Array end */
-
- return 0;
-}
-
-static pid_t *get_pids_non_root(void)
-{
- char *dir_name;
- DIR *dir;
- unsigned int pid_list_index = 0, pid_list_size = 1;
- pid_t *pid_list = NULL;
-
- dir_name = ustcomm_user_sock_dir();
- if (!dir_name) {
- return NULL;
- }
-
- dir = opendir(dir_name);
- if (!dir) {
- goto free_dir_name;
- }
-
- pid_list = malloc(pid_list_size * sizeof(pid_t));
- if (!pid_list) {
- goto close_dir;
- }
-
- if (get_pids_in_dir(dir, &pid_list, &pid_list_index, &pid_list_size)) {
- /* if any errors are encountered, force freeing of the list */
- pid_list[0] = 0;
- }
-
-close_dir:
- closedir(dir);
-
-free_dir_name:
- free(dir_name);
-
- return pid_list;
-}
-
-static pid_t *get_pids_root(void)
-{
- char *dir_name;
- DIR *tmp_dir, *dir;
- unsigned int pid_list_index = 0, pid_list_size = 1;
- pid_t *pid_list = NULL;
- struct dirent *dirent;
- int result;
-
- tmp_dir = opendir(USER_TMP_DIR);
- if (!tmp_dir) {
- return NULL;
- }
-
- pid_list = malloc(pid_list_size * sizeof(pid_t));
- if (!pid_list) {
- goto close_tmp_dir;
- }
-
- while ((dirent = readdir(tmp_dir))) {
- /* Compare the dir to check for the USER_SOCK_DIR_BASE prefix */
- if (!strncmp(dirent->d_name, USER_SOCK_DIR_BASE,
- strlen(USER_SOCK_DIR_BASE))) {
-
- if (asprintf(&dir_name, USER_TMP_DIR "/%s",
- dirent->d_name) < 0) {
- goto close_tmp_dir;
- }
-
- dir = opendir(dir_name);
-
- free(dir_name);
-
- if (!dir) {
- continue;
- }
-
- result = get_pids_in_dir(dir, &pid_list, &pid_list_index,
- &pid_list_size);
-
- closedir(dir);
-
- if (result) {
- /*
- * if any errors are encountered,
- * force freeing of the list
- */
- pid_list[0] = 0;
- break;
- }
- }
- }
-
-close_tmp_dir:
- closedir(tmp_dir);
-
- return pid_list;
-}
-
-pid_t *ustctl_get_online_pids(void)
-{
- pid_t *pid_list;
-
- if (geteuid()) {
- pid_list = get_pids_non_root();
- } else {
- pid_list = get_pids_root();
- }
-
- if (pid_list && pid_list[0] == 0) {
- /* No PID at all */
- free(pid_list);
- pid_list = NULL;
- }
-
- return pid_list;
-}
-
-/**
- * Sets ust_marker state (USTCTL_MS_ON or USTCTL_MS_OFF).
- *
- * @param mn Marker name
- * @param state Marker's new state
- * @param pid Traced process ID
- * @return 0 if successful, or errors {USTCTL_ERR_GEN, USTCTL_ERR_ARG}
- */
-int ustctl_set_ust_marker_state(int sock, const char *trace, const char *channel,
- const char *ust_marker, int state)
-{
- struct ustcomm_header req_header, res_header;
- struct ustcomm_ust_marker_info ust_marker_inf;
- int result;
-
- result = ustcomm_pack_ust_marker_info(&req_header,
- &ust_marker_inf,
- trace,
- channel,
- ust_marker);
- if (result < 0) {
- errno = -result;
- return -1;
- }
-
- req_header.command = state ? ENABLE_MARKER : DISABLE_MARKER;
-
- return do_cmd(sock, &req_header, (char *)&ust_marker_inf,
- &res_header, NULL);
-}
-
-/**
- * Set subbuffer size.
- *
- * @param channel_size Channel name and size
- * @param pid Traced process ID
- * @return 0 if successful, or error
- */
-int ustctl_set_subbuf_size(int sock, const char *trace, const char *channel,
- unsigned int subbuf_size)
-{
- struct ustcomm_header req_header, res_header;
- struct ustcomm_channel_info ch_inf;
- int result;
-
- result = ustcomm_pack_channel_info(&req_header,
- &ch_inf,
- trace,
- channel);
- if (result < 0) {
- errno = -result;
- return -1;
- }
-
- req_header.command = SET_SUBBUF_SIZE;
- ch_inf.subbuf_size = subbuf_size;
-
- return do_cmd(sock, &req_header, (char *)&ch_inf,
- &res_header, NULL);
-}
-
-/**
- * Set subbuffer num.
- *
- * @param channel_num Channel name and num
- * @param pid Traced process ID
- * @return 0 if successful, or error
- */
-int ustctl_set_subbuf_num(int sock, const char *trace, const char *channel,
- unsigned int num)
-{
- struct ustcomm_header req_header, res_header;
- struct ustcomm_channel_info ch_inf;
- int result;
-
- result = ustcomm_pack_channel_info(&req_header,
- &ch_inf,
- trace,
- channel);
- if (result < 0) {
- errno = -result;
- return -1;
- }
-
- req_header.command = SET_SUBBUF_NUM;
- ch_inf.subbuf_num = num;
-
- return do_cmd(sock, &req_header, (char *)&ch_inf,
- &res_header, NULL);
-
-}
-
-
-static int ustctl_get_subbuf_num_size(int sock, const char *trace, const char *channel,
- int *num, int *size)
-{
- struct ustcomm_header req_header, res_header;
- struct ustcomm_channel_info ch_inf, *ch_inf_res;
- int result;
-
-
- result = ustcomm_pack_channel_info(&req_header,
- &ch_inf,
- trace,
- channel);
- if (result < 0) {
- errno = -result;
- return -1;
- }
-
- req_header.command = GET_SUBBUF_NUM_SIZE;
-
- result = do_cmd(sock, &req_header, (char *)&ch_inf,
- &res_header, (char **)&ch_inf_res);
- if (result < 0) {
- return -1;
- }
-
- *num = ch_inf_res->subbuf_num;
- *size = ch_inf_res->subbuf_size;
-
- free(ch_inf_res);
-
- return 0;
-}
-
-/**
- * Get subbuffer num.
- *
- * @param channel Channel name
- * @param pid Traced process ID
- * @return subbuf cnf if successful, or error
- */
-int ustctl_get_subbuf_num(int sock, const char *trace, const char *channel)
-{
- int num, size, result;
-
- result = ustctl_get_subbuf_num_size(sock, trace, channel,
- &num, &size);
- if (result < 0) {
- errno = -result;
- return -1;
- }
-
- return num;
-}
-
-/**
- * Get subbuffer size.
- *
- * @param channel Channel name
- * @param pid Traced process ID
- * @return subbuf size if successful, or error
- */
-int ustctl_get_subbuf_size(int sock, const char *trace, const char *channel)
-{
- int num, size, result;
-
- result = ustctl_get_subbuf_num_size(sock, trace, channel,
- &num, &size);
- if (result < 0) {
- errno = -result;
- return -1;
- }
-
- return size;
-}
-
-static int do_trace_cmd(int sock, const char *trace, int command)
-{
- struct ustcomm_header req_header, res_header;
- struct ustcomm_single_field trace_inf;
- int result;
-
- result = ustcomm_pack_single_field(&req_header,
- &trace_inf,
- trace);
- if (result < 0) {
- errno = -result;
- return -1;
- }
-
- req_header.command = command;
-
- return do_cmd(sock, &req_header, (char *)&trace_inf, &res_header, NULL);
-}
-
-/**
- * Destroys an UST trace according to a PID.
- *
- * @param pid Traced process ID
- * @return 0 if successful, or error USTCTL_ERR_GEN
- */
-int ustctl_destroy_trace(int sock, const char *trace)
-{
- return do_trace_cmd(sock, trace, DESTROY_TRACE);
-}
-
-/**
- * Starts an UST trace (and setups it) according to a PID.
- *
- * @param pid Traced process ID
- * @return 0 if successful, or error USTCTL_ERR_GEN
- */
-int ustctl_setup_and_start(int sock, const char *trace)
-{
- return do_trace_cmd(sock, trace, START);
-}
-
-/**
- * Creates an UST trace according to a PID.
- *
- * @param pid Traced process ID
- * @return 0 if successful, or error USTCTL_ERR_GEN
- */
-int ustctl_create_trace(int sock, const char *trace)
-{
- return do_trace_cmd(sock, trace, CREATE_TRACE);
-}
-
-/**
- * Starts an UST trace according to a PID.
- *
- * @param pid Traced process ID
- * @return 0 if successful, or error USTCTL_ERR_GEN
- */
-int ustctl_start_trace(int sock, const char *trace)
-{
- return do_trace_cmd(sock, trace, START_TRACE);
-}
-
-/**
- * Alloc an UST trace according to a PID.
- *
- * @param pid Traced process ID
- * @return 0 if successful, or error USTCTL_ERR_GEN
- */
-int ustctl_alloc_trace(int sock, const char *trace)
-{
- return do_trace_cmd(sock, trace, ALLOC_TRACE);
-}
-
-
-int ustctl_force_switch(int sock, const char *trace)
-{
- return do_trace_cmd(sock, trace, FORCE_SUBBUF_SWITCH);
-}
-
-/**
- * Stops an UST trace according to a PID.
- *
- * @param pid Traced process ID
- * @return 0 if successful, or error USTCTL_ERR_GEN
- */
-int ustctl_stop_trace(int sock, const char *trace)
-{
- return do_trace_cmd(sock, trace, STOP_TRACE);
-}
-
-/**
- * Counts newlines ('\n') in a string.
- *
- * @param str String to search in
- * @return Total newlines count
- */
-unsigned int ustctl_count_nl(const char *str)
-{
- unsigned int i = 0, tot = 0;
-
- while (str[i] != '\0') {
- if (str[i] == '\n') {
- ++tot;
- }
- ++i;
- }
-
- return tot;
-}
-
-/**
- * Frees a CMSF array.
- *
- * @param cmsf CMSF array to free
- * @return 0 if successful, or error USTCTL_ERR_ARG
- */
-int ustctl_free_cmsf(struct ust_marker_status *cmsf)
-{
- if (cmsf == NULL) {
- return USTCTL_ERR_ARG;
- }
-
- unsigned int i = 0;
- while (cmsf[i].channel != NULL) {
- free(cmsf[i].channel);
- free(cmsf[i].ust_marker);
- free(cmsf[i].fs);
- ++i;
- }
- free(cmsf);
-
- return 0;
-}
-
-/**
- * Gets channel/ust_marker/state/format string for a given PID.
- *
- * @param cmsf Pointer to CMSF array to be filled (callee allocates, caller
- * frees with `ustctl_free_cmsf')
- * @param pid Targeted PID
- * @return 0 if successful, or -1 on error
- */
-int ustctl_get_cmsf(int sock, struct ust_marker_status **cmsf)
-{
- struct ustcomm_header req_header, res_header;
- char *big_str = NULL;
- int result;
- struct ust_marker_status *tmp_cmsf = NULL;
- unsigned int i = 0, cmsf_ind = 0;
-
- if (cmsf == NULL) {
- return -1;
- }
-
- req_header.command = LIST_MARKERS;
- req_header.size = 0;
-
- result = ustcomm_send(sock, &req_header, NULL);
- if (result <= 0) {
- PERROR("error while requesting ust_marker list");
- return -1;
- }
-
- result = ustcomm_recv_alloc(sock, &res_header, &big_str);
- if (result <= 0) {
- ERR("error while receiving ust_marker list");
- return -1;
- }
-
- tmp_cmsf = (struct ust_marker_status *) zmalloc(sizeof(struct ust_marker_status) *
- (ustctl_count_nl(big_str) + 1));
- if (tmp_cmsf == NULL) {
- ERR("Failed to allocate CMSF array");
- return -1;
- }
-
- /* Parse received reply string (format: "[chan]/[mark] [st] [fs]"): */
- while (big_str[i] != '\0') {
- char state;
-
- sscanf(big_str + i, "ust_marker: %a[^/]/%a[^ ] %c %a[^\n]",
- &tmp_cmsf[cmsf_ind].channel,
- &tmp_cmsf[cmsf_ind].ust_marker,
- &state,
- &tmp_cmsf[cmsf_ind].fs);
- tmp_cmsf[cmsf_ind].state = (state == USTCTL_MS_CHR_ON ?
- USTCTL_MS_ON : USTCTL_MS_OFF); /* Marker state */
-
- while (big_str[i] != '\n') {
- ++i; /* Go to next '\n' */
- }
- ++i; /* Skip current pointed '\n' */
- ++cmsf_ind;
- }
- tmp_cmsf[cmsf_ind].channel = NULL;
- tmp_cmsf[cmsf_ind].ust_marker = NULL;
- tmp_cmsf[cmsf_ind].fs = NULL;
-
- *cmsf = tmp_cmsf;
-
- free(big_str);
- return 0;
-}
-
-/**
- * Frees a TES array.
- *
- * @param tes TES array to free
- * @return 0 if successful, or error USTCTL_ERR_ARG
- */
-int ustctl_free_tes(struct trace_event_status *tes)
-{
- if (tes == NULL) {
- return USTCTL_ERR_ARG;
- }
-
- unsigned int i = 0;
- while (tes[i].name != NULL) {
- free(tes[i].name);
- ++i;
- }
- free(tes);
-
- return 0;
-}
-
-/**
- * Gets trace_events string for a given PID.
- *
- * @param tes Pointer to TES array to be filled (callee allocates, caller
- * frees with `ustctl_free_tes')
- * @param pid Targeted PID
- * @return 0 if successful, or -1 on error
- */
-int ustctl_get_tes(int sock, struct trace_event_status **tes)
-{
- struct ustcomm_header req_header, res_header;
- char *big_str = NULL;
- int result;
- struct trace_event_status *tmp_tes = NULL;
- unsigned int i = 0, tes_ind = 0;
-
- if (tes == NULL) {
- return -1;
- }
-
- req_header.command = LIST_TRACE_EVENTS;
- req_header.size = 0;
-
- result = ustcomm_send(sock, &req_header, NULL);
- if (result != 1) {
- ERR("error while requesting trace_event list");
- return -1;
- }
-
- result = ustcomm_recv_alloc(sock, &res_header, &big_str);
- if (result != 1) {
- ERR("error while receiving ust_marker list");
- return -1;
- }
-
- tmp_tes = (struct trace_event_status *)
- zmalloc(sizeof(struct trace_event_status) *
- (ustctl_count_nl(big_str) + 1));
- if (tmp_tes == NULL) {
- ERR("Failed to allocate TES array");
- return -1;
- }
-
- /* Parse received reply string (format: "[name]"): */
- while (big_str[i] != '\0') {
- sscanf(big_str + i, "trace_event: %a[^\n]",
- &tmp_tes[tes_ind].name);
- while (big_str[i] != '\n') {
- ++i; /* Go to next '\n' */
- }
- ++i; /* Skip current pointed '\n' */
- ++tes_ind;
- }
- tmp_tes[tes_ind].name = NULL;
-
- *tes = tmp_tes;
-
- free(big_str);
- return 0;
-}
-
-/**
- * Set sock path
- *
- * @param sock_path Sock path
- * @param pid Traced process ID
- * @return 0 if successful, or error
- */
-int ustctl_set_sock_path(int sock, const char *sock_path)
-{
- int result;
- struct ustcomm_header req_header, res_header;
- struct ustcomm_single_field sock_path_msg;
-
- result = ustcomm_pack_single_field(&req_header,
- &sock_path_msg,
- sock_path);
- if (result < 0) {
- errno = -result;
- return -1;
- }
-
- req_header.command = SET_SOCK_PATH;
-
- return do_cmd(sock, &req_header, (char *)&sock_path_msg,
- &res_header, NULL);
-}
-
-/**
- * Get sock path
- *
- * @param sock_path Pointer to where the sock path will be returned
- * @param pid Traced process ID
- * @return 0 if successful, or error
- */
-int ustctl_get_sock_path(int sock, char **sock_path)
-{
- int result;
- struct ustcomm_header req_header, res_header;
- struct ustcomm_single_field *sock_path_msg;
-
- req_header.command = GET_SOCK_PATH;
- req_header.size = 0;
-
- result = do_cmd(sock, &req_header, NULL, &res_header,
- (char **)&sock_path_msg);
- if (result < 0) {
- return -1;
- }
-
- result = ustcomm_unpack_single_field(sock_path_msg);
- if (result < 0) {
- return result;
- }
-
- *sock_path = strdup(sock_path_msg->field);
-
- free(sock_path_msg);
-
- return 0;
-}
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/libust -I$(top_srcdir)/libustcomm \
- -I$(top_srcdir)/include -I$(top_srcdir)/libustconsumer
-AM_CFLAGS = -fno-strict-aliasing
-
-bin_PROGRAMS = ust-consumerd
-
-ust_consumerd_SOURCES = ust-consumerd.c
-
-ust_consumerd_LDADD = \
- $(top_builddir)/snprintf/libustsnprintf.la \
- $(top_builddir)/libustcomm/libustcomm.la \
- $(top_builddir)/libustconsumer/libustconsumer.la
-
-ust_consumerd_CFLAGS = -DUST_COMPONENT=ust-consumerd -fno-strict-aliasing
+++ /dev/null
-This is ust-consumerd, the UST consumer daemon.
-
-This daemon is used to collect the traces for the traced programs and write them to disk.
+++ /dev/null
-/* Copyright (C) 2009 Pierre-Marc Fournier
- * 2010 Alexis Halle
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define _GNU_SOURCE
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/shm.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-#include <inttypes.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-#include <getopt.h>
-
-#include "ust/ustconsumer.h"
-#include "../libustconsumer/lowlevel.h"
-#include "usterr.h"
-
-char *sock_path=NULL;
-char *trace_path=NULL;
-int daemon_mode = 0;
-char *pidfile = NULL;
-
-struct ustconsumer_instance *instance;
-
-struct buffer_info_local {
- /* output file */
- int file_fd;
- /* the offset we must truncate to, to unput the last subbuffer */
- off_t previous_offset;
-};
-
-static int write_pidfile(const char *file_name, pid_t pid)
-{
- FILE *pidfp;
-
- pidfp = fopen(file_name, "w");
- if(!pidfp) {
- PERROR("fopen (%s)", file_name);
- WARN("killing child process");
- return -1;
- }
-
- fprintf(pidfp, "%d\n", pid);
-
- fclose(pidfp);
-
- return 0;
-}
-
-int create_dir_if_needed(char *dir)
-{
- int result;
- result = mkdir(dir, 0777);
- if(result == -1) {
- if(errno != EEXIST) {
- PERROR("mkdir");
- return -1;
- }
- }
-
- return 0;
-}
-
-int unwrite_last_subbuffer(struct buffer_info *buf)
-{
- int result;
- struct buffer_info_local *buf_local = buf->user_data;
-
- result = ftruncate(buf_local->file_fd, buf_local->previous_offset);
- if(result == -1) {
- PERROR("ftruncate");
- return -1;
- }
-
- result = lseek(buf_local->file_fd, buf_local->previous_offset, SEEK_SET);
- if(result == (int)(off_t)-1) {
- PERROR("lseek");
- return -1;
- }
-
- return 0;
-}
-
-int write_current_subbuffer(struct buffer_info *buf)
-{
- int result;
- struct buffer_info_local *buf_local = buf->user_data;
-
- void *subbuf_mem = buf->mem + (buf->consumed_old & (buf->n_subbufs * buf->subbuf_size-1));
-
- size_t cur_sb_size = subbuffer_data_size(subbuf_mem);
-
- off_t cur_offset = lseek(buf_local->file_fd, 0, SEEK_CUR);
- if(cur_offset == (off_t)-1) {
- PERROR("lseek");
- return -1;
- }
-
- buf_local->previous_offset = cur_offset;
- DBG("previous_offset: %ld", cur_offset);
-
- result = patient_write(buf_local->file_fd, subbuf_mem, cur_sb_size);
- if(result == -1) {
- PERROR("write");
- return -1;
- }
-
- return 0;
-}
-
-int on_read_subbuffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
-{
- return write_current_subbuffer(buf);
-}
-
-int on_read_partial_subbuffer(struct ustconsumer_callbacks *data, struct buffer_info *buf,
- long subbuf_index, unsigned long valid_length)
-{
- struct buffer_info_local *buf_local = buf->user_data;
- char *tmp;
- int result;
- unsigned long pad_size;
-
- result = patient_write(buf_local->file_fd, buf->mem + subbuf_index * buf->subbuf_size, valid_length);
- if(result == -1) {
- ERR("Error writing to buffer file");
- return result;
- }
-
- /* pad with empty bytes */
- pad_size = PAGE_ALIGN(valid_length)-valid_length;
- if(pad_size) {
- tmp = zmalloc(pad_size);
- result = patient_write(buf_local->file_fd, tmp, pad_size);
- if(result == -1) {
- ERR("Error writing to buffer file");
- return result;
- }
- free(tmp);
- }
- return result;
-}
-
-int on_open_buffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
-{
- char *tmp;
- int result;
- int fd;
- struct buffer_info_local *buf_local =
- zmalloc(sizeof(struct buffer_info_local));
-
- if(!buf_local) {
- ERR("could not allocate buffer_info_local struct");
- return 1;
- }
-
- buf->user_data = buf_local;
-
- /* open file for output */
- if(!trace_path) {
- /* Only create the directory if using the default path, because
- * of the risk of typo when using trace path override. We don't
- * want to risk creating plenty of useless directories in that case.
- */
- result = create_dir_if_needed(USTCONSUMER_DEFAULT_TRACE_PATH);
- if(result == -1) {
- ERR("could not create directory %s", USTCONSUMER_DEFAULT_TRACE_PATH);
- return 1;
- }
-
- trace_path = USTCONSUMER_DEFAULT_TRACE_PATH;
- }
-
- if (asprintf(&tmp, "%s/%u_%" PRId64 "", trace_path, buf->pid, buf->pidunique) < 0) {
- ERR("on_open_buffer : asprintf failed (%s/%u_%" PRId64 ")",
- trace_path, buf->pid, buf->pidunique);
- return 1;
- }
- result = create_dir_if_needed(tmp);
- if(result == -1) {
- ERR("could not create directory %s", tmp);
- free(tmp);
- return 1;
- }
- free(tmp);
-
- if (asprintf(&tmp, "%s/%u_%" PRId64 "/%s", trace_path, buf->pid, buf->pidunique, buf->name) < 0) {
- ERR("on_open_buffer : asprintf failed (%s/%u_%" PRId64 "/%s)",
- trace_path, buf->pid, buf->pidunique, buf->name);
- return 1;
- }
-again:
- result = fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 00600);
- if (result == -1 && errno == EINTR)
- goto again;
-
- if(result == -1) {
- PERROR("open");
- ERR("failed opening trace file %s", tmp);
- return 1;
- }
- buf_local->file_fd = fd;
- free(tmp);
-
- return 0;
-}
-
-int on_close_buffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
-{
- struct buffer_info_local *buf_local = buf->user_data;
- int result;
-
-again:
- result = close(buf_local->file_fd);
- if (result == -1 && errno == EINTR)
- goto again;
- free(buf_local);
- if(result == -1) {
- PERROR("close");
- }
- return 0;
-}
-
-int on_put_error(struct ustconsumer_callbacks *data, struct buffer_info *buf)
-{
- return unwrite_last_subbuffer(buf);
-}
-
-struct ustconsumer_callbacks *new_callbacks()
-{
- struct ustconsumer_callbacks *callbacks =
- zmalloc(sizeof(struct ustconsumer_callbacks));
-
- if(!callbacks)
- return NULL;
-
- callbacks->on_open_buffer = on_open_buffer;
- callbacks->on_close_buffer = on_close_buffer;
- callbacks->on_read_subbuffer = on_read_subbuffer;
- callbacks->on_read_partial_subbuffer = on_read_partial_subbuffer;
- callbacks->on_put_error = on_put_error;
- callbacks->on_new_thread = NULL;
- callbacks->on_close_thread = NULL;
- callbacks->on_trace_end = NULL;
-
- return callbacks;
-
-}
-
-int is_directory(const char *dir)
-{
- int result;
- struct stat st;
-
- result = stat(dir, &st);
- if(result == -1) {
- PERROR("stat");
- return 0;
- }
-
- if(!S_ISDIR(st.st_mode)) {
- return 0;
- }
-
- return 1;
-}
-
-void usage(void)
-{
- fprintf(stderr, "Usage:\nust-consumerd OPTIONS\n\nOptions:\n"
- "\t-h\t\tDisplay this usage.\n"
- "\t-o DIR\t\tSpecify the directory where to output the traces.\n"
- "\t-s PATH\t\tSpecify the path to use for the daemon socket.\n"
- "\t-d\t\tStart as a daemon.\n"
- "\t--pidfile FILE\tWrite the PID in this file (when using -d).\n");
-}
-
-int parse_args(int argc, char **argv)
-{
- int c;
-
- while (1) {
- int option_index = 0;
- static struct option long_options[] = {
- {"pidfile", 1, 0, 'p'},
- {"help", 0, 0, 'h'},
- {"version", 0, 0, 'V'},
- {0, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "hs:o:d", long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 0:
- printf("option %s", long_options[option_index].name);
- if (optarg)
- printf(" with arg %s", optarg);
- printf("\n");
- break;
- case 's':
- sock_path = optarg;
- break;
- case 'o':
- trace_path = optarg;
- if(!is_directory(trace_path)) {
- ERR("Not a valid directory. (%s)", trace_path);
- return -1;
- }
- break;
- case 'd':
- daemon_mode = 1;
- break;
- case 'p':
- pidfile = strdup(optarg);
- break;
- case 'h':
- usage();
- exit(0);
- case 'V':
- printf("Version 0.0\n");
- break;
-
- default:
- /* unknown option or other error; error is
- printed by getopt, just return */
- return -1;
- }
- }
-
- return 0;
-}
-
-void sigterm_handler(int sig)
-{
- ustconsumer_stop_instance(instance, 0);
-}
-
-int start_ustconsumer(int fd)
-{
- int result;
- sigset_t sigset;
- struct sigaction sa;
-
- struct ustconsumer_callbacks *callbacks = new_callbacks();
- if(!callbacks) {
- PERROR("new_callbacks");
- return 1;
- }
-
- result = sigemptyset(&sigset);
- if(result == -1) {
- PERROR("sigemptyset");
- return 1;
- }
- sa.sa_handler = sigterm_handler;
- sa.sa_mask = sigset;
- sa.sa_flags = 0;
- result = sigaction(SIGTERM, &sa, NULL);
- if(result == -1) {
- PERROR("sigaction");
- return 1;
- }
- result = sigaction(SIGINT, &sa, NULL);
- if(result == -1) {
- PERROR("sigaction");
- return 1;
- }
-
- instance = ustconsumer_new_instance(callbacks, sock_path);
- if(!instance) {
- ERR("failed to create ustconsumer instance");
- return 1;
- }
-
- result = ustconsumer_init_instance(instance);
- if(result) {
- ERR("failed to initialize ustconsumer instance");
- return 1;
- }
-
- /* setup handler for SIGPIPE */
- result = sigemptyset(&sigset);
- if(result == -1) {
- PERROR("sigemptyset");
- return 1;
- }
- result = sigaddset(&sigset, SIGPIPE);
- if(result == -1) {
- PERROR("sigaddset");
- return 1;
- }
- result = sigprocmask(SIG_BLOCK, &sigset, NULL);
- if(result == -1) {
- PERROR("sigprocmask");
- return 1;
- }
-
- /* Write pidfile */
- if(pidfile) {
- result = write_pidfile(pidfile, getpid());
- if(result == -1) {
- ERR("failed to write pidfile");
- return 1;
- }
- }
-
- /* Notify parent that we are successfully started. */
- if(fd != -1) {
- /* write any one character */
- result = write(fd, "!", 1);
- if(result == -1) {
- PERROR("write");
- return -1;
- }
- if(result != 1) {
- ERR("Problem sending confirmation of daemon start to parent");
- return -1;
- }
- result = close(fd);
- if(result == -1) {
- PERROR("close");
- }
- }
-
- ustconsumer_start_instance(instance);
-
- free(callbacks);
-
- return 0;
-}
-
-int start_ustconsumer_daemon()
-{
- int result;
- int fd[2];
- pid_t child_pid;
-
- result = pipe(fd);
-
- result = child_pid = fork();
- if(result == -1) {
- PERROR("fork");
- return -1;
- }
- else if(result == 0) {
- return start_ustconsumer(fd[1]);
- }
- else {
- char buf;
-
- result = read(fd[0], &buf, 1);
- if(result == -1) {
- PERROR("read");
- return -1;
- }
- if(result != 1) {
- ERR("did not receive valid confirmation that the daemon is started");
- return -1;
- }
-
- result = close(fd[0]);
- if(result == -1) {
- PERROR("close");
- }
-
- DBG("The daemon is now successfully started");
- }
-
- /* Wait for confirmation that the server is ready. */
-
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- int result;
-
- result = parse_args(argc, argv);
- if(result == -1) {
- exit(1);
- }
-
- if(daemon_mode) {
- result = start_ustconsumer_daemon();
- }
- else {
- result = start_ustconsumer(-1);
- }
-
- return result;
-}
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libustcomm \
- -I$(top_srcdir)/libustctl $(KCOMPAT_CFLAGS)
-AM_CFLAGS = -fno-strict-aliasing
-
-bin_PROGRAMS = ustctl
-
-ustctl_SOURCES = \
- ustctl.c marker_cmds.c trace_cmds.c channel_cmds.c cli.c cli.h scanning_functions.c scanning_functions.h
-
-ustctl_CFLAGS = -DUST_COMPONENT=ustctl -fno-strict-aliasing
-
-ustctl_LDADD = \
- $(top_builddir)/snprintf/libustsnprintf.la \
- $(top_builddir)/libustcomm/libustcomm.la \
- $(top_builddir)/libustctl/libustctl.la
+++ /dev/null
-ustctl is a helper application used to control tracing on programs that support tracing.
-
-It can list markers, enable/disable markers, start tracing and stop tracing.
+++ /dev/null
-/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <ust/ustctl.h>
-#include "scanning_functions.h"
-#include "usterr.h"
-#include "cli.h"
-
-static int set_subbuf_size(int argc, char *argv[])
-{
- int sock, result = 0;
- char *channel = NULL;
- unsigned int size;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (scan_ch_and_num(argv[3], &channel, &size)) {
- fprintf(stderr, "Failed to scan channel and size from"
- " %s\n", argv[3]);
- if (channel)
- free(channel);
- return -1;
- }
- if (ustctl_set_subbuf_size(sock, argv[2], channel, size)) {
- ERR("error while trying to set the size of subbuffers "
- "for PID %s\n",
- argv[1]);
- result = -1;
- }
-
- free(channel);
-
- return result;
-}
-
-static int set_subbuf_num(int argc, char *argv[])
-{
- int sock, result = 0;
- char *channel = NULL;
- unsigned int num;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (scan_ch_and_num(argv[3], &channel, &num)) {
- fprintf(stderr, "Failed to scan channel and number from"
- " %s\n", argv[3]);
- if (channel)
- free(channel);
- return -1;
- }
- if (ustctl_set_subbuf_num(sock, argv[2], channel, num)) {
- ERR("error while trying to set the number of subbuffers for PID %s\n",
- argv[1]);
- result = -1;
- }
-
- free(channel);
-
- return result;
-}
-
-static int get_subbuf_size(int argc, char *argv[])
-{
- int sock;
- unsigned int size;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if ((size = ustctl_get_subbuf_size(sock, argv[2], argv[3])) < 0) {
- ERR("error while trying to get the subbuffer size from PID %s\n",
- argv[1]);
- return -1;
- }
-
- printf("The subbufer size is %d bytes\n", size);
-
- return 0;
-}
-
-static int get_subbuf_num(int argc, char *argv[])
-{
- int sock;
- unsigned int num;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if ((num = ustctl_get_subbuf_num(sock, argv[2], argv[3])) < 0) {
- ERR("error while trying to get the subbuffer size from PID %s\n",
- argv[1]);
- return -1;
- }
-
- printf("There are %u subbufers in each buffer\n", num);
-
- return 0;
-}
-
-struct cli_cmd __cli_cmds channel_cmds[] = {
- {
- .name = "set-subbuf-size",
- .description = "Set the subbuffer size for a channel",
- .help_text = "set-subbuf-size <pid> <trace> <channel>/<size> \n"
- "Set the subbuffer size for a channel\n",
- .function = set_subbuf_size,
- .desired_args = 3,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "set-subbuf-num",
- .description = "Set the number of subbuffers for a channel",
- .help_text = "set-subbuf-num <pid> <trace> <channel>/<num> \n"
- "Set the number of subbuffers for a channel\n",
- .function = set_subbuf_num,
- .desired_args = 3,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "get-subbuf-size",
- .description = "Get the subbuffer size for a channel",
- .help_text = "get-subbuf-size <pid> <trace> <channel>\n"
- "Get the subbuffer size for a channel\n",
- .function = get_subbuf_size,
- .desired_args = 3,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "get-subbuf-num",
- .description = "Get the number of subbuffers for a channel",
- .help_text = "get-subbuf-num <pid> <trace> <channel>\n"
- "Get the number of subbuffers for a channel\n",
- .function = get_subbuf_num,
- .desired_args = 3,
- .desired_args_op = CLI_EQ,
- },
-};
+++ /dev/null
-/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "cli.h"
-
-/* This dummy command is needed to create the sections in cli.o before
- * other .o files have these sections, usefull for development.
- */
-static int _dummy(int argc, char *argv[]) {
- return 0;
-}
-
-/* Define a dummy cmd to guarantee existence of the builtin variables */
-struct cli_cmd __cli_cmds __dummy_cli_cmd[] = {
- {
- .name = "_dummy",
- .description = NULL,
- .help_text = NULL,
- .function = _dummy,
- .desired_args = 0,
- .desired_args_op = 0,
- },
-};
-
-extern struct cli_cmd __start___cli_cmds[] __attribute__((visibility("hidden")));
-extern struct cli_cmd __stop___cli_cmds[] __attribute__((visibility("hidden")));
-
-static struct cli_cmd **cli_cmd_list;
-static int cli_cmd_list_size;
-
-static char *process_name;
-
-static int compute_cli_cmds_size(void)
-{
- long cli_cmds_start, cli_cmds_end;
-
- cli_cmds_start = (long)__start___cli_cmds;
- cli_cmds_end = (long)__stop___cli_cmds;
-
- return (cli_cmds_end - cli_cmds_start) / sizeof(struct cli_cmd);
-}
-
-static void __attribute__((constructor)) generate_cli_cmd_list(int argc, char *argv[])
-{
- struct cli_cmd *cli_cmd;
- int section_size, i;
-
- process_name = basename(argv[0]);
-
- section_size = compute_cli_cmds_size();
-
- cli_cmd_list = malloc(section_size * sizeof(void *));
- if (!cli_cmd_list) {
- fprintf(stderr, "Failed to allocate command list!");
- exit(EXIT_FAILURE);
- }
-
- cli_cmd_list_size = 0;
-
- cli_cmd = __start___cli_cmds;
- for (i = 0; i < section_size; i++) {
- if (&cli_cmd[i] == &__dummy_cli_cmd[0]) {
- continue;
- }
-
- if (cli_cmd[i].name) {
- cli_cmd_list[cli_cmd_list_size++] = &cli_cmd[i];
- }
- }
-}
-
-struct cli_cmd *find_cli_cmd(const char *command)
-{
- int i;
-
- for (i = 0; i < cli_cmd_list_size; i++) {
- if (!strcmp(cli_cmd_list[i]->name, command)) {
- return cli_cmd_list[i];
- }
- }
-
- return NULL;
-}
-
-static int cmpcli_cmds(const void *p1, const void *p2)
-{
- return strcmp(* (char * const *) ((struct cli_cmd *)p1)->name,
- * (char * const *) ((struct cli_cmd *)p2)->name);
-}
-
-#define HELP_BUFFER_SIZE 4096
-
-static void print_cmd_help(const char *prefix, const char *infix,
- struct cli_cmd *cli_cmd)
-{
- if (cli_cmd->help_text) {
- fprintf(stderr, "%s%s%s",
- prefix,
- infix,
- cli_cmd->help_text);
- } else if (cli_cmd->description) {
- fprintf(stderr, "%s%s%s\n%s\n",
- prefix,
- infix,
- cli_cmd->name,
- cli_cmd->description);
- } else {
- fprintf(stderr, "No help available for %s\n",
- cli_cmd->name);
- }
-}
-
-void list_cli_cmds(int option)
-{
- int i;
-
- qsort(cli_cmd_list, cli_cmd_list_size, sizeof(void *), cmpcli_cmds);
-
- for (i = 0; i < cli_cmd_list_size; i++) {
- switch (option) {
- case CLI_SIMPLE_LIST:
- fprintf(stderr, "%s ", cli_cmd_list[i]->name);
- break;
- case CLI_DESCRIPTIVE_LIST:
- fprintf(stderr, " %-25s%s\n", cli_cmd_list[i]->name,
- cli_cmd_list[i]->description);
- break;
- case CLI_EXTENDED_LIST:
- print_cmd_help("", "", cli_cmd_list[i]);
- fprintf(stderr, "\n");
- break;
- }
- }
-
- if (option == CLI_SIMPLE_LIST) {
- fprintf(stderr, "\n");
- }
-}
-
-int cli_print_help(const char *command)
-{
- struct cli_cmd *cli_cmd;
-
- cli_cmd = find_cli_cmd(command);
- if (!cli_cmd) {
- return -1;
- }
-
- print_cmd_help(process_name, " ", cli_cmd);
-
- return 0;
-}
-
-static void cli_check_argc(const char *command, int args,
- int operator, int desired_args)
-{
- switch(operator) {
- case CLI_EQ:
- if (args != desired_args)
- goto print_error;
- break;
- case CLI_GE:
- if (args < desired_args)
- goto print_error;
- break;
- }
-
- return;
-
-print_error:
- fprintf(stderr, "%s %s requires %s%d argument%s, see usage.\n",
- process_name, command, operator == CLI_EQ ? "" : "at least ",
- desired_args, desired_args > 1 ? "s" : "");
- cli_print_help(command);
- exit(EXIT_FAILURE);
-}
-
-
-void cli_dispatch_cmd(struct cli_cmd *cmd, int argc, char *argv[])
-{
- cli_check_argc(cmd->name, argc - 1, cmd->desired_args_op,
- cmd->desired_args);
-
- if (cmd->function(argc, argv)) {
- exit(EXIT_FAILURE);
- }
-
- exit(EXIT_SUCCESS);
-}
+++ /dev/null
-/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef _CLI_H
-#define _CLI_H
-
-struct cli_cmd {
- const char *name;
- const char *description;
- const char *help_text;
- int (*function)(int, char **);
- int desired_args;
- int desired_args_op;
-} __attribute__((aligned(8)));
-
-#define __cli_cmds __attribute__((section("__cli_cmds"), aligned(8), used))
-
-struct cli_cmd *find_cli_cmd(const char *command);
-
-enum cli_list_opts {
- CLI_SIMPLE_LIST,
- CLI_DESCRIPTIVE_LIST,
- CLI_EXTENDED_LIST,
-};
-
-void list_cli_cmds(int option);
-
-int cli_print_help(const char *command);
-
-enum cli_arg_ops {
- CLI_EQ,
- CLI_GE,
-};
-
-void cli_dispatch_cmd(struct cli_cmd *cmd, int argc, char *argv[]);
-
-#endif /* _CLI_H */
+++ /dev/null
-/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <ust/ustctl.h>
-#include "scanning_functions.h"
-#include "usterr.h"
-#include "cli.h"
-
-static int list_markers(int argc, char *argv[])
-{
- struct ust_marker_status *cmsf = NULL;
- int i, sock;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_get_cmsf(sock, &cmsf)) {
- ERR("error while trying to list markers for PID %s\n", argv[1]);
- return -1;
- }
- for (i = 0; cmsf[i].channel; i++) {
- printf("{PID: %s, channel/marker: %s/%s, "
- "state: %u, fmt: %s}\n",
- argv[1],
- cmsf[i].channel,
- cmsf[i].ust_marker,
- cmsf[i].state,
- cmsf[i].fs);
- }
- ustctl_free_cmsf(cmsf);
- return 0;
-}
-
-static int enable_marker(int argc, char *argv[])
-{
- int i, sock, result = 0;
- char *channel, *marker;
-
- sock = parse_and_connect_pid(argv[1]);
-
- for (i = 3; i < argc; i++) {
- channel = NULL;
- marker = NULL;
- if (scan_ch_marker(argv[i],
- &channel, &marker)) {
- result = -1;
- fprintf(stderr, "Failed to scan channel and marker from"
- " %s\n", argv[i]);
- if (channel)
- free(channel);
- if (marker)
- free(marker);
- }
- if (ustctl_set_ust_marker_state(sock, argv[2], channel, marker, 1)) {
- PERROR("error while trying to enable marker %s with PID %s",
- argv[i], argv[1]);
- result = -1;
- }
- free(channel);
- free(marker);
- }
-
- return result;
-}
-
-static int disable_marker(int argc, char *argv[])
-{
- int i, sock, result = 0;
- char *channel, *marker;
-
- sock = parse_and_connect_pid(argv[1]);
-
- for (i = 3; i < argc; i++) {
- channel = NULL;
- marker = NULL;
- if (scan_ch_marker(argv[i],
- &channel, &marker)) {
- fprintf(stderr, "Failed to scan channel and marker from"
- " %s\n", argv[i]);
- if (channel)
- free(channel);
- if (marker)
- free(marker);
- return -1;
- }
- if (ustctl_set_ust_marker_state(sock, argv[2], channel, marker, 0)) {
- PERROR("error while trying to disable marker %s with PID %s",
- argv[i], argv[1]);
- result = -1;
- }
- free(channel);
- free(marker);
- }
-
- return result;
-}
-
-struct cli_cmd __cli_cmds ust_marker_cmds[] = {
- {
- .name = "list-markers",
- .description = "List markers for a given pid",
- .help_text = "list-markers <pid>\n"
- "List the markers in a process\n",
- .function = list_markers,
- .desired_args = 1,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "enable-marker",
- .description = "Enable markers for a given pid",
- .help_text = "enable-marker <pid> <trace> <channel>/<marker>... \n"
- "Enable the listed markers for the trace in process pid\n",
- .function = enable_marker,
- .desired_args = 3,
- .desired_args_op = CLI_GE,
- },
- {
- .name = "disable-marker",
- .description = "Disable markers for a given pid",
- .help_text = "disable-marker <pid> <trace> <channel>/<marker>... \n"
- "Disable the listed markers for the trace in process pid\n",
- .function = disable_marker,
- .desired_args = 3,
- .desired_args_op = CLI_GE,
- }
-};
+++ /dev/null
-/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <ust/ustctl.h>
-#include "usterr.h"
-
-
-int parse_and_connect_pid(const char *pid_string)
-{
- pid_t pid;
- int sock;
-
- errno = 0;
- pid = strtoull(pid_string, NULL, 10);
- if (errno) {
- perror("Failed to parse pid");
- exit(EXIT_FAILURE);
- }
-
- sock = ustctl_connect_pid(pid);
- if (sock < 0) {
- perror("Failed to connect to process");
- exit(EXIT_FAILURE);
- }
-
- return sock;
-}
-
-int scan_ch_marker(const char *channel_marker, char **channel, char **marker)
-{
- int result;
-
- *channel = NULL;
- *marker = NULL;
-
- result = sscanf(channel_marker, "%a[^/]/%as", channel, marker);
- if (result != 2) {
- if (errno) {
- PERROR("Failed to read channel and marker names");
- } else {
- ERR("Failed to parse marker and channel names");
- }
- if (*channel) {
- free(*channel);
- }
- if (*marker) {
- free(*marker);
- }
- return -1;
- }
-
- return 0;
-}
-
-int scan_ch_and_num(const char *ch_num, char **channel, unsigned int *num)
-{
- int result;
-
- *channel = NULL;
-
- result = sscanf(ch_num, "%a[^/]/%u", channel, num);
- if (result != 2) {
- if (errno) {
- PERROR("Failed to parse channel and number");
- } else {
- ERR("Failed to parse channel and number");
- }
- if (*channel) {
- free(*channel);
- }
- return -1;
- }
-
- return 0;
-}
+++ /dev/null
-/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef __SCANNING_FUNCTIONS_H
-#define __SCANNING_FUNCTIONS_H
-
-int parse_and_connect_pid(const char *pid_string);
-
-int scan_ch_marker(const char *channel_marker, char **channel, char **marker);
-
-int scan_ch_and_num(const char *ch_num, char **channel, unsigned int *num);
-
-#endif /* __SCANNING_FUNCTIONS_H */
+++ /dev/null
-/* Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <ust/ustctl.h>
-#include "scanning_functions.h"
-#include "usterr.h"
-#include "cli.h"
-
-
-static int create_trace(int argc, char *argv[])
-{
- int sock;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_create_trace(sock, argv[2])) {
- ERR("Failed to create trace %s for PID %s\n", argv[2], argv[1]);
- return -1;
- }
-
- return 0;
-}
-
-static int alloc_trace(int argc, char *argv[])
-{
- int sock;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_alloc_trace(sock, argv[2])) {
- ERR("Failed to allocate trace %s for PID %s\n", argv[2], argv[1]);
- return -1;
- }
- return 0;
-}
-
-static int start_trace(int argc, char *argv[])
-{
- int sock;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_start_trace(sock, argv[2])) {
- ERR("Failed to start trace %s for PID %s\n", argv[2], argv[1]);
- return -1;
- }
- return 0;
-}
-
-static int stop_trace(int argc, char *argv[])
-{
- int sock;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_stop_trace(sock, argv[2])) {
- ERR("Failed to stop trace %s for PID %s\n", argv[2], argv[1]);
- return -1;
- }
- return 0;
-}
-
-static int destroy_trace(int argc, char *argv[])
-{
- int sock;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_destroy_trace(sock, argv[2])) {
- ERR("Failed to destroy trace %s for PID %s\n", argv[2], argv[1]);
- return -1;
- }
- return 0;
-}
-
-static int force_subbuf_switch(int argc, char *argv[])
-{
- int sock;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_force_switch(sock, argv[2])) {
- ERR("error while trying to force switch for PID %s\n", argv[1]);
- return -1;
- }
-
- return 0;
-}
-
-struct cli_cmd __cli_cmds trace_cmds[] = {
- {
- .name = "create-trace",
- .description = "Create a trace for a process",
- .help_text = "create-trace <pid> <trace>\n"
- "Create a trace for a process\n",
- .function = create_trace,
- .desired_args = 2,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "alloc-trace",
- .description = "Allocate a trace for a process",
- .help_text = "alloc-trace <pid> <trace>\n"
- "Allocate a trace for a process\n",
- .function = alloc_trace,
- .desired_args = 2,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "start-trace",
- .description = "Start a trace for a process",
- .help_text = "start-trace <pid> <trace>\n"
- "Start a trace for a process\n",
- .function = start_trace,
- .desired_args = 2,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "stop-trace",
- .description = "Stop a trace for a process",
- .help_text = "stop-trace <pid> <trace>\n"
- "Stop a trace for a process\n",
- .function = stop_trace,
- .desired_args = 2,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "destroy-trace",
- .description = "Destroy a trace for a process",
- .help_text = "destroy-trace <pid> <trace>\n"
- "Destroy a trace for a process\n",
- .function = destroy_trace,
- .desired_args = 2,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "force-subbuf-switch",
- .description = "Force a subbuffer switch",
- .help_text = "force-subbuf-switch <pid> <trace>\n"
- "Force a subbuffer switch for a trace, currently this forces\n"
- "a subbuffer switch for all traces in a process\n",
- .function = force_subbuf_switch,
- .desired_args = 2,
- .desired_args_op = CLI_EQ,
- },
-};
+++ /dev/null
-/* Copyright (C) 2009 Pierre-Marc Fournier
- * Copyright (C) 2011 Ericsson AB, Nils Carlson <nils.carlson@ericsson.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <fcntl.h>
-
-#include "ust/ustctl.h"
-#include "usterr.h"
-#include "cli.h"
-#include "scanning_functions.h"
-
-void usage(const char *process_name)
-{
- fprintf(stderr, "Usage: %s COMMAND [ARGS]...\n", process_name);
- fprintf(stderr,
- "Control tracing within a process that supports UST,\n"
- " the Userspace Tracing libary\n"
- "Options:\n"
- " -h[<cmd>], --help[=<cmd>] "
- "help, for a command if provided\n"
- " -l, --list "
- "short list of commands\n"
- " -e, --extended-list "
- "extented list of commands with help\n"
- "Commands:\n");
- list_cli_cmds(CLI_DESCRIPTIVE_LIST);
-}
-
-/*
- * Provide backward compatibility for scripts that make use of the
- * --commands in ustctl version <= 0.11
- */
-enum command {
- CREATE_TRACE=1000,
- ALLOC_TRACE,
- START_TRACE,
- STOP_TRACE,
- DESTROY_TRACE,
- LIST_MARKERS,
- LIST_TRACE_EVENTS,
- ENABLE_MARKER,
- DISABLE_MARKER,
-};
-
-struct option options[] =
-{
- { "create-trace", 0, 0, CREATE_TRACE },
- { "alloc-trace", 0, 0, ALLOC_TRACE },
- { "start-trace", 0, 0, START_TRACE },
- { "stop-trace", 0, 0, STOP_TRACE },
- { "destroy-trace", 0, 0, DESTROY_TRACE },
- { "list-markers", 0, 0, LIST_MARKERS },
- { "list-trace-events", 0, 0, LIST_TRACE_EVENTS},
- { "enable-marker", 0, 0, ENABLE_MARKER },
- { "disable-marker", 0, 0, DISABLE_MARKER },
- {"help", 2, NULL, 'h'},
- {"list", 0, NULL, 'l'},
- {"extended-list", 0, NULL, 'e'},
- {NULL, 0, NULL, 0},
-};
-
-int main(int argc, char *argv[])
-{
- struct cli_cmd *cli_cmd;
- char **args = argv;
- int opt;
- int i;
-
- if(argc <= 1) {
- fprintf(stderr, "No operation specified.\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- while ((opt = getopt_long(argc, argv, "+h::le",
- options, NULL)) != -1) {
- switch (opt) {
- case 'h':
- if (!optarg) {
- usage(argv[0]);
- } else {
- if (cli_print_help(optarg)) {
- fprintf(stderr, "No such command %s\n",
- optarg);
- }
- }
- exit(EXIT_FAILURE);
- break;
- case 'l':
- list_cli_cmds(CLI_SIMPLE_LIST);
- exit(EXIT_FAILURE);
- break;
- case 'e':
- list_cli_cmds(CLI_EXTENDED_LIST);
- exit(EXIT_FAILURE);
- case LIST_MARKERS:
- case LIST_TRACE_EVENTS:
- case CREATE_TRACE:
- case ALLOC_TRACE:
- case START_TRACE:
- case STOP_TRACE:
- case DESTROY_TRACE:
- case ENABLE_MARKER:
- case DISABLE_MARKER:
- args = (char **)malloc(sizeof(char *) * (argc + 3));
- optind--;
- args[optind] = strdup(&argv[optind][2]);
- for (i = optind + 1; i < argc; i++) {
- args[i] = argv[i];
- }
- if (opt >= CREATE_TRACE && opt <= DESTROY_TRACE) {
- args[argc] = strdup("auto");
- argc++;
- }
- if (opt >= ENABLE_MARKER && opt <= DISABLE_MARKER) {
- args[argc] = args[argc - 2];
- args[argc - 2] = args[argc - 1];
- args[argc - 1] = strdup("auto");
- argc++;
- }
- args[argc] = NULL;
- goto do_cli;
- default:
- fprintf(stderr, "Unknown option\n");
- break;
- }
- }
-
-do_cli:
- cli_cmd = find_cli_cmd(args[optind]);
- if (!cli_cmd) {
- fprintf(stderr, "No such command %s\n",
- args[optind]);
- exit(EXIT_FAILURE);
- }
-
- cli_dispatch_cmd(cli_cmd, argc - optind, &args[optind]);
-
- return 0;
-}
-
-static int list_trace_events(int argc, char *argv[])
-{
- struct trace_event_status *tes = NULL;
- int i, sock;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_get_tes(sock, &tes)) {
- ERR("error while trying to list "
- "trace_events for PID %s\n",
- argv[1]);
- return -1;
- }
- i = 0;
- for (i = 0; tes[i].name; i++) {
- printf("{PID: %s, trace_event: %s}\n",
- argv[1],
- tes[i].name);
- }
- ustctl_free_tes(tes);
-
- return 0;
-}
-
-static int set_sock_path(int argc, char *argv[])
-{
- int sock;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_set_sock_path(sock, argv[2])) {
- ERR("error while trying to set sock path for PID %s\n", argv[1]);
- return -1;
- }
-
- return 0;
-}
-
-static int get_sock_path(int argc, char *argv[])
-{
- int sock;
- char *sock_path;
-
- sock = parse_and_connect_pid(argv[1]);
-
- if (ustctl_get_sock_path(sock, &sock_path)) {
- ERR("error while trying to get sock path for PID %s\n", argv[1]);
- return -1;
- }
- printf("The socket path is %s\n", sock_path);
- free(sock_path);
-
- return 0;
-}
-
-static int list_pids(int argc, char *argv[])
-{
- pid_t *pid_list;
- int i;
-
- pid_list = ustctl_get_online_pids();
- if (!pid_list) {
- return -1;
- }
-
- for (i = 0; pid_list[i]; i++) {
- printf("%ld\n", (long)pid_list[i]);
- }
-
- free(pid_list);
-
- return 0;
-}
-
-struct cli_cmd __cli_cmds general_cmds[] = {
- {
- .name = "list-trace-events",
- .description = "List trace-events for a given pid",
- .help_text = "list-trace-events <pid>\n"
- "List the trace-events in a process\n",
- .function = list_trace_events,
- .desired_args = 1,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "set-sock-path",
- .description = "Set the path to the consumer daemon socket",
- .help_text = "set-sock-path <pid> <sock-path>\n"
- "Set the path to the consumer daemon socket\n",
- .function = set_sock_path,
- .desired_args = 2,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "get-sock-path",
- .description = "Get the path to the consumer daemon socket",
- .help_text = "get-sock-path <pid>\n"
- "Get the path to the consumer daemon socket\n",
- .function = get_sock_path,
- .desired_args = 1,
- .desired_args_op = CLI_EQ,
- },
- {
- .name = "list-pids",
- .description = "List traceable pids",
- .help_text = "list-pids\n"
- "List the traceable pids for the current user\n",
- .function = list_pids,
- .desired_args = 0,
- .desired_args_op = CLI_EQ,
- },
-};