This is a fargoing but necessary renaming of some ust components.
The point of the renaming is to allow for a new daemon, ustd, which
will be able to keep track of tracing sessions and connect applications
with consumers. Also, the current names were non-sensical.
This patch is a step on the way towards creating a session daemon
that can connect trace producers and consumers in a nice way.
Signed-off-by: Nils Carlson <nils.carlson@ericsson.com>
libtool
ustctl/ustctl
-ustd/ustd
+ust-consumerd/ust-consumerd
# libust and '.' (that contains the linker script). However, '.'
# must be installed after libust so it can overwrite libust.so with
# the linker script.
-SUBDIRS = snprintf libustcomm libustcmd libust . tests libustinstr-malloc libustd ustd ustctl libustfork include doc
+SUBDIRS = snprintf libustcomm libustcmd libust . tests libustinstr-malloc libustconsumer ust-consumerd ustctl libustfork include doc
EXTRA_DIST = libust.ldscript.in libust-initializer.c libust-initializer.h
dist_bin_SCRIPTS = usttrace
- include
The public header files that will be installed on the system.
- - ustd
+ - ust-consumerd
The daemon that collects trace data and writes it to the disk.
- doc
A library to control tracing in other processes. Used by ustctl.
- libustcomm
- A static library shared between libust, ustd and libustcmd, that provides
- functions that allow these components to communicate together.
+ A static library shared between libust, ust-consumerd and libustcmd, that
+ provides functions that allow these components to communicate together.
+
+ - libustconsumer
+ A library to create ust consumers by registering callbacks, used by
+ ust-consumerd.
- snprintf
An asynchronous signal-safe version of snprintf.
- save_registers: save them only when the marker is active (complicated because we need to know their value at the address that is put in struct marker)
- make streaming work, including periodical flush
- make a system (signal-based?) that allow the listener thread to not be started initially
-- ustd should work as a pool of threads
+- ust-consumerd should work as a pool of threads
- support more than one marker with the same channel and name on the same line?
- make a mode where the listener thread can poll buffers to check if they are ready to be collected
This is to guarantee there will never be a system call in the tracing path. Currently there is a system
tests/ustcmd_function_tests/Makefile
libustinstr-malloc/Makefile
libustfork/Makefile
- libustd/Makefile
- ustd/Makefile
+ libustconsumer/Makefile
+ ust-consumerd/Makefile
ustctl/Makefile
libustcomm/Makefile
libustcmd/Makefile
-SUBDIRS = man info
\ No newline at end of file
+SUBDIRS = man info
@itemize @bullet
@item ustctl
@item libustcmd
-@item ustd
+@item ust-consumerd
@end itemize
@node Supported platforms
@item
ust
-This contains the tracing library, the ustd daemon, trace control tools
+This contains the tracing library, the ust-consumerd daemon, trace control tools
and other helper tools.
Repository: @url{http://git.dorsal.polymtl.ca}
# Make sure the directory for the communication sockets exists.
$ mkdir /tmp/ustsocks
-# Make sure the directory where ustd will write the trace exists.
+# Make sure the directory where ust-consumerd will write the trace exists.
$ mkdir /tmp/trace
# Start the daemon
-$ ustd
+$ ust-consumerd
# We assume the program we want to trace is already running and that
# it has pid 1234.
Libust is initialized by a constructor, which by definition runs before the
@code{main()} function of the application starts. This constructor creates a
thread called the @emph{listener thread}. The listener thread initializes a
-named socket and waits for connections for ustd or ustctl.
+named socket and waits for connections for ust-consumerd or ustctl.
Libust-specific code may:
@itemize @bullet
-EXTRA_DIST = ustctl.1 ustd.1 usttrace.1
-man_MANS = ustctl.1 ustd.1 usttrace.1
+EXTRA_DIST = ustctl.1 ust-consumerd.1 usttrace.1
+man_MANS = ustctl.1 ust-consumerd.1 usttrace.1
--- /dev/null
+.\" generated with Ronn/v0.5
+.\" http://github.com/rtomayko/ronn/
+.
+.TH "UST-CONSUMERD" "1" "May 2010" "" ""
+.
+.SH "NAME"
+\fBust-consumerd\fR \-\- a daemon that collects trace data and writes it to the disk
+.
+.SH "SYNOPSIS"
+\fBust-consumerd\fR [\fIoptions\fR]
+.
+.SH "DESCRIPTION"
+\fBust-consumerd\fR is a program that collects trace data and writes it to the disk.
+.
+.SH "OPTIONS"
+These programs follow the usual GNU command line syntax, with long options
+starting with two dashes(`\-'). A summary of options is included below.
+.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Show summary of options.
+.
+.TP
+\fB\-o\fR \fIDIR\fR
+Specify the directory where to output the traces.
+.
+.TP
+\fB\-s\fR \fIPATH\fR
+Specify the path to use for the daemon socket.
+.
+.TP
+\fB\-d\fR
+Start as a daemon.
+.
+.TP
+\fB\-p\fR, \fB\-\-pidfile\fR=\fIFILE\fR
+Write the PID in this file (when using \-d).
+.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Show version of program.
+.
+.SH "SEE ALSO"
+ustctl(1), usttrace(1)
+.
+.SH "AUTHOR"
+\fBust-consumerd\fR was written by Pierre\-Marc Fournier.
+.
+.P
+This manual page was written by Jon Bernard <jbernard@debian.org>, for
+the Debian project (and may be used by others).
--- /dev/null
+ust-consumerd(1) -- a daemon that collects trace data and writes it to the disk
+======================================================================
+
+## SYNOPSIS
+
+`ust-consumerd` [<options>]
+
+## DESCRIPTION
+
+`ust-consumerd` is a program that collects trace data and writes it to the disk.
+
+## OPTIONS
+
+These programs follow the usual GNU command line syntax, with long options
+starting with two dashes(`-'). A summary of options is included below.
+
+ * `-h`, `--help`:
+ Show summary of options.
+
+ * `-o` <DIR>:
+ Specify the directory where to output the traces.
+
+ * `-s` <PATH>:
+ Specify the path to use for the daemon socket.
+
+ * `-d`:
+ Start as a daemon.
+
+ * `-p`, `--pidfile`=<FILE>:
+ Write the PID in this file (when using -d).
+
+ * `-V`, `--version`:
+ Show version of program.
+
+## SEE ALSO
+
+ustctl(1), usttrace(1)
+
+## AUTHOR
+
+`ust-consumerd` was written by Pierre-Marc Fournier.
+
+This manual page was written by Jon Bernard <jbernard@debian.org>, for
+the Debian project (and may be used by others).
.P
Finally, when \fB\-\-destroy\-trace\fR is used, the trace buffers are unallocated.
However, the memory may not be effectively freed until the daemon finishes to
-collect them. When the trace is being collected by \fBustd\fR, this command
+collect them. When the trace is being collected by \fBust-consumerd\fR, this command
guarantees its full contents is flushed to the disk.
.
.SH "STRUCTURE OF A TRACE"
count is set with \fB\-\-set\-subbuf\-num\fR.
.
.SH "SEE ALSO"
-usttrace(1), ustd(1)
+usttrace(1), ust-consumerd(1)
.
.SH "AUTHOR"
\fBustctl\fR was written by Pierre\-Marc Fournier.
Finally, when `--destroy-trace` is used, the trace buffers are unallocated.
However, the memory may not be effectively freed until the daemon finishes to
-collect them. When the trace is being collected by `ustd`, this command
+collect them. When the trace is being collected by `ust-consumerd`, this command
guarantees its full contents is flushed to the disk.
## STRUCTURE OF A TRACE
## SEE ALSO
-usttrace(1), ustd(1)
+usttrace(1), ust-consumerd(1)
## AUTHOR
+++ /dev/null
-.\" generated with Ronn/v0.5
-.\" http://github.com/rtomayko/ronn/
-.
-.TH "USTD" "1" "May 2010" "" ""
-.
-.SH "NAME"
-\fBustd\fR \-\- a daemon that collects trace data and writes it to the disk
-.
-.SH "SYNOPSIS"
-\fBustd\fR [\fIoptions\fR]
-.
-.SH "DESCRIPTION"
-\fBustd\fR is a program that collects trace data and writes it to the disk.
-.
-.SH "OPTIONS"
-These programs follow the usual GNU command line syntax, with long options
-starting with two dashes(`\-'). A summary of options is included below.
-.
-.TP
-\fB\-h\fR, \fB\-\-help\fR
-Show summary of options.
-.
-.TP
-\fB\-o\fR \fIDIR\fR
-Specify the directory where to output the traces.
-.
-.TP
-\fB\-s\fR \fIPATH\fR
-Specify the path to use for the daemon socket.
-.
-.TP
-\fB\-d\fR
-Start as a daemon.
-.
-.TP
-\fB\-p\fR, \fB\-\-pidfile\fR=\fIFILE\fR
-Write the PID in this file (when using \-d).
-.
-.TP
-\fB\-V\fR, \fB\-\-version\fR
-Show version of program.
-.
-.SH "SEE ALSO"
-ustctl(1), usttrace(1)
-.
-.SH "AUTHOR"
-\fBustd\fR was written by Pierre\-Marc Fournier.
-.
-.P
-This manual page was written by Jon Bernard <jbernard@debian.org>, for
-the Debian project (and may be used by others).
+++ /dev/null
-ustd(1) -- a daemon that collects trace data and writes it to the disk
-======================================================================
-
-## SYNOPSIS
-
-`ustd` [<options>]
-
-## DESCRIPTION
-
-`ustd` is a program that collects trace data and writes it to the disk.
-
-## OPTIONS
-
-These programs follow the usual GNU command line syntax, with long options
-starting with two dashes(`-'). A summary of options is included below.
-
- * `-h`, `--help`:
- Show summary of options.
-
- * `-o` <DIR>:
- Specify the directory where to output the traces.
-
- * `-s` <PATH>:
- Specify the path to use for the daemon socket.
-
- * `-d`:
- Start as a daemon.
-
- * `-p`, `--pidfile`=<FILE>:
- Write the PID in this file (when using -d).
-
- * `-V`, `--version`:
- Show version of program.
-
-## SEE ALSO
-
-ustctl(1), usttrace(1)
-
-## AUTHOR
-
-`ustd` was written by Pierre-Marc Fournier.
-
-This manual page was written by Jon Bernard <jbernard@debian.org>, for
-the Debian project (and may be used by others).
Print the location of the last trace saved in the usttrace output directory.
.
.SH "SEE ALSO"
-ustctl(1), ustd(1)
+ustctl(1), ust-consumerd(1)
.
.SH "AUTHOR"
\fBusttrace\fR was written by Pierre\-Marc Fournier.
## SEE ALSO
-ustctl(1), ustd(1)
+ustctl(1), ust-consumerd(1)
## AUTHOR
ust/kcompat/types.h \
ust/kcompat/stringify.h \
ust/ustcmd.h \
- ust/ustd.h
+ ust/ustconsumer.h
noinst_HEADERS = share.h usterr.h ust_snprintf.h
--- /dev/null
+/*
+ * libustconsumer 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 _USTCONSUMER_H
+#define _USTCONSUMER_H
+
+#include <pthread.h>
+#include <dirent.h>
+#include <ust/kcompat/kcompat.h>
+#include <urcu/list.h>
+
+#define USTCONSUMER_DEFAULT_TRACE_PATH "/tmp/usttrace"
+
+struct ustcomm_sock;
+
+struct buffer_info {
+ char *name;
+ char *trace;
+ char *channel;
+ int channel_cpu;
+
+ pid_t pid;
+ int app_sock;
+ /* The pipe file descriptor */
+ int pipe_fd;
+
+ int shmid;
+ int bufstruct_shmid;
+
+ /* the buffer memory */
+ void *mem;
+ /* buffer size */
+ int memlen;
+ /* number of subbuffers in buffer */
+ int n_subbufs;
+ /* size of each subbuffer */
+ int subbuf_size;
+
+ /* the buffer information struct */
+ void *bufstruct_mem;
+
+ long consumed_old;
+
+ s64 pidunique;
+
+ void *user_data;
+};
+
+struct ustconsumer_callbacks;
+
+/**
+ * struct ustconsumer_instance - Contains the data associated with a trace instance.
+ * The lib user can read but MUST NOT change any attributes but callbacks.
+ * @callbacks: Contains the necessary callbacks for a tracing session.
+ */
+struct ustconsumer_instance {
+ struct ustconsumer_callbacks *callbacks;
+ int quit_program;
+ int is_init;
+ struct cds_list_head connections;
+ int epoll_fd;
+ struct ustcomm_sock *listen_sock;
+ char *sock_path;
+ pthread_mutex_t mutex;
+ int active_buffers;
+};
+
+/**
+* struct ustconsumer_callbacks - Contains the necessary callbacks for a tracing
+* session. The user can set the unnecessary functions to NULL if he does not
+* need them.
+*/
+struct ustconsumer_callbacks {
+ /**
+ * on_open_buffer - Is called after a buffer is attached to process memory
+ *
+ * @data: pointer to the callbacks structure that has been passed to the
+ * library.
+ * @buf: structure that contains the data associated with the buffer
+ *
+ * Returns 0 if the callback succeeds else not 0.
+ *
+ * It has to be thread safe, because it is called by many threads.
+ */
+ int (*on_open_buffer)(struct ustconsumer_callbacks *data,
+ struct buffer_info *buf);
+
+ /**
+ * on_close_buffer - Is called after a buffer is detached from process memory
+ *
+ * @data: pointer to the callbacks structure that has been passed to the
+ * library.
+ * @buf: structure that contains the data associated with the buffer
+ *
+ * Returns 0 if the callback succeeds else not 0.
+ *
+ * It has to be thread safe, because it is called by many threads.
+ */
+ int (*on_close_buffer)(struct ustconsumer_callbacks *data,
+ struct buffer_info *buf);
+
+ /**
+ * on_read_subbuffer - Is called after a subbuffer is a reserved.
+ *
+ * @data: pointer to the callbacks structure that has been passed to the
+ * library.
+ * @buf: structure that contains the data associated with the buffer
+ *
+ * Returns 0 if the callback succeeds else not 0.
+ *
+ * It has to be thread safe, because it is called by many threads.
+ */
+ int (*on_read_subbuffer)(struct ustconsumer_callbacks *data,
+ struct buffer_info *buf);
+
+ /**
+ * on_read_partial_subbuffer - Is called when an incomplete subbuffer
+ * is being salvaged from an app crash
+ *
+ * @data: pointer to the callbacks structure that has been passed to the
+ * library.
+ * @buf: structure that contains the data associated with the buffer
+ * @subbuf_index: index of the subbuffer to read in the buffer
+ * @valid_length: number of bytes considered safe to read
+ *
+ * Returns 0 if the callback succeeds else not 0.
+ *
+ * It has to be thread safe, because it is called by many threads.
+ */
+ int (*on_read_partial_subbuffer)(struct ustconsumer_callbacks *data,
+ struct buffer_info *buf,
+ long subbuf_index,
+ unsigned long valid_length);
+
+ /**
+ * on_put_error - Is called when a put error has occured and the last
+ * subbuffer read is no longer safe to keep
+ *
+ * @data: pointer to the callbacks structure that has been passed to the
+ * library.
+ * @buf: structure that contains the data associated with the buffer
+ *
+ * Returns 0 if the callback succeeds else not 0.
+ *
+ * It has to be thread safe, because it is called by many threads.
+ */
+ int (*on_put_error)(struct ustconsumer_callbacks *data,
+ struct buffer_info *buf);
+
+ /**
+ * on_new_thread - Is called when a new thread is created
+ *
+ * @data: pointer to the callbacks structure that has been passed to the
+ * library.
+ *
+ * Returns 0 if the callback succeeds else not 0.
+ *
+ * It has to be thread safe, because it is called by many threads.
+ */
+ int (*on_new_thread)(struct ustconsumer_callbacks *data);
+
+ /**
+ * on_close_thread - Is called just before a thread is destroyed
+ *
+ * @data: pointer to the callbacks structure that has been passed to the
+ * library.
+ *
+ * Returns 0 if the callback succeeds else not 0.
+ *
+ * It has to be thread safe, because it is called by many threads.
+ */
+ int (*on_close_thread)(struct ustconsumer_callbacks *data);
+
+ /**
+ * on_trace_end - Is called at the very end of the tracing session. At
+ * this time, everything has been closed and the threads have
+ * been destroyed.
+ *
+ * @instance: pointer to the instance structure that has been passed to
+ * the library.
+ *
+ * Returns 0 if the callback succeeds else not 0.
+ *
+ * After this callback is called, no other callback will be called
+ * again and the tracing instance will be deleted automatically by
+ * libustconsumer. After this call, the user must not use the libustconsumer instance.
+ */
+ int (*on_trace_end)(struct ustconsumer_instance *instance);
+
+ /**
+ * The library's data.
+ */
+ void *user_data;
+};
+
+/**
+ * ustconsumer_new_instance - Is called to create a new tracing session.
+ *
+ * @callbacks: Pointer to a callbacks structure that contain the user
+ * callbacks and data.
+ * @sock_path: Path to the socket used for communication with the traced app
+ *
+ * Returns the instance if the function succeeds else NULL.
+ */
+struct ustconsumer_instance *
+ustconsumer_new_instance(
+ struct ustconsumer_callbacks *callbacks, char *sock_path);
+
+/**
+ * ustconsumer_delete_instance - Is called to free a ustconsumer_instance struct
+ *
+ * @instance: The tracing session instance that needs to be freed.
+ *
+ * This function should only be called if the instance has not been started,
+ * as it will automatically be called at the end of ustconsumer_start_instance.
+ */
+void ustconsumer_delete_instance(struct ustconsumer_instance *instance);
+
+/**
+ * ustconsumer_init_instance - Is called to initiliaze a new tracing session
+ *
+ * @instance: The tracing session instance that needs to be started.
+ *
+ * Returns 0 if the function succeeds.
+ *
+ * This function must be called between ustconsumer_new_instance and
+ * ustconsumer_start_instance. It sets up the communication between the library
+ * and the tracing application.
+ */
+int ustconsumer_init_instance(struct ustconsumer_instance *instance);
+
+/**
+ * ustconsumer_start_instance - Is called to start a new tracing session.
+ *
+ * @instance: The tracing session instance that needs to be started.
+ *
+ * Returns 0 if the function succeeds.
+ *
+ * This is a blocking function. The caller will be blocked on it until the
+ * tracing session is stopped by the user using ustconsumer_stop_instance or until
+ * the traced application terminates
+ */
+int ustconsumer_start_instance(struct ustconsumer_instance *instance);
+
+/**
+ * ustconsumer_stop_instance - Is called to stop a tracing session.
+ *
+ * @instance: The tracing session instance that needs to be stoped.
+ * @send_msg: If true, a message will be sent to the listening thread through
+ * the daemon socket to force it to return from the poll syscall
+ * and realize that it must close. This is not necessary if the
+ * instance is being stopped as part of an interrupt handler, as
+ * the interrupt itself will cause poll to return.
+ *
+ * Returns 0 if the function succeeds.
+ *
+ * This function returns immediately, it only tells libustconsumer to stop the
+ * instance. The on_trace_end callback will be called when the tracing session
+ * will really be stopped. The instance is deleted automatically by libustconsumer
+ * after on_trace_end is called.
+ */
+int ustconsumer_stop_instance(struct ustconsumer_instance *instance, int send_msg);
+
+#endif /* _USTCONSUMER_H */
+
+++ /dev/null
-/*
- * 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 USTD_H
-#define USTD_H
-
-#include <pthread.h>
-#include <dirent.h>
-#include <ust/kcompat/kcompat.h>
-#include <urcu/list.h>
-
-#define USTD_DEFAULT_TRACE_PATH "/tmp/usttrace"
-
-struct ustcomm_sock;
-
-struct buffer_info {
- char *name;
- char *trace;
- char *channel;
- int channel_cpu;
-
- pid_t pid;
- int app_sock;
- /* The pipe file descriptor */
- int pipe_fd;
-
- int shmid;
- int bufstruct_shmid;
-
- /* the buffer memory */
- void *mem;
- /* buffer size */
- int memlen;
- /* number of subbuffers in buffer */
- int n_subbufs;
- /* size of each subbuffer */
- int subbuf_size;
-
- /* the buffer information struct */
- void *bufstruct_mem;
-
- long consumed_old;
-
- s64 pidunique;
-
- void *user_data;
-};
-
-struct libustd_callbacks;
-
-/**
- * struct libustd_instance - Contains the data associated with a trace instance.
- * The lib user can read but MUST NOT change any attributes but callbacks.
- * @callbacks: Contains the necessary callbacks for a tracing session.
- */
-struct libustd_instance {
- struct libustd_callbacks *callbacks;
- int quit_program;
- int is_init;
- struct cds_list_head connections;
- int epoll_fd;
- struct ustcomm_sock *listen_sock;
- char *sock_path;
- pthread_mutex_t mutex;
- int active_buffers;
-};
-
-/**
-* struct libustd_callbacks - Contains the necessary callbacks for a tracing
-* session. The user can set the unnecessary functions to NULL if he does not
-* need them.
-*/
-struct libustd_callbacks {
- /**
- * on_open_buffer - Is called after a buffer is attached to process memory
- *
- * @data: pointer to the callbacks structure that has been passed to the
- * library.
- * @buf: structure that contains the data associated with the buffer
- *
- * Returns 0 if the callback succeeds else not 0.
- *
- * It has to be thread safe, because it is called by many threads.
- */
- int (*on_open_buffer)(struct libustd_callbacks *data,
- struct buffer_info *buf);
-
- /**
- * on_close_buffer - Is called after a buffer is detached from process memory
- *
- * @data: pointer to the callbacks structure that has been passed to the
- * library.
- * @buf: structure that contains the data associated with the buffer
- *
- * Returns 0 if the callback succeeds else not 0.
- *
- * It has to be thread safe, because it is called by many threads.
- */
- int (*on_close_buffer)(struct libustd_callbacks *data,
- struct buffer_info *buf);
-
- /**
- * on_read_subbuffer - Is called after a subbuffer is a reserved.
- *
- * @data: pointer to the callbacks structure that has been passed to the
- * library.
- * @buf: structure that contains the data associated with the buffer
- *
- * Returns 0 if the callback succeeds else not 0.
- *
- * It has to be thread safe, because it is called by many threads.
- */
- int (*on_read_subbuffer)(struct libustd_callbacks *data,
- struct buffer_info *buf);
-
- /**
- * on_read_partial_subbuffer - Is called when an incomplete subbuffer
- * is being salvaged from an app crash
- *
- * @data: pointer to the callbacks structure that has been passed to the
- * library.
- * @buf: structure that contains the data associated with the buffer
- * @subbuf_index: index of the subbuffer to read in the buffer
- * @valid_length: number of bytes considered safe to read
- *
- * Returns 0 if the callback succeeds else not 0.
- *
- * It has to be thread safe, because it is called by many threads.
- */
- int (*on_read_partial_subbuffer)(struct libustd_callbacks *data,
- struct buffer_info *buf,
- long subbuf_index,
- unsigned long valid_length);
-
- /**
- * on_put_error - Is called when a put error has occured and the last
- * subbuffer read is no longer safe to keep
- *
- * @data: pointer to the callbacks structure that has been passed to the
- * library.
- * @buf: structure that contains the data associated with the buffer
- *
- * Returns 0 if the callback succeeds else not 0.
- *
- * It has to be thread safe, because it is called by many threads.
- */
- int (*on_put_error)(struct libustd_callbacks *data,
- struct buffer_info *buf);
-
- /**
- * on_new_thread - Is called when a new thread is created
- *
- * @data: pointer to the callbacks structure that has been passed to the
- * library.
- *
- * Returns 0 if the callback succeeds else not 0.
- *
- * It has to be thread safe, because it is called by many threads.
- */
- int (*on_new_thread)(struct libustd_callbacks *data);
-
- /**
- * on_close_thread - Is called just before a thread is destroyed
- *
- * @data: pointer to the callbacks structure that has been passed to the
- * library.
- *
- * Returns 0 if the callback succeeds else not 0.
- *
- * It has to be thread safe, because it is called by many threads.
- */
- int (*on_close_thread)(struct libustd_callbacks *data);
-
- /**
- * on_trace_end - Is called at the very end of the tracing session. At
- * this time, everything has been closed and the threads have
- * been destroyed.
- *
- * @instance: pointer to the instance structure that has been passed to
- * the library.
- *
- * Returns 0 if the callback succeeds else not 0.
- *
- * After this callback is called, no other callback will be called
- * again and the tracing instance will be deleted automatically by
- * libustd. After this call, the user must not use the libustd instance.
- */
- int (*on_trace_end)(struct libustd_instance *instance);
-
- /**
- * The library's data.
- */
- void *user_data;
-};
-
-/**
- * libustd_new_instance - Is called to create a new tracing session.
- *
- * @callbacks: Pointer to a callbacks structure that contain the user
- * callbacks and data.
- * @sock_path: Path to the socket used for communication with the traced app
- *
- * Returns the instance if the function succeeds else NULL.
- */
-struct libustd_instance *
-libustd_new_instance(
- struct libustd_callbacks *callbacks, char *sock_path);
-
-/**
- * libustd_delete_instance - Is called to free a libustd_instance struct
- *
- * @instance: The tracing session instance that needs to be freed.
- *
- * This function should only be called if the instance has not been started,
- * as it will automatically be called at the end of libustd_start_instance.
- */
-void libustd_delete_instance(struct libustd_instance *instance);
-
-/**
- * libustd_init_instance - Is called to initiliaze a new tracing session
- *
- * @instance: The tracing session instance that needs to be started.
- *
- * Returns 0 if the function succeeds.
- *
- * This function must be called between libustd_new_instance and
- * libustd_start_instance. It sets up the communication between the library
- * and the tracing application.
- */
-int libustd_init_instance(struct libustd_instance *instance);
-
-/**
- * libustd_start_instance - Is called to start a new tracing session.
- *
- * @instance: The tracing session instance that needs to be started.
- *
- * Returns 0 if the function succeeds.
- *
- * This is a blocking function. The caller will be blocked on it until the
- * tracing session is stopped by the user using libustd_stop_instance or until
- * the traced application terminates
- */
-int libustd_start_instance(struct libustd_instance *instance);
-
-/**
- * libustd_stop_instance - Is called to stop a tracing session.
- *
- * @instance: The tracing session instance that needs to be stoped.
- * @send_msg: If true, a message will be sent to the listening thread through
- * the daemon socket to force it to return from the poll syscall
- * and realize that it must close. This is not necessary if the
- * instance is being stopped as part of an interrupt handler, as
- * the interrupt itself will cause poll to return.
- *
- * Returns 0 if the function succeeds.
- *
- * This function returns immediately, it only tells libustd to stop the
- * instance. The on_trace_end callback will be called when the tracing session
- * will really be stopped. The instance is deleted automatically by libustd
- * after on_trace_end is called.
- */
-int libustd_stop_instance(struct libustd_instance *instance, int send_msg);
-
-#endif /* USTD_H */
-
unlock_trace_events();
}
-static int connect_ustd(void)
+static int connect_ustconsumer(void)
{
int result, fd;
- char default_daemon_path[] = SOCK_DIR "/ustd";
+ char default_daemon_path[] = SOCK_DIR "/ustconsumer";
char *explicit_daemon_path, *daemon_path;
explicit_daemon_path = getenv("UST_DAEMON_SOCKET");
result = ustcomm_connect_path(daemon_path, &fd);
if (result < 0) {
- WARN("connect_ustd failed, daemon_path: %s",
+ WARN("connect_ustconsumer failed, daemon_path: %s",
daemon_path);
return result;
}
struct ust_trace *trace;
const char *ch_name;
- sock = connect_ustd();
+ sock = connect_ustconsumer();
if (sock < 0) {
return;
}
- DBG("Connected to ustd");
+ DBG("Connected to ustconsumer");
ltt_lock_traces();
if (!sock_path_env) {
result = ustcomm_pack_single_field(reply_header,
sock_msg,
- SOCK_DIR "/ustd");
+ SOCK_DIR "/ustconsumer");
} else {
result = ustcomm_pack_single_field(reply_header,
}
/* This destructor keeps the process alive for a few seconds in order
- * to leave time to ustd to connect to its buffers. This is necessary
+ * to leave time for ustconsumer to connect to its buffers. This is necessary
* for programs whose execution is very short. It is also useful in all
* programs when tracing is started close to the end of the program
* execution.
}
if (dirent->d_type != DT_DIR &&
- !!strcmp(dirent->d_name, "ustd")) {
+ !!strcmp(dirent->d_name, "ust-consumer")) {
sscanf(dirent->d_name, "%u", (unsigned int *) &ret[i]);
/* FIXME: Here we previously called pid_is_online, which
--- /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.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, s64 *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;
+ }
+
+ /* 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->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, 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;
+ }
+
+ 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.
+ */
+ 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;
+
+ 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);
+
+ 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->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->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;
+ 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) {
+ 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;
+
+ result = fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if(result == -1) {
+ 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';
+
+ result = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if(result == -1) {
+ PERROR("connect");
+ }
+
+ while(bytes != sizeof(msg))
+ bytes += send(fd, msg, sizeof(msg), 0);
+
+ 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);
+ 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.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
+
+/* 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 *ustbuf = buf->bufstruct_mem;
+
+ long write_offset = uatomic_read(&ustbuf->offset);
+ long consumed_offset = uatomic_read(&ustbuf->consumed);
+
+ long i_subbuf;
+
+ DBG("processing dead buffer (%s)", buf->name);
+ DBG("consumed offset is %ld (%s)", consumed_offset, buf->name);
+ DBG("write offset is %ld (%s)", write_offset, buf->name);
+
+ /* First subbuf that we need to consume now. It is not modulo'd.
+ * Consumed_offset is the next byte to consume. */
+ long first_subbuf = consumed_offset / buf->subbuf_size;
+ /* Last subbuf that we need to consume now. It is not modulo'd.
+ * Write_offset is the next place to write so write_offset-1 is the
+ * last place written. */
+ long last_subbuf = (write_offset - 1) / buf->subbuf_size;
+
+ DBG("first_subbuf=%ld", first_subbuf);
+ DBG("last_subbuf=%ld", last_subbuf);
+
+ if(last_subbuf - first_subbuf >= buf->n_subbufs) {
+ DBG("an overflow has occurred, nothing can be recovered");
+ return;
+ }
+
+ /* Iterate on subbuffers to recover. */
+ for(i_subbuf = first_subbuf % buf->n_subbufs; ; i_subbuf++, i_subbuf %= buf->n_subbufs) {
+ /* 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. */
+ long commit_seq = uatomic_read(&ustbuf->commit_seq[i_subbuf]);
+
+ unsigned long valid_length = buf->subbuf_size;
+ long n_subbufs_order = get_count_order(buf->n_subbufs);
+ long commit_seq_mask = (~0UL >> n_subbufs_order);
+
+ struct ltt_subbuffer_header *header = (struct ltt_subbuffer_header *)((char *)buf->mem+i_subbuf*buf->subbuf_size);
+
+ if((commit_seq & commit_seq_mask) == 0) {
+ /* There is nothing to do. */
+ /* FIXME: is this needed? */
+ break;
+ }
+
+ /* Check if subbuf was fully written. This is from Mathieu's algorithm/paper. */
+ /* FIXME: not sure data_size = 0xffffffff when the buffer is not full. It might
+ * take the value of the header size initially */
+ if (((commit_seq - buf->subbuf_size) & commit_seq_mask)
+ - (USTD_BUFFER_TRUNC(consumed_offset, buf) >> n_subbufs_order) == 0
+ && header->data_size != 0xffffffff && header->sb_size != 0xffffffff) {
+ /* If it was, 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 (%d) with valid_length = %ld", i_subbuf, 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.
+ */
+
+ valid_length = commit_seq & (buf->subbuf_size-1);
+ DBG("writing unfull subbuffer (%d) with valid_length = %ld", i_subbuf, valid_length);
+ header->data_size = valid_length;
+ header->sb_size = PAGE_ALIGN(valid_length);
+ assert(i_subbuf == (last_subbuf % buf->n_subbufs));
+ }
+
+ if(callbacks->on_read_partial_subbuffer)
+ callbacks->on_read_partial_subbuffer(callbacks, buf, i_subbuf, valid_length);
+
+ if(i_subbuf == last_subbuf % buf->n_subbufs)
+ break;
+ }
+}
+
--- /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)/libust -I$(top_srcdir)/libustcomm \
- -I$(top_srcdir)/include
-AM_CFLAGS = -fno-strict-aliasing
-
-lib_LTLIBRARIES = libustd.la
-
-libustd_la_SOURCES = libustd.c lowlevel.c lowlevel.h
-
-libustd_la_LDFLAGS = -no-undefined -version-info 0:0:0
-
-libustd_la_LIBADD = \
- -lpthread \
- $(top_builddir)/snprintf/libustsnprintf.la \
- $(top_builddir)/libustcomm/libustcomm.la
-
-libustd_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/ustd.h>
-#include "lowlevel.h"
-#include "usterr.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 libustd_instance *instance = arg;
- pthread_mutex_lock(&instance->mutex);
- instance->active_buffers--;
- pthread_mutex_unlock(&instance->mutex);
-}
-
-static int get_pidunique(int sock, s64 *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 libustd_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;
- }
-
- /* 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 libustd_callbacks *callbacks,
- struct buffer_info *buf)
-{
- int result;
-
- 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 libustd_instance *instance, struct buffer_info *buf)
-{
- int result, 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;
- }
-
- 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.
- */
- 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 libustd_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;
-
- 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);
-
- free((void *)args->channel);
- free(args);
- return NULL;
-}
-
-int start_consuming_buffer(struct libustd_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->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->channel_cpu);
-
- return 0;
-}
-static void process_client_cmd(int sock, struct ustcomm_header *req_header,
- char *recvbuf, struct libustd_instance *instance)
-{
- int result;
- struct ustcomm_header _res_header;
- 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 libustd_start_instance(struct libustd_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("libustd 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("libustd_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("libustd_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) {
- 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);
-
- libustd_delete_instance(instance);
-
- return 0;
-}
-
-/* FIXME: threads and connections !? */
-void libustd_delete_instance(struct libustd_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 libustd_stop_instance(struct libustd_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;
-
- result = fd = socket(PF_UNIX, SOCK_STREAM, 0);
- if(result == -1) {
- 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';
-
- result = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
- if(result == -1) {
- PERROR("connect");
- }
-
- while(bytes != sizeof(msg))
- bytes += send(fd, msg, sizeof(msg), 0);
-
- close(fd);
-
- return 0;
-}
-
-struct libustd_instance
-*libustd_new_instance(struct libustd_callbacks *callbacks,
- char *sock_path)
-{
- struct libustd_instance *instance =
- zmalloc(sizeof(struct libustd_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_ustd_socket(struct libustd_instance *instance)
-{
- char *name;
-
- if (instance->sock_path) {
- if (asprintf(&name, "%s", instance->sock_path) < 0) {
- ERR("ustcomm_init_ustd : 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);
- if (result == -1) {
- ERR("Unable to create socket directory %s", SOCK_DIR);
- return -1;
- }
-
- if (asprintf(&name, "%s/%s", SOCK_DIR, "ustd") < 0) {
- ERR("ustcomm_init_ustd : asprintf failed (%s/ustd)",
- 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 libustd_init_instance(struct libustd_instance *instance)
-{
- int result;
- result = init_ustd_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/ustd.h"
-#include "buffers.h"
-#include "tracer.h"
-#include "usterr.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
-
-/* 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 libustd_callbacks *callbacks, struct buffer_info *buf)
-{
- struct ust_buffer *ustbuf = buf->bufstruct_mem;
-
- long write_offset = uatomic_read(&ustbuf->offset);
- long consumed_offset = uatomic_read(&ustbuf->consumed);
-
- long i_subbuf;
-
- DBG("processing dead buffer (%s)", buf->name);
- DBG("consumed offset is %ld (%s)", consumed_offset, buf->name);
- DBG("write offset is %ld (%s)", write_offset, buf->name);
-
- /* First subbuf that we need to consume now. It is not modulo'd.
- * Consumed_offset is the next byte to consume. */
- long first_subbuf = consumed_offset / buf->subbuf_size;
- /* Last subbuf that we need to consume now. It is not modulo'd.
- * Write_offset is the next place to write so write_offset-1 is the
- * last place written. */
- long last_subbuf = (write_offset - 1) / buf->subbuf_size;
-
- DBG("first_subbuf=%ld", first_subbuf);
- DBG("last_subbuf=%ld", last_subbuf);
-
- if(last_subbuf - first_subbuf >= buf->n_subbufs) {
- DBG("an overflow has occurred, nothing can be recovered");
- return;
- }
-
- /* Iterate on subbuffers to recover. */
- for(i_subbuf = first_subbuf % buf->n_subbufs; ; i_subbuf++, i_subbuf %= buf->n_subbufs) {
- /* 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. */
- long commit_seq = uatomic_read(&ustbuf->commit_seq[i_subbuf]);
-
- unsigned long valid_length = buf->subbuf_size;
- long n_subbufs_order = get_count_order(buf->n_subbufs);
- long commit_seq_mask = (~0UL >> n_subbufs_order);
-
- struct ltt_subbuffer_header *header = (struct ltt_subbuffer_header *)((char *)buf->mem+i_subbuf*buf->subbuf_size);
-
- if((commit_seq & commit_seq_mask) == 0) {
- /* There is nothing to do. */
- /* FIXME: is this needed? */
- break;
- }
-
- /* Check if subbuf was fully written. This is from Mathieu's algorithm/paper. */
- /* FIXME: not sure data_size = 0xffffffff when the buffer is not full. It might
- * take the value of the header size initially */
- if (((commit_seq - buf->subbuf_size) & commit_seq_mask)
- - (USTD_BUFFER_TRUNC(consumed_offset, buf) >> n_subbufs_order) == 0
- && header->data_size != 0xffffffff && header->sb_size != 0xffffffff) {
- /* If it was, 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 (%d) with valid_length = %ld", i_subbuf, 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.
- */
-
- valid_length = commit_seq & (buf->subbuf_size-1);
- DBG("writing unfull subbuffer (%d) with valid_length = %ld", i_subbuf, valid_length);
- header->data_size = valid_length;
- header->sb_size = PAGE_ALIGN(valid_length);
- assert(i_subbuf == (last_subbuf % buf->n_subbufs));
- }
-
- if(callbacks->on_read_partial_subbuffer)
- callbacks->on_read_partial_subbuffer(callbacks, buf, i_subbuf, valid_length);
-
- if(i_subbuf == last_subbuf % buf->n_subbufs)
- break;
- }
-}
-
+++ /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/ustd.h>
-
-void finish_consuming_dead_subbuffer(struct libustd_callbacks *callbacks, struct buffer_info *buf);
-size_t subbuffer_data_size(void *subbuf);
-
-#endif /* LOWLEVEL_H */
-
rm -rf "$TRACE_DIR"
mkdir "$TRACE_DIR"
-pidfilepath="/tmp/ust-testsuite-$USER-$(date +%Y%m%d%H%M%S%N)-ustd-pid"
+pidfilepath="/tmp/ust-testsuite-$USER-$(date +%Y%m%d%H%M%S%N)-ust-consumerd-pid"
mkfifo -m 0600 "$pidfilepath"
-ustd --pidfile "$pidfilepath" -o "$TRACE_DIR" >/dev/null 2>&1 &
-USTD_PID="$(<$pidfilepath)"
+ust-consumerd --pidfile "$pidfilepath" -o "$TRACE_DIR" >/dev/null 2>&1 &
+UST_CONSUMERD_PID="$(<$pidfilepath)"
LD_PRELOAD=/usr/local/lib/libust.so.0.0.0:/usr/local/lib/libustinstr-malloc.so find -L / >/dev/null 2>&1 &
PID=$!
okx ustctl --stop-trace $PID
okx ustctl --destroy-trace $PID
kill $PID
-kill -SIGTERM $USTD_PID
-wait $USTD_PID
+kill -SIGTERM $UST_CONSUMERD_PID
+wait $UST_CONSUMERD_PID
trace_matches -N "ust.malloc" "^ust.malloc:" "$TRACE_DIR"
simple_harness_run ./manual_mode_tracing.sh
-simple_harness_run ./valgrind_ustd.sh
+simple_harness_run ./valgrind_ust-consumerd.sh
simple_harness_run dlopen/dlopen.sh
--- /dev/null
+#!/bin/bash
+#
+# Copyright 2010 Ericsson AB
+#
+# This file is part of LTTng-UST.
+#
+# LTTng-UST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# LTTng-UST 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with LTTng-UST. If not, see <http://www.gnu.org/licenses/>.
+
+TESTDIR=$(dirname $0)
+
+source $TESTDIR/test_functions.sh
+source $TESTDIR/tap.sh
+
+starttest "ust-consumerd valgrind check"
+
+plan_tests 2
+
+TRACE_DIR="/tmp/ust-testsuite-ust-consumerdvalgrind-trace"
+rm -rf "$TRACE_DIR"
+mkdir "$TRACE_DIR"
+
+pidfilepath="/tmp/ust-testsuite-$USER-$(date +%Y%m%d%H%M%S%N)-ust-consumerd-pid"
+mkfifo -m 0600 "$pidfilepath"
+
+VALG_OUT=/tmp/ust-testsuite-valg.txt
+valgrind --suppressions=$TESTDIR/valgrind_suppress.txt -q ust-consumerd --pidfile "$pidfilepath" -o "$TRACE_DIR" >/dev/null 2>"$VALG_OUT" &
+VALG_PID=$!
+UST_CONSUMERD_PID="$(<$pidfilepath)"
+
+okx usttrace -s $TESTDIR/basic/.libs/basic
+
+kill -SIGTERM $UST_CONSUMERD_PID
+wait $!
+
+echo "Valgrind output is in $VALG_OUT"
+if [ -z "$(<$VALG_OUT)" ]; then
+ pass "Valgrind found no errors in ust-consumerd"
+else
+ fail "Valgrind found errors in ust-consumerd:"
+ cat $VALG_OUT | while read; do
+ diag $REPLY
+ done
+fi
+++ /dev/null
-#!/bin/bash
-#
-# Copyright 2010 Ericsson AB
-#
-# This file is part of LTTng-UST.
-#
-# LTTng-UST is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# LTTng-UST 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with LTTng-UST. If not, see <http://www.gnu.org/licenses/>.
-
-TESTDIR=$(dirname $0)
-
-source $TESTDIR/test_functions.sh
-source $TESTDIR/tap.sh
-
-starttest "ustd valgrind check"
-
-plan_tests 2
-
-TRACE_DIR="/tmp/ust-testsuite-ustdvalgrind-trace"
-rm -rf "$TRACE_DIR"
-mkdir "$TRACE_DIR"
-
-pidfilepath="/tmp/ust-testsuite-$USER-$(date +%Y%m%d%H%M%S%N)-ustd-pid"
-mkfifo -m 0600 "$pidfilepath"
-
-VALG_OUT=/tmp/ust-testsuite-valg.txt
-valgrind --suppressions=$TESTDIR/valgrind_suppress.txt -q ustd --pidfile "$pidfilepath" -o "$TRACE_DIR" >/dev/null 2>"$VALG_OUT" &
-VALG_PID=$!
-USTD_PID="$(<$pidfilepath)"
-
-okx usttrace -s $TESTDIR/basic/.libs/basic
-
-kill -SIGTERM $USTD_PID
-wait $!
-
-echo "Valgrind output is in $VALG_OUT"
-if [ -z "$(<$VALG_OUT)" ]; then
- pass "Valgrind found no errors in ustd"
-else
- fail "Valgrind found errors in ustd:"
- cat $VALG_OUT | while read; do
- diag $REPLY
- done
-fi
--- /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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <getopt.h>
+
+#include "ust/ustconsumer.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;
+ }
+
+ /* 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;
+ }
+ free(tmp);
+ }
+
+}
+
+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_%lld", trace_path, buf->pid, buf->pidunique) < 0) {
+ ERR("on_open_buffer : asprintf failed (%s/%u_%lld)",
+ 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_%lld/%s", trace_path, buf->pid, buf->pidunique, buf->name) < 0) {
+ ERR("on_open_buffer : asprintf failed (%s/%u_%lld/%s)",
+ trace_path, buf->pid, buf->pidunique, buf->name);
+ return 1;
+ }
+ result = fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 00600);
+ 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 = close(buf_local->file_fd);
+ free(buf_local);
+ if(result == -1) {
+ PERROR("close");
+ }
+ return 0;
+}
+
+int on_put_error(struct ustconsumer_callbacks *data, struct buffer_info *buf)
+{
+ 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)/libust -I$(top_srcdir)/libustcomm \
- -I$(top_srcdir)/include -I$(top_srcdir)/libustd
-AM_CFLAGS = -fno-strict-aliasing
-
-bin_PROGRAMS = ustd
-
-ustd_SOURCES = ustd.c
-
-ustd_LDADD = \
- $(top_builddir)/snprintf/libustsnprintf.la \
- $(top_builddir)/libustcomm/libustcomm.la \
- $(top_builddir)/libustd/libustd.la
-
-ustd_CFLAGS = -DUST_COMPONENT=ustd -fno-strict-aliasing
+++ /dev/null
-This is ustd, the UST daemon.
-
-This daemon is used to collect the traces for the traced programs.
+++ /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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-#include <getopt.h>
-
-#include "ust/ustd.h"
-#include "usterr.h"
-
-char *sock_path=NULL;
-char *trace_path=NULL;
-int daemon_mode = 0;
-char *pidfile = NULL;
-
-struct libustd_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 libustd_callbacks *data, struct buffer_info *buf)
-{
- return write_current_subbuffer(buf);
-}
-
-int on_read_partial_subbuffer(struct libustd_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;
- }
-
- /* 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;
- }
- free(tmp);
- }
-
-}
-
-int on_open_buffer(struct libustd_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(USTD_DEFAULT_TRACE_PATH);
- if(result == -1) {
- ERR("could not create directory %s", USTD_DEFAULT_TRACE_PATH);
- return 1;
- }
-
- trace_path = USTD_DEFAULT_TRACE_PATH;
- }
-
- if (asprintf(&tmp, "%s/%u_%lld", trace_path, buf->pid, buf->pidunique) < 0) {
- ERR("on_open_buffer : asprintf failed (%s/%u_%lld)",
- 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_%lld/%s", trace_path, buf->pid, buf->pidunique, buf->name) < 0) {
- ERR("on_open_buffer : asprintf failed (%s/%u_%lld/%s)",
- trace_path, buf->pid, buf->pidunique, buf->name);
- return 1;
- }
- result = fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 00600);
- 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 libustd_callbacks *data, struct buffer_info *buf)
-{
- struct buffer_info_local *buf_local = buf->user_data;
- int result = close(buf_local->file_fd);
- free(buf_local);
- if(result == -1) {
- PERROR("close");
- }
- return 0;
-}
-
-int on_put_error(struct libustd_callbacks *data, struct buffer_info *buf)
-{
- unwrite_last_subbuffer(buf);
-}
-
-struct libustd_callbacks *new_callbacks()
-{
- struct libustd_callbacks *callbacks =
- zmalloc(sizeof(struct libustd_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:\nustd 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)
-{
- libustd_stop_instance(instance, 0);
-}
-
-int start_ustd(int fd)
-{
- int result;
- sigset_t sigset;
- struct sigaction sa;
-
- struct libustd_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 = libustd_new_instance(callbacks, sock_path);
- if(!instance) {
- ERR("failed to create libustd instance");
- return 1;
- }
-
- result = libustd_init_instance(instance);
- if(result) {
- ERR("failed to initialize libustd 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");
- }
- }
-
- libustd_start_instance(instance);
-
- free(callbacks);
-
- return 0;
-}
-
-int start_ustd_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_ustd(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_ustd_daemon();
- }
- else {
- result = start_ustd(-1);
- }
-
- return result;
-}
function sighandler() {
echo "Caught Ctrl-C"
- if [ -z "$USTDPID" ]; then
- USTDPID="$(<$pidfilepath)"
+ if [ -z "$UST_CONSUMERDPID" ]; then
+ UST_CONSUMERDPID="$(<$pidfilepath)"
fi
# Tell the daemon to die
- kill -SIGTERM "$USTDPID"
+ kill -SIGTERM "$UST_CONSUMERDPID"
- echo "Waiting for ustd to shutdown..."
- wait "$USTDPID"
+ echo "Waiting for ust-consumerd to shutdown..."
+ wait "$UST_CONSUMERDPID"
rm "$pidfilepath"
}
USTTRACE_DIR="$(dirname $0)"
-if [ -x "${USTTRACE_DIR}/ustd/ustd" ] ; then
+if [ -x "${USTTRACE_DIR}/ust-consumerd/ust-consumerd" ] ; then
# Use the not installed libraries instead
- USTD="${USTTRACE_DIR}/ustd/ustd"
+ UST_CONSUMERD="${USTTRACE_DIR}/ust-consumerd/ust-consumerd"
LIBINTERFORK_PATH="${USTTRACE_DIR}/libustfork/.libs/libustfork.so"
LIBMALLOCWRAP_PATH="${USTTRACE_DIR}/libustinstr-malloc/.libs/libustinstr-malloc.so"
LIBUST_PATH="${USTTRACE_DIR}/libust/.libs/libust.so"
else
# Use the libraries that the dynamic link finds
- USTD="ustd"
- if [ ! -x "$(which ustd 2>/dev/null)" ]; then
- error "cannot find an executable ustd; make sure its location is in the PATH"
+ UST_CONSUMERD="ust-consumerd"
+ if [ ! -x "$(which ust-consumerd 2>/dev/null)" ]; then
+ error "cannot find an executable ust-consumerd; make sure its location is in the PATH"
exit 1
fi
LIBINTERFORK_PATH="libustfork.so"
fi
fi
-# Choose ustd socket path
-USTDSOCKPATH="/tmp/ustd-sock-$$"
+# Choose ust-consumerd socket path
+UST_CONSUMERDSOCKPATH="/tmp/ust-consumerd-sock-$$"
if [ "$arg_syswide_daemon" != "1" ];
then
- pidfilepath="/tmp/usttrace-$USER-$(date +%Y%m%d%H%M%S%N)-ustd-pid"
+ pidfilepath="/tmp/usttrace-$USER-$(date +%Y%m%d%H%M%S%N)-ust-consumerd-pid"
trap "sighandler $pidfilepath" SIGINT
mkfifo -m 0600 "$pidfilepath"
# Start daemon
- $USTD --pidfile "$pidfilepath" -s "$USTDSOCKPATH" -o "$OUTDIR" >"$OUTDIR/ustd.log" 2>&1 &
- # ustd sets up its server socket
- # ustd opens the pidfile, blocks because no one has opened it
+ $UST_CONSUMERD --pidfile "$pidfilepath" -s "$UST_CONSUMERDSOCKPATH" -o "$OUTDIR" >"$OUTDIR/ust-consumerd.log" 2>&1 &
+ # ust-consumerd sets up its server socket
+ # ust-consumerd opens the pidfile, blocks because no one has opened it
# we open pidfile
# we block reading pidfile
- # ustd writes to pidfile
- # ustd closes pidfile
+ # ust-consumerd writes to pidfile
+ # ust-consumerd closes pidfile
# we unblock reading pidfile
- USTDPID="$(<$pidfilepath)"
- export UST_DAEMON_SOCKET="$USTDSOCKPATH"
+ UST_CONSUMERDPID="$(<$pidfilepath)"
+ export UST_DAEMON_SOCKET="$UST_CONSUMERDSOCKPATH"
fi
# Establish the environment for the command
if [ "$arg_syswide_daemon" != "1" ];
then
# Tell the daemon to die
- kill -SIGTERM "$USTDPID"
+ kill -SIGTERM "$UST_CONSUMERDPID"
- echo "Waiting for ustd to shutdown..."
- wait "$USTDPID"
+ echo "Waiting for ust-consumerd to shutdown..."
+ wait "$UST_CONSUMERDPID"
rm "$pidfilepath"
fi