From: Nils Carlson Date: Wed, 5 Jan 2011 12:53:43 +0000 (+0100) Subject: Rename libustd to libustconsumer and ustd to ust-consumerd X-Git-Tag: v0.10~4 X-Git-Url: https://git.lttng.org/?p=ust.git;a=commitdiff_plain;h=9dc7b7ff797a5cbb1e9ffd59e053a04562f306c4 Rename libustd to libustconsumer and ustd to ust-consumerd 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 --- diff --git a/.gitignore b/.gitignore index 020c2f4..9935f11 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ stamp-h1 libtool ustctl/ustctl -ustd/ustd +ust-consumerd/ust-consumerd diff --git a/Makefile.am b/Makefile.am index 5132d59..249bf02 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,7 +5,7 @@ ACLOCAL_AMFLAGS = -I config # 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 diff --git a/README b/README index 8eaf618..a381c95 100644 --- a/README +++ b/README @@ -61,7 +61,7 @@ PACKAGE CONTENTS: - 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 @@ -83,8 +83,12 @@ PACKAGE CONTENTS: 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. diff --git a/TODO b/TODO index 46d5b64..0d07589 100644 --- a/TODO +++ b/TODO @@ -19,7 +19,7 @@ - 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 diff --git a/configure.ac b/configure.ac index 189ab12..b6cf946 100644 --- a/configure.ac +++ b/configure.ac @@ -129,8 +129,8 @@ AC_CONFIG_FILES([ 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 diff --git a/doc/Makefile.am b/doc/Makefile.am index 37a55ac..463203c 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1 +1 @@ -SUBDIRS = man info \ No newline at end of file +SUBDIRS = man info diff --git a/doc/info/ust.texi b/doc/info/ust.texi index e0565f3..a0ccdf1 100644 --- a/doc/info/ust.texi +++ b/doc/info/ust.texi @@ -92,7 +92,7 @@ Components licensed as GPL v2: @itemize @bullet @item ustctl @item libustcmd -@item ustd +@item ust-consumerd @end itemize @node Supported platforms @@ -112,7 +112,7 @@ The following packages are required: @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} @@ -385,11 +385,11 @@ First the daemon must be started. # 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. @@ -543,7 +543,7 @@ the application (or library) being linked to libust. 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 diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 7e18b8e..5adbba4 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -1,2 +1,2 @@ -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 diff --git a/doc/man/ust-consumerd.1 b/doc/man/ust-consumerd.1 new file mode 100644 index 0000000..c6ce6a4 --- /dev/null +++ b/doc/man/ust-consumerd.1 @@ -0,0 +1,51 @@ +.\" 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 , for +the Debian project (and may be used by others). diff --git a/doc/man/ust-consumerd.1.md b/doc/man/ust-consumerd.1.md new file mode 100644 index 0000000..1b93f47 --- /dev/null +++ b/doc/man/ust-consumerd.1.md @@ -0,0 +1,44 @@ +ust-consumerd(1) -- a daemon that collects trace data and writes it to the disk +====================================================================== + +## SYNOPSIS + +`ust-consumerd` [] + +## 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` : + Specify the directory where to output the traces. + + * `-s` : + Specify the path to use for the daemon socket. + + * `-d`: + Start as a daemon. + + * `-p`, `--pidfile`=: + 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). diff --git a/doc/man/ustctl.1 b/doc/man/ustctl.1 index 4f171c6..b0f97d9 100644 --- a/doc/man/ustctl.1 +++ b/doc/man/ustctl.1 @@ -114,7 +114,7 @@ have flushed to the disk the full contents of the buffer yet. .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" @@ -139,7 +139,7 @@ for a given channel may be chosen with \fB\-\-set\-subbuf\-size\fR while the sub 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. diff --git a/doc/man/ustctl.1.md b/doc/man/ustctl.1.md index 208e1dd..c8ad1f1 100644 --- a/doc/man/ustctl.1.md +++ b/doc/man/ustctl.1.md @@ -92,7 +92,7 @@ have flushed to the disk the full contents of the buffer yet. 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 @@ -118,7 +118,7 @@ count is set with `--set-subbuf-num`. ## SEE ALSO -usttrace(1), ustd(1) +usttrace(1), ust-consumerd(1) ## AUTHOR diff --git a/doc/man/ustd.1 b/doc/man/ustd.1 deleted file mode 100644 index f423978..0000000 --- a/doc/man/ustd.1 +++ /dev/null @@ -1,51 +0,0 @@ -.\" 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 , for -the Debian project (and may be used by others). diff --git a/doc/man/ustd.1.md b/doc/man/ustd.1.md deleted file mode 100644 index 296d1c4..0000000 --- a/doc/man/ustd.1.md +++ /dev/null @@ -1,44 +0,0 @@ -ustd(1) -- a daemon that collects trace data and writes it to the disk -====================================================================== - -## SYNOPSIS - -`ustd` [] - -## 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` : - Specify the directory where to output the traces. - - * `-s` : - Specify the path to use for the daemon socket. - - * `-d`: - Start as a daemon. - - * `-p`, `--pidfile`=: - 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). diff --git a/doc/man/usttrace.1 b/doc/man/usttrace.1 index e789e45..a56cfc4 100644 --- a/doc/man/usttrace.1 +++ b/doc/man/usttrace.1 @@ -91,7 +91,7 @@ Specify the number of subbuffers. 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. diff --git a/doc/man/usttrace.1.md b/doc/man/usttrace.1.md index 34acb36..75cff9a 100644 --- a/doc/man/usttrace.1.md +++ b/doc/man/usttrace.1.md @@ -64,7 +64,7 @@ starting with two dashes(`-'). A summary of options is included below. ## SEE ALSO -ustctl(1), ustd(1) +ustctl(1), ust-consumerd(1) ## AUTHOR diff --git a/include/Makefile.am b/include/Makefile.am index 0ace775..400e8b1 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -18,6 +18,6 @@ nobase_include_HEADERS = \ 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 diff --git a/include/ust/ustconsumer.h b/include/ust/ustconsumer.h new file mode 100644 index 0000000..e07b75e --- /dev/null +++ b/include/ust/ustconsumer.h @@ -0,0 +1,287 @@ +/* + * libustconsumer header file + * + * Copyright 2005-2010 - + * Mathieu Desnoyers + * Copyright 2010- + * Oumarou Dicko + * Michael Sills-Lavoie + * 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 + */ + +#ifndef _USTCONSUMER_H +#define _USTCONSUMER_H + +#include +#include +#include +#include + +#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 */ + diff --git a/include/ust/ustd.h b/include/ust/ustd.h deleted file mode 100644 index 6336e69..0000000 --- a/include/ust/ustd.h +++ /dev/null @@ -1,287 +0,0 @@ -/* - * libustd header file - * - * Copyright 2005-2010 - - * Mathieu Desnoyers - * Copyright 2010- - * Oumarou Dicko - * Michael Sills-Lavoie - * 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 - */ - -#ifndef USTD_H -#define USTD_H - -#include -#include -#include -#include - -#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 */ - diff --git a/libust/tracectl.c b/libust/tracectl.c index a7da945..b783c76 100644 --- a/libust/tracectl.c +++ b/libust/tracectl.c @@ -122,10 +122,10 @@ static void print_trace_events(FILE *fp) 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"); @@ -139,7 +139,7 @@ static int connect_ustd(void) 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; } @@ -194,12 +194,12 @@ static void inform_consumer_daemon(const char *trace_name) 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(); @@ -1007,7 +1007,7 @@ static void process_client_cmd(struct ustcomm_header *recv_header, 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, @@ -1496,7 +1496,7 @@ static void stop_listener(void) } /* 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. diff --git a/libustcmd/ustcmd.c b/libustcmd/ustcmd.c index 2c16e75..62c117f 100644 --- a/libustcmd/ustcmd.c +++ b/libustcmd/ustcmd.c @@ -104,7 +104,7 @@ pid_t *ustcmd_get_online_pids(void) } 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 diff --git a/libustconsumer/Makefile.am b/libustconsumer/Makefile.am new file mode 100644 index 0000000..1f87479 --- /dev/null +++ b/libustconsumer/Makefile.am @@ -0,0 +1,16 @@ +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 diff --git a/libustconsumer/libustconsumer.c b/libustconsumer/libustconsumer.c new file mode 100644 index 0000000..6cb3dbf --- /dev/null +++ b/libustconsumer/libustconsumer.c @@ -0,0 +1,891 @@ +/* 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#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; +} + diff --git a/libustconsumer/lowlevel.c b/libustconsumer/lowlevel.c new file mode 100644 index 0000000..7eb124b --- /dev/null +++ b/libustconsumer/lowlevel.c @@ -0,0 +1,143 @@ +/* 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 +#include +#include + +#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; + } +} + diff --git a/libustconsumer/lowlevel.h b/libustconsumer/lowlevel.h new file mode 100644 index 0000000..6ae6476 --- /dev/null +++ b/libustconsumer/lowlevel.h @@ -0,0 +1,35 @@ +/* + * lowlevel libustd header file + * + * Copyright 2005-2010 - + * Mathieu Desnoyers + * Copyright 2010- + * Oumarou Dicko + * Michael Sills-Lavoie + * 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 + */ + +#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 */ + diff --git a/libustd/Makefile.am b/libustd/Makefile.am deleted file mode 100644 index 1b9a961..0000000 --- a/libustd/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -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 - diff --git a/libustd/libustd.c b/libustd/libustd.c deleted file mode 100644 index 0dc6940..0000000 --- a/libustd/libustd.c +++ /dev/null @@ -1,891 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#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; -} - diff --git a/libustd/lowlevel.c b/libustd/lowlevel.c deleted file mode 100644 index a8abf92..0000000 --- a/libustd/lowlevel.c +++ /dev/null @@ -1,143 +0,0 @@ -/* 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 -#include -#include - -#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; - } -} - diff --git a/libustd/lowlevel.h b/libustd/lowlevel.h deleted file mode 100644 index a1d8da5..0000000 --- a/libustd/lowlevel.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * lowlevel libustd header file - * - * Copyright 2005-2010 - - * Mathieu Desnoyers - * Copyright 2010- - * Oumarou Dicko - * Michael Sills-Lavoie - * 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 - */ - -#ifndef LOWLEVEL_H -#define LOWLEVEL_H - -#include - -void finish_consuming_dead_subbuffer(struct libustd_callbacks *callbacks, struct buffer_info *buf); -size_t subbuffer_data_size(void *subbuf); - -#endif /* LOWLEVEL_H */ - diff --git a/tests/manual_mode_tracing.sh b/tests/manual_mode_tracing.sh index 3f203b1..09f0764 100755 --- a/tests/manual_mode_tracing.sh +++ b/tests/manual_mode_tracing.sh @@ -34,11 +34,11 @@ TRACE_DIR="/tmp/ust-testsuite-manual-trace" 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=$! @@ -54,7 +54,7 @@ sleep 0.5 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" diff --git a/tests/runtests b/tests/runtests index 9560c46..afc1e21 100755 --- a/tests/runtests +++ b/tests/runtests @@ -37,7 +37,7 @@ simple_harness_run test-libustinstr-malloc/test-libustinstr-malloc.sh 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 diff --git a/tests/valgrind_ust-consumerd.sh b/tests/valgrind_ust-consumerd.sh new file mode 100755 index 0000000..53b1208 --- /dev/null +++ b/tests/valgrind_ust-consumerd.sh @@ -0,0 +1,54 @@ +#!/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 . + +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 diff --git a/tests/valgrind_ustd.sh b/tests/valgrind_ustd.sh deleted file mode 100755 index 541d8ca..0000000 --- a/tests/valgrind_ustd.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/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 . - -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 diff --git a/ust-consumerd/Makefile.am b/ust-consumerd/Makefile.am new file mode 100644 index 0000000..01e3b82 --- /dev/null +++ b/ust-consumerd/Makefile.am @@ -0,0 +1,14 @@ +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 diff --git a/ust-consumerd/README b/ust-consumerd/README new file mode 100644 index 0000000..6df4015 --- /dev/null +++ b/ust-consumerd/README @@ -0,0 +1,3 @@ +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. diff --git a/ust-consumerd/ust-consumerd.c b/ust-consumerd/ust-consumerd.c new file mode 100644 index 0000000..fae4efa --- /dev/null +++ b/ust-consumerd/ust-consumerd.c @@ -0,0 +1,505 @@ +/* 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/ustd/Makefile.am b/ustd/Makefile.am deleted file mode 100644 index 991c717..0000000 --- a/ustd/Makefile.am +++ /dev/null @@ -1,14 +0,0 @@ -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 diff --git a/ustd/README b/ustd/README deleted file mode 100644 index 8fcd218..0000000 --- a/ustd/README +++ /dev/null @@ -1,3 +0,0 @@ -This is ustd, the UST daemon. - -This daemon is used to collect the traces for the traced programs. diff --git a/ustd/ustd.c b/ustd/ustd.c deleted file mode 100644 index e75fd9d..0000000 --- a/ustd/ustd.c +++ /dev/null @@ -1,505 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/usttrace b/usttrace index 94404dd..5d6729e 100755 --- a/usttrace +++ b/usttrace @@ -9,14 +9,14 @@ function error() { 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" @@ -24,17 +24,17 @@ function sighandler() { } 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" @@ -117,25 +117,25 @@ if [ ! -d "$OUTDIR" ]; then 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 @@ -206,10 +206,10 @@ fi 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