From: compudj Date: Sat, 11 Mar 2006 17:33:10 +0000 (+0000) Subject: initial move X-Git-Tag: 0.80~198 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=2727692a61acaf38e731277b6cc2a78a913d3e16;p=ltt-control.git initial move git-svn-id: http://ltt.polymtl.ca/svn@1685 04897980-b3bd-0310-b5e0-8ef037075253 --- diff --git a/ltt-control/AUTHORS b/ltt-control/AUTHORS new file mode 100644 index 0000000..032edf0 --- /dev/null +++ b/ltt-control/AUTHORS @@ -0,0 +1,25 @@ +Linux Trace Toolkit Viewer + +Contributors : + +Michel Dagenais (New trace format, lttv main) +Mathieu Desnoyers (Kernel Tracer, Directory structure, build with automake/conf, + lttv gui, control flow view, gui cooperative trace reading + scheduler with interruptible foreground and background + computation, detailed event list (rewrite), trace reading + library (rewrite)) +Benoit Des Ligneris, Éric Clement (Cluster adaptation, work in progress) +Xang-Xiu Yang (trace reading library and converter, lttv gui, + detailed event list and statistics view) +Tom Zanussi (RelayFS) + +Strongly inspired from the original Linux Trace Toolkit Visualizer made by +Karim Yaghmour. + +Linux Trace Toolkit Viewer, Copyright (C) 2004 + Michel Dagenais + Mathieu Desnoyers + Xang-Xiu Yang +Linux Trace Toolkit comes with ABSOLUTELY NO WARRANTY. +This is free software, and you are welcome to redistribute it +under certain conditions. See COPYING for details. diff --git a/ltt-control/Makefile.am b/ltt-control/Makefile.am new file mode 100644 index 0000000..66afa90 --- /dev/null +++ b/ltt-control/Makefile.am @@ -0,0 +1,6 @@ +# WARNING : ltt must come before lttv, so that the traceread library is +# up to date + +SUBDIRS = liblttctl ltt lttctl lttv lttd doc facilities + +EXTRA_DIST = QUICKSTART diff --git a/ltt-control/README b/ltt-control/README new file mode 100644 index 0000000..836472a --- /dev/null +++ b/ltt-control/README @@ -0,0 +1,77 @@ + +This package contains the trace reading library and trace viewing tools for +the new Linux Trace Toolkit trace format. It also contains the lttd, lttctl and +liblttctl programs which are necessary to obtain a trace. + +* Compiling + +gcc 3.2 or better +gtk 2.4 or better development libraries + (Debian : libgtk2.0, libgtk2.0-dev) + (Fedora : gtk2, gtk2-devel) + note : For Fedora users : this might require at least core 3 from Fedora, + or you might have to compile your own GTK2 library. +glib 2.4 or better development libraries + (Debian : libglib2.0-0, libglib2.0-dev) + (Fedora : glib2, glib2-devel) +libpopt development libraries + (Debian : libpopt0, libpopt-dev) + (Fedora : popt) +libpango development libraries + (Debian : libpango1.0, libpango1.0-dev) + (Fedora : pango, pango-devel) +libc6 development librairies + (Debian : libc6, libc6-dev) + (Fedora : glibc, glibc) + + +To compile the source tree from a tarball, simply follow these steps : + +- ./configure +- make +- make install + +After running ./configure, you can also go in specific subdirectories and +use make, make install. + + +* Quick Start + +See QUICKSTART + +* Source Tree Structure + +Here is the tree structure of the Linux Trace Toolkit Viewer package. + +ltt: new trace format reading library. +README: This file. +debian: debian config files (currently empty). +doc: Documentation. +doc/user: User related documentation. +doc/developer: Developer related documentation. +liblttctl: Library to communicate with the kernel tracer control module. +lttctl: Command line program to use the liblttctl library. +lttd: Linux Trace Toolkit daemon. +lttv: Linux Trace Toolkit trace analysis tool and viewer. +lttv/modules: Linux Trace Toolkit analysis tool and viewer plugin modules. +specs: RPM config files (currently empty). + + +* For Developers + +This source tree is based on the autotools suite from GNU to simplify +portability. Here are some things you should have on your system in order to +compile the subversion repository tree : + + +GNU autotools (automake-1,7, autoconf2.50, autoheader2.50) +(make sure your system wide "automake" points to version 1.7!) +GNU Libtool +(for more information, go to http://www.gnu.org/software/autoconf/) + +If you get the tree from the repository, you will need to use the autogen.sh +script. It calls all the GNU tools needed to prepare the tree configuration. + + + +Mathieu Desnoyers diff --git a/ltt-control/configure.in b/ltt-control/configure.in new file mode 100644 index 0000000..b9b8c83 --- /dev/null +++ b/ltt-control/configure.in @@ -0,0 +1,144 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License Version 2 as +# published by the Free Software Foundation; +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, +# MA 02111-1307, USA. + + + +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.57) +AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS) +#AC_WITH_LTDL # not needed ? +AM_INIT_AUTOMAKE(LinuxTraceToolkitViewer,0.8.31-11032006) +AM_CONFIG_HEADER(config.h) +AM_PROG_LIBTOOL + +AM_PATH_GLIB_2_0(2.4.0, ,AC_MSG_ERROR([glib is required in order to compile LinuxTraceToolkit - download it from ftp://ftp.gtk.org/pub/gtk]) , gmodule) + +AM_PATH_GTK_2_0(2.4.0, ,AC_MSG_ERROR([gtk is required in order to compile GUI - download it from ftp://ftp.gtk.org/pub/gtk]) , gmodule) + +AC_PATH_PROGS(BASH, bash) + +AC_SYS_LARGEFILE + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. +AC_CHECK_LIB([popt], [poptGetNextOpt], POPT_LIBS="-lpopt",AC_MSG_ERROR([libpopt is required in order to compile LinuxTraceToolkit]) ) +#AC_CHECK_LIB([m], [round], M_LIBS="-lm",AC_MSG_ERROR([Mathematical libraries are missing.]) ) + +AC_CHECK_LIB([util], [forkpty], UTIL_LIBS="-lutil", AC_MSG_ERROR([libutil is +required in order to compile LinuxTraceToolkit])) + + +# pthread for lttd +AC_CHECK_LIB(pthread, pthread_join,[THREAD_LIBS="-lpthread"], AC_MSG_ERROR([LinuxThreads is required in order to compile lttd])) + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h pthread.h]) + +AC_ISC_POSIX +AC_PROG_CC +AM_PROG_CC_STDC +AC_HEADER_STDC + +pkg_modules="gtk+-2.0 >= 2.0.0" +PKG_CHECK_MODULES(PACKAGE, [$pkg_modules]) +PACKAGE_CFLAGS="-Wall -Wformat" +AC_SUBST(PACKAGE_CFLAGS) +AC_SUBST(PACKAGE_LIBS) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_C_INLINE +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_HEADER_TIME + +# Checks for library functions. +AC_FUNC_ERROR_AT_LINE +#AC_FUNC_MALLOC +AC_FUNC_SELECT_ARGTYPES +AC_CHECK_FUNCS([select]) + +#CPPFLAGS="$CPPFLAGS -I" + +AM_CONDITIONAL(LTTVSTATIC, test "$enable_lttvstatic" = yes) +lttvlibdir="${libdir}/lttv" +lttvplugindir="${lttvlibdir}/plugins" +#lttlibdir="${libdir}/ltt" +top_lttvdir="\$(top_srcdir)/lttv" +top_lttvwindowdir="\$(top_srcdir)/lttv/modules/gui/lttvwindow" + +DEFAULT_INCLUDES="-I\$(top_srcdir) -I\$(top_lttvdir) -I\$(top_lttvwindowdir)" + +#CPPFLAGS="${GLIB_CFLAGS}" +#AC_SUBST(CPPFLAGS) + +lttincludedir="${includedir}/ltt" +lttvincludedir="${includedir}/lttv" +lttvwindowincludedir="${includedir}/lttvwindow" +lttctlincludedir="${includedir}/liblttctl" + +AC_SUBST(POPT_LIBS) +AC_SUBST(UTIL_LIBS) +AC_SUBST(THREAD_LIBS) +AC_SUBST(lttvlibdir) +AC_SUBST(lttvplugindir) +#AC_SUBST(lttlibdir) +AC_SUBST(top_lttvdir) +AC_SUBST(top_lttvwindowdir) +AC_SUBST(DEFAULT_INCLUDES) +AC_SUBST(lttincludedir) +AC_SUBST(lttvincludedir) +AC_SUBST(lttvwindowincludedir) +AC_SUBST(lttctlincludedir) + +AC_CONFIG_FILES([Makefile + liblttctl/Makefile + lttctl/Makefile + lttv/Makefile + lttv/lttv/Makefile + lttv/modules/Makefile + lttv/modules/text/Makefile + lttv/modules/gui/Makefile + lttv/modules/gui/lttvwindow/Makefile + lttv/modules/gui/interrupts/Makefile + lttv/modules/gui/diskperformance/Makefile + lttv/modules/gui/lttvwindow/lttvwindow/Makefile + lttv/modules/gui/lttvwindow/pixmaps/Makefile + lttv/modules/gui/controlflow/Makefile + lttv/modules/gui/detailedevents/Makefile + lttv/modules/gui/statistics/Makefile + lttv/modules/gui/filter/Makefile + lttv/modules/gui/tracecontrol/Makefile + lttd/Makefile + ltt/Makefile + doc/Makefile + doc/developer/Makefile + doc/developer/developer_guide/Makefile + doc/developer/developer_guide/docbook/Makefile + doc/developer/developer_guide/html/Makefile + doc/user/Makefile + doc/user/user_guide/Makefile + doc/user/user_guide/docbook/Makefile + doc/user/user_guide/html/Makefile + facilities/Makefile]) +AC_OUTPUT diff --git a/ltt-control/facilities/Makefile.am b/ltt-control/facilities/Makefile.am new file mode 100644 index 0000000..6e87ebd --- /dev/null +++ b/ltt-control/facilities/Makefile.am @@ -0,0 +1,41 @@ + +EXTRA_DIST = \ +core.xml \ +fs.xml \ +ipc.xml \ +kernel.xml \ +kernel_arch_arm.xml \ +kernel_arch_i386.xml \ +kernel_arch_mips.xml \ +stack_arch_i386.xml \ +locking.xml \ +memory.xml \ +network.xml \ +process.xml \ +socket.xml \ +statedump.xml \ +timer.xml \ +user_generic.xml \ +network_ip_interface.xml + +facilities_DATA = \ +core.xml \ +fs.xml \ +ipc.xml \ +kernel.xml \ +kernel_arch_arm.xml \ +kernel_arch_i386.xml \ +kernel_arch_mips.xml \ +stack_arch_i386.xml \ +locking.xml \ +memory.xml \ +network.xml \ +process.xml \ +socket.xml \ +statedump.xml \ +timer.xml \ +user_generic.xml \ +network_ip_interface.xml + + +facilitiesdir = $(pkgdatadir)/facilities diff --git a/ltt-control/facilities/core.xml b/ltt-control/facilities/core.xml new file mode 100644 index 0000000..796ffc5 --- /dev/null +++ b/ltt-control/facilities/core.xml @@ -0,0 +1,41 @@ + + + The core facility contains the basic tracing related events + + + Facility is loaded + + + + + + + + + + + + Facility is unloaded + + + + + System time values sent periodically to detect cycle counter + rollovers. Useful when only the 32 LSB of the TSC are saved in events + header : we save the full 64 bits in this event. + + + + + Facility is loaded while in state dump + + + + + + + + + + + diff --git a/ltt-control/facilities/fs.xml b/ltt-control/facilities/fs.xml new file mode 100644 index 0000000..75668d9 --- /dev/null +++ b/ltt-control/facilities/fs.xml @@ -0,0 +1,78 @@ + + + The fs facility contains events related to file system operation + + + Staring to wait for a buffer + Address of the buffer head. + + + + Ending to wait for a buffer + Address of the buffer head. + + + + Executing a file + File name + + + + + + + + + Opening a file + File name + + + + + + File descriptor + + + + Closing a file descriptor + File descriptor + + + + Reading from a file descriptor + File descriptor + Number of bytes to read + + + + Write to a file descriptor + File descriptor + Number of bytes to write + + + + Seek a file descriptor + File descriptor + Number of bytes to write + Number of bytes to write + + + + Do a IOCTL on a file descriptor + File descriptor + Command + Argument + + + + Do a select on a file descriptor + File descriptor + Time out + + + + Do a poll on a file descriptor + File descriptor + + + diff --git a/ltt-control/facilities/ipc.xml b/ltt-control/facilities/ipc.xml new file mode 100644 index 0000000..b4f0ee9 --- /dev/null +++ b/ltt-control/facilities/ipc.xml @@ -0,0 +1,31 @@ + + + The ipc facility contains events related to Inter Process Communication + + + + IPC call + Number of IPC call + + First argument + + + + Get an IPC message queue identifier + Message queue identifier + Message flags + + + + Get an IPC semaphore identifier + Semaphore identifier + Semaphore flags + + + + Get an IPC shared memory identifier + Shared memory identifier + Shared memory flags + + + diff --git a/ltt-control/facilities/kernel.xml b/ltt-control/facilities/kernel.xml new file mode 100644 index 0000000..66a5101 --- /dev/null +++ b/ltt-control/facilities/kernel.xml @@ -0,0 +1,63 @@ + + + The kernel facility has events related to kernel execution status. + + + + + + + + + + + + + Entry in a trap + Trap number + Address where trap occured + + + + Exit from a trap + + + + Soft IRQ entry + Soft IRQ number + + + + Soft IRQ exit + Soft IRQ number + + + + Tasklet entry + Tasklet priority + Tasklet function address + Tasklet data address + + + + Tasklet exit + Tasklet priority + Tasklet function address + Tasklet data address + + + + Entry in an irq + IRQ number + Are we executing kernel code + + + + Exit from an IRQ + + + diff --git a/ltt-control/facilities/kernel_arch_arm.xml b/ltt-control/facilities/kernel_arch_arm.xml new file mode 100644 index 0000000..981924d --- /dev/null +++ b/ltt-control/facilities/kernel_arch_arm.xml @@ -0,0 +1,319 @@ + + + The kernel facility has events related to kernel execution status for the arm architecture. + + + + + + + System call entry + Syscall entry number in entry.S + Address from which call was made + + + + System call exit + + diff --git a/ltt-control/facilities/kernel_arch_i386.xml b/ltt-control/facilities/kernel_arch_i386.xml new file mode 100644 index 0000000..ecab076 --- /dev/null +++ b/ltt-control/facilities/kernel_arch_i386.xml @@ -0,0 +1,312 @@ + + + The kernel facility has events related to kernel execution status for the i386 architecture. + + + + + + + System call entry + Syscall entry number in entry.S + Address from which call was made + + + + System call exit + + diff --git a/ltt-control/facilities/kernel_arch_mips.xml b/ltt-control/facilities/kernel_arch_mips.xml new file mode 100644 index 0000000..d2dc54a --- /dev/null +++ b/ltt-control/facilities/kernel_arch_mips.xml @@ -0,0 +1,808 @@ + + + The kernel facility has events related to kernel execution status + for the MIPS architecture. + + + + + + + System call entry + Syscall entry number in + entry.S + Address from which call was made + + + + System call exit + + diff --git a/ltt-control/facilities/locking.xml b/ltt-control/facilities/locking.xml new file mode 100644 index 0000000..9b80fcc --- /dev/null +++ b/ltt-control/facilities/locking.xml @@ -0,0 +1,25 @@ + + + The locking facility instruments the kernel locking. + + Takes a spinlock + Spinlock address + + + + Tries a spinlock + Spinlock address + + + + Get a spinlock + Spinlock address + + + + Releases a spinlock + Spinlock address + + + + diff --git a/ltt-control/facilities/memory.xml b/ltt-control/facilities/memory.xml new file mode 100644 index 0000000..abd7de5 --- /dev/null +++ b/ltt-control/facilities/memory.xml @@ -0,0 +1,37 @@ + + + The memory facility has memory management events. + + + Page allocation + Order of the page to allocate + Assigned page address, or 0 if failed. + + + + Page free + Order of the page to free + Address of the page to free. + + + + Page swapped into memory + Address of the page to swap in. + + + + Page swapped to disk + Address of the page to swap out. + + + + Staring to wait for a page + Address of the page we wait for. + + + + Ending wait for a page + Address of the page we wait for. + + + diff --git a/ltt-control/facilities/network.xml b/ltt-control/facilities/network.xml new file mode 100644 index 0000000..0bfcf9b --- /dev/null +++ b/ltt-control/facilities/network.xml @@ -0,0 +1,17 @@ + + + The network facility contains events related to low level network operations + + + A packet is arriving + Socket buffer pointer : identify the socket buffer + Protocol of the packet + + + + We send a packet + Socket buffer pointer : identify the socket buffer + Protocol of the packet + + + diff --git a/ltt-control/facilities/network_ip_interface.xml b/ltt-control/facilities/network_ip_interface.xml new file mode 100644 index 0000000..93f4dee --- /dev/null +++ b/ltt-control/facilities/network_ip_interface.xml @@ -0,0 +1,16 @@ + + + Network IP interface status + + + IP interface up + Interface name + IP address + + + + IP interface down + Interface name + + + diff --git a/ltt-control/facilities/process.xml b/ltt-control/facilities/process.xml new file mode 100644 index 0000000..6d80c55 --- /dev/null +++ b/ltt-control/facilities/process.xml @@ -0,0 +1,97 @@ + + + The process facility has events related to process handling in the kernel. + + + + + + + + Process fork + PID of the parent process + PID of the child process + + + + Just created a new kernel thread + PID of the kernel thread + Function called + + + + + Process exit + PID of the process + + + + Process wait + PID of the waiting process + PID of the process waited for + + + + Process kernel data structure free (end of life of a zombie) + PID of the freed process + + + + Process kill system call + PID of the process + PID of the process to kill + Signal number + + + + Process signal reception + PID of the receiving process + Signal number + + + + Process wakeup + PID of the receiving process + State of the awakened process. -1 unrunnable, 0 runnable, >0 stopped. + + + + Scheduling change + Outgoing process + Incoming process + Outgoing process' state. -1 unrunnable, 0 runnable, >0 stopped. + + + diff --git a/ltt-control/facilities/socket.xml b/ltt-control/facilities/socket.xml new file mode 100644 index 0000000..8439d19 --- /dev/null +++ b/ltt-control/facilities/socket.xml @@ -0,0 +1,38 @@ + + + The socket facility contains events related to sockets + + + Generic socket call : FIXME : should be more detailed. + Number of socket call + First argument of socket call + + + + Create a socket + Socket structure address + Socket family + Socket type + Socket protocol + Socket file descriptor + + + + Sending a socket message + Socket structure address + Socket family + Socket type + Socket protocol + Size of the message + + + + Receiving a socket message + Socket structure address + Socket family + Socket type + Socket protocol + Size of the message + + + diff --git a/ltt-control/facilities/stack_arch_i386.xml b/ltt-control/facilities/stack_arch_i386.xml new file mode 100644 index 0000000..3eeee38 --- /dev/null +++ b/ltt-control/facilities/stack_arch_i386.xml @@ -0,0 +1,25 @@ + + + The stack facility has events related to getting process stack. + + + Process EIP on the user stack + Sequence of EIP + + + + + + + + + EIP on the kernel stack + Sequence of EIP + + + + + + + + diff --git a/ltt-control/facilities/statedump.xml b/ltt-control/facilities/statedump.xml new file mode 100644 index 0000000..2d40984 --- /dev/null +++ b/ltt-control/facilities/statedump.xml @@ -0,0 +1,97 @@ + + + The statedump facility contains the events generated at trace startup + + + + + + + + + + + + + + + + + + + + + + + List of open file descriptors + File name + Process identifier + File descriptor index in this process's task_struct + + + + List of active vm maps + Process identifier + VM's start address + VM's end address + VM area flags + VM's page offset + Inode associated with this VM + + + + List of loaded kernel modules + Module name + Module's state + Number of references to this module + + + + List of registered interrupts + Interrupt name + action triggered by interrupt + Interrupt number + + + + State of each process when statedump is performed + Process identifier + Parent process identifier + Process name + Execution mode + Execution submode + Process status + + + + List of each IP interface up + Interface name + IP address + Status of the interface + + + + Kernel state dump complete + + + + + diff --git a/ltt-control/facilities/timer.xml b/ltt-control/facilities/timer.xml new file mode 100644 index 0000000..c07256f --- /dev/null +++ b/ltt-control/facilities/timer.xml @@ -0,0 +1,42 @@ + + + The timer facility has events related to timer events in the kernel. + + + + + + + + A timer or itimer has expired. + + PID of the process to wake up. + + + + + + The timer softirq is currently runned. + + + + An interval timer is set. + kind of interval timer. + + + + + + + + + diff --git a/ltt-control/facilities/user_generic.xml b/ltt-control/facilities/user_generic.xml new file mode 100644 index 0000000..2dc7267 --- /dev/null +++ b/ltt-control/facilities/user_generic.xml @@ -0,0 +1,33 @@ + + + Generic user space facility + + + Takes a string from user space + + + + + Takes a string and pointer from user space + + + + + + Takes a buffer of variable size (written by printf) and log it. + + + + + Entry in a function + + + + + + Exit from a function + + + + + diff --git a/ltt-control/liblttctl/Makefile.am b/ltt-control/liblttctl/Makefile.am new file mode 100644 index 0000000..1c650f0 --- /dev/null +++ b/ltt-control/liblttctl/Makefile.am @@ -0,0 +1,7 @@ + + +lib_LTLIBRARIES = liblttctl.la +liblttctl_la_SOURCES = liblttctl.c + +lttctlinclude_HEADERS = \ + lttctl.h diff --git a/ltt-control/liblttctl/liblttctl.c b/ltt-control/liblttctl/liblttctl.c new file mode 100644 index 0000000..d6f411b --- /dev/null +++ b/ltt-control/liblttctl/liblttctl.c @@ -0,0 +1,483 @@ +/* libltt + * + * Linux Trace Toolkit Netlink Control Library + * + * Controls the ltt-control kernel module through a netlink socket. + * + * Heavily inspired from libipq.c (iptables) made by + * James Morris + * + * Copyright 2005 - + * Mathieu Desnoyers + * + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* Private interface */ + +enum { + LTTCTL_ERR_NONE = 0, + LTTCTL_ERR_IMPL, + LTTCTL_ERR_HANDLE, + LTTCTL_ERR_SOCKET, + LTTCTL_ERR_BIND, + LTTCTL_ERR_BUFFER, + LTTCTL_ERR_RECV, + LTTCTL_ERR_NLEOF, + LTTCTL_ERR_ADDRLEN, + LTTCTL_ERR_STRUNC, + LTTCTL_ERR_RTRUNC, + LTTCTL_ERR_NLRECV, + LTTCTL_ERR_SEND, + LTTCTL_ERR_SUPP, + LTTCTL_ERR_RECVBUF, + LTTCTL_ERR_TIMEOUT, + LTTCTL_ERR_PROTOCOL +}; +#define LTTCTL_MAXERR LTTCTL_ERR_PROTOCOL + + +struct lttctl_errmap_t { + int errcode; + char *message; +} lttctl_errmap[] = { + { LTTCTL_ERR_NONE, "Unknown error" }, + { LTTCTL_ERR_IMPL, "Implementation error" }, + { LTTCTL_ERR_HANDLE, "Unable to create netlink handle" }, + { LTTCTL_ERR_SOCKET, "Unable to create netlink socket" }, + { LTTCTL_ERR_BIND, "Unable to bind netlink socket" }, + { LTTCTL_ERR_BUFFER, "Unable to allocate buffer" }, + { LTTCTL_ERR_RECV, "Failed to receive netlink message" }, + { LTTCTL_ERR_NLEOF, "Received EOF on netlink socket" }, + { LTTCTL_ERR_ADDRLEN, "Invalid peer address length" }, + { LTTCTL_ERR_STRUNC, "Sent message truncated" }, + { LTTCTL_ERR_RTRUNC, "Received message truncated" }, + { LTTCTL_ERR_NLRECV, "Received error from netlink" }, + { LTTCTL_ERR_SEND, "Failed to send netlink message" }, + { LTTCTL_ERR_SUPP, "Operation not supported" }, + { LTTCTL_ERR_RECVBUF, "Receive buffer size invalid" }, + { LTTCTL_ERR_TIMEOUT, "Timeout"}, + { LTTCTL_ERR_PROTOCOL, "Invalid protocol specified" } +}; + +static int lttctl_errno = LTTCTL_ERR_NONE; + + +static ssize_t lttctl_netlink_sendto(const struct lttctl_handle *h, + const void *msg, size_t len); + +static ssize_t lttctl_netlink_recvfrom(const struct lttctl_handle *h, + unsigned char *buf, size_t len, + int timeout); + +static ssize_t lttctl_netlink_sendmsg(const struct lttctl_handle *h, + const struct msghdr *msg, + unsigned int flags); + +static char *lttctl_strerror(int errcode); + +void lttctl_perror(const char *s); + +static ssize_t lttctl_netlink_sendto(const struct lttctl_handle *h, + const void *msg, size_t len) +{ + int status = sendto(h->fd, msg, len, 0, + (struct sockaddr *)&h->peer, sizeof(h->peer)); + if (status < 0) + lttctl_errno = LTTCTL_ERR_SEND; + + return status; +} + +static ssize_t lttctl_netlink_sendmsg(const struct lttctl_handle *h, + const struct msghdr *msg, + unsigned int flags) +{ + int status = sendmsg(h->fd, msg, flags); + if (status < 0) + lttctl_errno = LTTCTL_ERR_SEND; + return status; +} + +static ssize_t lttctl_netlink_recvfrom(const struct lttctl_handle *h, + unsigned char *buf, size_t len, + int timeout) +{ + int addrlen, status; + struct nlmsghdr *nlh; + + if (len < sizeof(struct nlmsghdr)) { + lttctl_errno = LTTCTL_ERR_RECVBUF; + lttctl_perror("Netlink recvfrom"); + return -1; + } + addrlen = sizeof(h->peer); + + if (timeout != 0) { + int ret; + struct timeval tv; + fd_set read_fds; + + if (timeout < 0) { + /* non-block non-timeout */ + tv.tv_sec = 0; + tv.tv_usec = 0; + } else { + tv.tv_sec = timeout / 1000000; + tv.tv_usec = timeout % 1000000; + } + + FD_ZERO(&read_fds); + FD_SET(h->fd, &read_fds); + ret = select(h->fd+1, &read_fds, NULL, NULL, &tv); + if (ret < 0) { + if (errno == EINTR) { + printf("eintr\n"); + return 0; + } else { + lttctl_errno = LTTCTL_ERR_RECV; + lttctl_perror("Netlink recvfrom"); + return -1; + } + } + if (!FD_ISSET(h->fd, &read_fds)) { + lttctl_errno = LTTCTL_ERR_TIMEOUT; + printf("timeout\n"); + return 0; + } + } + status = recvfrom(h->fd, buf, len, 0, + (struct sockaddr *)&h->peer, &addrlen); + + if (status < 0) { + lttctl_errno = LTTCTL_ERR_RECV; + lttctl_perror("Netlink recvfrom"); + return status; + } + if (addrlen != sizeof(h->peer)) { + lttctl_errno = LTTCTL_ERR_RECV; + lttctl_perror("Netlink recvfrom"); + return -1; + } + if (h->peer.nl_pid != 0) { + lttctl_errno = LTTCTL_ERR_RECV; + lttctl_perror("Netlink recvfrom"); + return -1; + } + if (status == 0) { + lttctl_errno = LTTCTL_ERR_NLEOF; + lttctl_perror("Netlink recvfrom"); + return -1; + } + nlh = (struct nlmsghdr *)buf; + if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) { + lttctl_errno = LTTCTL_ERR_RTRUNC; + lttctl_perror("Netlink recvfrom"); + return -1; + } + + + return status; +} + + +static char *lttctl_strerror(int errcode) +{ + if (errcode < 0 || errcode > LTTCTL_MAXERR) + errcode = LTTCTL_ERR_IMPL; + return lttctl_errmap[errcode].message; +} + + +char *lttctl_errstr(void) +{ + return lttctl_strerror(lttctl_errno); +} + +void lttctl_perror(const char *s) +{ + if (s) + fputs(s, stderr); + else + fputs("ERROR", stderr); + if (lttctl_errno) + fprintf(stderr, ": %s", lttctl_errstr()); + if (errno) + fprintf(stderr, ": %s", strerror(errno)); + fputc('\n', stderr); +} + +/* public interface */ + +/* + * Create and initialise an lttctl handle. + */ +struct lttctl_handle *lttctl_create_handle(void) +{ + int status; + struct lttctl_handle *h; + + h = (struct lttctl_handle *)malloc(sizeof(struct lttctl_handle)); + if (h == NULL) { + lttctl_errno = LTTCTL_ERR_HANDLE; + lttctl_perror("Create handle"); + goto alloc_error; + } + + memset(h, 0, sizeof(struct lttctl_handle)); + + h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_LTT); + + if (h->fd == -1) { + lttctl_errno = LTTCTL_ERR_SOCKET; + lttctl_perror("Create handle"); + goto socket_error; + } + memset(&h->local, 0, sizeof(struct sockaddr_nl)); + h->local.nl_family = AF_NETLINK; + h->local.nl_pid = getpid(); + h->local.nl_groups = 0; + status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local)); + if (status == -1) { + lttctl_errno = LTTCTL_ERR_BIND; + lttctl_perror("Create handle"); + goto bind_error; + } + memset(&h->peer, 0, sizeof(struct sockaddr_nl)); + h->peer.nl_family = AF_NETLINK; + h->peer.nl_pid = 0; + h->peer.nl_groups = 0; + return h; + + /* Error condition */ +bind_error: +socket_error: + close(h->fd); +alloc_error: + free(h); + return NULL; +} + +/* + * No error condition is checked here at this stage, but it may happen + * if/when reliable messaging is implemented. + */ +int lttctl_destroy_handle(struct lttctl_handle *h) +{ + if (h) { + close(h->fd); + free(h); + } + return 0; +} + + +int lttctl_create_trace(const struct lttctl_handle *h, + char *name, enum trace_mode mode, unsigned subbuf_size, unsigned n_subbufs) +{ + int err; + + struct { + struct nlmsghdr nlh; + lttctl_peer_msg_t msg; + } req; + struct { + struct nlmsghdr nlh; + struct nlmsgerr nlerr; + lttctl_peer_msg_t msg; + } ack; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(lttctl_peer_msg_t)); + req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + req.nlh.nlmsg_type = LTTCTLM_CONTROL; + req.nlh.nlmsg_pid = h->local.nl_pid; + req.nlh.nlmsg_seq = 0; + + strncpy(req.msg.trace_name, name, NAME_MAX); + req.msg.op = OP_CREATE; + req.msg.args.new_trace.mode = mode; + req.msg.args.new_trace.subbuf_size = subbuf_size; + req.msg.args.new_trace.n_subbufs = n_subbufs; + + err = lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); + if(err < 0) goto senderr; + + err = lttctl_netlink_recvfrom(h, (void*)&ack, sizeof(ack), 0); + if(err < 0) goto senderr; + + err = ack.nlerr.error; + if(err != 0) { + errno = err; + lttctl_perror("Create Trace Error"); + return err; + } + + return 0; + +senderr: + lttctl_perror("Create Trace Error"); + err = EPERM; + return err; +} + +int lttctl_destroy_trace(const struct lttctl_handle *h, + char *name) +{ + struct { + struct nlmsghdr nlh; + lttctl_peer_msg_t msg; + } req; + struct { + struct nlmsghdr nlh; + struct nlmsgerr nlerr; + lttctl_peer_msg_t msg; + } ack; + int err; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(lttctl_peer_msg_t)); + req.nlh.nlmsg_flags = NLM_F_REQUEST; + req.nlh.nlmsg_type = LTTCTLM_CONTROL; + req.nlh.nlmsg_pid = h->local.nl_pid; + + strncpy(req.msg.trace_name, name, NAME_MAX); + req.msg.op = OP_DESTROY; + + err = lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); + if(err < 0) goto senderr; + + err = lttctl_netlink_recvfrom(h, (void*)&ack, sizeof(ack), 0); + if(err < 0) goto senderr; + + err = ack.nlerr.error; + if(err != 0) { + errno = err; + lttctl_perror("Destroy Trace Channels Error"); + return err; + } + + return 0; + +senderr: + lttctl_perror("Destroy Trace Channels Error"); + err = EPERM; + return err; + +} + +int lttctl_start(const struct lttctl_handle *h, + char *name) +{ + struct { + struct nlmsghdr nlh; + lttctl_peer_msg_t msg; + } req; + struct { + struct nlmsghdr nlh; + struct nlmsgerr nlerr; + lttctl_peer_msg_t msg; + } ack; + + int err; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(lttctl_peer_msg_t)); + req.nlh.nlmsg_flags = NLM_F_REQUEST; + req.nlh.nlmsg_type = LTTCTLM_CONTROL; + req.nlh.nlmsg_pid = h->local.nl_pid; + + strncpy(req.msg.trace_name, name, NAME_MAX); + req.msg.op = OP_START; + + err = lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); + if(err < 0) goto senderr; + + err = lttctl_netlink_recvfrom(h, (void*)&ack, sizeof(ack), 0); + if(err < 0) goto senderr; + + err = ack.nlerr.error; + if(err != 0) { + errno = err; + lttctl_perror("Start Trace Error"); + return err; + } + + return 0; + +senderr: + err = EPERM; + lttctl_perror("Start Trace Error"); + return err; + +} + +int lttctl_stop(const struct lttctl_handle *h, + char *name) +{ + struct { + struct nlmsghdr nlh; + lttctl_peer_msg_t msg; + } req; + struct { + struct nlmsghdr nlh; + struct nlmsgerr nlerr; + lttctl_peer_msg_t msg; + } ack; + int err; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(lttctl_peer_msg_t)); + req.nlh.nlmsg_flags = NLM_F_REQUEST; + req.nlh.nlmsg_type = LTTCTLM_CONTROL; + req.nlh.nlmsg_pid = h->local.nl_pid; + + strncpy(req.msg.trace_name, name, NAME_MAX); + req.msg.op = OP_STOP; + + err = lttctl_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); + if(err < 0) goto senderr; + + err = lttctl_netlink_recvfrom(h, (void*)&ack, sizeof(ack), 0); + if(err < 0) goto senderr; + + err = ack.nlerr.error; + if(err != 0) { + errno = err; + lttctl_perror("Stop Trace Error"); + return err; + } + + return 0; + +senderr: + err = EPERM; + lttctl_perror("Stop Trace Error"); + return err; +} + diff --git a/ltt-control/liblttctl/lttctl.h b/ltt-control/liblttctl/lttctl.h new file mode 100644 index 0000000..cc028a5 --- /dev/null +++ b/ltt-control/liblttctl/lttctl.h @@ -0,0 +1,91 @@ +/* libltt header file + * + * Copyright 2005- + * Mathieu Desnoyers + * + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + * + * + * Inspired from iptables, by James Morris . + * + */ + +#ifndef _LIBLTT_H +#define _LIBLTT_H + +#include +#include +#include +#include + +#ifndef NETLINK_LTT +#define NETLINK_LTT 31 +#endif + + +enum trace_op { + OP_CREATE, + OP_DESTROY, + OP_START, + OP_STOP, + OP_NONE +}; + +enum trace_mode { + LTT_TRACE_NORMAL, + LTT_TRACE_FLIGHT +}; + +typedef struct lttctl_peer_msg { + char trace_name[NAME_MAX]; + enum trace_op op; + union { + struct { + enum trace_mode mode; + unsigned subbuf_size; + unsigned n_subbufs; + } new_trace; + } args; +} lttctl_peer_msg_t; + + +struct lttctl_handle +{ + int fd; + //u_int8_t blocking; + struct sockaddr_nl local; + struct sockaddr_nl peer; +}; + +typedef struct lttctl_resp_msg { + int err; +} lttctl_resp_msg_t; + +struct lttctl_handle *lttctl_create_handle(void); + +int lttctl_destroy_handle(struct lttctl_handle *h); + + +int lttctl_create_trace(const struct lttctl_handle *h, + char *name, enum trace_mode mode, unsigned subbuf_size, unsigned n_subbufs); + +int lttctl_destroy_trace(const struct lttctl_handle *handle, char *name); + +int lttctl_start(const struct lttctl_handle *handle, char *name); + +int lttctl_stop(const struct lttctl_handle *handle, char *name); + +#define LTTCTLM_BASE 0x10 +#define LTTCTLM_CONTROL (LTTCTLM_BASE + 1) /* LTT control message */ + + +#endif //_LIBLTT_H diff --git a/ltt-control/lttctl/Makefile.am b/ltt-control/lttctl/Makefile.am new file mode 100644 index 0000000..6a5180d --- /dev/null +++ b/ltt-control/lttctl/Makefile.am @@ -0,0 +1,11 @@ +## Process this file with automake to produce Makefile.in + +AM_CFLAGS = -DPACKAGE_DATA_DIR=\""$(datadir)"\" -DPACKAGE_BIN_DIR=\""$(bindir)"\" + +bin_PROGRAMS = lttctl + +lttctl_SOURCES = \ + lttctl.c +lttctl_DEPENDENCIES = ../liblttctl/liblttctl.la +lttctl_LDADD = $(lttctl_DEPENDENCIES) + diff --git a/ltt-control/lttctl/lttctl.c b/ltt-control/lttctl/lttctl.c new file mode 100644 index 0000000..11483c9 --- /dev/null +++ b/ltt-control/lttctl/lttctl.c @@ -0,0 +1,505 @@ +/* lttctl + * + * Linux Trace Toolkit Control + * + * Small program that controls LTT through libltt. + * + * Copyright 2005 - + * Mathieu Desnoyers + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Buffer for file copy : 4k seems optimal. */ +#define BUF_SIZE 4096 + +enum trace_ctl_op { + CTL_OP_CREATE_START, + CTL_OP_CREATE, + CTL_OP_DESTROY, + CTL_OP_STOP_DESTROY, + CTL_OP_START, + CTL_OP_STOP, + CTL_OP_DAEMON, + CTL_OP_DESCRIPTION, + CTL_OP_NONE +}; + +static char *trace_name = NULL; +static char *mode_name = NULL; +static unsigned subbuf_size = 0; +static unsigned n_subbufs = 0; +static unsigned append_trace = 0; +static enum trace_mode mode = LTT_TRACE_NORMAL; +static enum trace_ctl_op op = CTL_OP_NONE; +static char *channel_root = NULL; +static char *trace_root = NULL; +static char *num_threads = "1"; + +static int sigchld_received = 0; + +void sigchld_handler(int signo) +{ + printf("signal %d received\n", signo); + sigchld_received = 1; +} + + +/* Args : + * + */ +void show_arguments(void) +{ + printf("Please use the following arguments :\n"); + printf("\n"); + printf("-n name Name of the trace.\n"); + printf("-b Create trace channels and start tracing (no daemon).\n"); + printf("-c Create trace channels.\n"); + printf("-m mode Normal or flight recorder mode.\n"); + printf(" Mode values : normal (default) or flight.\n"); + printf("-r Destroy trace channels.\n"); + printf("-R Stop tracing and destroy trace channels.\n"); + printf("-s Start tracing.\n"); + //printf(" Note : will automatically create a normal trace if " + // "none exists.\n"); + printf("-q Stop tracing.\n"); + printf("-d Create trace, spawn a lttd daemon, start tracing.\n"); + printf(" (optionnaly, you can set LTT_DAEMON\n"); + printf(" and the LTT_FACILITIES env. vars.)\n"); + printf("-t Trace root path. (ex. /root/traces/example_trace)\n"); + printf("-l LTT channels root path. (ex. /mnt/relayfs/ltt)\n"); + printf("-z Size of the subbuffers (will be rounded to next page size)\n"); + printf("-x Number of subbuffers\n"); + printf("-e Get XML facilities description\n"); + printf("-a Append to trace\n"); + printf("-N Number of lttd threads\n"); + printf("\n"); +} + + +/* parse_arguments + * + * Parses the command line arguments. + * + * Returns -1 if the arguments were correct, but doesn't ask for program + * continuation. Returns EINVAL if the arguments are incorrect, or 0 if OK. + */ +int parse_arguments(int argc, char **argv) +{ + int ret = 0; + int argn = 1; + + if(argc == 2) { + if(strcmp(argv[1], "-h") == 0) { + return -1; + } + } + + while(argn < argc) { + + switch(argv[argn][0]) { + case '-': + switch(argv[argn][1]) { + case 'n': + if(argn+1 < argc) { + trace_name = argv[argn+1]; + argn++; + } else { + printf("Specify a trace name after -n.\n"); + printf("\n"); + ret = EINVAL; + } + + break; + case 'b': + op = CTL_OP_CREATE_START; + break; + case 'c': + op = CTL_OP_CREATE; + break; + case 'm': + if(argn+1 < argc) { + mode_name = argv[argn+1]; + argn++; + if(strcmp(mode_name, "normal") == 0) + mode = LTT_TRACE_NORMAL; + else if(strcmp(mode_name, "flight") == 0) + mode = LTT_TRACE_FLIGHT; + else { + printf("Invalid mode '%s'.\n", argv[argn]); + printf("\n"); + ret = EINVAL; + } + } else { + printf("Specify a mode after -m.\n"); + printf("\n"); + ret = EINVAL; + } + break; + case 'r': + op = CTL_OP_DESTROY; + break; + case 'R': + op = CTL_OP_STOP_DESTROY; + break; + case 's': + op = CTL_OP_START; + break; + case 'q': + op = CTL_OP_STOP; + break; + case 'z': + if(argn+1 < argc) { + subbuf_size = (unsigned)atoi(argv[argn+1]); + argn++; + } else { + printf("Specify a number of subbuffers after -z.\n"); + printf("\n"); + ret = EINVAL; + } + break; + case 'x': + if(argn+1 < argc) { + n_subbufs = (unsigned)atoi(argv[argn+1]); + argn++; + } else { + printf("Specify a subbuffer size after -x.\n"); + printf("\n"); + ret = EINVAL; + } + break; + case 'd': + op = CTL_OP_DAEMON; + break; + case 'e': + op = CTL_OP_DESCRIPTION; + break; + case 't': + if(argn+1 < argc) { + trace_root = argv[argn+1]; + argn++; + } else { + printf("Specify a trace root path after -t.\n"); + printf("\n"); + ret = EINVAL; + } + break; + case 'l': + if(argn+1 < argc) { + channel_root = argv[argn+1]; + argn++; + } else { + printf("Specify a channel root path after -l.\n"); + printf("\n"); + ret = EINVAL; + } + break; + case 'a': + append_trace = 1; + break; + case 'N': + if(argn+1 < argc) { + num_threads = argv[argn+1]; + argn++; + } + break; + default: + printf("Invalid argument '%s'.\n", argv[argn]); + printf("\n"); + ret = EINVAL; + } + break; + default: + printf("Invalid argument '%s'.\n", argv[argn]); + printf("\n"); + ret = EINVAL; + } + argn++; + } + + if(op != CTL_OP_DESCRIPTION && trace_name == NULL) { + printf("Please specify a trace name.\n"); + printf("\n"); + ret = EINVAL; + } + + if(op == CTL_OP_NONE) { + printf("Please specify an operation.\n"); + printf("\n"); + ret = EINVAL; + } + + if(op == CTL_OP_DAEMON) { + if(trace_root == NULL) { + printf("Please specify -t trace_root_path with the -d option.\n"); + printf("\n"); + ret = EINVAL; + } + if(channel_root == NULL) { + printf("Please specify -l ltt_root_path with the -d option.\n"); + printf("\n"); + ret = EINVAL; + } + } + + if(op == CTL_OP_DESCRIPTION) { + if(trace_root == NULL) { + printf("Please specify -t trace_root_path with the -e option.\n"); + printf("\n"); + ret = EINVAL; + } + } + + return ret; +} + +void show_info(void) +{ + printf("Linux Trace Toolkit Trace Control\n"); + printf("\n"); + if(trace_name != NULL) { + printf("Controlling trace : %s\n", trace_name); + printf("\n"); + } +} + +int create_eventdefs(void) +{ + int ret = 0; + char eventdefs_path[PATH_MAX]; + char eventdefs_file[PATH_MAX]; + char facilities_file[PATH_MAX]; + char read_buf[BUF_SIZE]; + struct dirent *entry; + char *facilities_path = getenv("LTT_FACILITIES"); + if(facilities_path == NULL) facilities_path = + PACKAGE_DATA_DIR "/" PACKAGE "/facilities"; + + ret = mkdir(trace_root, S_IRWXU|S_IRWXG|S_IRWXO); + if(ret == -1 && errno != EEXIST) { + ret = errno; + perror("Cannot create trace_root directory"); + printf("trace_root is %s\n", trace_root); + goto error; + } + ret = 0; + + size_t trace_root_len = strlen(trace_root); + strncpy(eventdefs_path, trace_root, PATH_MAX); + strncat(eventdefs_path, "/eventdefs/", PATH_MAX - trace_root_len); + size_t eventdefs_path_len = strlen(eventdefs_path); + ret = mkdir(eventdefs_path, S_IRWXU|S_IRWXG|S_IRWXO); + if(ret == -1 && (!append_trace || errno != EEXIST)) { + ret = errno; + perror("Cannot create eventdefs directory"); + goto error; + } + ret = 0; + + DIR *facilities_dir = opendir(facilities_path); + + if(facilities_dir == NULL) { + perror("Cannot open facilities directory"); + ret = EEXIST; + goto error; + } + + while((entry = readdir(facilities_dir)) != NULL) { + if(entry->d_name[0] == '.') continue; + + printf("Appending facility file %s\n", entry->d_name); + strncpy(eventdefs_file, eventdefs_path, PATH_MAX); + strncat(eventdefs_file, entry->d_name, PATH_MAX - eventdefs_path_len); + /* Append to the file */ + FILE *dest = fopen(eventdefs_file, "a"); + if(!dest) { + perror("Cannot create eventdefs file"); + continue; + } + strncpy(facilities_file, facilities_path, PATH_MAX); + size_t facilities_dir_len = strlen(facilities_path); + strncat(facilities_file, "/", PATH_MAX - facilities_dir_len); + strncat(facilities_file, entry->d_name, PATH_MAX - facilities_dir_len-1); + FILE *src = fopen(facilities_file, "r"); + if(!src) { + ret = errno; + perror("Cannot open eventdefs file for reading"); + goto close_dest; + } + + do { + size_t read_size, write_size; + read_size = fread(read_buf, sizeof(char), BUF_SIZE, src); + if(ferror(src)) { + ret = errno; + perror("Cannot read eventdefs file"); + goto close_src; + } + write_size = fwrite(read_buf, sizeof(char), read_size, dest); + if(ferror(dest)) { + ret = errno; + perror("Cannot write eventdefs file"); + goto close_src; + } + } while(!feof(src)); + + /* Add spacing between facilities */ + fwrite("\n", 1, 1, dest); + +close_src: + fclose(src); +close_dest: + fclose(dest); + } + + closedir(facilities_dir); + +error: + return ret; + +} + + +int lttctl_daemon(struct lttctl_handle *handle, char *trace_name) +{ + char channel_path[PATH_MAX] = ""; + pid_t pid; + int ret; + char *lttd_path = getenv("LTT_DAEMON"); + struct sigaction act; + + if(lttd_path == NULL) lttd_path = + PACKAGE_BIN_DIR "/lttd"; + + strcat(channel_path, channel_root); + strcat(channel_path, "/"); + strcat(channel_path, trace_name); + + + ret = lttctl_create_trace(handle, trace_name, mode, subbuf_size, n_subbufs); + if(ret != 0) goto create_error; + + act.sa_handler = sigchld_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGCHLD); + sigaction(SIGCHLD, &act, NULL); + + pid = fork(); + + if(pid > 0) { + int status; + /* parent */ + while(!(sigchld_received)) pause(); + + waitpid(pid, &status, 0); + ret = 0; + if(WIFEXITED(status)) + ret = WEXITSTATUS(status); + if(ret) goto start_error; + + printf("Creating supplementary trace files\n"); + ret = create_eventdefs(); + if(ret) goto start_error; + + } else if(pid == 0) { + /* child */ + int ret; + if(append_trace) + ret = execlp(lttd_path, lttd_path, "-t", trace_root, "-c", + channel_path, "-d", "-a", "-N", num_threads, NULL); + else + ret = execlp(lttd_path, lttd_path, "-t", trace_root, "-c", + channel_path, "-d", "-N", num_threads, NULL); + if(ret) { + ret = errno; + perror("Error in executing the lttd daemon"); + exit(ret); + } + } else { + /* error */ + perror("Error in forking for lttd daemon"); + } + + ret = lttctl_start(handle, trace_name); + if(ret != 0) goto start_error; + + return 0; + + /* error handling */ +start_error: + printf("Trace start error\n"); + ret |= lttctl_destroy_trace(handle, trace_name); +create_error: + return ret; +} + +int main(int argc, char ** argv) +{ + int ret; + struct lttctl_handle *handle; + + ret = parse_arguments(argc, argv); + + if(ret != 0) show_arguments(); + if(ret == EINVAL) return EINVAL; + if(ret == -1) return 0; + + show_info(); + + handle = lttctl_create_handle(); + + if(handle == NULL) return -1; + + switch(op) { + case CTL_OP_CREATE_START: + ret = lttctl_create_trace(handle, trace_name, mode, subbuf_size, + n_subbufs); + if(!ret) + ret = lttctl_start(handle, trace_name); + break; + case CTL_OP_CREATE: + ret = lttctl_create_trace(handle, trace_name, mode, subbuf_size, + n_subbufs); + break; + case CTL_OP_DESTROY: + ret = lttctl_destroy_trace(handle, trace_name); + break; + case CTL_OP_STOP_DESTROY: + ret = lttctl_stop(handle, trace_name); + if(!ret) + ret = lttctl_destroy_trace(handle, trace_name); + break; + case CTL_OP_START: + ret = lttctl_start(handle, trace_name); + break; + case CTL_OP_STOP: + ret = lttctl_stop(handle, trace_name); + break; + case CTL_OP_DAEMON: + ret = lttctl_daemon(handle, trace_name); + break; + case CTL_OP_DESCRIPTION: + ret = create_eventdefs(); + break; + case CTL_OP_NONE: + break; + } + + ret |= lttctl_destroy_handle(handle); + + return ret; +} diff --git a/ltt-control/lttd/Makefile.am b/ltt-control/lttd/Makefile.am new file mode 100644 index 0000000..bb860bc --- /dev/null +++ b/ltt-control/lttd/Makefile.am @@ -0,0 +1,8 @@ +# Empty TraceDaemon Makefile.am. Insert a real one here. + +LIBS += $(THREAD_LIBS) + +bin_PROGRAMS = lttd + +lttd_SOURCES = lttd.c + diff --git a/ltt-control/lttd/lttd.c b/ltt-control/lttd/lttd.c new file mode 100644 index 0000000..2601d80 --- /dev/null +++ b/ltt-control/lttd/lttd.c @@ -0,0 +1,665 @@ +/* lttd + * + * Linux Trace Toolkit Daemon + * + * This is a simple daemon that reads a few relayfs channels and save them in a + * trace. + * + * + * Copyright 2005 - + * Mathieu Desnoyers + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _REENTRANT +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Relayfs IOCTL */ +#include +#include + +/* Get the next sub buffer that can be read. */ +#define RELAYFS_GET_SUBBUF _IOR(0xF4, 0x00,__u32) +/* Release the oldest reserved (by "get") sub buffer. */ +#define RELAYFS_PUT_SUBBUF _IOW(0xF4, 0x01,__u32) +/* returns the number of sub buffers in the per cpu channel. */ +#define RELAYFS_GET_N_SUBBUFS _IOR(0xF4, 0x02,__u32) +/* returns the size of the sub buffers. */ +#define RELAYFS_GET_SUBBUF_SIZE _IOR(0xF4, 0x03,__u32) + + + +enum { + GET_SUBBUF, + PUT_SUBBUF, + GET_N_BUBBUFS, + GET_SUBBUF_SIZE +}; + +struct fd_pair { + int channel; + int trace; + unsigned int n_subbufs; + unsigned int subbuf_size; + void *mmap; + pthread_mutex_t mutex; +}; + +struct channel_trace_fd { + struct fd_pair *pair; + int num_pairs; +}; + +static char *trace_name = NULL; +static char *channel_name = NULL; +static int daemon_mode = 0; +static int append_mode = 0; +static unsigned long num_threads = 1; +volatile static int quit_program = 0; /* For signal handler */ + +/* Args : + * + * -t directory Directory name of the trace to write to. Will be created. + * -c directory Root directory of the relayfs trace channels. + * -d Run in background (daemon). + * -a Trace append mode. + * -s Send SIGUSR1 to parent when ready for IO. + */ +void show_arguments(void) +{ + printf("Please use the following arguments :\n"); + printf("\n"); + printf("-t directory Directory name of the trace to write to.\n" + " It will be created.\n"); + printf("-c directory Root directory of the relayfs trace channels.\n"); + printf("-d Run in background (daemon).\n"); + printf("-a Append to an possibly existing trace.\n"); + printf("-N Number of threads to start.\n"); + printf("\n"); +} + + +/* parse_arguments + * + * Parses the command line arguments. + * + * Returns 1 if the arguments were correct, but doesn't ask for program + * continuation. Returns -1 if the arguments are incorrect, or 0 if OK. + */ +int parse_arguments(int argc, char **argv) +{ + int ret = 0; + int argn = 1; + + if(argc == 2) { + if(strcmp(argv[1], "-h") == 0) { + return 1; + } + } + + while(argn < argc) { + + switch(argv[argn][0]) { + case '-': + switch(argv[argn][1]) { + case 't': + if(argn+1 < argc) { + trace_name = argv[argn+1]; + argn++; + } + break; + case 'c': + if(argn+1 < argc) { + channel_name = argv[argn+1]; + argn++; + } + break; + case 'd': + daemon_mode = 1; + break; + case 'a': + append_mode = 1; + break; + case 'N': + if(argn+1 < argc) { + num_threads = strtoul(argv[argn+1], NULL, 0); + argn++; + } + break; + default: + printf("Invalid argument '%s'.\n", argv[argn]); + printf("\n"); + ret = -1; + } + break; + default: + printf("Invalid argument '%s'.\n", argv[argn]); + printf("\n"); + ret = -1; + } + argn++; + } + + if(trace_name == NULL) { + printf("Please specify a trace name.\n"); + printf("\n"); + ret = -1; + } + + if(channel_name == NULL) { + printf("Please specify a channel name.\n"); + printf("\n"); + ret = -1; + } + + return ret; +} + +void show_info(void) +{ + printf("Linux Trace Toolkit Trace Daemon\n"); + printf("\n"); + printf("Reading from relayfs directory : %s\n", channel_name); + printf("Writing to trace directory : %s\n", trace_name); + printf("\n"); +} + + +/* signal handling */ + +static void handler(int signo) +{ + printf("Signal %d received : exiting cleanly\n", signo); + quit_program = 1; +} + + + +int open_channel_trace_pairs(char *subchannel_name, char *subtrace_name, + struct channel_trace_fd *fd_pairs) +{ + DIR *channel_dir = opendir(subchannel_name); + struct dirent *entry; + struct stat stat_buf; + int ret; + char path_channel[PATH_MAX]; + int path_channel_len; + char *path_channel_ptr; + char path_trace[PATH_MAX]; + int path_trace_len; + char *path_trace_ptr; + int open_ret = 0; + + if(channel_dir == NULL) { + perror(subchannel_name); + open_ret = ENOENT; + goto end; + } + + printf("Creating trace subdirectory %s\n", subtrace_name); + ret = mkdir(subtrace_name, S_IRWXU|S_IRWXG|S_IRWXO); + if(ret == -1) { + if(errno != EEXIST) { + perror(subtrace_name); + open_ret = -1; + goto end; + } + } + + strncpy(path_channel, subchannel_name, PATH_MAX-1); + path_channel_len = strlen(path_channel); + path_channel[path_channel_len] = '/'; + path_channel_len++; + path_channel_ptr = path_channel + path_channel_len; + + strncpy(path_trace, subtrace_name, PATH_MAX-1); + path_trace_len = strlen(path_trace); + path_trace[path_trace_len] = '/'; + path_trace_len++; + path_trace_ptr = path_trace + path_trace_len; + + while((entry = readdir(channel_dir)) != NULL) { + + if(entry->d_name[0] == '.') continue; + + strncpy(path_channel_ptr, entry->d_name, PATH_MAX - path_channel_len); + strncpy(path_trace_ptr, entry->d_name, PATH_MAX - path_trace_len); + + ret = stat(path_channel, &stat_buf); + if(ret == -1) { + perror(path_channel); + continue; + } + + printf("Channel file : %s\n", path_channel); + + if(S_ISDIR(stat_buf.st_mode)) { + + printf("Entering channel subdirectory...\n"); + ret = open_channel_trace_pairs(path_channel, path_trace, fd_pairs); + if(ret < 0) continue; + } else if(S_ISREG(stat_buf.st_mode)) { + printf("Opening file.\n"); + + fd_pairs->pair = realloc(fd_pairs->pair, + ++fd_pairs->num_pairs * sizeof(struct fd_pair)); + + /* Open the channel in read mode */ + fd_pairs->pair[fd_pairs->num_pairs-1].channel = + open(path_channel, O_RDONLY | O_NONBLOCK); + if(fd_pairs->pair[fd_pairs->num_pairs-1].channel == -1) { + perror(path_channel); + fd_pairs->num_pairs--; + continue; + } + /* Open the trace in write mode, only append if append_mode */ + ret = stat(path_trace, &stat_buf); + if(ret == 0) { + if(append_mode) { + printf("Appending to file %s as requested\n", path_trace); + + fd_pairs->pair[fd_pairs->num_pairs-1].trace = + open(path_trace, O_WRONLY|O_APPEND, + S_IRWXU|S_IRWXG|S_IRWXO); + + if(fd_pairs->pair[fd_pairs->num_pairs-1].trace == -1) { + perror(path_trace); + } + } else { + printf("File %s exists, cannot open. Try append mode.\n", path_trace); + open_ret = -1; + goto end; + } + } else { + if(errno == ENOENT) { + fd_pairs->pair[fd_pairs->num_pairs-1].trace = + open(path_trace, O_WRONLY|O_CREAT|O_EXCL, + S_IRWXU|S_IRWXG|S_IRWXO); + if(fd_pairs->pair[fd_pairs->num_pairs-1].trace == -1) { + perror(path_trace); + } + } + } + } + } + +end: + closedir(channel_dir); + + return open_ret; +} + + +int read_subbuffer(struct fd_pair *pair) +{ + unsigned int consumed_old; + int err, ret=0; + + + err = ioctl(pair->channel, RELAYFS_GET_SUBBUF, + &consumed_old); + printf("cookie : %u\n", consumed_old); + if(err != 0) { + ret = errno; + perror("Reserving sub buffer failed (everything is normal)"); + goto get_error; + } + + err = TEMP_FAILURE_RETRY(write(pair->trace, + pair->mmap + + (consumed_old & ((pair->n_subbufs * pair->subbuf_size)-1)), + pair->subbuf_size)); + + if(err < 0) { + ret = errno; + perror("Error in writing to file"); + goto write_error; + } +#if 0 + err = fsync(pair->trace); + if(err < 0) { + ret = errno; + perror("Error in writing to file"); + goto write_error; + } +#endif //0 +write_error: + err = ioctl(pair->channel, RELAYFS_PUT_SUBBUF, &consumed_old); + if(err != 0) { + ret = errno; + if(errno == -EFAULT) { + perror("Error in unreserving sub buffer\n"); + } else if(errno == -EIO) { + perror("Reader has been pushed by the writer, last subbuffer corrupted."); + /* FIXME : we may delete the last written buffer if we wish. */ + } + goto get_error; + } + +get_error: + return ret; +} + + + +int map_channels(struct channel_trace_fd *fd_pairs) +{ + int i,j; + int ret=0; + + if(fd_pairs->num_pairs <= 0) { + printf("No channel to read\n"); + goto end; + } + + /* Get the subbuf sizes and number */ + + for(i=0;inum_pairs;i++) { + struct fd_pair *pair = &fd_pairs->pair[i]; + + ret = ioctl(pair->channel, RELAYFS_GET_N_SUBBUFS, + &pair->n_subbufs); + if(ret != 0) { + perror("Error in getting the number of subbuffers"); + goto end; + } + ret = ioctl(pair->channel, RELAYFS_GET_SUBBUF_SIZE, + &pair->subbuf_size); + if(ret != 0) { + perror("Error in getting the size of the subbuffers"); + goto end; + } + ret = pthread_mutex_init(&pair->mutex, NULL); /* Fast mutex */ + if(ret != 0) { + perror("Error in mutex init"); + goto end; + } + } + + /* Mmap each FD */ + for(i=0;inum_pairs;i++) { + struct fd_pair *pair = &fd_pairs->pair[i]; + + pair->mmap = mmap(0, pair->subbuf_size * pair->n_subbufs, PROT_READ, + MAP_SHARED, pair->channel, 0); + if(pair->mmap == MAP_FAILED) { + perror("Mmap error"); + goto munmap; + } + } + + goto end; /* success */ + + /* Error handling */ + /* munmap only the successfully mmapped indexes */ +munmap: + /* Munmap each FD */ + for(j=0;jpair[j]; + int err_ret; + + err_ret = munmap(pair->mmap, pair->subbuf_size * pair->n_subbufs); + if(err_ret != 0) { + perror("Error in munmap"); + } + ret |= err_ret; + } + +end: + return ret; + + +} + + +int unmap_channels(struct channel_trace_fd *fd_pairs) +{ + int j; + int ret=0; + + /* Munmap each FD */ + for(j=0;jnum_pairs;j++) { + struct fd_pair *pair = &fd_pairs->pair[j]; + int err_ret; + + err_ret = munmap(pair->mmap, pair->subbuf_size * pair->n_subbufs); + if(err_ret != 0) { + perror("Error in munmap"); + } + ret |= err_ret; + err_ret = pthread_mutex_destroy(&pair->mutex); + if(err_ret != 0) { + perror("Error in mutex destroy"); + } + ret |= err_ret; + } + + return ret; +} + + +/* read_channels + * + * Thread worker. + * + * Read the relayfs channels and write them in the paired tracefiles. + * + * @fd_pairs : paired channels and trace files. + * + * returns (void*)0 on success, (void*)-1 on error. + * + * Note that the high priority polled channels are consumed first. We then poll + * again to see if these channels are still in priority. Only when no + * high priority channel is left, we start reading low priority channels. + * + * Note that a channel is considered high priority when the buffer is almost + * full. + */ + +void * read_channels(void *arg) +{ + struct pollfd *pollfd; + int i,j; + int num_rdy, num_hup; + int high_prio; + int ret = 0; + struct channel_trace_fd *fd_pairs = (struct channel_trace_fd *)arg; + + /* Start polling the FD */ + + pollfd = malloc(fd_pairs->num_pairs * sizeof(struct pollfd)); + + /* Note : index in pollfd is the same index as fd_pair->pair */ + for(i=0;inum_pairs;i++) { + pollfd[i].fd = fd_pairs->pair[i].channel; + pollfd[i].events = POLLIN|POLLPRI; + } + + while(1) { + high_prio = 0; + num_hup = 0; +#ifdef DEBUG + printf("Press a key for next poll...\n"); + char buf[1]; + read(STDIN_FILENO, &buf, 1); + printf("Next poll (polling %d fd) :\n", fd_pairs->num_pairs); +#endif //DEBUG + + /* Have we received a signal ? */ + if(quit_program) break; + + num_rdy = poll(pollfd, fd_pairs->num_pairs, -1); + if(num_rdy == -1) { + perror("Poll error"); + goto free_fd; + } + + printf("Data received\n"); + + for(i=0;inum_pairs;i++) { + switch(pollfd[i].revents) { + case POLLERR: + printf("Error returned in polling fd %d.\n", pollfd[i].fd); + num_hup++; + break; + case POLLHUP: + printf("Polling fd %d tells it has hung up.\n", pollfd[i].fd); + num_hup++; + break; + case POLLNVAL: + printf("Polling fd %d tells fd is not open.\n", pollfd[i].fd); + num_hup++; + break; + case POLLPRI: + if(pthread_mutex_trylock(&fd_pairs->pair[i].mutex) == 0) { + printf("Urgent read on fd %d\n", pollfd[i].fd); + /* Take care of high priority channels first. */ + high_prio = 1; + /* it's ok to have an unavailable subbuffer */ + ret = read_subbuffer(&fd_pairs->pair[i]); + if(ret == -EAGAIN) ret = 0; + + ret = pthread_mutex_unlock(&fd_pairs->pair[i].mutex); + if(ret) + printf("Error in mutex unlock : %s\n", strerror(ret)); + } + break; + } + } + /* If every FD has hung up, we end the read loop here */ + if(num_hup == fd_pairs->num_pairs) break; + + if(!high_prio) { + for(i=0;inum_pairs;i++) { + switch(pollfd[i].revents) { + case POLLIN: + if(pthread_mutex_trylock(&fd_pairs->pair[i].mutex) == 0) { + /* Take care of low priority channels. */ + printf("Normal read on fd %d\n", pollfd[i].fd); + /* it's ok to have an unavailable subbuffer */ + ret = read_subbuffer(&fd_pairs->pair[i]); + if(ret == -EAGAIN) ret = 0; + + ret = pthread_mutex_unlock(&fd_pairs->pair[i].mutex); + if(ret) + printf("Error in mutex unlock : %s\n", strerror(ret)); + } + break; + } + } + } + + } + +free_fd: + free(pollfd); + +end: + return (void*)ret; +} + + +void close_channel_trace_pairs(struct channel_trace_fd *fd_pairs) +{ + int i; + int ret; + + for(i=0;inum_pairs;i++) { + ret = close(fd_pairs->pair[i].channel); + if(ret == -1) perror("Close error on channel"); + ret = close(fd_pairs->pair[i].trace); + if(ret == -1) perror("Close error on trace"); + } + free(fd_pairs->pair); +} + +int main(int argc, char ** argv) +{ + int ret = 0; + struct channel_trace_fd fd_pairs = { NULL, 0 }; + struct sigaction act; + pthread_t *tids; + unsigned int i; + void *tret; + + ret = parse_arguments(argc, argv); + + if(ret != 0) show_arguments(); + if(ret < 0) return EINVAL; + if(ret > 0) return 0; + + show_info(); + + if(daemon_mode) { + ret = daemon(0, 0); + + if(ret == -1) { + perror("An error occured while daemonizing."); + exit(-1); + } + } + + /* Connect the signal handlers */ + act.sa_handler = handler; + act.sa_flags = 0; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGTERM); + sigaddset(&(act.sa_mask), SIGQUIT); + sigaddset(&(act.sa_mask), SIGINT); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGQUIT, &act, NULL); + sigaction(SIGINT, &act, NULL); + + + if(ret = open_channel_trace_pairs(channel_name, trace_name, &fd_pairs)) + goto close_channel; + + if(ret = map_channels(&fd_pairs)) + goto close_channel; + + tids = malloc(sizeof(pthread_t) * num_threads); + for(i=0; i - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#define _REENTRANT -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Relayfs IOCTL */ -#include -#include - -/* Get the next sub buffer that can be read. */ -#define RELAYFS_GET_SUBBUF _IOR(0xF4, 0x00,__u32) -/* Release the oldest reserved (by "get") sub buffer. */ -#define RELAYFS_PUT_SUBBUF _IOW(0xF4, 0x01,__u32) -/* returns the number of sub buffers in the per cpu channel. */ -#define RELAYFS_GET_N_SUBBUFS _IOR(0xF4, 0x02,__u32) -/* returns the size of the sub buffers. */ -#define RELAYFS_GET_SUBBUF_SIZE _IOR(0xF4, 0x03,__u32) - - - -enum { - GET_SUBBUF, - PUT_SUBBUF, - GET_N_BUBBUFS, - GET_SUBBUF_SIZE -}; - -struct fd_pair { - int channel; - int trace; - unsigned int n_subbufs; - unsigned int subbuf_size; - void *mmap; - pthread_mutex_t mutex; -}; - -struct channel_trace_fd { - struct fd_pair *pair; - int num_pairs; -}; - -static char *trace_name = NULL; -static char *channel_name = NULL; -static int daemon_mode = 0; -static int append_mode = 0; -static unsigned long num_threads = 1; -volatile static int quit_program = 0; /* For signal handler */ - -/* Args : - * - * -t directory Directory name of the trace to write to. Will be created. - * -c directory Root directory of the relayfs trace channels. - * -d Run in background (daemon). - * -a Trace append mode. - * -s Send SIGUSR1 to parent when ready for IO. - */ -void show_arguments(void) -{ - printf("Please use the following arguments :\n"); - printf("\n"); - printf("-t directory Directory name of the trace to write to.\n" - " It will be created.\n"); - printf("-c directory Root directory of the relayfs trace channels.\n"); - printf("-d Run in background (daemon).\n"); - printf("-a Append to an possibly existing trace.\n"); - printf("-N Number of threads to start.\n"); - printf("\n"); -} - - -/* parse_arguments - * - * Parses the command line arguments. - * - * Returns 1 if the arguments were correct, but doesn't ask for program - * continuation. Returns -1 if the arguments are incorrect, or 0 if OK. - */ -int parse_arguments(int argc, char **argv) -{ - int ret = 0; - int argn = 1; - - if(argc == 2) { - if(strcmp(argv[1], "-h") == 0) { - return 1; - } - } - - while(argn < argc) { - - switch(argv[argn][0]) { - case '-': - switch(argv[argn][1]) { - case 't': - if(argn+1 < argc) { - trace_name = argv[argn+1]; - argn++; - } - break; - case 'c': - if(argn+1 < argc) { - channel_name = argv[argn+1]; - argn++; - } - break; - case 'd': - daemon_mode = 1; - break; - case 'a': - append_mode = 1; - break; - case 'N': - if(argn+1 < argc) { - num_threads = strtoul(argv[argn+1], NULL, 0); - argn++; - } - break; - default: - printf("Invalid argument '%s'.\n", argv[argn]); - printf("\n"); - ret = -1; - } - break; - default: - printf("Invalid argument '%s'.\n", argv[argn]); - printf("\n"); - ret = -1; - } - argn++; - } - - if(trace_name == NULL) { - printf("Please specify a trace name.\n"); - printf("\n"); - ret = -1; - } - - if(channel_name == NULL) { - printf("Please specify a channel name.\n"); - printf("\n"); - ret = -1; - } - - return ret; -} - -void show_info(void) -{ - printf("Linux Trace Toolkit Trace Daemon\n"); - printf("\n"); - printf("Reading from relayfs directory : %s\n", channel_name); - printf("Writing to trace directory : %s\n", trace_name); - printf("\n"); -} - - -/* signal handling */ - -static void handler(int signo) -{ - printf("Signal %d received : exiting cleanly\n", signo); - quit_program = 1; -} - - - -int open_channel_trace_pairs(char *subchannel_name, char *subtrace_name, - struct channel_trace_fd *fd_pairs) -{ - DIR *channel_dir = opendir(subchannel_name); - struct dirent *entry; - struct stat stat_buf; - int ret; - char path_channel[PATH_MAX]; - int path_channel_len; - char *path_channel_ptr; - char path_trace[PATH_MAX]; - int path_trace_len; - char *path_trace_ptr; - int open_ret = 0; - - if(channel_dir == NULL) { - perror(subchannel_name); - open_ret = ENOENT; - goto end; - } - - printf("Creating trace subdirectory %s\n", subtrace_name); - ret = mkdir(subtrace_name, S_IRWXU|S_IRWXG|S_IRWXO); - if(ret == -1) { - if(errno != EEXIST) { - perror(subtrace_name); - open_ret = -1; - goto end; - } - } - - strncpy(path_channel, subchannel_name, PATH_MAX-1); - path_channel_len = strlen(path_channel); - path_channel[path_channel_len] = '/'; - path_channel_len++; - path_channel_ptr = path_channel + path_channel_len; - - strncpy(path_trace, subtrace_name, PATH_MAX-1); - path_trace_len = strlen(path_trace); - path_trace[path_trace_len] = '/'; - path_trace_len++; - path_trace_ptr = path_trace + path_trace_len; - - while((entry = readdir(channel_dir)) != NULL) { - - if(entry->d_name[0] == '.') continue; - - strncpy(path_channel_ptr, entry->d_name, PATH_MAX - path_channel_len); - strncpy(path_trace_ptr, entry->d_name, PATH_MAX - path_trace_len); - - ret = stat(path_channel, &stat_buf); - if(ret == -1) { - perror(path_channel); - continue; - } - - printf("Channel file : %s\n", path_channel); - - if(S_ISDIR(stat_buf.st_mode)) { - - printf("Entering channel subdirectory...\n"); - ret = open_channel_trace_pairs(path_channel, path_trace, fd_pairs); - if(ret < 0) continue; - } else if(S_ISREG(stat_buf.st_mode)) { - printf("Opening file.\n"); - - fd_pairs->pair = realloc(fd_pairs->pair, - ++fd_pairs->num_pairs * sizeof(struct fd_pair)); - - /* Open the channel in read mode */ - fd_pairs->pair[fd_pairs->num_pairs-1].channel = - open(path_channel, O_RDONLY | O_NONBLOCK); - if(fd_pairs->pair[fd_pairs->num_pairs-1].channel == -1) { - perror(path_channel); - fd_pairs->num_pairs--; - continue; - } - /* Open the trace in write mode, only append if append_mode */ - ret = stat(path_trace, &stat_buf); - if(ret == 0) { - if(append_mode) { - printf("Appending to file %s as requested\n", path_trace); - - fd_pairs->pair[fd_pairs->num_pairs-1].trace = - open(path_trace, O_WRONLY|O_APPEND, - S_IRWXU|S_IRWXG|S_IRWXO); - - if(fd_pairs->pair[fd_pairs->num_pairs-1].trace == -1) { - perror(path_trace); - } - } else { - printf("File %s exists, cannot open. Try append mode.\n", path_trace); - open_ret = -1; - goto end; - } - } else { - if(errno == ENOENT) { - fd_pairs->pair[fd_pairs->num_pairs-1].trace = - open(path_trace, O_WRONLY|O_CREAT|O_EXCL, - S_IRWXU|S_IRWXG|S_IRWXO); - if(fd_pairs->pair[fd_pairs->num_pairs-1].trace == -1) { - perror(path_trace); - } - } - } - } - } - -end: - closedir(channel_dir); - - return open_ret; -} - - -int read_subbuffer(struct fd_pair *pair) -{ - unsigned int consumed_old; - int err, ret=0; - - - err = ioctl(pair->channel, RELAYFS_GET_SUBBUF, - &consumed_old); - printf("cookie : %u\n", consumed_old); - if(err != 0) { - ret = errno; - perror("Reserving sub buffer failed (everything is normal)"); - goto get_error; - } - - err = TEMP_FAILURE_RETRY(write(pair->trace, - pair->mmap - + (consumed_old & ((pair->n_subbufs * pair->subbuf_size)-1)), - pair->subbuf_size)); - - if(err < 0) { - ret = errno; - perror("Error in writing to file"); - goto write_error; - } -#if 0 - err = fsync(pair->trace); - if(err < 0) { - ret = errno; - perror("Error in writing to file"); - goto write_error; - } -#endif //0 -write_error: - err = ioctl(pair->channel, RELAYFS_PUT_SUBBUF, &consumed_old); - if(err != 0) { - ret = errno; - if(errno == -EFAULT) { - perror("Error in unreserving sub buffer\n"); - } else if(errno == -EIO) { - perror("Reader has been pushed by the writer, last subbuffer corrupted."); - /* FIXME : we may delete the last written buffer if we wish. */ - } - goto get_error; - } - -get_error: - return ret; -} - - - -int map_channels(struct channel_trace_fd *fd_pairs) -{ - int i,j; - int ret=0; - - if(fd_pairs->num_pairs <= 0) { - printf("No channel to read\n"); - goto end; - } - - /* Get the subbuf sizes and number */ - - for(i=0;inum_pairs;i++) { - struct fd_pair *pair = &fd_pairs->pair[i]; - - ret = ioctl(pair->channel, RELAYFS_GET_N_SUBBUFS, - &pair->n_subbufs); - if(ret != 0) { - perror("Error in getting the number of subbuffers"); - goto end; - } - ret = ioctl(pair->channel, RELAYFS_GET_SUBBUF_SIZE, - &pair->subbuf_size); - if(ret != 0) { - perror("Error in getting the size of the subbuffers"); - goto end; - } - ret = pthread_mutex_init(&pair->mutex, NULL); /* Fast mutex */ - if(ret != 0) { - perror("Error in mutex init"); - goto end; - } - } - - /* Mmap each FD */ - for(i=0;inum_pairs;i++) { - struct fd_pair *pair = &fd_pairs->pair[i]; - - pair->mmap = mmap(0, pair->subbuf_size * pair->n_subbufs, PROT_READ, - MAP_SHARED, pair->channel, 0); - if(pair->mmap == MAP_FAILED) { - perror("Mmap error"); - goto munmap; - } - } - - goto end; /* success */ - - /* Error handling */ - /* munmap only the successfully mmapped indexes */ -munmap: - /* Munmap each FD */ - for(j=0;jpair[j]; - int err_ret; - - err_ret = munmap(pair->mmap, pair->subbuf_size * pair->n_subbufs); - if(err_ret != 0) { - perror("Error in munmap"); - } - ret |= err_ret; - } - -end: - return ret; - - -} - - -int unmap_channels(struct channel_trace_fd *fd_pairs) -{ - int j; - int ret=0; - - /* Munmap each FD */ - for(j=0;jnum_pairs;j++) { - struct fd_pair *pair = &fd_pairs->pair[j]; - int err_ret; - - err_ret = munmap(pair->mmap, pair->subbuf_size * pair->n_subbufs); - if(err_ret != 0) { - perror("Error in munmap"); - } - ret |= err_ret; - err_ret = pthread_mutex_destroy(&pair->mutex); - if(err_ret != 0) { - perror("Error in mutex destroy"); - } - ret |= err_ret; - } - - return ret; -} - - -/* read_channels - * - * Thread worker. - * - * Read the relayfs channels and write them in the paired tracefiles. - * - * @fd_pairs : paired channels and trace files. - * - * returns (void*)0 on success, (void*)-1 on error. - * - * Note that the high priority polled channels are consumed first. We then poll - * again to see if these channels are still in priority. Only when no - * high priority channel is left, we start reading low priority channels. - * - * Note that a channel is considered high priority when the buffer is almost - * full. - */ - -void * read_channels(void *arg) -{ - struct pollfd *pollfd; - int i,j; - int num_rdy, num_hup; - int high_prio; - int ret = 0; - struct channel_trace_fd *fd_pairs = (struct channel_trace_fd *)arg; - - /* Start polling the FD */ - - pollfd = malloc(fd_pairs->num_pairs * sizeof(struct pollfd)); - - /* Note : index in pollfd is the same index as fd_pair->pair */ - for(i=0;inum_pairs;i++) { - pollfd[i].fd = fd_pairs->pair[i].channel; - pollfd[i].events = POLLIN|POLLPRI; - } - - while(1) { - high_prio = 0; - num_hup = 0; -#ifdef DEBUG - printf("Press a key for next poll...\n"); - char buf[1]; - read(STDIN_FILENO, &buf, 1); - printf("Next poll (polling %d fd) :\n", fd_pairs->num_pairs); -#endif //DEBUG - - /* Have we received a signal ? */ - if(quit_program) break; - - num_rdy = poll(pollfd, fd_pairs->num_pairs, -1); - if(num_rdy == -1) { - perror("Poll error"); - goto free_fd; - } - - printf("Data received\n"); - - for(i=0;inum_pairs;i++) { - switch(pollfd[i].revents) { - case POLLERR: - printf("Error returned in polling fd %d.\n", pollfd[i].fd); - num_hup++; - break; - case POLLHUP: - printf("Polling fd %d tells it has hung up.\n", pollfd[i].fd); - num_hup++; - break; - case POLLNVAL: - printf("Polling fd %d tells fd is not open.\n", pollfd[i].fd); - num_hup++; - break; - case POLLPRI: - if(pthread_mutex_trylock(&fd_pairs->pair[i].mutex) == 0) { - printf("Urgent read on fd %d\n", pollfd[i].fd); - /* Take care of high priority channels first. */ - high_prio = 1; - /* it's ok to have an unavailable subbuffer */ - ret = read_subbuffer(&fd_pairs->pair[i]); - if(ret == -EAGAIN) ret = 0; - - ret = pthread_mutex_unlock(&fd_pairs->pair[i].mutex); - if(ret) - printf("Error in mutex unlock : %s\n", strerror(ret)); - } - break; - } - } - /* If every FD has hung up, we end the read loop here */ - if(num_hup == fd_pairs->num_pairs) break; - - if(!high_prio) { - for(i=0;inum_pairs;i++) { - switch(pollfd[i].revents) { - case POLLIN: - if(pthread_mutex_trylock(&fd_pairs->pair[i].mutex) == 0) { - /* Take care of low priority channels. */ - printf("Normal read on fd %d\n", pollfd[i].fd); - /* it's ok to have an unavailable subbuffer */ - ret = read_subbuffer(&fd_pairs->pair[i]); - if(ret == -EAGAIN) ret = 0; - - ret = pthread_mutex_unlock(&fd_pairs->pair[i].mutex); - if(ret) - printf("Error in mutex unlock : %s\n", strerror(ret)); - } - break; - } - } - } - - } - -free_fd: - free(pollfd); - -end: - return (void*)ret; -} - - -void close_channel_trace_pairs(struct channel_trace_fd *fd_pairs) -{ - int i; - int ret; - - for(i=0;inum_pairs;i++) { - ret = close(fd_pairs->pair[i].channel); - if(ret == -1) perror("Close error on channel"); - ret = close(fd_pairs->pair[i].trace); - if(ret == -1) perror("Close error on trace"); - } - free(fd_pairs->pair); -} - -int main(int argc, char ** argv) -{ - int ret = 0; - struct channel_trace_fd fd_pairs = { NULL, 0 }; - struct sigaction act; - pthread_t *tids; - unsigned int i; - void *tret; - - ret = parse_arguments(argc, argv); - - if(ret != 0) show_arguments(); - if(ret < 0) return EINVAL; - if(ret > 0) return 0; - - show_info(); - - if(daemon_mode) { - ret = daemon(0, 0); - - if(ret == -1) { - perror("An error occured while daemonizing."); - exit(-1); - } - } - - /* Connect the signal handlers */ - act.sa_handler = handler; - act.sa_flags = 0; - sigemptyset(&(act.sa_mask)); - sigaddset(&(act.sa_mask), SIGTERM); - sigaddset(&(act.sa_mask), SIGQUIT); - sigaddset(&(act.sa_mask), SIGINT); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGQUIT, &act, NULL); - sigaction(SIGINT, &act, NULL); - - - if(ret = open_channel_trace_pairs(channel_name, trace_name, &fd_pairs)) - goto close_channel; - - if(ret = map_channels(&fd_pairs)) - goto close_channel; - - tids = malloc(sizeof(pthread_t) * num_threads); - for(i=0; i