*.info
*.bz2
*.tar
+.dirstamp
configure
aclocal.m4
autom4te.cache/
extras/bindings/swig/python/lttng.i
extras/bindings/swig/python/lttng.py
extras/bindings/swig/python/lttng_wrap.c
+extras/core-handler/crash
.checkpatch.conf
tests/regression/ust/fork/fork
tests/regression/ust/fork/fork2
tests/regression/ust/libc-wrapper/prog
+tests/utils/testapp/gen-ust-nevents/gen-ust-nevents
benchmark/
+2013-09-03 lttng-tools 2.3.0
+ * Fix: remove periodical flush test from make check
+
+2013-08-30 lttng-tools 2.3.0-rc3
+ * Fix: hashtable: take split_count_order into account
+ * Fix: remove wrong doing asserts in sessiond
+ * Tests: fix periodical flush tests to stop app
+ * Fix: correctly close metadata on sessiond thread shutdown
+ * Fix: delete the trace directory used for the test
+ * Fix: remove bad check after epoll wait in consumer
+ * Fix: missing data pending signess conversion
+ * Fix: consumer data pending for empty streams
+ * Fix: hash table growth (for small tables) should be limited (v2)
+ * Fix: run_as gid/uid test should return result to parent
+ * Fix: missing check for metadata data pending
+ * Tests: change buffers UID test to PID
+ * Tests: fix health tests to use custom socket timeout
+ * Fix: remove health test from fast regression
+ * Use socket timeout value for tcp timeout if available
+ * Fix: set the health delta tcp timeout aware
+ * Get the maximum TCP timeout in sessiond
+ * Fix: don't report error if UST app dies
+ * Fix: support VPATH build for tests
+ * Improve comments after review
+ * Rename consumer socket fd to fd_ptr
+ * Lock consumer data before fd check during destroy
+ * Use single callsite for send/recv ops. for consumer in sessiond
+ * Use consumer fd reference in consumer socket obj
+ * Update bash completion
+ * Add --list-commands option to the snapshot command
+ * Reorder functions _lttng_cmd_* functions in bash completion
+ * Use parse_size_suffix in snapshot
+ * Fix: snapshot record error handling
+ * Fix: improve error message when UST support is disabled
+ * Fix: add missing short filter option in help
+ * Fix: typo in configure.ac for version check
+ * Fix: remove calibrate syscall option from code
+ * Fix: snapshot should fail if no successful snapshot is taken
+ * Fix: check UST float field mantissa length
+ * Fix: add UST context in the same order the user enabled them
+ * Introduce configure --with-lttng-system-rundir
+ * Add .dirstamp to gitignore file
+ * Fix: snapshot with multiple UIDs
+ * Prepare for automake deprecation of missing subdir-objects
+ * Fix: typos in --help and manpage
+ * Fix: add-context without -c apply to all channels
+ * Fix: channels can be _enabled_ after tracing is started, but not created
+ * Fix filter parser segmentation fault with bison 3.0
+ * Fix: typo in error msg
+ * Fix: imprecise error message about root sessiond/tracing group
+ * Fix: don't skip chmod if tracing group is not found
+ * Tracepoint probes don't need extern C
+ * Fix: Snapshot should be taken asap in core handler script
+ * Fix: reset out_fd_offset when we rotate the trace file
+ * Fix: LTTNG_ERR_NEED_ROOT_SESSIOND error message
+ * inet/inet6 sockets: apply timeout
+ * Implement timeout for connect ipv4/ipv6
+ * Introduce LTTNG_NETWORK_SOCKET_TIMEOUT env. var
+ * bash completion: add calls to _lttng_complete_sessions
+ * bash completion: Fix copy-paste typo
+ * Extras: Remove deprecated consumer commands in bash completion
+ * bash completion: Remove underscores in handler function names
+ * bash completion: Remove --event for add-context
+ * Update gitignore
+ * relayd: use version macros from build rather than scanf
+ * sessiond: use version major/minor from build for communication with relayd
+ * build: export major/minor/patchlevel numbers
+ * Fix: Dead code when checking return value from (ust_app|kernel)_snapshot_record
+ * Test: enable kernel events after start
+ * Fix: kernel ctl error codes are based on errno
+ * Fix: format string mismatch
+ * test: test_periodical_metadata_flush shrink timer period, kill app
+ * Fix: format string type mismatch
+ * snapshot howto: update text
+ * Add snapshot howto
+
+2013-07-19 lttng-tools 2.3.0-rc2
+ * Add core-handler README to dist tarball
+ * extras: core-handler: simplify, allow usage from tracing group
+ * Cleanup: add missing dot
+ * Fix: documentation: create name and options
+ * Add core dump snapshot handler script
+ * Fix: sym name len (kernel)
+ * Fix: tests: provide channel name when a non-default channel exists: per-pid
+ * Fix: tests: provide channel name when a non-default channel exists
+ * Fix: data pending race
+ * Fix hang in make check snapshots/test_ust
+ * Cleanup: ust-consumer: wrong indentation
+ * Fix: print dots while waiting for data availability
+ * Fix: remove sleep(1) added by "Fix: (slight UI change) refuse missing -c if non-default channel exists"
+ * Fix: (slight UI change) refuse missing -c if non-default channel exists
+ * Fix: push metadata on stop for per-UID buffers
+ * README: update python documentation
+ * Manpage: other layout cleanups
+ * Manpage: cleanup layout of view
+ * Manpage: cleanup layout of version
+ * Manpage: cleanup layout of stop
+ * Manpage: cleanup layout of start
+ * Manpage: cleanup layout of snapshot
+ * Manpage: cleanup layout of set-session
+ * Manpage: cleanup layout of list
+ * Manpage: cleanup layout of disable-event
+ * Manpage: cleanup layout of disable-channel
+ * Manpage: cleanup layout of enable-event
+ * Manpage: cleanup layout of enable-channel
+ * Manpage: cleanup layout of destroy
+ * Manpage: cleanup layout of create
+ * Manpage: cleanup layout of calibrate
+ * Manpage: cleanup layout of add-context
+ * Documentation: create --snapshot in manpage
+
+2013-07-17 lttng-tools 2.3.0-rc1
+ * Fix: add missing snapshot header to dist tarball
+ * Documentation: fix thread quit pipe comment
+ * Test for presence of bison and flex when building from git
+ * Test UST snapshot with large metadata
+ * Add test application with large metadata
+ * Cleanup: remove redundant assignment
+ * Fix: use per-uid buffer registry for UID buffer snapshots
+ * cmd.c: fix typos in snapshot commands
+ * Test snapshot per-uid post-mortem
+ * Remove leftover fprintf
+ * Fix deadlock: don't take channel lock in timer
+ * Introduce channel timer lock
+ * document lttng_ustconsumer_request_metadata locking constraints
+ * consumer: remove unused lttng_ustconsumer_push_metadata
+ * Document metadata_socket_lock nesting
+ * lttng_ustconsumer_recv_metadata does not need all those locks
+ * document metadata_switch_timer() deadlock
+ * Fix: add missing metadata socket lock
+ * document metadata_switch_timer() locking constraints
+ * consumer: remove timeout for UST metadata
+ * Introduce pipe for UST metadata cache and stream
+ * consumer: replace DBG2() instances by DBG()
+ * Introduce utils_create_pipe_cloexec_nonblock()
+ * ust consumer: data_pending check is endpoint active
+ * Fix: kernel consumer: data_pending check if endpoint active
+ * consumer: explicitly set endpoint status to active at init
+ * document consumer_metadata_cache_flushed use of consumer_data.lock
+ * consumer: introduce channel lock
+ * Merge branch 'master' of git://git.lttng.org/lttng-tools
+ * Fix: update lttng snapshot help output
+ * Man: fix part of snapshot documentation
+ * Fix: set tracefile size test with PID buffers
+ * Fix: Babelstats fail to parse output with no process name or pid
+ * Missing NULL pointer init in tap.c
+ * Fix: Unchecked asprintf/vasprintf return values
+ * Add snapshots test to fast regression
+ * Fix: kernel data unit test
+ * Fix: snapshot returned valid LTTNG_ERR code
+ * Add the number of snapshot taken to the output path
+ * Fix: RCU read side lock unbalanced
+ * Fix: zeroed snapshot output at init
+ * Support del-output with an output name
+ * Update man page with snapshot command
+ * New UST default buffers is now per UID
+ * Bump UST ABI major version for 2.3 release
+ * Add snapshot mode to lttng list session
+ * Fix: support temporary snapshot max size and name
+ * Support snapshot max-size limitation
+ * Tests: per-UID UST snapshot
+ * Fix: snapshot support for UST and kernel in same session
+ * Implement lttng create --snapshot command
+ * Add create session snapshot API in lttng-sessiond
+ * Add snapshot output init call that uses URIs
+ * Fix: consumer err_sock cloexec
+ * Callsite: add "ip" context
+ * Fix: possible consumer sockets double close on cleanup
+ * Automatically load kvm-x86 and kvm-x86-mmu probes.
+ * Fix: consumer: use uint64_t for all sessiond_id
+ * Fix: add gpl and lgpl files to tarball
+ * Fix: don't install libtap system wide
+ * Fix: close consumer sockets in sessiond cleanup
+ * Fix: set globally visible flag to kernel stream
+ * Fix: lttng: memory leak in snapshot record command
+ * Fix: kernel-consumer: double-close
+ * Fix: consumer: incorrect size zmalloc
+ * Fix: don't try to send stream to relayd if not in streaming
+ * Fix: relayd refcount updates for stream
+ * Fix: don't send error to sessiond on orderly shutdown
+ * Fix: bad pathname used when sending kernel stream to relayd
+ * Fix: add globally visible flag in stream
+ * Fix: destroy metadata stream on setup metadata error path
+ * Fix: send kernel stream to relayd only if needed
+ * Fix: destroy streams for kernel snapshot sessions as well
+ * Fix: close and destroy metadata stream after a kernel snapshot
+ * Fix: print errno message on connect() error
+ * Fix: possible double-close on stream out_fd
+ * Fix: session ID signess to uin64_t in sessiond
+ * Tests: fix validation trace path in kernel snapshot
+ * Tests: Add UST snapshot local and streaming
+ * Add UST snapshot support
+ * Fix: consumer_add_relayd_socket() report errors to sessiond
+ * Fix: add missing enum lttcomm_return_code entries
+ * Fix: UST per-UID channels persist across application teardown
+ * Fix: kernel snapshot metadata handling and error paths
+ * Fix: coding style and debug statement
+ * Fix: put subbuffer back in kernel snapshot error path
+ * Fix: overflow in uri_to_str_url
+ * Fix: detect the correct version of LTTng-UST
+ * Fix: sessiond: use uint64_t for all session ids
+ * Tests: add kernel snapshot streaming to root regression
+ * Tests: remove debug output from test
+ * Tests: Add kernel snapshot streaming
+ * Fix: use snapshot consumer output for kernel
+ * Fix: periodical flush check trace before stop
+ * Fix: consumer: 64-bit index for relayd rather than 32-bit (v2)
+ * Fix UST channel/stream output assignation
+ * Fix: send per-pid session id in channel creation
+ * Fix: consumer double-close on error
+ * Update URCU detection to correctly check for a 0.7 version
+ * Fix: snapshot path
+ * Add utils function to format current time as a string
+ * Fix: set hidden attribute to utils_* calls
+ * Fix: consumer handling of metadata for relayd
+ * Add kernel snapshot support
+ * Support flight recorder mode for a session
+ * Implement snapshot commands in lttng-sessiond
+ * Add snapshot command to lttng UI
+ * Initial import of the snapshot ABI/API in lttng-ctl
+ * Use the consumer stream API in consumer_del_stream()
+ * Add consumer-stream.c/.h in libconsumer
+ * Move multiple URLs parsing fct from lttng-ctl to uri.c
+ * Add a lttng-ctl header to facilitate code separation
+
2013-06-25 lttng-tools 2.2.0 (National Catfish Day)
* STABLE VERSION
* Fix: if relayd is unreachable, disable consumer for the session
dist_noinst_DATA = CodingStyle
-EXTRA_DIST = extras/lttng-bash_completion
+EXTRA_DIST = extras/lttng-bash_completion gpl-2.0.txt lgpl-2.1.txt
- python-dev (optional)
Python headers
- * Debian/Ubuntu package: python-dev
+ * Debian/Ubuntu package: python3-dev
- For kernel tracing: modprobe
the configure script, to generate it.
If you want Python bindings, run ./configure --enable-python-bindings.
+ Please note that some distributions will need the following
+ environment variables set before running configure:
+
+ export PYTHON="python3"
+ export PYTHON_CONFIG="/usr/bin/python3-config"
USAGE:
-AC_INIT([lttng-tools],[2.2.0],[dgoulet@efficios.com],[],[http://lttng.org])
+AC_INIT([lttng-tools],[2.4.0-pre1],[dgoulet@efficios.com],[],[http://lttng.org])
AC_CONFIG_AUX_DIR([config])
AC_CANONICAL_TARGET
AC_CANONICAL_HOST
AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
-version_name="Cuda"
-version_description="Brewed at the Benelux microbrewery, this IPA has huge floral, citric and resinous hop aroma, simply an amazing nose. The flavor is very fresh with a light caramel malting touch completing a strong body. Huge amounts of hops, lots of grapefruit, lemon and oranges. This is an outstanding IPA!"
+# Compute minor/major/patchlevel version numbers
+AC_PROG_SED
+major_version=$(echo AC_PACKAGE_VERSION | sed 's/^\([[0-9]]\)*\.[[0-9]]*\.[[0-9]]*.*$/\1/')
+minor_version=$(echo AC_PACKAGE_VERSION | sed 's/^[[0-9]]*\.\([[0-9]]*\)\.[[0-9]]*.*$/\1/')
+patchlevel_version=$(echo AC_PACKAGE_VERSION | sed 's/^[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\).*$/\1/')
+AC_SUBST([MAJOR_VERSION], [$major_version])
+AC_SUBST([MINOR_VERSION], [$minor_version])
+AC_SUBST([PATCHLEVEL_VERSION], [$patchlevel_version])
+AC_DEFINE_UNQUOTED([VERSION_MAJOR], $major_version, [LTTng-Tools major version number])
+AC_DEFINE_UNQUOTED([VERSION_MINOR], $minor_version, [LTTng-Tools minor version number])
+AC_DEFINE_UNQUOTED([VERSION_PATCHLEVEL], $patchlevel_version, [LTTng-Tools patchlevel version number])
+
+version_name="Dominus Vobiscum"
+version_description="A very succulent line-up of beers brewed at Microbrasserie Charlevoix. Elaborated starting from special malts and fermented with a Belgian yeast. These beers are refermented in bottle and will make you discover the richness of wheat, amber and triple styles."
AC_DEFINE_UNQUOTED([VERSION_NAME], ["$version_name"], "")
AC_DEFINE_UNQUOTED([VERSION_DESCRIPTION], ["$version_description"], "")
[SESSIOND_BIN=''])
AC_SUBST([SESSIOND_BIN])
+AC_ARG_WITH([lttng-system-rundir],
+ AS_HELP_STRING([--with-lttng-system-rundir],
+ [Location of the system directory where the system-wide lttng-sessiond runtime files are kept. The default is "/var/run/lttng".]),
+ [LTTNG_SYSTEM_RUNDIR="$withval"],
+ [LTTNG_SYSTEM_RUNDIR="/var/run/lttng"])
+AC_SUBST([LTTNG_SYSTEM_RUNDIR])
+
AC_DEFINE_UNQUOTED([CONFIG_CONSUMERD32_BIN], "$CONSUMERD32_BIN", [Location of the 32-bit consumerd executable.])
AC_DEFINE_UNQUOTED([CONFIG_CONSUMERD64_BIN], "$CONSUMERD64_BIN", [Location of the 64-bit consumerd executable])
AC_DEFINE_UNQUOTED([CONFIG_CONSUMERD32_LIBDIR], "$CONSUMERD32_LIBDIR", [Search for consumerd 32-bit libraries in this location.])
AC_DEFINE_UNQUOTED([CONFIG_BABELTRACE_BIN], "$BABELTRACE_BIN", [Location of the babeltrace viewer executable.])
AC_DEFINE_UNQUOTED([CONFIG_LTTV_GUI_BIN], "$LTTV_GUI_BIN", [Location of the lttv GUI viewer executable.])
AC_DEFINE_UNQUOTED([CONFIG_SESSIOND_BIN], "$SESSIOND_BIN", [Location of the sessiond executable.])
+AC_DEFINE_UNQUOTED([CONFIG_LTTNG_SYSTEM_RUNDIR], ["$LTTNG_SYSTEM_RUNDIR"], [LTTng system runtime directory])
# Check for pthread
AC_CHECK_LIB([pthread], [pthread_create], [],
AC_PROG_YACC
AC_PROG_LEX
-AC_DEFUN([AC_PROG_BISON], [AC_CHECK_PROGS(BISON, bison, bison)])
+if test ! -f "$srcdir/src/lib/lttng-ctl/filter/filter-parser.h"; then
+ if test x"$YACC" != "xbison -y"; then
+ AC_MSG_ERROR([[bison not found and is required when building from git.
+ Please install bison]])
+ fi
+fi
+
+if test ! -f "$srcdir/src/lib/lttng-ctl/filter/filter-lexer.c"; then
+ if test x"$LEX" != "xflex"; then
+ AC_MSG_ERROR([[flex not found and is required when building from git.
+ Please install flex]])
+ fi
+fi
CFLAGS="-Wall $CFLAGS -g -fno-strict-aliasing"
extras/bindings/Makefile
extras/bindings/swig/Makefile
extras/bindings/swig/python/Makefile
+ extras/core-handler/Makefile
src/Makefile
src/common/Makefile
src/common/kernel-ctl/Makefile
src/common/compat/Makefile
src/common/relayd/Makefile
src/common/testpoint/Makefile
+ src/common/index/Makefile
+ src/common/health/Makefile
src/lib/Makefile
src/lib/lttng-ctl/Makefile
src/lib/lttng-ctl/filter/Makefile
tests/regression/ust/high-throughput/Makefile
tests/regression/ust/low-throughput/Makefile
tests/regression/ust/before-after/Makefile
- tests/regression/ust/buffers-uid/Makefile
+ tests/regression/ust/buffers-pid/Makefile
tests/regression/ust/periodical-metadata-flush/Makefile
tests/regression/ust/multi-session/Makefile
tests/regression/ust/overlap/Makefile
tests/utils/tap/Makefile
tests/utils/testapp/Makefile
tests/utils/testapp/gen-ust-events/Makefile
+ tests/utils/testapp/gen-ust-nevents/Makefile
])
AC_OUTPUT
SUBDIRS = man
EXTRA_DIST = quickstart.txt streaming-howto.txt python-howto.txt \
- calibrate.txt kernel-CodingStyle.txt
+ snapshot-howto.txt calibrate.txt kernel-CodingStyle.txt \
+ live-reading-howto.txt live-reading-protocol.txt
dist_doc_DATA = quickstart.txt streaming-howto.txt python-howto.txt \
- calibrate.txt
+ snapshot-howto.txt calibrate.txt live-reading-howto.txt \
+ live-reading-protocol.txt
--- /dev/null
+LTTng Live trace reading how-to
+
+Julien Desfossez
+September 27th, 2013
+
+This document presents a summary on how to use the live trace reading feature
+introduced in LTTng 2.4. For the details about the protocol, please refer to
+the live-reading-protocol.txt document.
+
+Live trace reading refers to the feature of reading the trace while it is being
+recorded. In order to do that, the trace must be streamed a relay even if the
+viewer is running on the same machine as the tracer.
+
+So, the first thing to do is to start a lttng-relayd process. It can be
+anywhere on the network (including localhost) as long as the sessiond/consumerd
+and the viewer can communicate with it over TCP/IP.
+
+$ lttng-relayd -d
+
+Then, we can create a session configured for streaming with the new --live
+parameter.
+
+$ lttng create --live 1000000 -U net://localhost
+
+The --live parameter activates a session-wide timer (usec) that is responsible
+for checking at a user-defined rate if new data is available. If there is new
+data, it is flushed automatically, otherwise a beacon is sent to the relayd to
+inform it that the stream is currently empty and the viewer can ignore this
+stream up to a certain point in time.
+
+Once the session is created, the user can activate events as usual.
+
+In order to view the live trace, the viewer must implement the live-reading
+protocol.
+
+For now, a basic client is available in the branch index2013 of the git
+repository :
+https://github.com/jdesfossez/babeltrace-dev.git
+
+This client is still in heavy development and the branch will be rebased, it is
+only provided as a proof-of-concept and an exemple on how to use the protocol.
+Once compiled and installed, just run :
+$ test-live hostname 2>/dev/null
+
+If you want to see all the debug, just get rid of "2>/dev/null".
+Once again, it is a client in development, the name is purposely bad and the
+debug output is relatively heavy.
+
+Known viewer issues :
+- adding metadata on the fly (enabling events when a viewer is connected)
+- destroy not clean
+- aggressive polling when all the streams are inactive (after a lttng stop)
+- restart reading from the beginning (SEEK_LAST not implemented yet)
--- /dev/null
+LTTng Live trace reading protocol
+
+Julien Desfossez
+September 18th, 2013
+
+This document describes the protocol of live trace reading. It is mainly
+intended for trace-viewer developers.
+
+Live trace reading allows a viewer to read safely a LTTng trace while it is
+being recorded. The protocol ensures that the viewer is never in a position
+where it can read for which it does not have the metadata, and also allows to
+viewer to know about inactive streams and skip those up to a certain point in
+time.
+
+This feature is implemented starting at lttng-tools 2.4
+
+* General informations
+All the data structures required to implement this protocole are provided in
+the lttng-viewer.h header. All integer are encoded in big endian during the
+transfer.
+
+Just like the streaming protocol, this protocol works always in only one
+direction : from the viewer to the relay. After each command, the relay sends a
+reply to the viewer (or closes the connection on fatal error).
+
+When the viewer sends a command to the relay, it sends a struct
+lttng_viewer_cmd which contains the command (enum lttcomm_relayd_command) and
+the size of the actual command if the command requires a payload.
+For example, if the viewer wants to list the sessions, it sends a struct
+lttng_viewer_cmd with :
+cmd = htobe32(VIEWER_CONNECT);
+data_size = htobe64(sizeof(struct lttng_viewer_connect));
+
+The cmd_version is currently unused, but might get useful when we extend the
+protocol later.
+
+* Protocol sequence
+In this section, we describe the normal sequence of event during a live-reading
+session. The viewer is abbreviated V and the relay R for conciseness.
+
+Establishing a connection :
+- V connects to R on port TCP/5344
+- V establishes a session by sending a VIEWER_CONNECT command, payload in
+ struct lttng_viewer_connect. In this struct, it sets its major and minor
+ version of the protocol it implements, and the type of connection it wants,
+ for now only VIEWER_CLIENT_COMMAND is supported.
+- If the protocol implemented by V and R are compatible, R sends back the same
+ struct with its own version and the newly assigned viewer_session_id.
+ Protocols are compatible if they have the same major number. At this point,
+ if the communication continues and the minor are not the same, it is implied
+ that the two processes will use the min(minor) of the protocol during this
+ connection. Protocol versions follow lttng-tools version, so if R implements
+ the 2.5 protocol and V implements the 2.4 protocol, R will use the 2.4
+ protocol for this connection.
+
+List the sessions :
+Once V and R agree on a protocol, V can start interacting with R. The first
+thing to do is list the sessions currently active on R.
+- V sends the command VIEWER_LIST_SESSIONS with no payload (data_size ignored)
+- R sends back a struct lttng_viewer_list_sessions which contains the number of
+ sessions to receive, and then for each session (sessions_count), it sends a
+ struct lttng_viewer_session
+- V must first receive the struct lttng_viewer_list_sessions and then receive
+ all the lttng_viewer_session structs. The live_timer corresponds to the value
+ set by the user who created the tracing session, if it is 0, the session
+ cannot be read in live. This timer in microseconds is the minimum rate at
+ which R receives information about the running session. The "clients" field
+ contains the number of connected clients to this session, for now only one
+ client at a time can attach to session.
+
+Attach to a session :
+Now V can select a session and attach to it. In order to do so, it sends the
+command VIEWER_ATTACH_SESSION with the session_id it wants. The "seek"
+parameter allows the viewer to attach to a session from its beginning (it will
+receive all trace data still on the relayd) or from now (data will be available
+to read starting at the next packet received on the relay). Only one session
+can be established by connection. Lots of clock synchronisation issues can
+happen when connecting to multiple sessions from multiple hosts at the same
+time, we let the viewers have fun with it ;-)
+R replies with a struct lttng_viewer_attach_session_response with a status and
+the number of streams currently active in this session. Then, for each stream,
+it sends a struct lttng_viewer_stream. Just like with the session list, V must
+receive the first header and then all the stream structs. The ctf_trace_id
+parameter in the struct lttng_viewer_stream is particularly important since it
+allows the viewer to match all the streams belonging to the same CTF trace (so
+one metadata file and multiple stream files). If the stream is a metadata
+stream, metadata_flag will be set to 1.
+
+#### below needs to be well written, but the essential is here ###
+
+Get metadata :
+A CTF trace cannot be read without the complete metadata.
+Send the command VIEWER_GET_METADATA and the struct lttng_viewer_get_metadata.
+
+Once we have all the metadata, we can start processing the trace. In order to
+do that, we work with the indexes. Whenever we need to read a new packet from a
+stream, we first ask for the next index for this stream and then ask for a
+trace packet at a certain offset and length.
+
+Get the next index :
+Command VIEWER_GET_NEXT_INDEX
+struct lttng_viewer_get_next_index
+Receive back a struct lttng_viewer_index
+
+Get data packet :
+Command VIEWER_GET_PACKET
+struct lttng_viewer_get_packet
+Receive back a struct lttng_viewer_trace_packet
+
+For the VIEWER_GET_NEXT_INDEX and VIEWER_GET_PACKET, the viewer must check the
+"flags" element of the struct it receives, because it contains important
+information such as the information that new metadata must be received before
+being able to receive and read the next packet.
+When new metadata is added during a session, the GET_NEXT_INDEX will succeed
+but it will have the flag LTTNG_VIEWER_FLAG_NEW_METADATA, but the
+GET_DATA_PACKET will fail with the same flag as long as the metadata is not
+downloaded.
dist_man1_MANS = lttng.1
-dist_man3_MANS = lttng-health-check.3
dist_man8_MANS = lttng-sessiond.8 lttng-relayd.8
.TH LTTNG_HEALTH_CHECK 3 2012-09-19 "LTTng" "LTTng Developer Manual"
.SH NAME
+.B DEPRECATED
+
lttng_health_check \- Monitor health of the session daemon
.SH SYNOPSIS
.nf
.TP
.BR "-V, --version"
Show version number
+.SH "ENVIRONMENT VARIABLES"
+
+.PP
+.IP "LTTNG_NETWORK_SOCKET_TIMEOUT"
+Control timeout of socket connection, receive and send. Takes an integer
+parameter: the timeout value, in milliseconds. A value of 0 or -1 uses
+the timeout of the operating system (this is the default).
+.PP
+
.SH "SEE ALSO"
.PP
debuggers to work with sessiond on some operating systems.
.IP "LTTNG_APP_SOCKET_TIMEOUT"
Control the timeout of application's socket when sending and receiving
-commands. After this period of time, the application is unregistered by the
-session daemon. A value of 0 or -1 means an infinite timeout. Default value is
-5 seconds.
+commands. Takes an integer parameter: the timeout value, in seconds.
+After this period of time, the application is unregistered by the
+session daemon. A value of 0 or -1 means an infinite timeout. Default
+value is 5 seconds.
+.IP "LTTNG_NETWORK_SOCKET_TIMEOUT"
+Control timeout of socket connection, receive and send. Takes an integer
+parameter: the timeout value, in milliseconds. A value of 0 or -1 uses
+the timeout of the operating system (this is the default).
.SH "SEE ALSO"
.PP
-.TH "LTTNG" "1" "December 3rd, 2012" "" ""
+.TH "LTTNG" "1" "July 18th, 2013" "" ""
.SH "NAME"
lttng \(em LTTng 2.x tracer control command line tool
.SH "SYNOPSIS"
.PP
-.nf
lttng [OPTIONS] <COMMAND>
-.fi
.SH "DESCRIPTION"
.PP
The LTTng project aims at providing highly efficient tracing tools for Linux.
-It's tracers help tracking down performance issues and debugging problems
+Its tracers help track down performance issues and debug problems
involving multiple concurrent processes and threads. Tracing across multiple
systems is also possible.
The \fBlttng\fP command line tool from the lttng-tools package is used to control
-both kernel and user-space tracing. Every interactions with the tracer should
-be done by this tool or by the liblttng-ctl provided with the lttng-tools
+both kernel and user-space tracing. Every interaction with the tracer should
+be done by this tool or by the liblttng-ctl library provided by the lttng-tools
package.
LTTng uses a session daemon (lttng-sessiond(8)), acting as a tracing registry,
We introduce the notion of \fBtracing domains\fP which is essentially a type of
tracer (kernel or user space for now). In the future, we could see a third
tracer being for instance an hypervisor. For some commands, you'll need to
-specify on which domain the command applies (-u or -k). For instance, enabling
-a kernel event, you must specify the kernel domain to the command so we know
-for which tracer this event is for.
+specify on which domain the command operates (-u or -k). For instance, the
+kernel domain must be specified when enabling a kernel event.
In order to trace the kernel, the session daemon needs to be running as root.
LTTng provides the use of a \fBtracing group\fP (default: tracing). Whomever is
in that group can interact with the root session daemon and thus trace the
-kernel. Session daemons can co-exist meaning that you can have a session daemon
+kernel. Session daemons can co-exist, meaning that you can have a session daemon
running as Alice that can be used to trace her applications along side with a
-root daemon or even a Bob daemon. We highly recommend to start the session
+root daemon or even a Bob daemon. We highly recommend starting the session
daemon at boot time for stable and long term tracing.
-Every user-space applications instrumented with lttng-ust(3), will
+All user-space applications instrumented with lttng-ust(3) will
automatically register to the session daemon. This feature gives you the
ability to list available traceable applications and tracepoints on a per user
basis. (See \fBlist\fP command).
Simple listing of lttng commands.
.SH "COMMANDS"
-.TP
-\fBadd-context\fP
-.nf
+.PP
+\fBadd-context\fP [OPTIONS]
+.RS
Add context to event(s) and/or channel(s).
A context is basically extra information appended to a channel. For instance,
counters (hardware branch misses and cache misses), to all events in the trace
data output:
-# lttng add-context \-k \-t prio \-t perf:branch-misses \-t perf:cache-misses
+.nf
+# lttng add-context \-k \-t prio \-t perf:branch-misses \\
+ \-t perf:cache-misses
+.fi
Please take a look at the help (\-h/\-\-help) for a detailed list of available
contexts.
If \fB\-s, \-\-session\fP is omitted, the session name is taken from the .lttngrc
file.
-.fi
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-s, \-\-session NAME
- Apply on session name.
-\-c, \-\-channel NAME
- Apply on channel name.
-\-k, \-\-kernel
- Apply for the kernel tracer
-\-u, \-\-userspace
- Apply for the user-space tracer
-\-t, \-\-type TYPE
- Context type. You can repeat this option on the command line. Please
- use "lttng add-context \-h" to list all available types.
-.fi
-
-.IP
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-s, \-\-session NAME"
+Apply on session name.
+.TP
+.BR "\-c, \-\-channel NAME"
+Apply on channel name.
+.TP
+.BR "\-k, \-\-kernel"
+Apply for the kernel tracer
+.TP
+.BR "\-u, \-\-userspace"
+Apply for the user-space tracer
+.TP
+.BR "\-t, \-\-type TYPE"
+Context type. You can repeat this option on the command line. Please
+use "lttng add-context \-h" to list all available types.
+.RE
+.PP
-.IP "\fBcalibrate\fP"
-.nf
+.PP
+\fBcalibrate\fP [OPTIONS]
+.RS
Quantify LTTng overhead
The LTTng calibrate command can be used to find out the combined average
information (see lttng add-context \-\-help to see the list of available PMU
counters).
+.nf
# lttng create calibrate-function
-# lttng enable-event calibrate \-\-kernel \-\-function lttng_calibrate_kretprobe
-# lttng add-context \-\-kernel \-t perf:LLC-load-misses \-t perf:LLC-store-misses \\
- \-t perf:LLC-prefetch-misses
+# lttng enable-event calibrate \-\-kernel \\
+ \-\-function lttng_calibrate_kretprobe
+# lttng add-context \-\-kernel \-t perf:LLC-load-misses \\
+ \-t perf:LLC-store-misses \\
+ \-t perf:LLC-prefetch-misses
# lttng start
# for a in $(seq 1 10); do \\
lttng calibrate \-\-kernel \-\-function;
done
# lttng destroy
-# babeltrace $(ls \-1drt ~/lttng-traces/calibrate-function-* | tail \-n 1)
+# babeltrace $(ls \-1drt ~/lttng-traces/calibrate-function-* \\
+ | tail \-n 1)
+.fi
The output from babeltrace can be saved to a text file and opened in a
spreadsheet (e.g. oocalc) to focus on the per-PMU counter delta between
The average result, for the i7, on 10 samples:
+.nf
Average Std.Dev.
perf_LLC_load_misses: 5.0 0.577
perf_LLC_store_misses: 1.6 0.516
perf_LLC_prefetch_misses: 9.0 14.742
+.fi
As we can notice, the load and store misses are relatively stable across runs
(their standard deviation is relatively low) compared to the prefetch misses.
accounted for quite precisely, but prefetches within a function seems to behave
too erratically (not much causality link between the code executed and the CPU
prefetch activity) to be accounted for.
-.fi
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-k, \-\-kernel
- Apply for the kernel tracer
-\-u, \-\-userspace
- Apply for the user-space tracer
-\-\-function
- Dynamic function entry/return probe (default)
-.fi
-
-.IP
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-k, \-\-kernel"
+Apply for the kernel tracer
+.TP
+.BR "\-u, \-\-userspace"
+Apply for the user-space tracer
+.TP
+.BR "\-\-function"
+Dynamic function entry/return probe (default)
+.RE
+.PP
-.IP "\fBcreate\fP [NAME] [OPTIONS]
-.nf
+.PP
+\fBcreate\fP [NAME] [OPTIONS]
+.RS
Create tracing session.
A tracing session contains channel(s) which contains event(s). It is domain
-agnostic meaning that you can enable channels and events for either the
+agnostic, meaning that channels and events can be enabled for the
user-space tracer and/or the kernel tracer. It acts like a container
aggregating multiple tracing sources.
The $HOME environment variable can be overridden by defining the environment
variable LTTNG_HOME. This is useful when the user running the commands has
a non-writeable home directory.
-.fi
.B OPTIONS:
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.TP
+.BR "\-o, \-\-output PATH"
+Specify output path for traces
+.TP
+.BR "\-\-no-output"
+Traces will not be output
+.TP
+.BR "\-\-snapshot"
+Set the session in snapshot mode. Created in no-output mode and uses the
+URL, if one is specified, as the default snapshot output. Every channel will be set
+in overwrite mode and with mmap output (splice not supported).
+.TP
+.BR "\-\-live USEC"
+Set the session exclusively in live mode. The paremeter is the delay in micro
+seconds before the data is flushed and streamed. The live mode allows you to
+stream the trace and view it while it's being recorded by any tracer. For that,
+you need a lttng-relayd and this session requires a network URL (\-U or
+\-C/\-D).
+
+To read a live session, you can use babeltrace(1) or the live streaming
+protocol in doc/live-reading-protocol.txt. Here is an example:
+
.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-\-list-options
- Simple listing of options
-\-o, \-\-output PATH
- Specify output path for traces
+$ lttng-relayd -o /tmp/lttng
+$ lttng create --live 200000 -U net://localhost
+$ lttng enable-event -a --userspace
+$ lttng start
+.fi
+
+After the start, you'll be able to read the events while they are being
+recorded in /tmp/lttng.
+.TP
+.BR "\-U, \-\-set-url=URL"
+Set URL for the consumer output destination. It is persistent for the
+session lifetime. Redo the command to change it. This will set both data
+and control URL for network.
+.TP
+.BR "\-C, \-\-ctrl-url=URL"
+Set control path URL. (Must use -D also)
+.TP
+.BR "\-D, \-\-data-url=URL"
+Set data path URL. (Must use -C also)
+.PP
Using these options, each API call can be controlled individually. For
instance, \-C does not enable the consumer automatically. You'll need the \-e
option for that.
-\-U, \-\-set-url=URL
- Set URL for the consumer output destination. It is persistent for the
- session lifetime. Redo the command to change it. This will set both
- data and control URL for network.
-\-C, \-\-ctrl-url=URL
- Set control path URL. (Must use -D also)
-\-D, \-\-data-url=URL
- Set data path URL. (Must use -C also)
-
.B URL FORMAT:
proto://[HOST|IP][:PORT1[:PORT2]][/TRACE_PATH]
Supported protocols are (proto):
-> file://...
- Local filesystem full path.
+.TP
+.BR "file://..."
+Local filesystem full path.
-> net://...
- This will use the default network transport layer which is TCP for both
- control (PORT1) and data port (PORT2). The default ports are
- respectively 5342 and 5343. Note that net[6]:// is not yet supported.
+.TP
+.BR "net://..."
+This will use the default network transport layer which is TCP for both
+control (PORT1) and data port (PORT2). The default ports are
+respectively 5342 and 5343. Note that net[6]:// is not yet supported.
-> tcp[6]://...
- Can only be used with -C and -D together
+.TP
+.BR "tcp[6]://..."
+Can only be used with -C and -D together
NOTE: IPv6 address MUST be enclosed in brackets '[]' (rfc2732)
.B EXAMPLES:
+.nf
# lttng create -U net://192.168.1.42
+.fi
Uses TCP and default ports for the given destination.
+.nf
# lttng create -U net6://[fe80::f66d:4ff:fe53:d220]
+.fi
Uses TCP, default ports and IPv6.
+.nf
# lttng create s1 -U net://myhost.com:3229
-Create session s1 and set its consumer to myhost.com on port 3229 for control.
.fi
+Create session s1 and set its consumer to myhost.com on port 3229 for control.
+.RE
+.PP
-.IP
-
-.IP "\fBdestroy\fP [OPTIONS] [NAME]"
-.nf
+.PP
+\fBdestroy\fP [NAME] [OPTIONS]
+.RS
Teardown tracing session
Free memory on the session daemon and tracer side. It's gone!
If NAME is omitted, the session name is taken from the .lttngrc file.
-.fi
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-a, \-\-all
- Destroy all sessions
-\-\-list-options
- Simple listing of options
-.fi
-
-.IP
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-a, \-\-all"
+Destroy all sessions
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.RE
+.PP
-.IP "\fBenable-channel\fP NAME[,NAME2,...] [-k|-u] [OPTIONS]"
-.nf
+.PP
+\fBenable-channel\fP NAME[,NAME2,...] (\-k | \-u) [OPTIONS]
+.RS
Enable tracing channel
To enable an event, you must enable both the event and the channel that
If \fB\-s, \-\-session\fP is omitted, the session name is taken from the .lttngrc
file.
+Exactly one of \-k or -u must be specified.
+
It is important to note that if a certain type of buffers is used, the session
will be set with that type and all other subsequent channel needs to have the
same type.
Note that once the session has been started and enabled on the tracer side,
it's not possible anymore to enable a new channel for that session.
-.fi
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show this help
-\-\-list-options
- Simple listing of options
-\-s, \-\-session NAME
- Apply on session name
-\-k, \-\-kernel
- Apply to the kernel tracer
-\-u, \-\-userspace
- Apply to the user-space tracer
-
-\-\-discard
- Discard event when subbuffers are full (default)
-\-\-overwrite
- Flight recorder mode : overwrites events when subbuffers are full
-\-\-subbuf-size SIZE
- Subbuffer size in bytes {+k,+M,+G}
- (default UST uid: 131072, UST pid: 4096, kernel: 262144, metadata: 4096)
- Rounded up to the next power of 2.
-
- The minimum subbuffer size, for each tracer, is the max value between
- the default above and the system page size. You can issue this command
- to get the current page size on your system: \fB$ getconf PAGE_SIZE\fP
-\-\-num-subbuf NUM
- Number of subbuffers. (default UST uid: 4, UST pid: 4, kernel: 4, metadata: 2)
- Rounded up to the next power of 2.
-\-\-switch-timer USEC
- Switch subbuffer timer interval in µsec.
- (default UST uid: 0, UST pid: 0, kernel: 0, metadata: 0)
-\-\-read-timer USEC
- Read timer interval in µsec.
- (default UST uid: 0, UST pid: 0, kernel: 200000, metadata: 0)
-\-\-output TYPE
- Channel output type. Possible values: mmap, splice
- (default UST uid: mmap, UST pid: mmap, kernel: splice, metadata: mmap)
-\-\-buffers-uid
- Use per UID buffer (\-u only). Buffers are shared between applications
- that have the same UID.
-\-\-buffers-pid
- Use per PID buffer (\-u only). Each application has its own buffers.
-\-\-buffers-global
- Use shared buffer for the whole system (\-k only)
-\-C, \-\-tracefile-size SIZE
- Maximum size of each tracefile within a stream (in bytes).
- 0 means unlimited. (default: 0)
-\-W, \-\-tracefile-count COUNT
- Used in conjunction with \-C option, this will limit the number
- of files created to the specified count. 0 means unlimited. (default: 0)
+.TP
+.BR "\-h, \-\-help"
+Show this help
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.TP
+.BR "\-s, \-\-session NAME"
+Apply on session name
+.TP
+.BR "\-k, \-\-kernel"
+Apply to the kernel tracer
+.TP
+.BR "\-u, \-\-userspace"
+Apply to the user-space tracer
+.TP
+.BR "\-\-discard"
+Discard event when subbuffers are full (default)
+.TP
+.BR "\-\-overwrite"
+Flight recorder mode : overwrites events when subbuffers are full
+.TP
+.BR "\-\-subbuf-size SIZE"
+Subbuffer size in bytes {+k,+M,+G}.
+(default UST uid: 131072, UST pid: 4096, kernel: 262144, metadata: 4096)
+Rounded up to the next power of 2.
+
+The minimum subbuffer size, for each tracer, is the max value between
+the default above and the system page size. You can issue this command
+to get the current page size on your system: \fB$ getconf PAGE_SIZE\fP
+.TP
+.BR "\-\-num-subbuf NUM"
+Number of subbuffers. (default UST uid: 4, UST pid: 4, kernel: 4,
+metadata: 2) Rounded up to the next power of 2.
+.TP
+.BR "\-\-switch-timer USEC"
+Switch subbuffer timer interval in µsec.
+(default UST uid: 0, UST pid: 0, kernel: 0, metadata: 0)
+.TP
+.BR "\-\-read-timer USEC"
+Read timer interval in µsec.
+(default UST uid: 0, UST pid: 0, kernel: 200000, metadata: 0)
+.TP
+.BR "\-\-output TYPE"
+Channel output type. Possible values: mmap, splice
+(default UST uid: mmap, UST pid: mmap, kernel: splice, metadata: mmap)
+.TP
+.BR "\-\-buffers-uid"
+Use per UID buffer (\-u only). Buffers are shared between applications
+that have the same UID.
+.TP
+.BR "\-\-buffers-pid"
+Use per PID buffer (\-u only). Each application has its own buffers.
+.TP
+.BR "\-\-buffers-global"
+Use shared buffer for the whole system (\-k only)
+.TP
+.BR "\-C, \-\-tracefile-size SIZE"
+Maximum size of each tracefile within a stream (in bytes).
+0 means unlimited. (default: 0)
+.TP
+.BR "\-W, \-\-tracefile-count COUNT"
+Used in conjunction with \-C option, this will limit the number of files
+created to the specified count. 0 means unlimited. (default: 0)
.B EXAMPLES:
-$ lttng enable-channel -C 4096 -W 32 chan1
-For each stream, the maximum size of each trace file will be 4096 bytes, and
+.nf
+$ lttng enable-channel -k -C 4096 -W 32 chan1
+.fi
+For each stream, the maximum size of each trace file will be 4096 bytes and
there will be a maximum of 32 different files. The file count is appended after
the stream number as seen in the following example. The last trace file is
smaller than 4096 since it was not completely filled.
+.nf
~/lttng-traces/[...]/chan1_0_0 (4096)
~/lttng-traces/[...]/chan1_0_1 (4096)
~/lttng-traces/[...]/chan1_0_2 (3245)
~/lttng-traces/[...]/chan1_1_0 (4096)
...
+.fi
-$ lttng enable-channel -C 4096
+.nf
+$ lttng enable-channel -k -C 4096
+.fi
This will create trace files of 4096 bytes and will create new ones as long as
there is data available.
-.fi
-
-.IP
+.RE
+.PP
-.IP "\fBenable-event\fP NAME[,NAME2,...] [-k|-u] [OPTIONS]"
-.nf
+.PP
+\fBenable-event\fP NAME[,NAME2,...] [-k|-u] [OPTIONS]
+.RS
Enable tracing event
A tracing event is always assigned to a channel. If \fB\-c, \-\-channel\fP is
omitted, a default channel named '\fBchannel0\fP' is created and the event is
-added to it. For the user-space tracer, using \fB\-a, \-\-all\fP is the same as
-using the wildcard "*".
+added to it. If \fB\-c, \-\-channel\fP is omitted, but a non-default
+channel already exists within the session, an error is returned. For the
+user-space tracer, using \fB\-a, \-\-all\fP is the same as using the
+wildcard "*".
If \fB\-s, \-\-session\fP is omitted, the session name is taken from the .lttngrc
file.
-.fi
.B OPTIONS:
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.TP
+.BR "\-s, \-\-session NAME"
+Apply on session name
+.TP
+.BR "\-c, \-\-channel NAME"
+Apply on channel name
+.TP
+.BR "\-a, \-\-all"
+Enable all tracepoints and syscalls. This actually enables a single
+wildcard event "*".
+.TP
+.BR "\-k, \-\-kernel"
+Apply for the kernel tracer
+.TP
+.BR "\-u, \-\-userspace"
+Apply for the user-space tracer
+.TP
+.BR "\-\-tracepoint"
+Tracepoint event (default). Userspace tracer supports wildcards at the end
+of string. Don't forget to quote to deal with bash expansion.
+e.g.:
.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-\-list-options
- Simple listing of options
-\-s, \-\-session NAME
- Apply on session name
-\-c, \-\-channel NAME
- Apply on channel name
-\-a, \-\-all
- Enable all tracepoints and syscalls. This actually enable a single
- wildcard event "*".
-\-k, \-\-kernel
- Apply for the kernel tracer
-\-u, \-\-userspace
- Apply for the user-space tracer
-
-\-\-tracepoint
- Tracepoint event (default)
- - userspace tracer supports wildcards at end of string. Don't forget to
- quote to deal with bash expansion.
- e.g.:
"*"
"app_component:na*"
-\-\-loglevel NAME
- Tracepoint loglevel range from 0 to loglevel. Listed in the help (\-h).
-\-\-loglevel-only NAME
- Tracepoint loglevel (only this loglevel).
-
- The loglevel or loglevel-only options should be combined with a
- tracepoint name or tracepoint wildcard.
-\-\-probe [addr | symbol | symbol+offset]
- Dynamic probe. Addr and offset can be octal (0NNN...), decimal (NNN...)
- or hexadecimal (0xNNN...)
-\-\-function [addr | symbol | symbol+offset]
- Dynamic function entry/return probe. Addr and offset can be octal
- (0NNN...), decimal (NNN...) or hexadecimal (0xNNN...)
-\-\-syscall
- System call event. Enabling syscalls tracing (kernel tracer), you will
- not be able to disable them with disable-event. This is a known
- limitation. You can disable the entire channel to do the trick.
-
-\-\-filter 'expression'
- Set a filter on a newly enabled event. Filter expression on event
- fields and context. Event recording depends on evaluation. Only
- specify on first activation of a given event within a session.
- Filter only allowed when enabling events within a session before
- tracing is started. If the filter fails to link with the event
- within the traced domain, the event will be discarded.
- Currently, filter is only implemented for the user-space tracer.
-
- Expression examples:
-
- 'intfield > 500 && intfield < 503'
- '(stringfield == "test" || intfield != 10) && intfield > 33'
- 'doublefield > 1.1 && intfield < 5.3'
-
- Wildcards are allowed at the end of strings:
- 'seqfield1 == "te*"'
- In string literals, the escape character is a '\\'. Use '\\*' for
- the '*' character, and '\\\\' for the '\\' character. Wildcard
- match any sequence of characters, including an empty sub-string
- (match 0 or more characters).
-
- Context information can be used for filtering. The examples
- below show usage of context filtering on process name (with a
- wildcard), process ID range, and unique thread ID for filtering.
- The process and thread ID of running applications can be found
- under columns "PID" and "LWP" of the "ps -eLf" command.
-
- '$ctx.procname == "demo*"'
- '$ctx.vpid >= 4433 && $ctx.vpid < 4455'
- '$ctx.vtid == 1234'
.fi
+.TP
+.BR "\-\-loglevel NAME"
+Tracepoint loglevel range from 0 to loglevel. Listed in the help (\-h).
+.TP
+.BR "\-\-loglevel-only NAME"
+Tracepoint loglevel (only this loglevel).
+The loglevel or loglevel-only options should be combined with a
+tracepoint name or tracepoint wildcard.
+.TP
+.BR "\-\-probe (addr | symbol | symbol+offset)"
+Dynamic probe. Addr and offset can be octal (0NNN...), decimal (NNN...)
+or hexadecimal (0xNNN...)
+.TP
+.BR "\-\-function (addr | symbol | symbol+offset)"
+Dynamic function entry/return probe. Addr and offset can be octal
+(0NNN...), decimal (NNN...) or hexadecimal (0xNNN...)
+.TP
+.BR "\-\-syscall"
+System call event. Enabling syscalls tracing (kernel tracer), you will
+not be able to disable them with disable-event. This is a known
+limitation. You can disable the entire channel to do the trick.
+.TP
+.BR "\-\-filter 'expression'"
+Set a filter on a newly enabled event. Filter expression on event
+fields and context. The event will be recorded if the filter's
+expression evaluates to TRUE. Only specify on first activation of a
+given event within a session.
+Specifying a filter is only allowed when enabling events within a session before
+tracing is started. If the filter fails to link with the event
+within the traced domain, the event will be discarded.
+Filtering is currently only implemented for the user-space tracer.
+
+Expression examples:
-.IP "\fBdisable-channel\fP NAME[,NAME2,...] [\-k|\-u] [OPTIONS]"
.nf
+ 'intfield > 500 && intfield < 503'
+ '(strfield == "test" || intfield != 10) && intfield > 33'
+ 'doublefield > 1.1 && intfield < 5.3'
+.fi
+
+Wildcards are allowed at the end of strings:
+ 'seqfield1 == "te*"'
+In string literals, the escape character is a '\\'. Use '\\*' for
+the '*' character, and '\\\\' for the '\\' character sequence. Wildcard
+matches any sequence of characters, including an empty sub-string
+(matches 0 or more characters).
+
+Context information can be used for filtering. The examples below shows
+usage of context filtering on the process name (using a wildcard), process ID
+range, and unique thread ID. The process and thread IDs of
+running applications can be found under columns "PID" and "LWP" of the
+"ps -eLf" command.
+
+.nf
+ '$ctx.procname == "demo*"'
+ '$ctx.vpid >= 4433 && $ctx.vpid < 4455'
+ '$ctx.vtid == 1234'
+.fi
+
+.RE
+.PP
+
+.PP
+\fBdisable-channel\fP NAME[,NAME2,...] (\-k | \-u) [OPTIONS]
+.RS
Disable tracing channel
-Disabling a channel makes all event(s) in that channel to stop tracing. You can
-enable it back by calling \fBlttng enable-channel NAME\fP again.
+Disabling a channel disables the tracing of all of the channel's events. A channel
+can be reenabled by calling \fBlttng enable-channel NAME\fP again.
If \fB\-s, \-\-session\fP is omitted, the session name is taken from the .lttngrc
file.
-.fi
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-\-list-options
- Simple listing of options
-\-s, \-\-session NAME
- Apply on session name
-\-k, \-\-kernel
- Apply for the kernel tracer
-\-u, \-\-userspace
- Apply for the user-space tracer
-.fi
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.TP
+.BR "\-s, \-\-session NAME"
+Apply on session name
+.TP
+.BR "\-k, \-\-kernel"
+Apply for the kernel tracer
+.TP
+.BR "\-u, \-\-userspace"
+Apply for the user-space tracer
+.RE
+.PP
-.IP "\fBdisable-event\fP NAME[,NAME2,...] [\-k|\-u] [OPTIONS]"
-.nf
+.PP
+\fBdisable-event\fP NAME[,NAME2,...] (\-k | \-u) [OPTIONS]
+.RS
Disable tracing event
The event, once disabled, can be re-enabled by calling \fBlttng enable-event
If \fB\-s, \-\-session\fP is omitted, the session name is taken from the .lttngrc
file.
-.fi
+
+If \fB\-c, \-\-channel\fP is omitted, the default channel name is used.
+If \fB\-c, \-\-channel\fP is omitted, but a non-default channel already
+exists within the session, an error is returned.
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-\-list-options
- Simple listing of options
-\-s, \-\-session NAME
- Apply on session name
-\-a, \-\-all-events
- Disable all events. This does NOT disable "*" but rather
- every known events of the session.
-\-k, \-\-kernel
- Apply for the kernel tracer
-\-u, \-\-userspace
- Apply for the user-space tracer
-.fi
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.TP
+.BR "\-s, \-\-session NAME"
+Apply on session name
+.TP
+.BR "\-c, \-\-channel NAME"
+Apply on channel name
+.TP
+.BR "\-a, \-\-all-events"
+Disable all events. This does NOT disable "*" but rather every known
+events of the session.
+.TP
+.BR "\-k, \-\-kernel"
+Apply for the kernel tracer
+.TP
+.BR "\-u, \-\-userspace"
+Apply for the user-space tracer
+.RE
+.PP
-.IP "\fBlist\fP [\-k|\-u] [SESSION [SESSION_OPTIONS]]"
-.nf
+.PP
+\fBlist\fP [OPTIONS] [SESSION [SESSION OPTIONS]]
+.RS
List tracing session information.
With no arguments, it will list available tracing session(s).
With \-u alone, it will list all available user-space events from registered
applications. Here is an example of 'lttng list \-u':
+.nf
PID: 7448 - Name: /tmp/lttng-ust/tests/hello/.libs/lt-hello
ust_tests_hello:tptest_sighandler (type: tracepoint)
ust_tests_hello:tptest (type: tracepoint)
+.fi
You can now enable any event listed by using the name :
\fBust_tests_hello:tptest\fP.
-.fi
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-\-list-options
- Simple listing of options
-\-k, \-\-kernel
- Select kernel domain
-\-u, \-\-userspace
- Select user-space domain.
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.TP
+.BR "\-k, \-\-kernel"
+Select kernel domain
+.TP
+.BR "\-u, \-\-userspace"
+Select user-space domain.
+.PP
.B SESSION OPTIONS:
-\-c, \-\-channel NAME
- List details of a channel
-\-d, \-\-domain
- List available domain(s)
-.fi
+.TP
+.BR "\-c, \-\-channel NAME"
+List details of a channel
+.TP
+.BR "\-d, \-\-domain"
+List available domain(s)
+.RE
+.PP
-.IP "\fBset-session\fP NAME"
-.nf
+.PP
+\fBset-session\fP NAME [OPTIONS]
+.RS
Set current session name
Will change the session name in the .lttngrc file.
-.fi
.B OPTIONS:
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.RE
+.PP
+
+.PP
+\fBsnapshot\fP [OPTIONS] ACTION
+.RS
+Snapshot command for LTTng session.
+
+.B OPTIONS:
+
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+
+.PP
+.B ACTION:
+
+.TP
+\fBadd-output\fP [-m <SIZE>] [-s <NAME>] [-n <NAME>] <URL> | -C <URL> -D <URL>
+
+Setup and add an snapshot output for a session. Output are the destination
+where the snapshot will be sent. Only one output is permitted. To change it,
+you'll need to delete it and add back the new one.
+
+.TP
+\fBdel-output\fP ID | NAME [-s <NAME>]
+
+Delete an output for a session using the ID. You can either specify the
+output's ID that can be found with list-output or the name.
+
+.TP
+\fBlist-output\fP [-s <NAME>]
+
+List the output of a session. Attributes of the output are printed.
+
+.TP
+\fBrecord\fP [-m <SIZE>] [-s <NAME>] [-n <NAME>] [<URL> | -C <URL> -D <URL>]
+
+Snapshot a session's buffer(s) for all domains. If an URL is specified, it is
+used instead of a previously added output. Specifying only a name or/and a max
+size will override the current output values. For instance, you can record a
+snapshot with a custom maximum size or with a different name.
+
.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-\-list-options
- Simple listing of options
+$ lttng snapshot add-output -n mysnapshot file:///data/snapshot
+[...]
+$ lttng snapshot record -n new_name_snapshot
.fi
-.IP
+The above will create a snapshot in /data/snapshot/new_name_snapshot* directory
+rather then in mysnapshot*/
-.IP "\fBstart\fP [NAME] [OPTIONS]"
-.nf
+.PP
+.B DETAILED ACTION OPTIONS
+
+.TP
+.BR "\-s, \-\-session NAME"
+Apply to session name.
+.TP
+.BR "\-n, \-\-name NAME"
+Name of the snapshot's output.
+.TP
+.BR "\-m, \-\-max-size SIZE"
+Maximum size in bytes of the snapshot. The maxium size does not include the
+metadata file. Human readable format is accepted: {+k,+M,+G}. For instance,
+\-\-max-size 5M
+.TP
+.BR "\-C, \-\-ctrl-url URL"
+Set control path URL. (Must use -D also)
+.TP
+.BR "\-D, \-\-data-url URL"
+Set data path URL. (Must use -C also)
+.RE
+.PP
+
+.PP
+\fBstart\fP [NAME] [OPTIONS]
+.RS
Start tracing
It will start tracing for all tracers for a specific tracing session.
-
If NAME is omitted, the session name is taken from the .lttngrc file.
-.fi
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-\-list-options
- Simple listing of options
-.fi
-
-.IP
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.RE
+.PP
-.IP "\fBstop\fP [NAME] [OPTIONS]"
-.nf
+.PP
+\fBstop\fP [NAME] [OPTIONS]
+.RS
Stop tracing
It will stop tracing for all tracers for a specific tracing session. Before
behavior.
If NAME is omitted, the session name is taken from the .lttngrc file.
-.fi
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-\-list-options
- Simple listing of options
-\-\-no-wait
- Don't wait for data availability.
-.fi
-
-.IP
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.TP "\-\-no-wait"
+Don't wait for data availability.
+.RE
+.PP
-.IP "\fBversion\fP"
-.nf
+.PP
+\fBversion\fP
+.RS
Show version information
-.fi
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show summary of possible options and commands.
-\-\-list-options
- Simple listing of options
-.fi
-
-.IP
-
-.IP "\fBview\fP [SESSION_NAME] [OPTIONS]"
-.nf
-View traces of a tracing session
-
-By default, the babeltrace viewer will be used for text viewing.
-
-If SESSION_NAME is omitted, the session name is taken from the .lttngrc file.
+.TP
+.BR "\-h, \-\-help"
+Show summary of possible options and commands.
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.RE
+.PP
-.fi
+.PP
+\fBview\fP [SESSION_NAME] [OPTIONS]
+.RS
+View traces of a tracing session. By default, the babeltrace viewer
+will be used for text viewing. If SESSION_NAME is omitted, the session
+name is taken from the .lttngrc file.
.B OPTIONS:
-.nf
-\-h, \-\-help
- Show this help
-\-\-list-options
- Simple listing of options
-\-t, \-\-trace-path PATH
- Trace directory path for the viewer
-\-e, \-\-viewer CMD
- Specify viewer and/or options to use
- This will completely override the default viewers so
- please make sure to specify the full command. The trace
- directory path of the session will be appended at the end
- to the arguments
-.fi
+.TP
+.BR "\-h, \-\-help"
+Show this help
+.TP
+.BR "\-\-list-options"
+Simple listing of options
+.TP
+.BR "\-t, \-\-trace-path PATH"
+Trace directory path for the viewer
+.TP
+.BR "\-e, \-\-viewer CMD"
+Specify viewer and/or options to use This will completely override the
+default viewers so please make sure to specify the full command. The
+trace directory path of the session will be appended at the end to the
+arguments
+.RE
+.PP
.SH "EXIT VALUES"
+.PP
On success 0 is returned and a positive value on error. Value of 1 means a command
error, 2 an undefined command, 3 a fatal error and 4 a command warning meaning that
something went wrong during the command.
Any other value above 10, please refer to
-.BR <lttng/lttng-error.h>
+.BR "<lttng/lttng-error.h>"
for a detailed list or use lttng_strerror() to get a human readable string of
the error code.
-
.PP
+
.SH "ENVIRONMENT VARIABLES"
.PP
.IP "LTTNG_SESSIOND_PATH"
Allows one to specify the full session daemon binary path to lttng command line
tool. You can also use \-\-sessiond-path option having the same effect.
+.PP
+
.SH "SEE ALSO"
.BR babeltrace(1),
.BR lttng-ust(3),
.BR lttng-sessiond(8),
.BR lttng-relayd(8),
-.BR lttng-health-check(3)
+
.SH "BUGS"
+.PP
If you encounter any issues or usability problem, please report it on our
mailing list <lttng-dev@lists.lttng.org> to help improve this project or
at https://bugs.lttng.org which is a bugtracker.
+.PP
+
.SH "CREDITS"
.PP
--- /dev/null
+RFC - LTTng snapshot
+
+Author: David Goulet <dgoulet@efficios.com>
+
+Version:
+ - v0.1: 11/04/2013
+ * Initial proposal
+
+Motivation
+----------
+
+This proposal is for the snapshot feature in lttng-tools. The idea is to be
+able to snapshot a portion of the trace and write it to a specified output
+(disk or network). This could be particularly useful in flight recorder mode
+where you detect a problem on your system for instance and then use this
+snapshot feature to save the latest buffers which should contain information to
+help understand the issue.
+
+Requirements
+-----------------
+
+In order to snapshot a session, it must be set in flight recorder mode meaning
+that there is *no* consumer extracting the trace and writing it to a
+destination. To do that, the --no-output option is added to "lttng create"
+command.
+
+ $ lttng create --no-output
+ Create a session with active tracing but no data being collected.
+
+ For the API call lttng_create_session(), simply set the URL to NULL.
+
+Furthermore, by default, this command will set all subsequent channel in
+overwrite mode. You can force the discard value (overwrite=0) but it is a bit
+pointless since the snapshot does NOT remove the data from the buffers.
+
+Proposed Solution
+-----------------
+
+First, the new lttng command line UI is presented followed by the new API
+calls.
+
+This new command uses a git-alike UI, but in the form of the object first
+followed by the desired action, whereas other commands only use actions such as
+"enable-event".
+
+ $ lttng snapshot [ACTION] [OPTIONS]
+
+ ACTION: (detailed below)
+
+The user can specify an output destination either for the current session
+(being the default) or on the spot when the command is executed. To specify an
+output for the session, use the "add-output" action.
+
+ $ lttng snapshot add-output [URL] [OPTIONS]
+
+ OPTIONS:
+ -s, --session NAME
+ -C, --ctrl-url URL
+ -D, --data-url URL
+ -a, --alias ALIAS Name of the output in the session.
+ -m, --max-size SIZE Maximum bytes size of the snapshot.
+
+Without the -s, the current session is used. The above command adds an output
+location to the session so when a snapshot is recorded later on, it's sent
+there. This action command takes either a valid lttng URL (see proposal 0004)
+or the -C/-D options from "lttng create" can be used to define relayd on
+different ports. The alias option can be used to give a name to the output so
+it's recognizable in the list command and can also be used with the del
+command.
+
+Following that, two new actions are available to control outputs. You can list
+and delete outputs.
+
+ $ lttng snapshot list-output
+ [1]: file://[...]
+ [2]: net://1.1.1.1:8762:9123
+ [3] - ALIAS: file://[...]
+
+ $ lttng snapshot del-output ID|ALIAS
+
+ The output identified by the ID or alias is removed from the session. In
+ this case the ID is the number returned by list-output (e.g.: 2).
+
+To specify an output destination on the spot when the snapshot is taken, use
+the record action.
+
+ $ lttng snapshot record [URL] [OPTIONS]
+
+ OPTIONS:
+ -s, --session NAME Session name
+ -n, --name NAME Name of the snapshot to recognize it afterwards.
+ -m, --max-size SIZE Maximum bytes size of the snapshot.
+ -C, --ctrl-url URL
+ -D, --data-url URL
+
+No URL means that the default output of the session is used. The max-size is
+the maximum size of the trace you want to snapshot. The name is used so you can
+recognize the snapshot once taken and written on disk. Finally, the -s let the
+user specify a session name or else the current session is used (in .lttngrc).
+
+Finally, we allow the snapshot command to be used without an action which
+basically do "lttng snapshot record".
+
+ $ lttng snapshot [OPTIONS]
+
+ OPTIONS:
+ -s, --session NAME
+ -m, --max-size SIZE Maximum bytes size of the snapshot.
+
+ $ lttng snapshot -s mysession -m 8192
+
+ Snapshot the session and puts it in the session define output directory.
+
+By default, the snapshot(s) are saved in the session directory in the snapshot/ directory.
+
+ SESSION_DIR/snapshot/<name>-<date>-<time>/[...]
+
+Public API
+----------
+
+/*
+ * Snapshot output structure. Padding will be added once this RFC is accepted.
+ */
+struct lttng_snapshot_output {
+ int id;
+ uint64_t max_size; /* 0: unlimited. */
+ char alias[NAME_MAX];
+ char url[PATH_MAX];
+};
+
+/*
+ * Return the ID of the output or a negative value being a LTTNG_ERR* code.
+ */
+int lttng_snapshot_add_output(struct lttng_handle *handle,
+ struct lttng_snapshot_output *output);
+
+/*
+ * Return 0 on success or else a negative LTTNG_ERR* code.
+ */
+int lttng_snapshot_del_output(int id, char *alias);
+
+/*
+ * Return the number of output and set outputs with the returned info.
+ *
+ * On error, a negative LTTNG_ERR* code is returned.
+ */
+ssize_t lttng_snapshot_list_output(struct lttng_handle *handle,
+ struct lttng_snapshot_output **outputs);
+
+/*
+ * If output is specified, use it as output only for this snapshot or else if
+ * NULL, the default snapshot destination of the session is used. If name is
+ * specified, write it in <name>-<date>-<time> or else if name is NULL, only
+ * the date and time will be used for the directory name.
+ *
+ * This is a blocking call meaning that it will return only if the snapshot is
+ * completed or an error occured. For now, no-wait is not supported but we keep
+ * a parameter for that future case. The wait param is ignored.
+ *
+ * Return 0 on success or else a negative LTTNG_ERR* code.
+ */
+int lttng_snapshot_record(struct lttng_handle *handle,
+ struct lttng_snapshot_output *output,
+ char *name, int wait);
--- /dev/null
+LTTng Flight Recorder Snapshot HOWTO
+
+Mathieu Desnoyers
+July 21st, 2013
+
+This document presents how to use the snapshot feature of LTTng.
+
+Snapshots allow to grab the content of flight recorder tracing buffers
+at the time the snapshot record command is invoked. Flight recorder
+tracing gather trace data in memory, overwriting the oldest information,
+without requiring any disk I/O. The snapshot record command exports the
+snapshot to the destination specified by the user.
+
+Basic usage:
+
+Session daemon started as root for kernel tracing:
+
+# lttng-sessiond -d
+
+From a user part of the tracing group (for kernel tracing):
+
+$ lttng create --snapshot
+$ lttng enable-event -k -a # enable kernel tracing
+$ lttng enable-event -u -a # enable user-space tracing
+$ lttng start
+
+ [ do something, generate activity on the system ]
+
+$ lttng snapshot record
+
+ [ do more stuff... ]
+
+$ lttng snapshot record
+
+$ lttng stop
+$ lttng destroy
+
+Each "lttng snapshot" command records a snapshot of the current buffer
+state. "lttng enable --snapshot" automatically setups the buffers in
+overwrite mode for flight recording, and does not attach any output file
+to the trace. The "lttng snapshot record" command can be performed
+either while tracing is started or stopped.
+
+As an example, this generates the following hierarchy under the
+directory reported by the "create" command above:
+
+.
+├── snapshot-1-20130721-141838-0
+│  ├── kernel
+│  │  ├── channel0_0
+│  │  ├── channel0_1
+│  │  ├── channel0_2
+│  │  ├── channel0_3
+│  │  └── metadata
+│  └── ust
+│  └── uid
+│  └── 1000
+│  └── 64-bit
+│  ├── channel0_0
+│  ├── channel0_1
+│  ├── channel0_2
+│  ├── channel0_3
+│  └── metadata
+└── snapshot-1-20130721-141842-1
+ ├── kernel
+ │  ├── channel0_0
+ │  ├── channel0_1
+ │  ├── channel0_2
+ │  ├── channel0_3
+ │  └── metadata
+ └── ust
+ └── uid
+ └── 1000
+ └── 64-bit
+ ├── channel0_0
+ ├── channel0_1
+ ├── channel0_2
+ ├── channel0_3
+ └── metadata
+
+
+Then, running babeltrace on, e.g.
+
+babeltrace snapshot-1-20130721-141838-0
+
+shows the content of the first snapshot.
+
+Please refer to the lttng(1) manpage for details on the snapshot mode.
-SUBDIRS = bindings
+SUBDIRS = bindings core-handler
--- /dev/null
+AM_CFLAGS = -O2 -g
+AM_LDFLAGS =
+
+noinst_PROGRAMS = crash
+crash_SOURCES = crash.c
+
+noinst_SCRIPTS = handler.sh install.sh test.sh
+EXTRA_DIST = handler.sh install.sh test.sh README
--- /dev/null
+LTTng core dump snapshot handler
+Christian Babeux, June 2013
+
+This is a custom core dump program that will be called when a core dump
+occurs. The program will save the core data in CORE_PATH and also, if a
+root session daemon is running, will record a snapshot of tracing data
+using the lttng command line utility.
+
+The core dump snapshot handler can be installed by using the provided
+install.sh script or by adding the appropriate program pipe line to
+/proc/sys/kernel/core_pattern. Refer to core(5) for more information
+about the Linux kernel core dump handling and custom handler mechanism.
+
+Installation:
+
+# ./install.sh
+Backup current core_pattern in core_pattern.bkp.
+Successfully installed core_pattern.
+
+How to use:
+
+You can use the provided test.sh script to test that the core dump snapshot
+handler is working properly:
+
+# ./test.sh
+Setup coredump-handler...
+Session coredump-handler created.
+Default snapshot output set to: /tmp/lttng/snapshot
+Snapshot mode set. Every channel enabled for that session will be set in overwrite mode and mmap output
+kernel event sched_switch created in channel channel0
+Tracing started for session coredump-handler
+Sleeping...
+Crashing...
+Segmentation fault (core dumped)
+Sleeping...
+Waiting for data availability
+Tracing stopped for session coredump-handler
+Session coredump-handler destroyed
+Core dump will be available in /tmp/lttng/core.
+Snapshot will be available in /tmp/lttng/snapshot.
+
+# tree /tmp/lttng
+/tmp/lttng
+├── core
+│  └── core.29085
+└── snapshot
+ └── snapshot-1-20130719-175041-0
+ └── kernel
+ ├── channel0_0
+ ├── channel0_1
+ ├── channel0_2
+ ├── channel0_3
+ └── metadata
+
+Chaining with other core dump handler:
+
+Some Linux distributions already use their own core dump handler
+(such as systemd 'systemd-coredump' utility). It is possible to chain these
+core dump utility with the core dump snapshot handler. In order to achieve
+this, the core dump snapshot handler must be first in the chain (e.g.
+installed in /proc/sys/kernel/core_pattern) and the other core dump
+handler must be called from within the core dump snapshot handler script.
+
+Example (chaining with systemd systemd-coredump):
+
+# cat /proc/sys/kernel/core_pattern
+|/path/to/lttng/handler.sh %p %u %g %s %t %h %e %E %c
+
+In LTTng handler.sh script:
+
+[...]
+# Save core dump from stdin.
+#$MKDIR_BIN -p "${CORE_PATH}"
+#$CAT_BIN - > "${CORE_PATH}/${CORE_PREFIX}.$p"
+
+# Optional, chain core dump handler with original systemd script.
+$CAT_BIN - | /usr/lib/systemd/systemd-coredump $p $u $g $s $t $e
+[...]
--- /dev/null
+/*
+ * Copyright (C) 2013 - Christian Babeux <christian.babeux@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include <signal.h>
+
+int main(int argc, char *argv[])
+{
+ raise(SIGSEGV);
+ return 0;
+}
--- /dev/null
+#!/bin/sh
+#
+# Copyright (C) 2013 - Christian Babeux <christian.babeux@efficios.com>
+#
+# 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; only version 2
+# of the License.
+#
+# 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.
+#
+
+# System binaries paths.
+CAT_BIN="cat"
+PGREP_BIN="pgrep"
+MKDIR_BIN="mkdir"
+LTTNG_BIN="lttng"
+
+# Session name
+SESSION_NAME="coredump-handler"
+
+# Sessiond binary name.
+SESSIOND_BIN_NAME="lttng-sessiond"
+
+# TODO: Checking for a sessiond lockfile would be more appropriate.
+if $PGREP_BIN -u root "${SESSIOND_BIN_NAME}" > /dev/null 2>&1
+then
+ $LTTNG_BIN snapshot record -s ${SESSION_NAME} > /dev/null 2>&1
+fi
+
+# Core file settings.
+CORE_PATH="/tmp/lttng/core"
+CORE_PREFIX="core"
+
+# Core specifiers, see man core(5)
+
+p=$1 # PID of dumped process
+u=$2 # (numeric) real UID of dumped process
+g=$3 # (numeric) real GID of dumped process
+s=$4 # number of signal causing dump
+t=$5 # time of dump, expressed as seconds since the Epoch,
+ # 1970-01-01 00:00:00 +0000 (UTC)
+h=$6 # hostname (same as nodename returned by uname(2))
+e=$7 # executable filename (without path prefix)
+E=$8 # pathname of executable, with slashes ('/') replaced
+ # by exclamation marks ('!').
+c=$9 # core file size soft resource limit of crashing process
+ # (since Linux 2.6.24)
+
+# Save core dump from stdin.
+$MKDIR_BIN -p "${CORE_PATH}"
+$CAT_BIN - > "${CORE_PATH}/${CORE_PREFIX}.$p"
+
+# Optional, chain core dump handler with original systemd script.
+#$CAT_BIN - | /usr/lib/systemd/systemd-coredump $p $u $g $s $t $e
--- /dev/null
+#!/bin/sh
+#
+# Copyright (C) 2013 - Christian Babeux <christian.babeux@efficios.com>
+#
+# 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; only version 2
+# of the License.
+#
+# 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.
+#
+
+CORE_PATTERN_PATH="/proc/sys/kernel/core_pattern"
+CORE_HANDLER_PATH="$(dirname $(readlink -e $0))/handler.sh"
+CORE_PATTERN="$(cat ${CORE_PATTERN_PATH})"
+
+echo ${CORE_PATTERN} > core_pattern.bkp
+
+echo "Backup current core_pattern in core_pattern.bkp."
+
+echo "|$CORE_HANDLER_PATH %p %u %g %s %t %h %e %E %c" > ${CORE_PATTERN_PATH}
+
+if [ $? -eq 0 ]
+then
+ echo "Successfully installed core_pattern."
+else
+ echo "Installation of core_pattern failed."
+ exit 1
+fi
--- /dev/null
+#!/bin/sh
+#
+# Copyright (C) 2013 - Christian Babeux <christian.babeux@efficios.com>
+#
+# 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; only version 2
+# of the License.
+#
+# 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.
+#
+
+LTTNG_BIN="lttng"
+
+SESSION_NAME="coredump-handler"
+
+# Just recording kernel event sched_switch as an example, but we can as
+# well record user-space events from UST domain.
+EVENT_NAME="sched_switch"
+
+SNAPSHOT_PATH="/tmp/lttng/snapshot"
+SNAPSHOT_URI="file://${SNAPSHOT_PATH}"
+
+echo "Setup ${SESSION_NAME}..."
+$LTTNG_BIN create ${SESSION_NAME} --snapshot -U ${SNAPSHOT_PATH}
+$LTTNG_BIN enable-event ${EVENT_NAME} -k -s ${SESSION_NAME}
+$LTTNG_BIN start ${SESSION_NAME}
+
+echo "Sleeping..."
+sleep 10
+
+echo "Crashing..."
+$(dirname $0)/crash
+
+echo "Sleeping..."
+sleep 10
+
+$LTTNG_BIN stop ${SESSION_NAME}
+$LTTNG_BIN destroy ${SESSION_NAME}
+
+echo "Core dump will be available in /tmp/lttng/core."
+echo "Snapshot will be available in ${SNAPSHOT_PATH}."
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
+# Generates COMPREPLY with the existing session names
_lttng_complete_sessions() {
- # TODO, maybe have a lttng list --simple or something like that
+ # TODO
+ # This code does nothing for now. When there is a mecanism to get the
+ # existing sessions, use it to fill the sessions variable.
+ local sessions
+ sessions=""
+ COMPREPLY=( $(compgen -W "${sessions}" -- $cur) )
return
}
-_lttng_cmd_add_context() {
- local add_context_opts
- add_context_opts=$(lttng add-context --list-options)
+# Generates COMPREPLY with whatever is in the $options variable.
+_lttng_complete_options() {
+ COMPREPLY=( $(compgen -W "${options}" -- $cur) )
+}
+
+# Generates COMPREPLY with whatever is in the $commands variable.
+_lttng_complete_commands() {
+ COMPREPLY=( $(compgen -W "${commands}" -- $cur) )
+}
+
+_lttng_cmd_addcontext() {
+ options=$(lttng add-context --list-options)
case $prev in
--session|-s)
--channel|-c)
return
;;
- --event|-e)
+ --type|-t)
return
;;
- --type|-t)
+ esac
+
+ case $cur in
+ -*)
+ _lttng_complete_options
return
;;
esac
+}
+
+_lttng_cmd_calibrate() {
+ options=$(lttng calibrate --list-options)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${add_context_opts}" -- $cur) )
+ _lttng_complete_options
return
;;
esac
}
_lttng_cmd_create() {
- local create_opts
- create_opts=$(lttng create --list-options)
+ options=$(lttng create --list-options)
case $prev in
--output|-o)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${create_opts}" -- $cur) )
+ _lttng_complete_options
return
;;
esac
}
_lttng_cmd_destroy() {
- local destroy_opts
- destroy_opts=$(lttng destroy --list-options)
+ options=$(lttng destroy --list-options)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${destroy_opts}" -- $cur) )
+ _lttng_complete_options
+ return
;;
*)
_lttng_complete_sessions
+ return
;;
esac
}
-
-_lttng_cmd_enablechannel() {
- local enable_channel_opts
- enable_channel_opts=$(lttng enable-channel --list-options)
+_lttng_cmd_disablechannel() {
+ options=$(lttng disable-channel --list-options)
case $prev in
--session|-s)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${enable_channel_opts}" -- $cur) )
+ _lttng_complete_options
return
;;
esac
}
-
-_lttng_cmd_enableevent() {
- local enable_event_opts
- enable_event_opts=$(lttng enable-event --list-options)
+_lttng_cmd_disableevent() {
+ options=$(lttng disable-event --list-options)
case $prev in
--session|-s)
--channel|-c)
return
;;
- --probe)
- return
- ;;
- --function)
- return
- ;;
- esac
-
- case $cur in
- -*)
- COMPREPLY=( $(compgen -W "${enable_event_opts}" -- $cur) )
- return
- ;;
- esac
-}
-
-_lttng_cmd_enableconsumer() {
- local enable_consumer_opts
- enable_consumer_opts=$(lttng enable-consumer --list-options)
-
- case $prev in
- --session|-s)
- _lttng_complete_sessions
- return
- ;;
esac
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${enable_consumer_opts}" -- $cur) )
+ _lttng_complete_options
return
;;
esac
}
-_lttng_cmd_disableconsumer() {
- local disable_consumer_opts
- disable_consumer_opts=$(lttng disable-consumer --list-options)
+_lttng_cmd_enablechannel() {
+ options=$(lttng enable-channel --list-options)
case $prev in
--session|-s)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${disable_consumer_opts}" -- $cur) )
+ _lttng_complete_options
return
;;
esac
}
-_lttng_cmd_disablechannel() {
- local disable_channel_opts
- disable_channel_opts=$(lttng disable-channel --list-options)
+_lttng_cmd_enableevent() {
+ options=$(lttng enable-event --list-options)
case $prev in
--session|-s)
_lttng_complete_sessions
return
;;
- esac
-
- case $cur in
- -*)
- COMPREPLY=( $(compgen -W "${disable_channel_opts}" -- $cur) )
+ --channel|-c)
return
;;
- esac
-}
-
-_lttng_cmd_disable_event() {
- local disable_event_opts
- disable_channel_opts=$(lttng disable-event --list-options)
-
- case $prev in
- --session|-s)
- _lttng_complete_sessions
+ --probe)
return
;;
- --channel|-c)
+ --function)
return
;;
esac
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${disable_event_opts}" -- $cur) )
+ _lttng_complete_options
return
;;
esac
}
_lttng_cmd_list() {
- local list_opts
- list_opts=$(lttng list --list-options)
+ options=$(lttng list --list-options)
case $prev in
--channel|-c)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${list_opts}" -- $cur) )
+ _lttng_complete_options
return
;;
+ *)
+ _lttng_complete_sessions
+ return
esac
}
-_lttng_cmd_set_session() {
- local set_session_opts
- set_session_opts=$(lttng set-session --list-options)
+_lttng_cmd_setsession() {
+ options=$(lttng set-session --list-options)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${set_session_opts}" -- $cur) )
+ _lttng_complete_options
+ return
+ ;;
+ *)
+ _lttng_complete_sessions
return
;;
esac
}
+_lttng_cmd_snapshot() {
+ options=$(lttng snapshot --list-options)
+ commands=$(lttng snapshot --list-commands)
+
+ _lttng_find_command $((command_found_index + 1))
+
+ if _lttng_cursor_is_after_command; then
+ case $prev in
+ --session|-s)
+ _lttng_complete_sessions
+ return
+ ;;
+ esac
+
+ case $cur in
+ -*)
+ _lttng_complete_options
+ ;;
+ esac
+ else
+ _lttng_complete_commands
+ fi
+}
+
_lttng_cmd_start() {
- local start_opts
- start_opts=$(lttng start --list-options)
+ options=$(lttng start --list-options)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${start_opts}" -- $cur) )
+ _lttng_complete_options
+ return
;;
*)
_lttng_complete_sessions
+ return
;;
esac
}
_lttng_cmd_stop() {
- local stop_opts
- stop_opts=$(lttng stop --list-options)
+ options=$(lttng stop --list-options)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${stop_opts}" -- $cur) )
+ _lttng_complete_options
;;
*)
_lttng_complete_sessions
}
_lttng_cmd_version() {
- local version_opts
- version_opts=$(lttng version --list-options)
+ options=$(lttng version --list-options)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${version_opts}" -- $cur) )
+ _lttng_complete_options
;;
esac
}
-_lttng_cmd_calibrate() {
- local calibrate_opts
- calibrate_opts=$(lttng calibrate --list-options)
+_lttng_cmd_view() {
+ options=$(lttng view --list-options)
case $cur in
-*)
- COMPREPLY=( $(compgen -W "${calibrate_opts}" -- $cur) )
- ;;
- esac
-}
-
-_lttng_cmd_view() {
- local view_opts
- view_opts=$(lttng view --list-options)
-
- case $cur in
- -*)
- COMPREPLY=( $(compgen -W "${view_opts}" -- $cur) )
+ _lttng_complete_options
;;
esac
}
-_lttng_opts() {
- local opts
- opts=$(lttng --list-options)
- COMPREPLY=( $(compgen -W "${opts}" -- $cur) )
-}
-
-_lttng_commands() {
- COMPREPLY=( $(compgen -W "$commands" -- $cur) )
-}
_lttng_before_command() {
# Check if the previous word should alter the behavior
case $cur in
-*)
# If the current word starts with a dash, complete with options
- _lttng_opts
+ _lttng_complete_options
;;
*)
# Otherwise complete with commands
- _lttng_commands
+ _lttng_complete_commands
;;
esac
}
_lttng_after_command() {
local cmd_name
- cmd_name=_lttng_cmd_${command//-/}
+ cmd_name=_lttng_cmd_${command_found//-/}
type -t $cmd_name | grep -q 'function' && $cmd_name
}
+# Check if the word passed as the first parameter corresponds to a
+# command. $command must be set to the list of possible commands.
_lttng_is_command() {
for command in $commands; do
if [ "$1" == "$command" ]; then
return 1
}
-_lttng() {
- local cur prev commands command_found command_found_index
-
- # Get the current and previous word
- _get_comp_words_by_ref cur prev
-
- # Get the valid LTTng commands
- commands=$(lttng --list-commands)
+# Try to find a command in the current command line. Possible commands
+# are passed in $commands.
+#
+# This function takes an optional parameter that indicates the index
+# where to start the search in COMP_WORDS. If omitted, it defaults to 1.
+#
+# If a command is found, $command_found is filled with the name of the
+# command and $command_found_index is set to the index of the command in
+# $COMP_WORDS. If no command is found, $command_found is an empty string
+# and $command_found_index is set to -1.
+_lttng_find_command() {
+ start=${1:-1}
# The text of the found command
command_found=""
# The index of the found command in COMP_WORDS
command_found_index=-1
- for (( i = 1 ; i < ${#COMP_WORDS[@]} ; i++ )); do
+ for (( i = start ; i < ${#COMP_WORDS[@]} ; i++ )); do
_lttng_is_command ${COMP_WORDS[$i]}
if [ $? -eq 0 ]; then
command_found=${COMP_WORDS[$i]}
command_found_index=$i
break
fi
-
done
+}
+
+_lttng_cursor_is_after_command() {
+ [ -n "$command_found" ] && [ "$COMP_CWORD" -gt "$command_found_index" ]
+}
+
+_lttng() {
+ local cur prev commands command_found command_found_index
+
+ # Get the current and previous word
+ _get_comp_words_by_ref cur prev
+
+ # Get the valid first-level LTTng commands and options
+ commands=$(lttng --list-commands)
+ options=$(lttng --list-options)
+
+ _lttng_find_command
# Check if the cursor is before or after the command keyword
- if [ -n "$command_found" ] && [ "$COMP_CWORD" -gt "$command_found_index" ]; then
+ if _lttng_cursor_is_after_command; then
_lttng_after_command
else
_lttng_before_command
-lttnginclude_HEADERS = lttng/lttng.h lttng/lttng-error.h lttng/snapshot.h
+lttnginclude_HEADERS = \
+ lttng/health.h \
+ lttng/lttng.h \
+ lttng/lttng-error.h \
+ lttng/snapshot.h
-noinst_HEADERS = lttng/snapshot-internal.h
+noinst_HEADERS = \
+ lttng/snapshot-internal.h \
+ lttng/health-internal.h
--- /dev/null
+#ifndef HEALTH_INTERNAL_H
+#define HEALTH_INTERNAL_H
+
+/*
+ * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
+ * Copyright (C) 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <assert.h>
+#include <time.h>
+#include <pthread.h>
+#include <urcu/tls-compat.h>
+#include <urcu/uatomic.h>
+#include <urcu/list.h>
+#include <lttng/health.h>
+#include <common/macros.h>
+
+/*
+ * These are the value added to the current state depending of the position in
+ * the thread where is either waiting on a poll() or running in the code.
+ */
+#define HEALTH_POLL_VALUE (1UL << 0)
+#define HEALTH_CODE_VALUE (1UL << 1)
+
+#define HEALTH_IS_IN_POLL(x) ((x) & HEALTH_POLL_VALUE)
+
+struct health_app;
+
+enum health_flags {
+ HEALTH_ERROR = (1U << 0),
+};
+
+struct health_state {
+ /*
+ * last counter and last_time are only read and updated by the health_check
+ * thread (single updater).
+ */
+ unsigned long last;
+ struct timespec last_time;
+
+ /*
+ * current and flags are updated by multiple threads concurrently.
+ */
+ unsigned long current; /* progress counter, updated atomically */
+ enum health_flags flags; /* other flags, updated atomically */
+ int type; /* Indicates the nature of the thread. */
+ /* Node of the global TLS state list. */
+ struct cds_list_head node;
+};
+
+enum health_cmd {
+ HEALTH_CMD_CHECK = 0,
+};
+
+struct health_comm_msg {
+ uint32_t cmd; /* enum health_cmd */
+} LTTNG_PACKED;
+
+struct health_comm_reply {
+ uint64_t ret_code; /* bitmask of threads in bad health */
+} LTTNG_PACKED;
+
+/* Declare TLS health state. */
+extern DECLARE_URCU_TLS(struct health_state, health_state);
+
+/*
+ * Update current counter by 1 to indicate that the thread entered or left a
+ * blocking state caused by a poll(). If the counter's value is not an even
+ * number (meaning a code execution flow), an assert() is raised.
+ */
+static inline void health_poll_entry(void)
+{
+ /* Code MUST be in code execution state which is an even number. */
+ assert(!(uatomic_read(&URCU_TLS(health_state).current)
+ & HEALTH_POLL_VALUE));
+
+ uatomic_add(&URCU_TLS(health_state).current, HEALTH_POLL_VALUE);
+}
+
+/*
+ * Update current counter by 1 indicating the exit of a poll or blocking call.
+ * If the counter's value is not an odd number (a poll execution), an assert()
+ * is raised.
+ */
+static inline void health_poll_exit(void)
+{
+ /* Code MUST be in poll execution state which is an odd number. */
+ assert(uatomic_read(&URCU_TLS(health_state).current)
+ & HEALTH_POLL_VALUE);
+
+ uatomic_add(&URCU_TLS(health_state).current, HEALTH_POLL_VALUE);
+}
+
+/*
+ * Update current counter by 2 indicates progress in execution of a
+ * thread.
+ */
+static inline void health_code_update(void)
+{
+ uatomic_add(&URCU_TLS(health_state).current, HEALTH_CODE_VALUE);
+}
+
+/*
+ * Set health "error" flag.
+ */
+static inline void health_error(void)
+{
+ uatomic_or(&URCU_TLS(health_state).flags, HEALTH_ERROR);
+}
+
+struct health_app *health_app_create(int nr_types);
+void health_app_destroy(struct health_app *ha);
+int health_check_state(struct health_app *ha, int type);
+void health_register(struct health_app *ha, int type);
+void health_unregister(struct health_app *ha);
+
+#endif /* HEALTH_INTERNAL_H */
--- /dev/null
+#ifndef LTTNG_HEALTH_H
+#define LTTNG_HEALTH_H
+
+/*
+ * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
+ * Copyright (C) 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+struct lttng_health;
+struct lttng_health_thread;
+
+enum lttng_health_consumerd {
+ LTTNG_HEALTH_CONSUMERD_UST_32,
+ LTTNG_HEALTH_CONSUMERD_UST_64,
+ LTTNG_HEALTH_CONSUMERD_KERNEL,
+
+ NR_LTTNG_HEALTH_CONSUMERD,
+};
+
+/**
+ * lttng_health_create_sessiond - Create sessiond health object
+ *
+ * Return a newly allocated health object, or NULL on error.
+ */
+struct lttng_health *lttng_health_create_sessiond(void);
+
+/**
+ * lttng_health_create_consumerd - Create consumerd health object
+ * @consumerd: consumer daemon identifier
+ *
+ * Return a newly allocated health object, or NULL on error.
+ */
+struct lttng_health *
+ lttng_health_create_consumerd(enum lttng_health_consumerd consumerd);
+
+/**
+ * lttng_health_create_relayd - Create relayd health object
+ * @path: path to relay daemon health socket.
+ *
+ * "path" needs to refer to a local unix socket file matching the file
+ * used by the relay daemon to query.
+ *
+ * Return a newly allocated health object, or NULL on error.
+ */
+struct lttng_health *lttng_health_create_relayd(const char *path);
+
+/**
+ * lttng_health_destroy - Destroy health object
+ * @health: health object to destroy
+ */
+void lttng_health_destroy(struct lttng_health *health);
+
+/**
+ * lttng_health_query - Query component health
+ * @health: health state (input/output).
+ *
+ * Return 0 on success, negative value on error. This return value only
+ * reports if the query has been successfully performed, *NOT* the
+ * actual state. lttng_health_state() should be used for the latter.
+ */
+int lttng_health_query(struct lttng_health *health);
+
+/**
+ * lttng_health_state - Inspect the state of a health structure
+ * @health: health state (input).
+ *
+ * "path" needs to refer to a local unix socket file matching the file
+ * used by the relay daemon to query.
+ *
+ * Return 0 on success, negative value if the component has at least one
+ * thread in error. It also returns a negative return value if
+ * lttng_health_query() has not yet successfully completed on @health.
+ */
+int lttng_health_state(const struct lttng_health *health);
+
+/**
+ * lttng_health_get_nr_threads - Get number of threads in health component
+ * @health: health state (input)
+ *
+ * Return the number of threads (>= 0) on success, else negative value
+ * on error.
+ */
+int lttng_health_get_nr_threads(const struct lttng_health *health);
+
+/**
+ * lttng_health_get_thread - Get thread health
+ * @health: health state (input)
+ * @nth_thread: nth thread to lookup
+ *
+ * Return a pointer to the health thread, else NULL on error. This
+ * pointer should not be freed by the caller, and can be used until
+ * lttng_health_destroy() is called on @health.
+ */
+const struct lttng_health_thread *
+ lttng_health_get_thread(const struct lttng_health *health,
+ unsigned int nth_thread);
+
+/**
+ * lttng_health_thread_state - Get thread health state
+ * @thread: thread health
+ *
+ * Return 0 if thread is OK, else negative error value.
+ */
+int lttng_health_thread_state(const struct lttng_health_thread *thread);
+
+/**
+ * lttng_health_thread_name - Get thread name
+ * @thread: thread health
+ *
+ * Return thread name, NULL on error.
+ */
+const char *lttng_health_thread_name(const struct lttng_health_thread *thread);
+
+#endif /* LTTNG_HEALTH_H */
LTTNG_ERR_KERN_CONSUMER_FAIL = 48, /* Kernel consumer start failed */
LTTNG_ERR_KERN_STREAM_FAIL = 49, /* Kernel create stream failed */
LTTNG_ERR_START_SESSION_ONCE = 50, /* Session needs to be started once. */
- /* 51 */
- /* 52 */
+ LTTNG_ERR_SNAPSHOT_FAIL = 51, /* Snapshot record failed. */
+ LTTNG_ERR_NO_STREAM = 52, /* Index without stream on relay. */
LTTNG_ERR_KERN_LIST_FAIL = 53, /* Kernel listing events failed */
LTTNG_ERR_UST_CALIBRATE_FAIL = 54, /* UST calibration failed */
LTTNG_ERR_UST_EVENT_ENABLED = 55, /* UST event already enabled. */
LTTNG_ERR_UST_CHAN_NOT_FOUND = 59, /* UST channel not found */
LTTNG_ERR_UST_CHAN_DISABLE_FAIL = 60, /* UST disable channel failed */
LTTNG_ERR_UST_CHAN_ENABLE_FAIL = 61, /* UST enable channel failed */
- /* 62 */
+ LTTNG_ERR_CHAN_EXIST = 62, /* Channel already exists. */
LTTNG_ERR_UST_ENABLE_FAIL = 63, /* UST enable event failed */
LTTNG_ERR_UST_DISABLE_FAIL = 64, /* UST disable event failed */
LTTNG_ERR_UST_META_FAIL = 65, /* UST open metadata failed */
LTTNG_ERR_TRACE_ALREADY_STARTED = 80, /* Tracing already started */
LTTNG_ERR_TRACE_ALREADY_STOPPED = 81, /* Tracing already stopped */
LTTNG_ERR_KERN_EVENT_ENOSYS = 82, /* Kernel event type not supported */
- /* 83 */
- /* 84 */
+ LTTNG_ERR_NEED_CHANNEL_NAME = 83, /* Non-default channel exists within session: channel name needs to be specified with '-c name' */
+ LTTNG_ERR_NO_UST = 84, /* LTTng-UST tracer is not supported. Please rebuild lttng-tools with lttng-ust support enabled. */
/* 85 */
/* 86 */
/* 87 */
* Domain types: the different possible tracers.
*/
enum lttng_domain_type {
- LTTNG_DOMAIN_KERNEL = 1,
- LTTNG_DOMAIN_UST = 2,
-
- /*
- * For now, the domains below are not implemented. However, we keep them
- * here in order to retain their enum values for future development. Note
- * that it is on the roadmap to implement them.
- *
- LTTNG_DOMAIN_UST_EXEC_NAME = 3,
- LTTNG_DOMAIN_UST_PID = 4,
- LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN = 5,
- */
+ LTTNG_DOMAIN_KERNEL = 1, /* Linux Kernel tracer. */
+ LTTNG_DOMAIN_UST = 2, /* Global Userspace tracer. */
+ LTTNG_DOMAIN_JUL = 3, /* Java Util Logging. */
};
/*
LTTNG_EVENT_CONTEXT_VPPID = 9,
LTTNG_EVENT_CONTEXT_PTHREAD_ID = 10,
LTTNG_EVENT_CONTEXT_HOSTNAME = 11,
+ LTTNG_EVENT_CONTEXT_IP = 12,
};
enum lttng_calibrate_type {
*
* The structures should be initialized to zero before use.
*/
-#define LTTNG_CHANNEL_ATTR_PADDING1 LTTNG_SYMBOL_NAME_LEN + 16
+#define LTTNG_CHANNEL_ATTR_PADDING1 LTTNG_SYMBOL_NAME_LEN + 12
struct lttng_channel_attr {
int overwrite; /* 1: overwrite, 0: discard */
uint64_t subbuf_size; /* bytes */
/* LTTng 2.1 padding limit */
uint64_t tracefile_size; /* bytes */
uint64_t tracefile_count; /* number of tracefiles */
+ /* LTTng 2.3 padding limit */
+ unsigned int live_timer_interval; /* usec */
char padding[LTTNG_CHANNEL_ATTR_PADDING1];
};
*
* The structures should be initialized to zero before use.
*/
-#define LTTNG_SESSION_PADDING1 16
+#define LTTNG_SESSION_PADDING1 12
struct lttng_session {
char name[NAME_MAX];
/* The path where traces are written */
char path[PATH_MAX];
uint32_t enabled; /* enabled/started: 1, disabled/stopped: 0 */
+ uint32_t snapshot_mode;
+ unsigned int live_timer_interval; /* usec */
char padding[LTTNG_SESSION_PADDING1];
};
*/
extern int lttng_create_session(const char *name, const char *url);
+/*
+ * Create a tracing session that will exclusively be used for snapshot meaning
+ * the session will be in no output mode and every channel enabled for that
+ * session will be set in overwrite mode and in mmap output since splice is not
+ * supported.
+ *
+ * If an url is given, it will be used to create a default snapshot output
+ * using it as a destination. If NULL, no output will be defined and an
+ * add-output call will be needed.
+ *
+ * Name can't be NULL.
+ */
+extern int lttng_create_session_snapshot(const char *name,
+ const char *snapshot_url);
+
+/*
+ * Create a session exclusively used for live reading.
+ *
+ * In this mode, the switch-timer parameter is forced for each UST channel, a
+ * live-switch-timer is enabled for kernel channels, manually setting
+ * switch-timer is forbidden. Synchronization beacons are sent to the relayd,
+ * indexes are sent and metadata is checked for each packet.
+ *
+ * Returns LTTNG_OK on success or a negative error code.
+ */
+extern int lttng_create_session_live(const char *name, const char *url,
+ unsigned int timer_interval);
+
/*
* Destroy a tracing session.
*
*
* Please see lttng-health-check(3) man page for more information.
*/
-extern int lttng_health_check(enum lttng_health_component c);
+extern LTTNG_DEPRECATED("This call is now obsolete.")
+int lttng_health_check(enum lttng_health_component c);
/*
* For a given session name, this call checks if the data is ready to be read
lttnglibexec_PROGRAMS = lttng-consumerd
-lttng_consumerd_SOURCES = lttng-consumerd.c lttng-consumerd.h
+lttng_consumerd_SOURCES = lttng-consumerd.c \
+ lttng-consumerd.h \
+ health-consumerd.h \
+ health-consumerd.c
lttng_consumerd_LDADD = \
$(top_builddir)/src/common/libconsumer.la \
$(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \
$(top_builddir)/src/common/libcommon.la \
+ $(top_builddir)/src/common/index/libindex.la \
+ $(top_builddir)/src/common/health/libhealth.la \
-lrt
if HAVE_LIBLTTNG_UST_CTL
--- /dev/null
+/*
+ * Copyright (C) 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/resource.h>
+#include <sys/shm.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <urcu/list.h>
+#include <poll.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <config.h>
+#include <urcu/compiler.h>
+#include <ulimit.h>
+#include <inttypes.h>
+
+#include <common/defaults.h>
+#include <common/common.h>
+#include <common/consumer.h>
+#include <common/consumer-timer.h>
+#include <common/compat/poll.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/utils.h>
+
+#include "lttng-consumerd.h"
+#include "health-consumerd.h"
+
+/* Global health check unix path */
+static char health_unix_sock_path[PATH_MAX];
+
+int health_quit_pipe[2];
+
+/*
+ * Check if the thread quit pipe was triggered.
+ *
+ * Return 1 if it was triggered else 0;
+ */
+static
+int check_health_quit_pipe(int fd, uint32_t events)
+{
+ if (fd == health_quit_pipe[0] && (events & LPOLLIN)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Send data on a unix socket using the liblttsessiondcomm API.
+ *
+ * Return lttcomm error code.
+ */
+static int send_unix_sock(int sock, void *buf, size_t len)
+{
+ /* Check valid length */
+ if (len == 0) {
+ return -1;
+ }
+
+ return lttcomm_send_unix_sock(sock, buf, len);
+}
+
+static
+int setup_health_path(void)
+{
+ int is_root, ret = 0;
+ enum lttng_consumer_type type;
+ const char *home_path;
+
+ type = lttng_consumer_get_type();
+ is_root = !getuid();
+
+ if (is_root) {
+ if (strlen(health_unix_sock_path) != 0) {
+ goto end;
+ }
+ switch (type) {
+ case LTTNG_CONSUMER_KERNEL:
+ snprintf(health_unix_sock_path, sizeof(health_unix_sock_path),
+ DEFAULT_GLOBAL_KCONSUMER_HEALTH_UNIX_SOCK);
+ break;
+ case LTTNG_CONSUMER64_UST:
+ snprintf(health_unix_sock_path, sizeof(health_unix_sock_path),
+ DEFAULT_GLOBAL_USTCONSUMER64_HEALTH_UNIX_SOCK);
+ break;
+ case LTTNG_CONSUMER32_UST:
+ snprintf(health_unix_sock_path, sizeof(health_unix_sock_path),
+ DEFAULT_GLOBAL_USTCONSUMER32_HEALTH_UNIX_SOCK);
+ break;
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+ } else {
+ home_path = utils_get_home_dir();
+ if (home_path == NULL) {
+ /* TODO: Add --socket PATH option */
+ ERR("Can't get HOME directory for sockets creation.");
+ ret = -EPERM;
+ goto end;
+ }
+
+ /* Set health check Unix path */
+ if (strlen(health_unix_sock_path) != 0) {
+ goto end;
+ }
+ switch (type) {
+ case LTTNG_CONSUMER_KERNEL:
+ snprintf(health_unix_sock_path, sizeof(health_unix_sock_path),
+ DEFAULT_HOME_KCONSUMER_HEALTH_UNIX_SOCK, home_path);
+ break;
+ case LTTNG_CONSUMER64_UST:
+ snprintf(health_unix_sock_path, sizeof(health_unix_sock_path),
+ DEFAULT_HOME_USTCONSUMER64_HEALTH_UNIX_SOCK, home_path);
+ break;
+ case LTTNG_CONSUMER32_UST:
+ snprintf(health_unix_sock_path, sizeof(health_unix_sock_path),
+ DEFAULT_HOME_USTCONSUMER32_HEALTH_UNIX_SOCK, home_path);
+ break;
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
+/*
+ * Thread managing health check socket.
+ */
+void *thread_manage_health(void *data)
+{
+ int sock = -1, new_sock = -1, ret, i, pollfd, err = -1;
+ uint32_t revents, nb_fd;
+ struct lttng_poll_event events;
+ struct health_comm_msg msg;
+ struct health_comm_reply reply;
+ int is_root;
+
+ DBG("[thread] Manage health check started");
+
+ setup_health_path();
+
+ rcu_register_thread();
+
+ /* We might hit an error path before this is created. */
+ lttng_poll_init(&events);
+
+ /* Create unix socket */
+ sock = lttcomm_create_unix_sock(health_unix_sock_path);
+ if (sock < 0) {
+ ERR("Unable to create health check Unix socket");
+ ret = -1;
+ goto error;
+ }
+
+ is_root = !getuid();
+ if (is_root) {
+ /* lttng health client socket path permissions */
+ ret = chown(health_unix_sock_path, 0,
+ utils_get_group_id(tracing_group_name));
+ if (ret < 0) {
+ ERR("Unable to set group on %s", health_unix_sock_path);
+ PERROR("chown");
+ ret = -1;
+ goto error;
+ }
+
+ ret = chmod(health_unix_sock_path,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ if (ret < 0) {
+ ERR("Unable to set permissions on %s", health_unix_sock_path);
+ PERROR("chmod");
+ ret = -1;
+ goto error;
+ }
+ }
+
+ /*
+ * Set the CLOEXEC flag. Return code is useless because either way, the
+ * show must go on.
+ */
+ (void) utils_set_fd_cloexec(sock);
+
+ ret = lttcomm_listen_unix_sock(sock);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* Size is set to 1 for the consumer_channel pipe */
+ ret = lttng_poll_create(&events, 2, LTTNG_CLOEXEC);
+ if (ret < 0) {
+ ERR("Poll set creation failed");
+ goto error;
+ }
+
+ ret = lttng_poll_add(&events, health_quit_pipe[0], LPOLLIN);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* Add the application registration socket */
+ ret = lttng_poll_add(&events, sock, LPOLLIN | LPOLLPRI);
+ if (ret < 0) {
+ goto error;
+ }
+
+ while (1) {
+ DBG("Health check ready");
+
+ /* Inifinite blocking call, waiting for transmission */
+restart:
+ ret = lttng_poll_wait(&events, -1);
+ if (ret < 0) {
+ /*
+ * Restart interrupted system call.
+ */
+ if (errno == EINTR) {
+ goto restart;
+ }
+ goto error;
+ }
+
+ nb_fd = ret;
+
+ for (i = 0; i < nb_fd; i++) {
+ /* Fetch once the poll data */
+ revents = LTTNG_POLL_GETEV(&events, i);
+ pollfd = LTTNG_POLL_GETFD(&events, i);
+
+ /* Thread quit pipe has been closed. Killing thread. */
+ ret = check_health_quit_pipe(pollfd, revents);
+ if (ret) {
+ err = 0;
+ goto exit;
+ }
+
+ /* Event on the registration socket */
+ if (pollfd == sock) {
+ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+ ERR("Health socket poll error");
+ goto error;
+ }
+ }
+ }
+
+ new_sock = lttcomm_accept_unix_sock(sock);
+ if (new_sock < 0) {
+ goto error;
+ }
+
+ /*
+ * Set the CLOEXEC flag. Return code is useless because either way, the
+ * show must go on.
+ */
+ (void) utils_set_fd_cloexec(new_sock);
+
+ DBG("Receiving data from client for health...");
+ ret = lttcomm_recv_unix_sock(new_sock, (void *)&msg, sizeof(msg));
+ if (ret <= 0) {
+ DBG("Nothing recv() from client... continuing");
+ ret = close(new_sock);
+ if (ret) {
+ PERROR("close");
+ }
+ new_sock = -1;
+ continue;
+ }
+
+ rcu_thread_online();
+
+ assert(msg.cmd == HEALTH_CMD_CHECK);
+
+ reply.ret_code = 0;
+ for (i = 0; i < NR_HEALTH_CONSUMERD_TYPES; i++) {
+ /*
+ * health_check_state return 0 if thread is in
+ * error.
+ */
+ if (!health_check_state(health_consumerd, i)) {
+ reply.ret_code |= 1ULL << i;
+ }
+ }
+
+ DBG2("Health check return value %" PRIx64, reply.ret_code);
+
+ ret = send_unix_sock(new_sock, (void *) &reply, sizeof(reply));
+ if (ret < 0) {
+ ERR("Failed to send health data back to client");
+ }
+
+ /* End of transmission */
+ ret = close(new_sock);
+ if (ret) {
+ PERROR("close");
+ }
+ new_sock = -1;
+ }
+
+exit:
+error:
+ if (err) {
+ ERR("Health error occurred in %s", __func__);
+ }
+ DBG("Health check thread dying");
+ unlink(health_unix_sock_path);
+ if (sock >= 0) {
+ ret = close(sock);
+ if (ret) {
+ PERROR("close");
+ }
+ }
+
+ lttng_poll_clean(&events);
+
+ rcu_unregister_thread();
+ return NULL;
+}
--- /dev/null
+#ifndef HEALTH_CONSUMERD_H
+#define HEALTH_CONSUMERD_H
+
+/*
+ * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
+ * Copyright (C) 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <lttng/health-internal.h>
+
+enum health_type_consumerd {
+ HEALTH_CONSUMERD_TYPE_CHANNEL = 0,
+ HEALTH_CONSUMERD_TYPE_METADATA = 1,
+ HEALTH_CONSUMERD_TYPE_DATA = 2,
+ HEALTH_CONSUMERD_TYPE_SESSIOND = 3,
+ HEALTH_CONSUMERD_TYPE_METADATA_TIMER = 4,
+
+ NR_HEALTH_CONSUMERD_TYPES,
+};
+
+/* Consumerd health monitoring */
+extern struct health_app *health_consumerd;
+
+void *thread_manage_health(void *data);
+
+extern int health_quit_pipe[2];
+
+#endif /* HEALTH_CONSUMERD_H */
#include <common/consumer-timer.h>
#include <common/compat/poll.h>
#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/utils.h>
#include "lttng-consumerd.h"
+#include "health-consumerd.h"
/* TODO : support UST (all direct kernel-ctl accesses). */
/* threads (channel handling, poll, metadata, sessiond) */
-static pthread_t channel_thread, data_thread, metadata_thread, sessiond_thread;
-static pthread_t metadata_timer_thread;
+static pthread_t channel_thread, data_thread, metadata_thread,
+ sessiond_thread, metadata_timer_thread, health_thread;
/* to count the number of times the user pressed ctrl+c */
static int sigintcount = 0;
/* the liblttngconsumerd context */
static struct lttng_consumer_local_data *ctx;
+/* Consumerd health monitoring */
+struct health_app *health_consumerd;
+
+const char *tracing_group_name = DEFAULT_TRACING_GROUP;
+
+enum lttng_consumer_type lttng_consumer_get_type(void)
+{
+ if (!ctx) {
+ return LTTNG_CONSUMER_UNKNOWN;
+ }
+ return ctx->type;
+}
+
/*
* Signal handler for the daemon
*/
fprintf(fp, "Usage: %s OPTIONS\n\nOptions:\n", progname);
fprintf(fp, " -h, --help "
"Display this usage.\n");
- fprintf(fp, " -c, --consumerd-cmd-sock PATH "
+ fprintf(fp, " -c, --consumerd-cmd-sock PATH "
"Specify path for the command socket\n");
- fprintf(fp, " -e, --consumerd-err-sock PATH "
+ fprintf(fp, " -e, --consumerd-err-sock PATH "
"Specify path for the error socket\n");
fprintf(fp, " -d, --daemonize "
"Start as a daemon.\n");
"Verbose mode. Activate DBG() macro.\n");
fprintf(fp, " -V, --version "
"Show version number.\n");
+ fprintf(fp, " -g, --group NAME "
+ "Specify the tracing group name. (default: tracing)\n");
fprintf(fp, " -k, --kernel "
"Consumer kernel buffers (default).\n");
fprintf(fp, " -u, --ust "
{ "consumerd-cmd-sock", 1, 0, 'c' },
{ "consumerd-err-sock", 1, 0, 'e' },
{ "daemonize", 0, 0, 'd' },
+ { "group", 1, 0, 'g' },
{ "help", 0, 0, 'h' },
{ "quiet", 0, 0, 'q' },
{ "verbose", 0, 0, 'v' },
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "dhqvVku" "c:e:", long_options, &option_index);
+ c = getopt_long(argc, argv, "dhqvVku" "c:e:g:", long_options, &option_index);
if (c == -1) {
break;
}
case 'd':
opt_daemon = 1;
break;
+ case 'g':
+ tracing_group_name = optarg;
+ break;
case 'h':
usage(stdout);
exit(EXIT_SUCCESS);
set_ulimit();
}
+ health_consumerd = health_app_create(NR_HEALTH_CONSUMERD_TYPES);
+ if (!health_consumerd) {
+ goto error;
+ }
+
/* create the consumer instance with and assign the callbacks */
ctx = lttng_consumer_create(opt_type, lttng_consumer_read_subbuffer,
NULL, lttng_consumer_on_recv_stream, NULL);
lttng_consumer_set_error_sock(ctx, ret);
/*
- * For UST consumer, we block RT signals used for periodical metadata flush
- * in main and create a dedicated thread to handle these signals.
+ * Block RT signals used for UST periodical metadata flush and the live
+ * timer in main, and create a dedicated thread to handle these signals.
*/
- switch (opt_type) {
- case LTTNG_CONSUMER32_UST:
- case LTTNG_CONSUMER64_UST:
- consumer_signal_init();
- break;
- default:
- break;
- }
+ consumer_signal_init();
+
ctx->type = opt_type;
+ /* Initialize communication library */
+ lttcomm_init();
+
+ ret = utils_create_pipe(health_quit_pipe);
+ if (ret < 0) {
+ goto error_health_pipe;
+ }
+
+ /* Create thread to manage the client socket */
+ ret = pthread_create(&health_thread, NULL,
+ thread_manage_health, (void *) NULL);
+ if (ret != 0) {
+ PERROR("pthread_create health");
+ goto health_error;
+ }
+
/* Create thread to manage channels */
ret = pthread_create(&channel_thread, NULL, consumer_thread_channel_poll,
(void *) ctx);
if (ret != 0) {
perror("pthread_create");
- goto error;
+ goto channel_error;
}
/* Create thread to manage the polling/writing of trace metadata */
goto sessiond_error;
}
- switch (opt_type) {
- case LTTNG_CONSUMER32_UST:
- case LTTNG_CONSUMER64_UST:
- /* Create the thread to manage the metadata periodic timers */
- ret = pthread_create(&metadata_timer_thread, NULL,
- consumer_timer_metadata_thread, (void *) ctx);
- if (ret != 0) {
- perror("pthread_create");
- goto metadata_timer_error;
- }
+ /*
+ * Create the thread to manage the UST metadata periodic timer and
+ * live timer.
+ */
+ ret = pthread_create(&metadata_timer_thread, NULL,
+ consumer_timer_thread, (void *) ctx);
+ if (ret != 0) {
+ perror("pthread_create");
+ goto metadata_timer_error;
+ }
- ret = pthread_detach(metadata_timer_thread);
- if (ret) {
- errno = ret;
- perror("pthread_detach");
- }
- break;
- default:
- break;
+ ret = pthread_detach(metadata_timer_thread);
+ if (ret) {
+ errno = ret;
+ perror("pthread_detach");
}
metadata_timer_error:
goto error;
}
+channel_error:
+ ret = pthread_join(health_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join health thread");
+ goto error; /* join error, exit without cleanup */
+ }
+
+health_error:
+ utils_close_pipe(health_quit_pipe);
+
+error_health_pipe:
if (!ret) {
ret = EXIT_SUCCESS;
lttng_consumer_send_error(ctx, LTTCOMM_CONSUMERD_EXIT_SUCCESS);
end:
lttng_consumer_destroy(ctx);
lttng_consumer_cleanup();
+ if (health_consumerd) {
+ health_app_destroy(health_consumerd);
+ }
return ret;
}
#ifndef _LTTNG_CONSUMERD_H
#define _LTTNG_CONSUMERD_H
+const char *tracing_group_name;
+
+enum lttng_consumer_type lttng_consumer_get_type(void);
+
#endif /* _LTTNG_CONSUMERD_H */
bin_PROGRAMS = lttng-relayd
lttng_relayd_SOURCES = main.c lttng-relayd.h utils.h utils.c cmd.h \
+ index.c index.h live.c live.h ctf-trace.c ctf-trace.h \
cmd-generic.c cmd-generic.h \
cmd-2-1.c cmd-2-1.h \
- cmd-2-2.c cmd-2-2.h
+ cmd-2-2.c cmd-2-2.h \
+ cmd-2-4.c cmd-2-4.h \
+ health-relayd.c health-relayd.h
# link on liblttngctl for check if relayd is already alive.
lttng_relayd_LDADD = -lrt -lurcu-common -lurcu \
$(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \
$(top_builddir)/src/common/hashtable/libhashtable.la \
$(top_builddir)/src/common/libcommon.la \
- $(top_builddir)/src/common/compat/libcompat.la
+ $(top_builddir)/src/common/compat/libcompat.la \
+ $(top_builddir)/src/common/index/libindex.la \
+ $(top_builddir)/src/common/health/libhealth.la
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <string.h>
+
+#include <common/common.h>
+#include <common/sessiond-comm/relayd.h>
+
+#include "cmd-generic.h"
+#include "lttng-relayd.h"
+
+int cmd_create_session_2_4(struct relay_command *cmd,
+ struct relay_session *session)
+{
+ int ret;
+ struct lttcomm_relayd_create_session_2_4 session_info;
+
+ assert(cmd);
+ assert(session);
+
+ ret = cmd_recv(cmd->sock, &session_info, sizeof(session_info));
+ if (ret < 0) {
+ ERR("Unable to recv session info version 2.4");
+ goto error;
+ }
+
+ strncpy(session->session_name, session_info.session_name,
+ sizeof(session->session_name));
+ strncpy(session->hostname, session_info.hostname,
+ sizeof(session->hostname));
+ session->live_timer = be32toh(session_info.live_timer);
+ session->snapshot = be32toh(session_info.snapshot);
+
+ ret = 0;
+
+error:
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RELAYD_CMD_2_4_H
+#define RELAYD_CMD_2_4_H
+
+#include "lttng-relayd.h"
+
+int cmd_create_session_2_4(struct relay_command *cmd,
+ struct relay_session *session);
+
+#endif /* RELAYD_CMD_2_4_H */
#include "cmd-generic.h"
#include "cmd-2-1.h"
#include "cmd-2-2.h"
+#include "cmd-2-4.h"
#endif /* RELAYD_CMD_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+
+#include <common/common.h>
+#include <common/utils.h>
+
+#include "ctf-trace.h"
+
+static uint64_t last_relay_ctf_trace_id;
+
+/*
+ * Try to destroy a ctf_trace object meaning that the refcount is decremented
+ * and checked if down to 0 which will free it.
+ */
+void ctf_trace_try_destroy(struct ctf_trace *obj)
+{
+ unsigned long ret_ref;
+
+ if (!obj) {
+ return;
+ }
+
+ ret_ref = uatomic_add_return(&obj->refcount, -1);
+ assert(ret_ref >= 0);
+ if (ret_ref == 0) {
+ DBG("Freeing ctf_trace %" PRIu64, obj->id);
+ free(obj);
+ }
+}
+
+/*
+ * Create and return an allocated ctf_trace object. NULL on error.
+ */
+struct ctf_trace *ctf_trace_create(void)
+{
+ struct ctf_trace *obj;
+
+ obj = zmalloc(sizeof(*obj));
+ if (!obj) {
+ PERROR("ctf_trace alloc");
+ goto error;
+ }
+
+ obj->id = ++last_relay_ctf_trace_id;
+ DBG("Created ctf_trace %" PRIu64, obj->id);
+
+error:
+ return obj;
+}
+
+/*
+ * Check if we can assign the ctf_trace id and metadata stream to one or all
+ * the streams with the same path_name (our unique ID for ctf traces).
+ *
+ * The given stream MUST be new and NOT visible (in any hash table).
+ */
+void ctf_trace_assign(struct lttng_ht *ht, struct relay_stream *stream)
+{
+ struct lttng_ht_iter iter;
+ struct relay_stream *tmp_stream;
+
+ assert(ht);
+ assert(stream);
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry_duplicate(ht->ht,
+ ht->hash_fct((void *) stream->path_name, lttng_ht_seed),
+ ht->match_fct, (void *) stream->path_name,
+ &iter.iter, tmp_stream, ctf_trace_node.node) {
+ if (stream->metadata_flag) {
+ /*
+ * The new stream is the metadata stream for this trace,
+ * assign the ctf_trace pointer to all the streams in
+ * this bucket.
+ */
+ pthread_mutex_lock(&tmp_stream->lock);
+ tmp_stream->ctf_trace = stream->ctf_trace;
+ uatomic_inc(&tmp_stream->ctf_trace->refcount);
+ pthread_mutex_unlock(&tmp_stream->lock);
+ DBG("Assigned ctf_trace %" PRIu64 " to stream %" PRIu64,
+ tmp_stream->ctf_trace->id, tmp_stream->stream_handle);
+ } else if (tmp_stream->ctf_trace) {
+ /*
+ * The ctf_trace already exists for this bucket,
+ * just assign the pointer to the new stream and exit.
+ */
+ stream->ctf_trace = tmp_stream->ctf_trace;
+ uatomic_inc(&stream->ctf_trace->refcount);
+ DBG("Assigned ctf_trace %" PRIu64 " to stream %" PRIu64,
+ tmp_stream->ctf_trace->id, tmp_stream->stream_handle);
+ goto end;
+ } else {
+ /*
+ * We don't know yet the ctf_trace ID (no metadata has been added),
+ * so leave it there until the metadata stream arrives.
+ */
+ goto end;
+ }
+ }
+
+end:
+ rcu_read_unlock();
+ return;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _CTF_TRACE_H
+#define _CTF_TRACE_H
+
+#include <inttypes.h>
+
+#include <common/hashtable/hashtable.h>
+
+#include "lttng-relayd.h"
+
+struct ctf_trace {
+ int refcount;
+ uint64_t id;
+ uint64_t metadata_received;
+ uint64_t metadata_sent;
+ struct relay_stream *metadata_stream;
+};
+
+void ctf_trace_assign(struct lttng_ht *ht, struct relay_stream *stream);
+struct ctf_trace *ctf_trace_create(void);
+void ctf_trace_try_destroy(struct ctf_trace *obj);
+
+#endif /* _CTF_TRACE_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/resource.h>
+#include <sys/shm.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <urcu/list.h>
+#include <poll.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <config.h>
+#include <urcu/compiler.h>
+#include <ulimit.h>
+#include <inttypes.h>
+
+#include <common/defaults.h>
+#include <common/common.h>
+#include <common/consumer.h>
+#include <common/consumer-timer.h>
+#include <common/compat/poll.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/utils.h>
+
+#include "lttng-relayd.h"
+#include "health-relayd.h"
+
+/* Global health check unix path */
+static char health_unix_sock_path[PATH_MAX];
+
+int health_quit_pipe[2];
+
+/*
+ * Check if the thread quit pipe was triggered.
+ *
+ * Return 1 if it was triggered else 0;
+ */
+static
+int check_health_quit_pipe(int fd, uint32_t events)
+{
+ if (fd == health_quit_pipe[0] && (events & LPOLLIN)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Send data on a unix socket using the liblttsessiondcomm API.
+ *
+ * Return lttcomm error code.
+ */
+static int send_unix_sock(int sock, void *buf, size_t len)
+{
+ /* Check valid length */
+ if (len == 0) {
+ return -1;
+ }
+
+ return lttcomm_send_unix_sock(sock, buf, len);
+}
+
+static int create_lttng_rundir_with_perm(const char *rundir)
+{
+ int ret;
+
+ DBG3("Creating LTTng run directory: %s", rundir);
+
+ ret = mkdir(rundir, S_IRWXU);
+ if (ret < 0) {
+ if (errno != EEXIST) {
+ ERR("Unable to create %s", rundir);
+ goto error;
+ } else {
+ ret = 0;
+ }
+ } else if (ret == 0) {
+ int is_root = !getuid();
+
+ if (is_root) {
+ ret = chown(rundir, 0,
+ utils_get_group_id(tracing_group_name));
+ if (ret < 0) {
+ ERR("Unable to set group on %s", rundir);
+ PERROR("chown");
+ ret = -1;
+ goto error;
+ }
+
+ ret = chmod(rundir,
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+ if (ret < 0) {
+ ERR("Unable to set permissions on %s", health_unix_sock_path);
+ PERROR("chmod");
+ ret = -1;
+ goto error;
+ }
+ }
+ }
+
+error:
+ return ret;
+}
+
+static
+int setup_health_path(void)
+{
+ int is_root, ret = 0;
+ char *home_path = NULL, *rundir = NULL, *relayd_path;
+
+ is_root = !getuid();
+
+ if (is_root) {
+ rundir = strdup(DEFAULT_LTTNG_RUNDIR);
+ } else {
+ /*
+ * Create rundir from home path. This will create something like
+ * $HOME/.lttng
+ */
+ home_path = utils_get_home_dir();
+
+ if (home_path == NULL) {
+ /* TODO: Add --socket PATH option */
+ ERR("Can't get HOME directory for sockets creation.");
+ ret = -EPERM;
+ goto end;
+ }
+
+ ret = asprintf(&rundir, DEFAULT_LTTNG_HOME_RUNDIR, home_path);
+ if (ret < 0) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ }
+
+ ret = asprintf(&relayd_path, DEFAULT_RELAYD_PATH, rundir);
+ if (ret < 0) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ ret = create_lttng_rundir_with_perm(rundir);
+ if (ret < 0) {
+ goto end;
+ }
+
+ ret = create_lttng_rundir_with_perm(relayd_path);
+ if (ret < 0) {
+ goto end;
+ }
+
+ if (is_root) {
+ if (strlen(health_unix_sock_path) != 0) {
+ goto end;
+ }
+ snprintf(health_unix_sock_path, sizeof(health_unix_sock_path),
+ DEFAULT_GLOBAL_RELAY_HEALTH_UNIX_SOCK,
+ getpid());
+ } else {
+ /* Set health check Unix path */
+ if (strlen(health_unix_sock_path) != 0) {
+ goto end;
+ }
+
+ snprintf(health_unix_sock_path, sizeof(health_unix_sock_path),
+ DEFAULT_HOME_RELAY_HEALTH_UNIX_SOCK,
+ home_path, getpid());
+ }
+
+end:
+ free(rundir);
+ return ret;
+}
+
+/*
+ * Thread managing health check socket.
+ */
+void *thread_manage_health(void *data)
+{
+ int sock = -1, new_sock = -1, ret, i, pollfd, err = -1;
+ uint32_t revents, nb_fd;
+ struct lttng_poll_event events;
+ struct health_comm_msg msg;
+ struct health_comm_reply reply;
+ int is_root;
+
+ DBG("[thread] Manage health check started");
+
+ setup_health_path();
+
+ rcu_register_thread();
+
+ /* We might hit an error path before this is created. */
+ lttng_poll_init(&events);
+
+ /* Create unix socket */
+ sock = lttcomm_create_unix_sock(health_unix_sock_path);
+ if (sock < 0) {
+ ERR("Unable to create health check Unix socket");
+ ret = -1;
+ goto error;
+ }
+
+ is_root = !getuid();
+ if (is_root) {
+ /* lttng health client socket path permissions */
+ ret = chown(health_unix_sock_path, 0,
+ utils_get_group_id(tracing_group_name));
+ if (ret < 0) {
+ ERR("Unable to set group on %s", health_unix_sock_path);
+ PERROR("chown");
+ ret = -1;
+ goto error;
+ }
+
+ ret = chmod(health_unix_sock_path,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ if (ret < 0) {
+ ERR("Unable to set permissions on %s", health_unix_sock_path);
+ PERROR("chmod");
+ ret = -1;
+ goto error;
+ }
+ }
+
+ /*
+ * Set the CLOEXEC flag. Return code is useless because either way, the
+ * show must go on.
+ */
+ (void) utils_set_fd_cloexec(sock);
+
+ ret = lttcomm_listen_unix_sock(sock);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* Size is set to 1 for the consumer_channel pipe */
+ ret = lttng_poll_create(&events, 2, LTTNG_CLOEXEC);
+ if (ret < 0) {
+ ERR("Poll set creation failed");
+ goto error;
+ }
+
+ ret = lttng_poll_add(&events, health_quit_pipe[0], LPOLLIN);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* Add the application registration socket */
+ ret = lttng_poll_add(&events, sock, LPOLLIN | LPOLLPRI);
+ if (ret < 0) {
+ goto error;
+ }
+
+ while (1) {
+ DBG("Health check ready");
+
+ /* Inifinite blocking call, waiting for transmission */
+restart:
+ ret = lttng_poll_wait(&events, -1);
+ if (ret < 0) {
+ /*
+ * Restart interrupted system call.
+ */
+ if (errno == EINTR) {
+ goto restart;
+ }
+ goto error;
+ }
+
+ nb_fd = ret;
+
+ for (i = 0; i < nb_fd; i++) {
+ /* Fetch once the poll data */
+ revents = LTTNG_POLL_GETEV(&events, i);
+ pollfd = LTTNG_POLL_GETFD(&events, i);
+
+ /* Thread quit pipe has been closed. Killing thread. */
+ ret = check_health_quit_pipe(pollfd, revents);
+ if (ret) {
+ err = 0;
+ goto exit;
+ }
+
+ /* Event on the registration socket */
+ if (pollfd == sock) {
+ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+ ERR("Health socket poll error");
+ goto error;
+ }
+ }
+ }
+
+ new_sock = lttcomm_accept_unix_sock(sock);
+ if (new_sock < 0) {
+ goto error;
+ }
+
+ /*
+ * Set the CLOEXEC flag. Return code is useless because either way, the
+ * show must go on.
+ */
+ (void) utils_set_fd_cloexec(new_sock);
+
+ DBG("Receiving data from client for health...");
+ ret = lttcomm_recv_unix_sock(new_sock, (void *)&msg, sizeof(msg));
+ if (ret <= 0) {
+ DBG("Nothing recv() from client... continuing");
+ ret = close(new_sock);
+ if (ret) {
+ PERROR("close");
+ }
+ new_sock = -1;
+ continue;
+ }
+
+ rcu_thread_online();
+
+ assert(msg.cmd == HEALTH_CMD_CHECK);
+
+ reply.ret_code = 0;
+ for (i = 0; i < NR_HEALTH_RELAYD_TYPES; i++) {
+ /*
+ * health_check_state return 0 if thread is in
+ * error.
+ */
+ if (!health_check_state(health_relayd, i)) {
+ reply.ret_code |= 1ULL << i;
+ }
+ }
+
+ DBG2("Health check return value %" PRIx64, reply.ret_code);
+
+ ret = send_unix_sock(new_sock, (void *) &reply, sizeof(reply));
+ if (ret < 0) {
+ ERR("Failed to send health data back to client");
+ }
+
+ /* End of transmission */
+ ret = close(new_sock);
+ if (ret) {
+ PERROR("close");
+ }
+ new_sock = -1;
+ }
+
+exit:
+error:
+ if (err) {
+ ERR("Health error occurred in %s", __func__);
+ }
+ DBG("Health check thread dying");
+ unlink(health_unix_sock_path);
+ if (sock >= 0) {
+ ret = close(sock);
+ if (ret) {
+ PERROR("close");
+ }
+ }
+
+ lttng_poll_clean(&events);
+
+ rcu_unregister_thread();
+ return NULL;
+}
--- /dev/null
+#ifndef HEALTH_RELAYD_H
+#define HEALTH_RELAYD_H
+
+/*
+ * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
+ * Copyright (C) 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <lttng/health-internal.h>
+
+enum health_type_relayd {
+ HEALTH_RELAYD_TYPE_DISPATCHER = 0,
+ HEALTH_RELAYD_TYPE_WORKER = 1,
+ HEALTH_RELAYD_TYPE_LISTENER = 2,
+ HEALTH_RELAYD_TYPE_LIVE_DISPATCHER = 3,
+ HEALTH_RELAYD_TYPE_LIVE_WORKER = 4,
+ HEALTH_RELAYD_TYPE_LIVE_LISTENER = 5,
+
+ NR_HEALTH_RELAYD_TYPES,
+};
+
+extern struct health_app *health_relayd;
+
+extern int health_quit_pipe[2];
+
+void *thread_manage_health(void *data);
+
+#endif /* HEALTH_RELAYD_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+
+#include <common/common.h>
+#include <common/utils.h>
+
+#include "lttng-relayd.h"
+#include "index.h"
+
+/*
+ * Deferred free of a relay index object. MUST only be called by a call RCU.
+ */
+static void deferred_free_relay_index(struct rcu_head *head)
+{
+ struct relay_index *index =
+ caa_container_of(head, struct relay_index, rcu_node);
+
+ if (index->to_close_fd >= 0) {
+ int ret;
+
+ ret = close(index->to_close_fd);
+ if (ret < 0) {
+ PERROR("Relay index to close fd %d", index->to_close_fd);
+ }
+ }
+
+ relay_index_free(index);
+}
+
+/*
+ * Allocate a new relay index object using the given stream ID and sequence
+ * number as the hash table key.
+ *
+ * Return allocated object or else NULL on error.
+ */
+struct relay_index *relay_index_create(uint64_t stream_id,
+ uint64_t net_seq_num)
+{
+ struct relay_index *index;
+
+ DBG2("Creating relay index with stream id %" PRIu64 " and seqnum %" PRIu64,
+ stream_id, net_seq_num);
+
+ index = zmalloc(sizeof(*index));
+ if (index == NULL) {
+ PERROR("Relay index zmalloc");
+ goto error;
+ }
+
+ index->to_close_fd = -1;
+ lttng_ht_node_init_two_u64(&index->index_n, stream_id, net_seq_num);
+
+error:
+ return index;
+}
+
+/*
+ * Find a relayd index in the given hash table.
+ *
+ * Return index object or else NULL on error.
+ */
+struct relay_index *relay_index_find(uint64_t stream_id, uint64_t net_seq_num)
+{
+ struct lttng_ht_node_two_u64 *node;
+ struct lttng_ht_iter iter;
+ struct lttng_ht_two_u64 key;
+ struct relay_index *index = NULL;
+
+ DBG3("Finding index for stream id %" PRIu64 " and seq_num %" PRIu64,
+ stream_id, net_seq_num);
+
+ key.key1 = stream_id;
+ key.key2 = net_seq_num;
+
+ lttng_ht_lookup(indexes_ht, (void *)(&key), &iter);
+ node = lttng_ht_iter_get_node_two_u64(&iter);
+ if (node == NULL) {
+ goto end;
+ }
+ index = caa_container_of(node, struct relay_index, index_n);
+
+end:
+ DBG2("Index %sfound in HT for stream ID %" PRIu64 " and seqnum %" PRIu64,
+ (index == NULL) ? "NOT " : "", stream_id, net_seq_num);
+ return index;
+}
+
+/*
+ * Add unique relay index to the given hash table. In case of a collision, the
+ * already existing object is put in the given _index variable.
+ *
+ * RCU read side lock MUST be acquired.
+ */
+void relay_index_add(struct relay_index *index, struct relay_index **_index)
+{
+ struct cds_lfht_node *node_ptr;
+
+ assert(index);
+
+ DBG2("Adding relay index with stream id %" PRIu64 " and seqnum %" PRIu64,
+ index->key.key1, index->key.key2);
+
+ node_ptr = cds_lfht_add_unique(indexes_ht->ht,
+ indexes_ht->hash_fct((void *) &index->index_n.key, lttng_ht_seed),
+ indexes_ht->match_fct, (void *) &index->index_n.key,
+ &index->index_n.node);
+ if (node_ptr != &index->index_n.node) {
+ *_index = caa_container_of(node_ptr, struct relay_index, index_n.node);
+ }
+}
+
+/*
+ * Write index on disk to the given fd. Once done error or not, it is removed
+ * from the hash table and destroy the object.
+ *
+ * MUST be called with a RCU read side lock held.
+ *
+ * Return 0 on success else a negative value.
+ */
+int relay_index_write(int fd, struct relay_index *index)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ DBG2("Writing index for stream ID %" PRIu64 " and seq num %" PRIu64
+ " on fd %d", index->key.key1, index->key.key2, fd);
+
+ /* Delete index from hash table. */
+ iter.iter.node = &index->index_n.node;
+ ret = lttng_ht_del(indexes_ht, &iter);
+ assert(!ret);
+ call_rcu(&index->rcu_node, deferred_free_relay_index);
+
+ return index_write(fd, &index->index_data, sizeof(index->index_data));
+}
+
+/*
+ * Free the given index.
+ */
+void relay_index_free(struct relay_index *index)
+{
+ free(index);
+}
+
+/*
+ * Safely free the given index using a call RCU.
+ */
+void relay_index_free_safe(struct relay_index *index)
+{
+ if (!index) {
+ return;
+ }
+
+ call_rcu(&index->rcu_node, deferred_free_relay_index);
+}
+
+/*
+ * Delete index from the given hash table.
+ *
+ * RCU read side lock MUST be acquired.
+ */
+void relay_index_delete(struct relay_index *index)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ DBG3("Relay index with stream ID %" PRIu64 " and seq num %" PRIu64
+ "deleted.", index->key.key1, index->key.key2);
+
+ /* Delete index from hash table. */
+ iter.iter.node = &index->index_n.node;
+ ret = lttng_ht_del(indexes_ht, &iter);
+ assert(!ret);
+}
+
+/*
+ * Destroy every relay index with the given stream id as part of the key.
+ */
+void relay_index_destroy_by_stream_id(uint64_t stream_id)
+{
+ struct lttng_ht_iter iter;
+ struct relay_index *index;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(indexes_ht->ht, &iter.iter, index, index_n.node) {
+ if (index->key.key1 == stream_id) {
+ relay_index_delete(index);
+ relay_index_free_safe(index);
+ }
+ }
+ rcu_read_unlock();
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _RELAY_INDEX_H
+#define _RELAY_INDEX_H
+
+#include <inttypes.h>
+#include <pthread.h>
+
+#include <common/hashtable/hashtable.h>
+#include <common/index/index.h>
+
+struct relay_index {
+ /* FD on which to write the index data. */
+ int fd;
+ /*
+ * When destroying this object, this fd is checked and if valid, close it
+ * so this is basically a lazy close of the previous fd corresponding to
+ * the same stream id. This is used for the rotate file feature.
+ */
+ int to_close_fd;
+
+ /* Index packet data. This is the data that is written on disk. */
+ struct lttng_packet_index index_data;
+
+ /* key1 = stream_id, key2 = net_seq_num */
+ struct lttng_ht_two_u64 key;
+ struct lttng_ht_node_two_u64 index_n;
+ struct rcu_head rcu_node;
+ pthread_mutex_t mutex;
+};
+
+struct relay_index *relay_index_create(uint64_t stream_id,
+ uint64_t net_seq_num);
+struct relay_index *relay_index_find(uint64_t stream_id, uint64_t net_seq_num);
+void relay_index_add(struct relay_index *index, struct relay_index **_index);
+int relay_index_write(int fd, struct relay_index *index);
+void relay_index_free(struct relay_index *index);
+void relay_index_free_safe(struct relay_index *index);
+void relay_index_delete(struct relay_index *index);
+void relay_index_destroy_by_stream_id(uint64_t stream_id);
+
+#endif /* _RELAY_INDEX_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <grp.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <inttypes.h>
+#include <urcu/futex.h>
+#include <urcu/uatomic.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <config.h>
+
+#include <lttng/lttng.h>
+#include <common/common.h>
+#include <common/compat/poll.h>
+#include <common/compat/socket.h>
+#include <common/defaults.h>
+#include <common/futex.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/sessiond-comm/inet.h>
+#include <common/sessiond-comm/relayd.h>
+#include <common/uri.h>
+#include <common/utils.h>
+
+#include "cmd.h"
+#include "live.h"
+#include "lttng-relayd.h"
+#include "lttng-viewer.h"
+#include "utils.h"
+#include "health-relayd.h"
+
+static struct lttng_uri *live_uri;
+
+/*
+ * Quit pipe for all threads. This permits a single cancellation point
+ * for all threads when receiving an event on the pipe.
+ */
+static int live_thread_quit_pipe[2] = { -1, -1 };
+
+/*
+ * This pipe is used to inform the worker thread that a command is queued and
+ * ready to be processed.
+ */
+static int live_relay_cmd_pipe[2] = { -1, -1 };
+
+/* Shared between threads */
+static int live_dispatch_thread_exit;
+
+static pthread_t live_listener_thread;
+static pthread_t live_dispatcher_thread;
+static pthread_t live_worker_thread;
+
+/*
+ * Relay command queue.
+ *
+ * The live_thread_listener and live_thread_dispatcher communicate with this
+ * queue.
+ */
+static struct relay_cmd_queue viewer_cmd_queue;
+
+static uint64_t last_relay_viewer_session_id;
+
+/*
+ * Cleanup the daemon
+ */
+static
+void cleanup(void)
+{
+ DBG("Cleaning up");
+
+ free(live_uri);
+}
+
+/*
+ * Write to writable pipe used to notify a thread.
+ */
+static
+int notify_thread_pipe(int wpipe)
+{
+ int ret;
+
+ do {
+ ret = write(wpipe, "!", 1);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0 || ret != 1) {
+ PERROR("write poll pipe");
+ }
+
+ return ret;
+}
+
+/*
+ * Stop all threads by closing the thread quit pipe.
+ */
+static
+void stop_threads(void)
+{
+ int ret;
+
+ /* Stopping all threads */
+ DBG("Terminating all live threads");
+ ret = notify_thread_pipe(live_thread_quit_pipe[1]);
+ if (ret < 0) {
+ ERR("write error on thread quit pipe");
+ }
+
+ /* Dispatch thread */
+ CMM_STORE_SHARED(live_dispatch_thread_exit, 1);
+ futex_nto1_wake(&viewer_cmd_queue.futex);
+}
+
+/*
+ * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set.
+ */
+static
+int create_thread_poll_set(struct lttng_poll_event *events, int size)
+{
+ int ret;
+
+ if (events == NULL || size == 0) {
+ ret = -1;
+ goto error;
+ }
+
+ ret = lttng_poll_create(events, size, LTTNG_CLOEXEC);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* Add quit pipe */
+ ret = lttng_poll_add(events, live_thread_quit_pipe[0], LPOLLIN);
+ if (ret < 0) {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Check if the thread quit pipe was triggered.
+ *
+ * Return 1 if it was triggered else 0;
+ */
+static
+int check_thread_quit_pipe(int fd, uint32_t events)
+{
+ if (fd == live_thread_quit_pipe[0] && (events & LPOLLIN)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Create and init socket from uri.
+ */
+static
+struct lttcomm_sock *init_socket(struct lttng_uri *uri)
+{
+ int ret;
+ struct lttcomm_sock *sock = NULL;
+
+ sock = lttcomm_alloc_sock_from_uri(uri);
+ if (sock == NULL) {
+ ERR("Allocating socket");
+ goto error;
+ }
+
+ ret = lttcomm_create_sock(sock);
+ if (ret < 0) {
+ goto error;
+ }
+ DBG("Listening on sock %d for live", sock->fd);
+
+ ret = sock->ops->bind(sock);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = sock->ops->listen(sock, -1);
+ if (ret < 0) {
+ goto error;
+
+ }
+
+ return sock;
+
+error:
+ if (sock) {
+ lttcomm_destroy_sock(sock);
+ }
+ return NULL;
+}
+
+/*
+ * This thread manages the listening for new connections on the network
+ */
+static
+void *thread_listener(void *data)
+{
+ int i, ret, pollfd, err = -1;
+ int val = 1;
+ uint32_t revents, nb_fd;
+ struct lttng_poll_event events;
+ struct lttcomm_sock *live_control_sock;
+
+ DBG("[thread] Relay live listener started");
+
+ health_register(health_relayd, HEALTH_RELAYD_TYPE_LIVE_LISTENER);
+
+ health_code_update();
+
+ live_control_sock = init_socket(live_uri);
+ if (!live_control_sock) {
+ goto error_sock_control;
+ }
+
+ /*
+ * Pass 3 as size here for the thread quit pipe, control and data socket.
+ */
+ ret = create_thread_poll_set(&events, 2);
+ if (ret < 0) {
+ goto error_create_poll;
+ }
+
+ /* Add the control socket */
+ ret = lttng_poll_add(&events, live_control_sock->fd, LPOLLIN | LPOLLRDHUP);
+ if (ret < 0) {
+ goto error_poll_add;
+ }
+
+ while (1) {
+ health_code_update();
+
+ DBG("Listener accepting live viewers connections");
+
+restart:
+ health_poll_entry();
+ ret = lttng_poll_wait(&events, -1);
+ health_poll_exit();
+ if (ret < 0) {
+ /*
+ * Restart interrupted system call.
+ */
+ if (errno == EINTR) {
+ goto restart;
+ }
+ goto error;
+ }
+ nb_fd = ret;
+
+ DBG("Relay new viewer connection received");
+ for (i = 0; i < nb_fd; i++) {
+ health_code_update();
+
+ /* Fetch once the poll data */
+ revents = LTTNG_POLL_GETEV(&events, i);
+ pollfd = LTTNG_POLL_GETFD(&events, i);
+
+ /* Thread quit pipe has been closed. Killing thread. */
+ ret = check_thread_quit_pipe(pollfd, revents);
+ if (ret) {
+ err = 0;
+ goto exit;
+ }
+
+ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+ ERR("socket poll error");
+ goto error;
+ } else if (revents & LPOLLIN) {
+ /*
+ * Get allocated in this thread, enqueued to a global queue,
+ * dequeued and freed in the worker thread.
+ */
+ struct relay_command *relay_cmd;
+ struct lttcomm_sock *newsock;
+
+ relay_cmd = zmalloc(sizeof(*relay_cmd));
+ if (!relay_cmd) {
+ PERROR("relay command zmalloc");
+ goto error;
+ }
+
+ assert(pollfd == live_control_sock->fd);
+ newsock = live_control_sock->ops->accept(live_control_sock);
+ if (!newsock) {
+ PERROR("accepting control sock");
+ free(relay_cmd);
+ goto error;
+ }
+ DBG("Relay viewer connection accepted socket %d", newsock->fd);
+ ret = setsockopt(newsock->fd, SOL_SOCKET, SO_REUSEADDR, &val,
+ sizeof(int));
+ if (ret < 0) {
+ PERROR("setsockopt inet");
+ lttcomm_destroy_sock(newsock);
+ free(relay_cmd);
+ goto error;
+ }
+ relay_cmd->sock = newsock;
+
+ /*
+ * Lock free enqueue the request.
+ */
+ cds_wfq_enqueue(&viewer_cmd_queue.queue, &relay_cmd->node);
+
+ /*
+ * Wake the dispatch queue futex. Implicit memory
+ * barrier with the exchange in cds_wfq_enqueue.
+ */
+ futex_nto1_wake(&viewer_cmd_queue.futex);
+ }
+ }
+ }
+
+exit:
+error:
+error_poll_add:
+ lttng_poll_clean(&events);
+error_create_poll:
+ if (live_control_sock->fd >= 0) {
+ ret = live_control_sock->ops->close(live_control_sock);
+ if (ret) {
+ PERROR("close");
+ }
+ }
+ lttcomm_destroy_sock(live_control_sock);
+error_sock_control:
+ if (err) {
+ health_error();
+ DBG("Live viewer listener thread exited with error");
+ }
+ health_unregister(health_relayd);
+ DBG("Live viewer listener thread cleanup complete");
+ stop_threads();
+ return NULL;
+}
+
+/*
+ * This thread manages the dispatching of the requests to worker threads
+ */
+static
+void *thread_dispatcher(void *data)
+{
+ int ret, err = -1;
+ struct cds_wfq_node *node;
+ struct relay_command *relay_cmd = NULL;
+
+ DBG("[thread] Live viewer relay dispatcher started");
+
+ health_register(health_relayd, HEALTH_RELAYD_TYPE_LIVE_DISPATCHER);
+
+ health_code_update();
+
+ while (!CMM_LOAD_SHARED(live_dispatch_thread_exit)) {
+ health_code_update();
+
+ /* Atomically prepare the queue futex */
+ futex_nto1_prepare(&viewer_cmd_queue.futex);
+
+ do {
+ health_code_update();
+
+ /* Dequeue commands */
+ node = cds_wfq_dequeue_blocking(&viewer_cmd_queue.queue);
+ if (node == NULL) {
+ DBG("Woken up but nothing in the live-viewer "
+ "relay command queue");
+ /* Continue thread execution */
+ break;
+ }
+
+ relay_cmd = caa_container_of(node, struct relay_command, node);
+ DBG("Dispatching viewer request waiting on sock %d",
+ relay_cmd->sock->fd);
+
+ /*
+ * Inform worker thread of the new request. This call is blocking
+ * so we can be assured that the data will be read at some point in
+ * time or wait to the end of the world :)
+ */
+ do {
+ ret = write(live_relay_cmd_pipe[1], relay_cmd,
+ sizeof(*relay_cmd));
+ } while (ret < 0 && errno == EINTR);
+ free(relay_cmd);
+ if (ret < 0 || ret != sizeof(struct relay_command)) {
+ PERROR("write cmd pipe");
+ goto error;
+ }
+ } while (node != NULL);
+
+ /* Futex wait on queue. Blocking call on futex() */
+ health_poll_entry();
+ futex_nto1_wait(&viewer_cmd_queue.futex);
+ health_poll_exit();
+ }
+
+ /* Normal exit, no error */
+ err = 0;
+
+error:
+ if (err) {
+ health_error();
+ ERR("Health error occurred in %s", __func__);
+ }
+ health_unregister(health_relayd);
+ DBG("Live viewer dispatch thread dying");
+ stop_threads();
+ return NULL;
+}
+
+/*
+ * Establish connection with the viewer and check the versions.
+ *
+ * Return 0 on success or else negative value.
+ */
+static
+int viewer_connect(struct relay_command *cmd)
+{
+ int ret;
+ struct lttng_viewer_connect reply, msg;
+
+ assert(cmd);
+
+ cmd->version_check_done = 1;
+
+ health_code_update();
+
+ /* Get version from the other side. */
+ ret = cmd->sock->ops->recvmsg(cmd->sock, &msg, sizeof(msg), 0);
+ if (ret < 0 || ret != sizeof(msg)) {
+ if (ret == 0) {
+ /* Orderly shutdown. Not necessary to print an error. */
+ DBG("Socket %d did an orderly shutdown", cmd->sock->fd);
+ } else {
+ ERR("Relay failed to receive the version values.");
+ }
+ ret = -1;
+ goto end;
+ }
+
+ health_code_update();
+
+ reply.major = RELAYD_VERSION_COMM_MAJOR;
+ reply.minor = RELAYD_VERSION_COMM_MINOR;
+
+ /* Major versions must be the same */
+ if (reply.major != be32toh(msg.major)) {
+ DBG("Incompatible major versions (%u vs %u)", reply.major,
+ be32toh(msg.major));
+ ret = 0;
+ goto end;
+ }
+
+ cmd->major = reply.major;
+ /* We adapt to the lowest compatible version */
+ if (reply.minor <= be32toh(msg.minor)) {
+ cmd->minor = reply.minor;
+ } else {
+ cmd->minor = be32toh(msg.minor);
+ }
+
+ if (be32toh(msg.type) == VIEWER_CLIENT_COMMAND) {
+ cmd->type = RELAY_VIEWER_COMMAND;
+ } else if (be32toh(msg.type) == VIEWER_CLIENT_NOTIFICATION) {
+ cmd->type = RELAY_VIEWER_NOTIFICATION;
+ } else {
+ ERR("Unknown connection type : %u", be32toh(msg.type));
+ ret = -1;
+ goto end;
+ }
+
+ reply.major = htobe32(reply.major);
+ reply.minor = htobe32(reply.minor);
+ if (cmd->type == RELAY_VIEWER_COMMAND) {
+ reply.viewer_session_id = htobe64(++last_relay_viewer_session_id);
+ }
+
+ health_code_update();
+
+ ret = cmd->sock->ops->sendmsg(cmd->sock, &reply,
+ sizeof(struct lttng_viewer_connect), 0);
+ if (ret < 0) {
+ ERR("Relay sending version");
+ }
+
+ health_code_update();
+
+ DBG("Version check done using protocol %u.%u", cmd->major, cmd->minor);
+ ret = 0;
+
+end:
+ return ret;
+}
+
+/*
+ * Send the viewer the list of current sessions.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static
+int viewer_list_sessions(struct relay_command *cmd,
+ struct lttng_ht *sessions_ht)
+{
+ int ret;
+ struct lttng_viewer_list_sessions session_list;
+ unsigned long count;
+ long approx_before, approx_after;
+ struct lttng_ht_node_ulong *node;
+ struct lttng_ht_iter iter;
+ struct lttng_viewer_session send_session;
+ struct relay_session *session;
+
+ DBG("List sessions received");
+
+ if (cmd->version_check_done == 0) {
+ ERR("Trying to list sessions before version check");
+ ret = -1;
+ goto end_no_session;
+ }
+
+ rcu_read_lock();
+ cds_lfht_count_nodes(sessions_ht->ht, &approx_before, &count, &approx_after);
+ session_list.sessions_count = htobe32(count);
+
+ health_code_update();
+
+ ret = cmd->sock->ops->sendmsg(cmd->sock, &session_list,
+ sizeof(session_list), 0);
+ if (ret < 0) {
+ ERR("Relay sending sessions list");
+ goto end_unlock;
+ }
+
+ health_code_update();
+
+ cds_lfht_for_each_entry(sessions_ht->ht, &iter.iter, node, node) {
+ health_code_update();
+
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (!node) {
+ goto end_unlock;
+ }
+ session = caa_container_of(node, struct relay_session, session_n);
+
+ strncpy(send_session.session_name, session->session_name,
+ sizeof(send_session.session_name));
+ strncpy(send_session.hostname, session->hostname,
+ sizeof(send_session.hostname));
+ send_session.id = htobe64(session->id);
+ send_session.live_timer = htobe32(session->live_timer);
+ send_session.clients = htobe32(session->viewer_attached);
+
+ health_code_update();
+
+ ret = cmd->sock->ops->sendmsg(cmd->sock, &send_session,
+ sizeof(send_session), 0);
+ if (ret < 0) {
+ ERR("Relay sending session info");
+ goto end_unlock;
+ }
+ }
+ health_code_update();
+
+ rcu_read_unlock();
+ ret = 0;
+ goto end;
+
+end_unlock:
+ rcu_read_unlock();
+
+end:
+end_no_session:
+ return ret;
+}
+
+/*
+ * Allocate and init a new viewer_stream.
+ *
+ * Copies the values from the stream passed in parameter and insert the new
+ * stream in the viewer_streams_ht.
+ *
+ * MUST be called with rcu_read_lock held.
+ *
+ * Returns 0 on success or a negative value on error.
+ */
+static
+int init_viewer_stream(struct relay_stream *stream)
+{
+ int ret;
+ struct relay_viewer_stream *viewer_stream;
+
+ assert(stream);
+
+ viewer_stream = zmalloc(sizeof(*viewer_stream));
+ if (!viewer_stream) {
+ PERROR("relay viewer stream zmalloc");
+ ret = -1;
+ goto error;
+ }
+
+ viewer_stream->read_fd = -1;
+ viewer_stream->index_read_fd = -1;
+ viewer_stream->session_id = stream->session->id;
+ viewer_stream->stream_handle = stream->stream_handle;
+ viewer_stream->path_name = strndup(stream->path_name,
+ LTTNG_VIEWER_PATH_MAX);
+ viewer_stream->channel_name = strndup(stream->channel_name,
+ LTTNG_VIEWER_NAME_MAX);
+ viewer_stream->total_index_received = stream->total_index_received;
+ viewer_stream->tracefile_size = stream->tracefile_size;
+ viewer_stream->tracefile_count = stream->tracefile_count;
+ viewer_stream->metadata_flag = stream->metadata_flag;
+
+ /*
+ * This is to avoid a race between the initialization of this object and
+ * the close of the given stream. If the stream is unable to find this
+ * viewer stream when closing, this copy will at least take the latest
+ * value.
+ */
+ viewer_stream->total_index_received = stream->total_index_received;
+
+ /*
+ * The deletion of this ctf_trace object is only done in a call RCU of the
+ * relay stream making it valid as long as we have the read side lock.
+ */
+ viewer_stream->ctf_trace = stream->ctf_trace;
+ uatomic_inc(&viewer_stream->ctf_trace->refcount);
+
+ lttng_ht_node_init_u64(&viewer_stream->stream_n, stream->stream_handle);
+ lttng_ht_add_unique_u64(viewer_streams_ht, &viewer_stream->stream_n);
+
+ ret = 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Send the viewer the list of current sessions.
+ */
+static
+int viewer_attach_session(struct relay_command *cmd,
+ struct lttng_ht *sessions_ht)
+{
+ int ret, send_streams = 0, nb_streams = 0;
+ struct lttng_viewer_attach_session_request request;
+ struct lttng_viewer_attach_session_response response;
+ struct lttng_viewer_stream send_stream;
+ struct relay_stream *stream;
+ struct relay_viewer_stream *viewer_stream;
+ struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node64;
+ struct lttng_ht_iter iter;
+ struct relay_session *session;
+
+ assert(cmd);
+ assert(sessions_ht);
+
+ DBG("Attach session received");
+
+ if (cmd->version_check_done == 0) {
+ ERR("Trying to attach session before version check");
+ ret = -1;
+ goto end_no_session;
+ }
+
+ health_code_update();
+
+ ret = cmd->sock->ops->recvmsg(cmd->sock, &request, sizeof(request), 0);
+ if (ret < 0 || ret != sizeof(request)) {
+ if (ret == 0) {
+ /* Orderly shutdown. Not necessary to print an error. */
+ DBG("Socket %d did an orderly shutdown", cmd->sock->fd);
+ } else {
+ ERR("Relay failed to receive the attach parameters.");
+ }
+ ret = -1;
+ goto error;
+ }
+
+ health_code_update();
+
+ rcu_read_lock();
+ lttng_ht_lookup(sessions_ht,
+ (void *)((unsigned long) be64toh(request.session_id)), &iter);
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (node == NULL) {
+ DBG("Relay session %" PRIu64 " not found",
+ be64toh(request.session_id));
+ response.status = htobe32(VIEWER_ATTACH_UNK);
+ goto send_reply;
+ }
+
+ session = caa_container_of(node, struct relay_session, session_n);
+ if (cmd->session_id == session->id) {
+ /* Same viewer already attached, just send the stream list. */
+ send_streams = 1;
+ response.status = htobe32(VIEWER_ATTACH_OK);
+ } else if (session->viewer_attached != 0) {
+ DBG("Already a viewer attached");
+ response.status = htobe32(VIEWER_ATTACH_ALREADY);
+ goto send_reply;
+ } else if (session->live_timer == 0) {
+ DBG("Not live session");
+ response.status = htobe32(VIEWER_ATTACH_NOT_LIVE);
+ goto send_reply;
+ } else {
+ session->viewer_attached++;
+ send_streams = 1;
+ response.status = htobe32(VIEWER_ATTACH_OK);
+ cmd->session_id = session->id;
+ cmd->session = session;
+ }
+
+ switch (be32toh(request.seek)) {
+ case VIEWER_SEEK_BEGINNING:
+ /* Default behaviour. */
+ break;
+ case VIEWER_SEEK_LAST:
+ /* TODO */
+ break;
+ default:
+ ERR("Wrong seek parameter");
+ response.status = htobe32(VIEWER_ATTACH_SEEK_ERR);
+ send_streams = 0;
+ goto send_reply;
+ }
+
+ if (send_streams) {
+ /* We should only be there if we have a session to attach to. */
+ assert(session);
+
+ /*
+ * Fill the viewer_streams_ht to count the number of streams
+ * ready to be sent and avoid concurrency issues on the
+ * relay_streams_ht and don't rely on a total session stream count.
+ */
+ cds_lfht_for_each_entry(relay_streams_ht->ht, &iter.iter, node, node) {
+ struct relay_viewer_stream *vstream;
+
+ health_code_update();
+
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (!node) {
+ continue;
+ }
+ stream = caa_container_of(node, struct relay_stream, stream_n);
+ if (stream->session != cmd->session) {
+ continue;
+ }
+
+ /*
+ * Don't send streams with no ctf_trace, they are not ready to be
+ * read.
+ */
+ if (!stream->ctf_trace) {
+ continue;
+ }
+
+ vstream = live_find_viewer_stream_by_id(stream->stream_handle);
+ if (!vstream) {
+ ret = init_viewer_stream(stream);
+ if (ret < 0) {
+ goto end_unlock;
+ }
+ }
+ nb_streams++;
+ }
+ response.streams_count = htobe32(nb_streams);
+ }
+
+send_reply:
+ health_code_update();
+ ret = cmd->sock->ops->sendmsg(cmd->sock, &response, sizeof(response), 0);
+ if (ret < 0) {
+ ERR("Relay sending viewer attach response");
+ goto end_unlock;
+ }
+ health_code_update();
+
+ /*
+ * Unknown or busy session, just return gracefully, the viewer knows what
+ * is happening.
+ */
+ if (!send_streams) {
+ ret = 0;
+ goto end_unlock;
+ }
+
+ /* We should only be there if we have a session to attach to. */
+ assert(session);
+ cds_lfht_for_each_entry(viewer_streams_ht->ht, &iter.iter, node, node) {
+ health_code_update();
+
+ node64 = lttng_ht_iter_get_node_u64(&iter);
+ if (!node64) {
+ continue;
+ }
+ viewer_stream = caa_container_of(node64, struct relay_viewer_stream,
+ stream_n);
+ if (viewer_stream->session_id != cmd->session->id) {
+ continue;
+ }
+
+ send_stream.id = htobe64(viewer_stream->stream_handle);
+ send_stream.ctf_trace_id = htobe64(viewer_stream->ctf_trace->id);
+ send_stream.metadata_flag = htobe32(viewer_stream->metadata_flag);
+ strncpy(send_stream.path_name, viewer_stream->path_name,
+ sizeof(send_stream.path_name));
+ strncpy(send_stream.channel_name, viewer_stream->channel_name,
+ sizeof(send_stream.channel_name));
+
+ ret = cmd->sock->ops->sendmsg(cmd->sock, &send_stream,
+ sizeof(send_stream), 0);
+ if (ret < 0) {
+ ERR("Relay sending stream %" PRIu64, viewer_stream->stream_handle);
+ goto end_unlock;
+ }
+ DBG("Sent stream %" PRIu64 " to viewer", viewer_stream->stream_handle);
+ }
+ ret = 0;
+
+end_unlock:
+ rcu_read_unlock();
+end_no_session:
+error:
+ return ret;
+}
+
+/*
+ * Open index file using a given viewer stream.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int open_index(struct relay_viewer_stream *stream)
+{
+ int ret;
+ char fullpath[PATH_MAX];
+ struct lttng_packet_index_file_hdr hdr;
+
+ if (stream->tracefile_size > 0) {
+ /* For now we don't support on-disk ring buffer. */
+ ret = -1;
+ goto end;
+ } else {
+ ret = snprintf(fullpath, sizeof(fullpath), "%s/" DEFAULT_INDEX_DIR
+ "/%s" DEFAULT_INDEX_FILE_SUFFIX,
+ stream->path_name, stream->channel_name);
+ if (ret < 0) {
+ PERROR("snprintf index path");
+ goto error;
+ }
+ }
+
+ DBG("Opening index file %s in read only", fullpath);
+ ret = open(fullpath, O_RDONLY);
+ if (ret < 0) {
+ if (errno == ENOENT) {
+ ret = ENOENT;
+ goto error;
+ } else {
+ PERROR("opening index in read-only");
+ }
+ goto error;
+ }
+ stream->index_read_fd = ret;
+ DBG("Opening index file %s in read only, (fd: %d)", fullpath, ret);
+
+ do {
+ health_code_update();
+ ret = read(stream->index_read_fd, &hdr, sizeof(hdr));
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ PERROR("Reading index header");
+ goto error;
+ }
+ if (strncmp(hdr.magic, INDEX_MAGIC, sizeof(hdr.magic)) != 0) {
+ ERR("Invalid header magic");
+ ret = -1;
+ goto error;
+ }
+ if (be32toh(hdr.index_major) != INDEX_MAJOR ||
+ be32toh(hdr.index_minor) != INDEX_MINOR) {
+ ERR("Invalid header version");
+ ret = -1;
+ goto error;
+ }
+ ret = 0;
+
+error:
+end:
+ return ret;
+}
+
+/*
+ * Get viewer stream from stream id.
+ *
+ * RCU read side lock MUST be acquired.
+ */
+struct relay_viewer_stream *live_find_viewer_stream_by_id(uint64_t stream_id)
+{
+ struct lttng_ht_node_u64 *node;
+ struct lttng_ht_iter iter;
+ struct relay_viewer_stream *stream = NULL;
+
+ lttng_ht_lookup(viewer_streams_ht, &stream_id, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
+ if (node == NULL) {
+ DBG("Relay viewer stream %" PRIu64 " not found", stream_id);
+ goto end;
+ }
+ stream = caa_container_of(node, struct relay_viewer_stream, stream_n);
+
+end:
+ return stream;
+}
+
+/*
+ * Send the next index for a stream.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static
+int viewer_get_next_index(struct relay_command *cmd,
+ struct lttng_ht *sessions_ht)
+{
+ int ret;
+ struct lttng_viewer_get_next_index request_index;
+ struct lttng_viewer_index viewer_index;
+ struct lttng_packet_index packet_index;
+ struct relay_viewer_stream *vstream;
+ struct relay_stream *rstream;
+
+ assert(cmd);
+ assert(sessions_ht);
+
+ DBG("Viewer get next index");
+
+ if (cmd->version_check_done == 0) {
+ ERR("Trying to request index before version check");
+ ret = -1;
+ goto end_no_session;
+ }
+
+ health_code_update();
+ ret = cmd->sock->ops->recvmsg(cmd->sock, &request_index,
+ sizeof(request_index), 0);
+ if (ret < 0 || ret != sizeof(request_index)) {
+ ret = -1;
+ ERR("Relay didn't receive the whole packet");
+ goto end;
+ }
+ health_code_update();
+
+ rcu_read_lock();
+ vstream = live_find_viewer_stream_by_id(be64toh(request_index.stream_id));
+ if (!vstream) {
+ ret = -1;
+ goto end_unlock;
+ }
+
+ memset(&viewer_index, 0, sizeof(viewer_index));
+
+ /*
+ * The viewer should not ask for index on metadata stream.
+ */
+ if (vstream->metadata_flag) {
+ viewer_index.status = htobe32(VIEWER_INDEX_HUP);
+ goto send_reply;
+ }
+
+ /* First time, we open the index file */
+ if (vstream->index_read_fd < 0) {
+ ret = open_index(vstream);
+ if (ret == ENOENT) {
+ /*
+ * The index is created only when the first data packet arrives, it
+ * might not be ready at the beginning of the session
+ */
+ viewer_index.status = htobe32(VIEWER_INDEX_RETRY);
+ goto send_reply;
+ } else if (ret < 0) {
+ viewer_index.status = htobe32(VIEWER_INDEX_ERR);
+ goto send_reply;
+ }
+ }
+
+ rstream = relay_stream_find_by_id(vstream->stream_handle);
+ if (rstream) {
+ if (rstream->beacon_ts_end != -1ULL &&
+ vstream->last_sent_index == rstream->total_index_received) {
+ viewer_index.status = htobe32(VIEWER_INDEX_INACTIVE);
+ viewer_index.timestamp_end = htobe64(rstream->beacon_ts_end);
+ goto send_reply;
+ }
+
+ if (rstream->total_index_received <= vstream->last_sent_index) {
+ /* No new index to send, retry later. */
+ viewer_index.status = htobe32(VIEWER_INDEX_RETRY);
+ goto send_reply;
+ }
+ } else if (!rstream &&
+ vstream->total_index_received == vstream->last_sent_index) {
+ /* Last index sent and stream closed */
+ viewer_index.status = htobe32(VIEWER_INDEX_HUP);
+ goto send_reply;
+ }
+
+ if (!vstream->ctf_trace->metadata_received ||
+ vstream->ctf_trace->metadata_received >
+ vstream->ctf_trace->metadata_sent) {
+ viewer_index.flags |= LTTNG_VIEWER_FLAG_NEW_METADATA;
+ }
+
+ do {
+ health_code_update();
+ ret = read(vstream->index_read_fd, &packet_index,
+ sizeof(packet_index));
+ } while (ret < 0 && errno == EINTR);
+ if (ret < sizeof(packet_index)) {
+ PERROR("Relay reading index file");
+ viewer_index.status = htobe32(VIEWER_INDEX_ERR);
+ } else {
+ viewer_index.status = htobe32(VIEWER_INDEX_OK);
+ vstream->last_sent_index++;
+ }
+
+ /*
+ * Indexes are stored in big endian, no need to switch before sending.
+ */
+ viewer_index.offset = packet_index.offset;
+ viewer_index.packet_size = packet_index.packet_size;
+ viewer_index.content_size = packet_index.content_size;
+ viewer_index.timestamp_begin = packet_index.timestamp_begin;
+ viewer_index.timestamp_end = packet_index.timestamp_end;
+ viewer_index.events_discarded = packet_index.events_discarded;
+ viewer_index.stream_id = packet_index.stream_id;
+
+send_reply:
+ viewer_index.flags = htobe32(viewer_index.flags);
+ health_code_update();
+ ret = cmd->sock->ops->sendmsg(cmd->sock, &viewer_index,
+ sizeof(viewer_index), 0);
+ if (ret < 0) {
+ ERR("Relay index to viewer");
+ goto end_unlock;
+ }
+ health_code_update();
+
+ DBG("Index %" PRIu64 "for stream %" PRIu64 "sent",
+ vstream->last_sent_index, vstream->stream_handle);
+
+end_unlock:
+ rcu_read_unlock();
+
+end_no_session:
+end:
+ return ret;
+}
+
+/*
+ * Send the next index for a stream
+ *
+ * Return 0 on success or else a negative value.
+ */
+static
+int viewer_get_packet(struct relay_command *cmd)
+{
+ int ret, send_data = 0;
+ char *data = NULL;
+ uint32_t len = 0;
+ ssize_t read_len;
+ struct lttng_viewer_get_packet get_packet_info;
+ struct lttng_viewer_trace_packet reply;
+ struct relay_viewer_stream *stream;
+
+ assert(cmd);
+
+ DBG2("Relay get data packet");
+
+ if (cmd->version_check_done == 0) {
+ ERR("Trying to get packet before version check");
+ ret = -1;
+ goto end;
+ }
+
+ health_code_update();
+ ret = cmd->sock->ops->recvmsg(cmd->sock, &get_packet_info,
+ sizeof(get_packet_info), 0);
+ if (ret < 0 || ret != sizeof(get_packet_info)) {
+ ret = -1;
+ ERR("Relay didn't receive the whole packet");
+ goto end;
+ }
+ health_code_update();
+
+ rcu_read_lock();
+ stream = live_find_viewer_stream_by_id(be64toh(get_packet_info.stream_id));
+ if (!stream) {
+ goto error;
+ }
+ assert(stream->ctf_trace);
+
+ /*
+ * First time we read this stream, we need open the tracefile, we should
+ * only arrive here if an index has already been sent to the viewer, so the
+ * tracefile must exist, if it does not it is a fatal error.
+ */
+ if (stream->read_fd < 0) {
+ char fullpath[PATH_MAX];
+
+ ret = snprintf(fullpath, PATH_MAX, "%s/%s", stream->path_name,
+ stream->channel_name);
+ if (ret < 0) {
+ goto error;
+ }
+ ret = open(fullpath, O_RDONLY);
+ if (ret < 0) {
+ PERROR("Relay opening trace file");
+ goto error;
+ }
+ stream->read_fd = ret;
+ }
+
+ memset(&reply, 0, sizeof(reply));
+
+ if (!stream->ctf_trace->metadata_received ||
+ stream->ctf_trace->metadata_received >
+ stream->ctf_trace->metadata_sent) {
+ reply.status = htobe32(VIEWER_GET_PACKET_ERR);
+ reply.flags |= LTTNG_VIEWER_FLAG_NEW_METADATA;
+
+ goto send_reply;
+ }
+
+ len = be32toh(get_packet_info.len);
+ data = zmalloc(len);
+ if (!data) {
+ PERROR("relay data zmalloc");
+ goto error;
+ }
+
+ ret = lseek(stream->read_fd, be64toh(get_packet_info.offset), SEEK_SET);
+ if (ret < 0) {
+ PERROR("lseek");
+ goto error;
+ }
+ read_len = read(stream->read_fd, data, len);
+ if (read_len < (ssize_t) len) {
+ PERROR("Relay reading trace file, fd: %d, offset: %" PRIu64,
+ stream->read_fd, be64toh(get_packet_info.offset));
+ goto error;
+ }
+ reply.status = htobe32(VIEWER_GET_PACKET_OK);
+ reply.len = htobe32(len);
+ send_data = 1;
+ goto send_reply;
+
+error:
+ reply.status = htobe32(VIEWER_GET_PACKET_ERR);
+
+send_reply:
+ reply.flags = htobe32(reply.flags);
+
+ health_code_update();
+ ret = cmd->sock->ops->sendmsg(cmd->sock, &reply, sizeof(reply), 0);
+ if (ret < 0) {
+ ERR("Relay data header to viewer");
+ goto end_unlock;
+ }
+ health_code_update();
+
+ if (send_data) {
+ health_code_update();
+ ret = cmd->sock->ops->sendmsg(cmd->sock, data, len, 0);
+ if (ret < 0) {
+ ERR("Relay send data to viewer");
+ goto end_unlock;
+ }
+ health_code_update();
+ }
+
+ DBG("Sent %u bytes for stream %" PRIu64, len,
+ be64toh(get_packet_info.stream_id));
+
+end_unlock:
+ free(data);
+ rcu_read_unlock();
+
+end:
+ return ret;
+}
+
+/*
+ * Send the session's metadata
+ *
+ * Return 0 on success else a negative value.
+ */
+static
+int viewer_get_metadata(struct relay_command *cmd)
+{
+ int ret = 0;
+ ssize_t read_len;
+ uint64_t len = 0;
+ char *data = NULL;
+ struct lttng_viewer_get_metadata request;
+ struct lttng_viewer_metadata_packet reply;
+ struct relay_viewer_stream *stream;
+
+ assert(cmd);
+
+ DBG("Relay get metadata");
+
+ if (cmd->version_check_done == 0) {
+ ERR("Trying to get metadata before version check");
+ ret = -1;
+ goto end;
+ }
+
+ health_code_update();
+ ret = cmd->sock->ops->recvmsg(cmd->sock, &request,
+ sizeof(request), 0);
+ if (ret < 0 || ret != sizeof(request)) {
+ ret = -1;
+ ERR("Relay didn't receive the whole packet");
+ goto end;
+ }
+ health_code_update();
+
+ rcu_read_lock();
+ stream = live_find_viewer_stream_by_id(be64toh(request.stream_id));
+ if (!stream || !stream->metadata_flag) {
+ ERR("Invalid metadata stream");
+ goto error;
+ }
+ assert(stream->ctf_trace);
+ assert(stream->ctf_trace->metadata_sent <=
+ stream->ctf_trace->metadata_received);
+
+ len = stream->ctf_trace->metadata_received -
+ stream->ctf_trace->metadata_sent;
+ if (len == 0) {
+ reply.status = htobe32(VIEWER_NO_NEW_METADATA);
+ goto send_reply;
+ }
+
+ /* first time, we open the metadata file */
+ if (stream->read_fd < 0) {
+ char fullpath[PATH_MAX];
+
+ ret = snprintf(fullpath, PATH_MAX, "%s/%s", stream->path_name,
+ stream->channel_name);
+ if (ret < 0) {
+ goto error;
+ }
+ ret = open(fullpath, O_RDONLY);
+ if (ret < 0) {
+ PERROR("Relay opening metadata file");
+ goto error;
+ }
+ stream->read_fd = ret;
+ }
+
+ reply.len = htobe64(len);
+ data = zmalloc(len);
+ if (!data) {
+ PERROR("viewer metadata zmalloc");
+ goto error;
+ }
+
+ read_len = read(stream->read_fd, data, len);
+ if (read_len < (ssize_t) len) {
+ PERROR("Relay reading metadata file");
+ goto error;
+ }
+ stream->ctf_trace->metadata_sent += read_len;
+ reply.status = htobe32(VIEWER_METADATA_OK);
+ goto send_reply;
+
+error:
+ reply.status = htobe32(VIEWER_METADATA_ERR);
+
+send_reply:
+ health_code_update();
+ ret = cmd->sock->ops->sendmsg(cmd->sock, &reply, sizeof(reply), 0);
+ if (ret < 0) {
+ ERR("Relay data header to viewer");
+ goto end_unlock;
+ }
+ health_code_update();
+
+ if (len > 0) {
+ ret = cmd->sock->ops->sendmsg(cmd->sock, data, len, 0);
+ if (ret < 0) {
+ ERR("Relay send data to viewer");
+ goto end_unlock;
+ }
+ }
+
+ DBG("Sent %" PRIu64 " bytes of metadata for stream %" PRIu64, len,
+ be64toh(request.stream_id));
+
+ DBG("Metadata sent");
+
+end_unlock:
+ free(data);
+ rcu_read_unlock();
+end:
+ return ret;
+}
+
+/*
+ * live_relay_unknown_command: send -1 if received unknown command
+ */
+static
+void live_relay_unknown_command(struct relay_command *cmd)
+{
+ struct lttcomm_relayd_generic_reply reply;
+ int ret;
+
+ reply.ret_code = htobe32(LTTNG_ERR_UNK);
+ ret = cmd->sock->ops->sendmsg(cmd->sock, &reply,
+ sizeof(struct lttcomm_relayd_generic_reply), 0);
+ if (ret < 0) {
+ ERR("Relay sending unknown command");
+ }
+}
+
+/*
+ * Process the commands received on the control socket
+ */
+static
+int process_control(struct lttng_viewer_cmd *recv_hdr,
+ struct relay_command *cmd, struct lttng_ht *sessions_ht)
+{
+ int ret = 0;
+
+ switch (be32toh(recv_hdr->cmd)) {
+ case VIEWER_CONNECT:
+ ret = viewer_connect(cmd);
+ break;
+ case VIEWER_LIST_SESSIONS:
+ ret = viewer_list_sessions(cmd, sessions_ht);
+ break;
+ case VIEWER_ATTACH_SESSION:
+ ret = viewer_attach_session(cmd, sessions_ht);
+ break;
+ case VIEWER_GET_NEXT_INDEX:
+ ret = viewer_get_next_index(cmd, sessions_ht);
+ break;
+ case VIEWER_GET_PACKET:
+ ret = viewer_get_packet(cmd);
+ break;
+ case VIEWER_GET_METADATA:
+ ret = viewer_get_metadata(cmd);
+ break;
+ default:
+ ERR("Received unknown viewer command (%u)", be32toh(recv_hdr->cmd));
+ live_relay_unknown_command(cmd);
+ ret = -1;
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+static
+void cleanup_poll_connection(struct lttng_poll_event *events, int pollfd)
+{
+ int ret;
+
+ assert(events);
+
+ lttng_poll_del(events, pollfd);
+
+ ret = close(pollfd);
+ if (ret < 0) {
+ ERR("Closing pollfd %d", pollfd);
+ }
+}
+
+/*
+ * Create and add connection to the given hash table.
+ *
+ * Return poll add value or else -1 on error.
+ */
+static
+int add_connection(int fd, struct lttng_poll_event *events,
+ struct lttng_ht *relay_connections_ht)
+{
+ int ret;
+ struct relay_command *relay_connection;
+
+ assert(events);
+ assert(relay_connections_ht);
+
+ relay_connection = zmalloc(sizeof(struct relay_command));
+ if (relay_connection == NULL) {
+ PERROR("Relay command zmalloc");
+ goto error;
+ }
+
+ do {
+ health_code_update();
+ ret = read(fd, relay_connection, sizeof(*relay_connection));
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0 || ret < sizeof(*relay_connection)) {
+ PERROR("read relay cmd pipe");
+ goto error_read;
+ }
+
+ lttng_ht_node_init_ulong(&relay_connection->sock_n,
+ (unsigned long) relay_connection->sock->fd);
+ rcu_read_lock();
+ lttng_ht_add_unique_ulong(relay_connections_ht,
+ &relay_connection->sock_n);
+ rcu_read_unlock();
+
+ return lttng_poll_add(events, relay_connection->sock->fd,
+ LPOLLIN | LPOLLRDHUP);
+
+error_read:
+ free(relay_connection);
+error:
+ return -1;
+}
+
+static
+void deferred_free_connection(struct rcu_head *head)
+{
+ struct relay_command *relay_connection =
+ caa_container_of(head, struct relay_command, rcu_node);
+
+ if (relay_connection->session &&
+ relay_connection->session->viewer_attached > 0) {
+ relay_connection->session->viewer_attached--;
+ }
+ lttcomm_destroy_sock(relay_connection->sock);
+ free(relay_connection);
+}
+
+static
+void deferred_free_viewer_stream(struct rcu_head *head)
+{
+ struct relay_viewer_stream *stream =
+ caa_container_of(head, struct relay_viewer_stream, rcu_node);
+
+ if (stream->ctf_trace) {
+ uatomic_dec(&stream->ctf_trace->refcount);
+ assert(uatomic_read(&stream->ctf_trace->refcount) >= 0);
+ if (uatomic_read(&stream->ctf_trace->refcount) == 0) {
+ DBG("Freeing ctf_trace %" PRIu64, stream->ctf_trace->id);
+ free(stream->ctf_trace);
+ }
+ }
+
+ free(stream->path_name);
+ free(stream->channel_name);
+ free(stream);
+}
+
+static
+void viewer_del_streams(uint64_t session_id)
+{
+ int ret;
+ struct relay_viewer_stream *stream;
+ struct lttng_ht_node_u64 *node;
+ struct lttng_ht_iter iter;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(viewer_streams_ht->ht, &iter.iter, node, node) {
+ health_code_update();
+
+ node = lttng_ht_iter_get_node_u64(&iter);
+ if (!node) {
+ continue;
+ }
+
+ stream = caa_container_of(node, struct relay_viewer_stream, stream_n);
+ if (stream->session_id != session_id) {
+ continue;
+ }
+
+ if (stream->read_fd > 0) {
+ ret = close(stream->read_fd);
+ if (ret < 0) {
+ PERROR("close read_fd");
+ }
+ }
+ if (stream->index_read_fd > 0) {
+ ret = close(stream->index_read_fd);
+ if (ret < 0) {
+ PERROR("close index_read_fd");
+ }
+ }
+ if (stream->metadata_flag && stream->ctf_trace) {
+ stream->ctf_trace->metadata_sent = 0;
+ }
+ ret = lttng_ht_del(viewer_streams_ht, &iter);
+ assert(!ret);
+ call_rcu(&stream->rcu_node, deferred_free_viewer_stream);
+ }
+ rcu_read_unlock();
+}
+
+/*
+ * Delete and free a connection.
+ *
+ * RCU read side lock MUST be acquired.
+ */
+static
+void del_connection(struct lttng_ht *relay_connections_ht,
+ struct lttng_ht_iter *iter, struct relay_command *relay_connection)
+{
+ int ret;
+
+ assert(relay_connections_ht);
+ assert(iter);
+ assert(relay_connection);
+
+ ret = lttng_ht_del(relay_connections_ht, iter);
+ assert(!ret);
+
+ viewer_del_streams(relay_connection->session_id);
+
+ call_rcu(&relay_connection->rcu_node, deferred_free_connection);
+}
+
+/*
+ * This thread does the actual work
+ */
+static
+void *thread_worker(void *data)
+{
+ int ret, err = -1;
+ uint32_t nb_fd;
+ struct relay_command *relay_connection;
+ struct lttng_poll_event events;
+ struct lttng_ht *relay_connections_ht;
+ struct lttng_ht_node_ulong *node;
+ struct lttng_ht_iter iter;
+ struct lttng_viewer_cmd recv_hdr;
+ struct relay_local_data *relay_ctx = (struct relay_local_data *) data;
+ struct lttng_ht *sessions_ht = relay_ctx->sessions_ht;
+
+ DBG("[thread] Live viewer relay worker started");
+
+ rcu_register_thread();
+
+ health_register(health_relayd, HEALTH_RELAYD_TYPE_LIVE_WORKER);
+
+ /* table of connections indexed on socket */
+ relay_connections_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ if (!relay_connections_ht) {
+ goto relay_connections_ht_error;
+ }
+
+ ret = create_thread_poll_set(&events, 2);
+ if (ret < 0) {
+ goto error_poll_create;
+ }
+
+ ret = lttng_poll_add(&events, live_relay_cmd_pipe[0], LPOLLIN | LPOLLRDHUP);
+ if (ret < 0) {
+ goto error;
+ }
+
+restart:
+ while (1) {
+ int i;
+
+ health_code_update();
+
+ /* Infinite blocking call, waiting for transmission */
+ DBG3("Relayd live viewer worker thread polling...");
+ health_poll_entry();
+ ret = lttng_poll_wait(&events, -1);
+ health_poll_exit();
+ if (ret < 0) {
+ /*
+ * Restart interrupted system call.
+ */
+ if (errno == EINTR) {
+ goto restart;
+ }
+ goto error;
+ }
+
+ nb_fd = ret;
+
+ /*
+ * Process control. The control connection is prioritised so we don't
+ * starve it with high throughput tracing data on the data
+ * connection.
+ */
+ for (i = 0; i < nb_fd; i++) {
+ /* Fetch once the poll data */
+ uint32_t revents = LTTNG_POLL_GETEV(&events, i);
+ int pollfd = LTTNG_POLL_GETFD(&events, i);
+
+ health_code_update();
+
+ /* Thread quit pipe has been closed. Killing thread. */
+ ret = check_thread_quit_pipe(pollfd, revents);
+ if (ret) {
+ err = 0;
+ goto exit;
+ }
+
+ /* Inspect the relay cmd pipe for new connection */
+ if (pollfd == live_relay_cmd_pipe[0]) {
+ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+ ERR("Relay live pipe error");
+ goto error;
+ } else if (revents & LPOLLIN) {
+ DBG("Relay live viewer command received");
+ ret = add_connection(live_relay_cmd_pipe[0],
+ &events, relay_connections_ht);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+ } else if (revents) {
+ rcu_read_lock();
+ lttng_ht_lookup(relay_connections_ht,
+ (void *)((unsigned long) pollfd), &iter);
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (node == NULL) {
+ DBG2("Relay viewer sock %d not found", pollfd);
+ rcu_read_unlock();
+ goto error;
+ }
+ relay_connection = caa_container_of(node, struct relay_command,
+ sock_n);
+
+ if (revents & (LPOLLERR)) {
+ cleanup_poll_connection(&events, pollfd);
+ del_connection(relay_connections_ht, &iter,
+ relay_connection);
+ } else if (revents & (LPOLLHUP | LPOLLRDHUP)) {
+ DBG("Viewer socket %d hung up", pollfd);
+ cleanup_poll_connection(&events, pollfd);
+ del_connection(relay_connections_ht, &iter,
+ relay_connection);
+ } else if (revents & LPOLLIN) {
+ ret = relay_connection->sock->ops->recvmsg(
+ relay_connection->sock, &recv_hdr,
+ sizeof(struct lttng_viewer_cmd),
+ 0);
+ /* connection closed */
+ if (ret <= 0) {
+ cleanup_poll_connection(&events, pollfd);
+ del_connection( relay_connections_ht, &iter,
+ relay_connection);
+ DBG("Viewer control connection closed with %d",
+ pollfd);
+ } else {
+ if (relay_connection->session) {
+ DBG2("Relay viewer worker receiving data for "
+ "session: %" PRIu64,
+ relay_connection->session->id);
+ }
+ ret = process_control(&recv_hdr, relay_connection,
+ sessions_ht);
+ if (ret < 0) {
+ /* Clear the session on error. */
+ cleanup_poll_connection(&events, pollfd);
+ del_connection(relay_connections_ht, &iter,
+ relay_connection);
+ DBG("Viewer connection closed with %d", pollfd);
+ }
+ }
+ }
+ rcu_read_unlock();
+ }
+ }
+ }
+
+exit:
+error:
+ lttng_poll_clean(&events);
+
+ /* empty the hash table and free the memory */
+ rcu_read_lock();
+ cds_lfht_for_each_entry(relay_connections_ht->ht, &iter.iter, node, node) {
+ health_code_update();
+
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (!node) {
+ continue;
+ }
+
+ relay_connection = caa_container_of(node, struct relay_command,
+ sock_n);
+ del_connection(relay_connections_ht, &iter, relay_connection);
+ }
+ rcu_read_unlock();
+error_poll_create:
+ lttng_ht_destroy(relay_connections_ht);
+relay_connections_ht_error:
+ /* Close relay cmd pipes */
+ utils_close_pipe(live_relay_cmd_pipe);
+ if (err) {
+ DBG("Viewer worker thread exited with error");
+ }
+ DBG("Viewer worker thread cleanup complete");
+ if (err) {
+ health_error();
+ ERR("Health error occurred in %s", __func__);
+ }
+ health_unregister(health_relayd);
+ stop_threads();
+ rcu_unregister_thread();
+ return NULL;
+}
+
+/*
+ * Create the relay command pipe to wake thread_manage_apps.
+ * Closed in cleanup().
+ */
+static int create_relay_cmd_pipe(void)
+{
+ int ret;
+
+ ret = utils_create_pipe_cloexec(live_relay_cmd_pipe);
+
+ return ret;
+}
+
+void live_stop_threads()
+{
+ int ret;
+ void *status;
+
+ stop_threads();
+
+ ret = pthread_join(live_listener_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join live listener");
+ goto error; /* join error, exit without cleanup */
+ }
+
+ ret = pthread_join(live_worker_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join live worker");
+ goto error; /* join error, exit without cleanup */
+ }
+
+ ret = pthread_join(live_dispatcher_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join live dispatcher");
+ goto error; /* join error, exit without cleanup */
+ }
+
+ cleanup();
+
+error:
+ return;
+}
+
+/*
+ * main
+ */
+int live_start_threads(struct lttng_uri *uri,
+ struct relay_local_data *relay_ctx, int quit_pipe[2])
+{
+ int ret = 0;
+ void *status;
+ int is_root;
+
+ assert(uri);
+ live_uri = uri;
+
+ live_thread_quit_pipe[0] = quit_pipe[0];
+ live_thread_quit_pipe[1] = quit_pipe[1];
+
+ /* Check if daemon is UID = 0 */
+ is_root = !getuid();
+
+ if (!is_root) {
+ if (live_uri->port < 1024) {
+ ERR("Need to be root to use ports < 1024");
+ ret = -1;
+ goto exit;
+ }
+ }
+
+ /* Setup the thread apps communication pipe. */
+ if ((ret = create_relay_cmd_pipe()) < 0) {
+ goto exit;
+ }
+
+ /* Init relay command queue. */
+ cds_wfq_init(&viewer_cmd_queue.queue);
+
+ /* Set up max poll set size */
+ lttng_poll_set_max_size();
+
+ /* Setup the dispatcher thread */
+ ret = pthread_create(&live_dispatcher_thread, NULL,
+ thread_dispatcher, (void *) NULL);
+ if (ret != 0) {
+ PERROR("pthread_create viewer dispatcher");
+ goto exit_dispatcher;
+ }
+
+ /* Setup the worker thread */
+ ret = pthread_create(&live_worker_thread, NULL,
+ thread_worker, relay_ctx);
+ if (ret != 0) {
+ PERROR("pthread_create viewer worker");
+ goto exit_worker;
+ }
+
+ /* Setup the listener thread */
+ ret = pthread_create(&live_listener_thread, NULL,
+ thread_listener, (void *) NULL);
+ if (ret != 0) {
+ PERROR("pthread_create viewer listener");
+ goto exit_listener;
+ }
+
+ ret = 0;
+ goto end;
+
+exit_listener:
+ ret = pthread_join(live_listener_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join live listener");
+ goto error; /* join error, exit without cleanup */
+ }
+
+exit_worker:
+ ret = pthread_join(live_worker_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join live worker");
+ goto error; /* join error, exit without cleanup */
+ }
+
+exit_dispatcher:
+ ret = pthread_join(live_dispatcher_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join live dispatcher");
+ goto error; /* join error, exit without cleanup */
+ }
+
+exit:
+ cleanup();
+
+end:
+error:
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LTTNG_RELAYD_LIVE_H
+#define LTTNG_RELAYD_LIVE_H
+
+#include <common/uri.h>
+
+#include "lttng-relayd.h"
+
+int live_start_threads(struct lttng_uri *live_uri,
+ struct relay_local_data *relay_ctx, int quit_pipe[2]);
+void live_stop_threads(void);
+
+struct relay_viewer_stream *live_find_viewer_stream_by_id(uint64_t stream_id);
+
+#endif /* LTTNG_RELAYD_LIVE_H */
#define LTTNG_RELAYD_H
#define _LGPL_SOURCE
+#include <limits.h>
#include <urcu.h>
#include <urcu/wfqueue.h>
+
#include <common/hashtable/hashtable.h>
+#include <common/index/lttng-index.h>
+
+#include "ctf-trace.h"
/*
* Queue used to enqueue relay requests
};
enum connection_type {
- RELAY_DATA,
- RELAY_CONTROL,
+ RELAY_DATA = 1,
+ RELAY_CONTROL = 2,
+ RELAY_VIEWER_COMMAND = 3,
+ RELAY_VIEWER_NOTIFICATION = 4,
};
/*
*/
uint64_t id;
struct lttcomm_sock *sock;
+ char session_name[NAME_MAX];
+ char hostname[HOST_NAME_MAX];
+ uint32_t live_timer;
+ struct lttng_ht_node_ulong session_n;
+ struct rcu_head rcu_node;
+ uint32_t viewer_attached;
+ uint32_t stream_count;
+ /* Tell if this session is for a snapshot or not. */
+ unsigned int snapshot:1;
+
+ /*
+ * Indicate version protocol for this session. This is especially useful
+ * for the data thread that has no idea which version it operates on since
+ * linking control/data sockets is non trivial.
+ */
+ uint64_t minor;
+ uint64_t major;
};
/*
struct relay_session *session;
struct rcu_head rcu_node;
int fd;
+ /* FD on which to write the index data. */
+ int index_fd;
+ /* FD on which to read the index data for the viewer. */
+ int read_index_fd;
char *path_name;
char *channel_name;
uint64_t tracefile_count;
uint64_t tracefile_count_current;
+ uint64_t total_index_received;
+ struct relay_viewer_stream *viewer_stream;
+ uint64_t last_net_seq_num;
+
+ /*
+ * This node is added to the *control* connection hash table and the
+ * pointer is copied in here so we can access it when deleting this object.
+ * When deleting this, the ctf trace ht MUST NOT be destroyed. This happens
+ * at connection deletion.
+ */
+ struct lttng_ht_node_str ctf_trace_node;
+ struct lttng_ht *ctf_traces_ht;
+
+ /*
+ * To protect from concurrent read/update between the
+ * streaming-side and the viewer-side.
+ * This lock must be held, we reading/updating the
+ * ctf_trace pointer.
+ */
+ pthread_mutex_t lock;
+
+ struct ctf_trace *ctf_trace;
+ /*
+ * If the stream is inactive, this field is updated with the live beacon
+ * timestamp end, when it is active, this field == -1ULL.
+ */
+ uint64_t beacon_ts_end;
+
/* Information telling us when to close the stream */
unsigned int close_flag:1;
- uint64_t last_net_seq_num;
/* Indicate if the stream was initialized for a data pending command. */
unsigned int data_pending_check_done:1;
+ unsigned int metadata_flag:1;
+};
+
+/*
+ * Shadow copy of the relay_stream structure for the viewer side. The only
+ * fields updated by the writer (streaming side) after allocation are :
+ * total_index_received and close_flag. Everything else is updated by the
+ * reader (viewer side).
+ */
+struct relay_viewer_stream {
+ uint64_t stream_handle;
+ uint64_t session_id;
+ int read_fd;
+ int index_read_fd;
+ char *path_name;
+ char *channel_name;
+ uint64_t last_sent_index;
+ uint64_t total_index_received;
+ uint64_t tracefile_size;
+ uint64_t tracefile_size_current;
+ uint64_t tracefile_count;
+ uint64_t tracefile_count_current;
+ struct lttng_ht_node_u64 stream_n;
+ struct rcu_head rcu_node;
+ struct ctf_trace *ctf_trace;
+ /* Information telling us if the stream is a metadata stream. */
+ unsigned int metadata_flag:1;
};
/*
/* protocol version to use for this session */
uint32_t major;
uint32_t minor;
+ struct lttng_ht *ctf_traces_ht; /* indexed by path name */
+ uint64_t session_id;
+};
+
+struct relay_local_data {
+ struct lttng_ht *sessions_ht;
};
extern char *opt_output_path;
+extern struct lttng_ht *relay_streams_ht;
+extern struct lttng_ht *viewer_streams_ht;
+extern struct lttng_ht *indexes_ht;
+
+extern const char *tracing_group_name;
+
+struct relay_stream *relay_stream_find_by_id(uint64_t stream_id);
+
#endif /* LTTNG_RELAYD_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef LTTNG_VIEWER_H
+#define LTTNG_VIEWER_H
+
+#include <limits.h>
+
+#define LTTNG_VIEWER_PATH_MAX 4096
+#define LTTNG_VIEWER_NAME_MAX 255
+#define LTTNG_VIEWER_HOST_NAME_MAX 64
+
+/* Flags in reply to get_next_index and get_packet. */
+/* New metadata is required to read this packet. */
+#define LTTNG_VIEWER_FLAG_NEW_METADATA (1 << 0)
+/* New stream got added to the trace */
+#define LTTNG_VIEWER_FLAG_NEW_STREAM (1 << 1)
+
+enum lttng_viewer_command {
+ VIEWER_CONNECT = 1,
+ VIEWER_LIST_SESSIONS = 2,
+ VIEWER_ATTACH_SESSION = 3,
+ VIEWER_GET_NEXT_INDEX = 4,
+ VIEWER_GET_PACKET = 5,
+ VIEWER_GET_METADATA = 6,
+};
+
+enum lttng_viewer_attach_return_code {
+ VIEWER_ATTACH_OK = 1, /* If the attach command succeeded. */
+ VIEWER_ATTACH_ALREADY = 2, /* If a viewer is already attached. */
+ VIEWER_ATTACH_UNK = 3, /* If the session ID is unknown. */
+ VIEWER_ATTACH_NOT_LIVE = 4, /* If the session is not live. */
+ VIEWER_ATTACH_SEEK_ERR = 5, /* Seek error. */
+};
+
+enum lttng_viewer_next_index_return_code {
+ VIEWER_INDEX_OK = 1, /* Index is available. */
+ VIEWER_INDEX_RETRY = 2, /* Index not yet available. */
+ VIEWER_INDEX_HUP = 3, /* Index closed (trace destroyed). */
+ VIEWER_INDEX_ERR = 4, /* Unknow error. */
+ VIEWER_INDEX_INACTIVE = 5, /* Inactive stream beacon. */
+};
+
+enum lttng_viewer_get_packet_return_code {
+ VIEWER_GET_PACKET_OK = 1,
+ VIEWER_GET_PACKET_RETRY = 2,
+ VIEWER_GET_PACKET_ERR = 3,
+};
+
+enum lttng_viewer_get_metadata_return_code {
+ VIEWER_METADATA_OK = 1,
+ VIEWER_NO_NEW_METADATA = 2,
+ VIEWER_METADATA_ERR = 3,
+};
+
+enum lttng_viewer_connection_type {
+ VIEWER_CLIENT_COMMAND = 1,
+ VIEWER_CLIENT_NOTIFICATION = 2,
+};
+
+enum lttng_viewer_seek {
+ VIEWER_SEEK_BEGINNING = 1, /* Receive the trace packets from the beginning. */
+ VIEWER_SEEK_LAST = 2, /* Receive the trace packets from now. */
+};
+
+struct lttng_viewer_session {
+ uint64_t id;
+ char hostname[LTTNG_VIEWER_HOST_NAME_MAX];
+ char session_name[LTTNG_VIEWER_NAME_MAX];
+ uint32_t live_timer;
+ uint32_t clients;
+} __attribute__((__packed__));
+
+struct lttng_viewer_stream {
+ uint64_t id;
+ uint64_t ctf_trace_id;
+ char path_name[LTTNG_VIEWER_PATH_MAX];
+ char channel_name[LTTNG_VIEWER_NAME_MAX];
+ int metadata_flag;
+} __attribute__((__packed__));
+
+struct lttng_viewer_cmd {
+ uint64_t data_size; /* data size following this header */
+ uint32_t cmd; /* enum lttcomm_relayd_command */
+ uint32_t cmd_version; /* command version */
+} __attribute__((__packed__));
+
+/*
+ * CONNECT payload.
+ */
+struct lttng_viewer_connect {
+ uint32_t major;
+ uint32_t minor;
+ uint32_t type; /* enum lttng_viewer_connection_type */
+ uint64_t viewer_session_id; /* session ID assigned by the relay for command connections */
+} __attribute__((__packed__));
+
+/*
+ * VIEWER_LIST_SESSIONS payload.
+ */
+struct lttng_viewer_list_sessions {
+ uint32_t sessions_count;
+ char session_list[]; /* struct lttng_viewer_session */
+} __attribute__((__packed__));
+
+/*
+ * VIEWER_ATTACH_SESSION payload.
+ */
+struct lttng_viewer_attach_session_request {
+ uint64_t session_id;
+ uint32_t seek; /* enum lttng_viewer_seek */
+ uint64_t offset; /* unused for now */
+} __attribute__((__packed__));
+
+struct lttng_viewer_attach_session_response {
+ uint32_t status; /* enum lttng_viewer_attach_return_code */
+ uint32_t streams_count;
+ char stream_list[]; /* struct lttng_viewer_stream */
+} __attribute__((__packed__));
+
+/*
+ * VIEWER_GET_NEXT_INDEX payload.
+ */
+struct lttng_viewer_get_next_index {
+ uint64_t stream_id;
+} __attribute__ ((__packed__));
+
+struct lttng_viewer_index {
+ uint32_t status; /* enum lttng_viewer_next_index_return_code */
+ uint64_t offset;
+ uint64_t packet_size;
+ uint64_t content_size;
+ uint64_t timestamp_begin;
+ uint64_t timestamp_end;
+ uint64_t events_discarded;
+ uint64_t stream_id;
+ uint32_t flags; /* LTTNG_VIEWER_FLAG_* */
+} __attribute__ ((__packed__));
+
+/*
+ * VIEWER_GET_PACKET payload.
+ */
+struct lttng_viewer_get_packet {
+ uint64_t stream_id;
+ uint64_t offset;
+ uint32_t len;
+} __attribute__((__packed__));
+
+struct lttng_viewer_trace_packet {
+ uint32_t status; /* enum lttng_viewer_get_packet_return_code */
+ uint32_t len;
+ uint32_t flags; /* LTTNG_VIEWER_FLAG_* */
+ char data[];
+} __attribute__((__packed__));
+
+/*
+ * VIEWER_GET_METADATA payload.
+ */
+struct lttng_viewer_get_metadata {
+ uint64_t stream_id;
+} __attribute__((__packed__));
+
+struct lttng_viewer_metadata_packet {
+ uint32_t status; /* enum lttng_viewer_get_metadata_return_code */
+ uint64_t len;
+ char data[];
+} __attribute__((__packed__));
+
+#endif /* LTTNG_VIEWER_H */
#include <common/utils.h>
#include "cmd.h"
+#include "ctf-trace.h"
+#include "index.h"
#include "utils.h"
#include "lttng-relayd.h"
+#include "live.h"
+#include "health-relayd.h"
/* command line options */
char *opt_output_path;
static int opt_daemon;
static struct lttng_uri *control_uri;
static struct lttng_uri *data_uri;
+static struct lttng_uri *live_uri;
const char *progname;
-static int is_root; /* Set to 1 if the daemon is running as root */
+
+const char *tracing_group_name = DEFAULT_TRACING_GROUP;
/*
* Quit pipe for all threads. This permits a single cancellation point
static pthread_t listener_thread;
static pthread_t dispatcher_thread;
static pthread_t worker_thread;
+static pthread_t health_thread;
static uint64_t last_relay_stream_id;
static uint64_t last_relay_session_id;
static char *data_buffer;
static unsigned int data_buffer_size;
+/* We need those values for the file/dir creation. */
+static uid_t relayd_uid;
+static gid_t relayd_gid;
+
+/* Global relay stream hash table. */
+struct lttng_ht *relay_streams_ht;
+
+/* Global relay viewer stream hash table. */
+struct lttng_ht *viewer_streams_ht;
+
+/* Global hash table that stores relay index object. */
+struct lttng_ht *indexes_ht;
+
+/* Relayd health monitoring */
+struct health_app *health_relayd;
+
/*
* usage function on stderr
*/
fprintf(stderr, " -D, --data-port URL Data port listening.\n");
fprintf(stderr, " -o, --output PATH Output path for traces. Must use an absolute path.\n");
fprintf(stderr, " -v, --verbose Verbose mode. Activate DBG() macro.\n");
+ fprintf(stderr, " -g, --group NAME Specify the tracing group name. (default: tracing)\n");
}
static
{ "control-port", 1, 0, 'C', },
{ "data-port", 1, 0, 'D', },
{ "daemonize", 0, 0, 'd', },
+ { "group", 1, 0, 'g', },
{ "help", 0, 0, 'h', },
{ "output", 1, 0, 'o', },
{ "verbose", 0, 0, 'v', },
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "dhv" "C:D:o:",
+ c = getopt_long(argc, argv, "dhv" "C:D:o:g:",
long_options, &option_index);
if (c == -1) {
break;
case 'd':
opt_daemon = 1;
break;
+ case 'g':
+ tracing_group_name = optarg;
+ break;
case 'h':
usage();
exit(EXIT_FAILURE);
goto exit;
}
}
+ if (live_uri == NULL) {
+ ret = asprintf(&default_address, "tcp://0.0.0.0:%d",
+ DEFAULT_NETWORK_VIEWER_PORT);
+ if (ret < 0) {
+ PERROR("asprintf default viewer control address");
+ goto exit;
+ }
+
+ ret = uri_parse(default_address, &live_uri);
+ free(default_address);
+ if (ret < 0) {
+ ERR("Invalid viewer control URI specified");
+ goto exit;
+ }
+ }
exit:
return ret;
return ret;
}
+static void notify_health_quit_pipe(int *pipe)
+{
+ int ret;
+
+ do {
+ ret = write(pipe[1], "4", 1);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0 || ret != 1) {
+ PERROR("write relay health quit");
+ }
+}
+
/*
* Stop all threads by closing the thread quit pipe.
*/
ERR("write error on thread quit pipe");
}
+ notify_health_quit_pipe(health_quit_pipe);
+
/* Dispatch thread */
CMM_STORE_SHARED(dispatch_thread_exit, 1);
futex_nto1_wake(&relay_cmd_queue.futex);
DBG("[thread] Relay listener started");
+ health_register(health_relayd, HEALTH_RELAYD_TYPE_LISTENER);
+
+ health_code_update();
+
control_sock = relay_init_sock(control_uri);
if (!control_sock) {
goto error_sock_control;
}
while (1) {
+ health_code_update();
+
DBG("Listener accepting connections");
restart:
+ health_poll_entry();
ret = lttng_poll_wait(&events, -1);
+ health_poll_exit();
if (ret < 0) {
/*
* Restart interrupted system call.
DBG("Relay new connection received");
for (i = 0; i < nb_fd; i++) {
+ health_code_update();
+
/* Fetch once the poll data */
revents = LTTNG_POLL_GETEV(&events, i);
pollfd = LTTNG_POLL_GETFD(&events, i);
lttcomm_destroy_sock(control_sock);
error_sock_control:
if (err) {
- DBG("Thread exited with error");
+ health_error();
+ ERR("Health error occurred in %s", __func__);
}
+ health_unregister(health_relayd);
DBG("Relay listener thread cleanup complete");
stop_threads();
return NULL;
static
void *relay_thread_dispatcher(void *data)
{
- int ret;
+ int ret, err = -1;
struct cds_wfq_node *node;
struct relay_command *relay_cmd = NULL;
DBG("[thread] Relay dispatcher started");
+ health_register(health_relayd, HEALTH_RELAYD_TYPE_DISPATCHER);
+
+ health_code_update();
+
while (!CMM_LOAD_SHARED(dispatch_thread_exit)) {
+ health_code_update();
+
/* Atomically prepare the queue futex */
futex_nto1_prepare(&relay_cmd_queue.futex);
do {
+ health_code_update();
+
/* Dequeue commands */
node = cds_wfq_dequeue_blocking(&relay_cmd_queue.queue);
if (node == NULL) {
} while (node != NULL);
/* Futex wait on queue. Blocking call on futex() */
+ health_poll_entry();
futex_nto1_wait(&relay_cmd_queue.futex);
+ health_poll_exit();
}
+ /* Normal exit, no error */
+ err = 0;
+
error:
+ if (err) {
+ health_error();
+ ERR("Health error occurred in %s", __func__);
+ }
+ health_unregister(health_relayd);
DBG("Dispatch thread dying");
stop_threads();
return NULL;
* Get stream from stream id.
* Need to be called with RCU read-side lock held.
*/
-static
-struct relay_stream *relay_stream_from_stream_id(uint64_t stream_id,
- struct lttng_ht *streams_ht)
+struct relay_stream *relay_stream_find_by_id(uint64_t stream_id)
{
struct lttng_ht_node_ulong *node;
struct lttng_ht_iter iter;
struct relay_stream *ret;
- lttng_ht_lookup(streams_ht,
+ lttng_ht_lookup(relay_streams_ht,
(void *)((unsigned long) stream_id),
&iter);
node = lttng_ht_iter_get_node_ulong(&iter);
{
struct relay_stream *stream =
caa_container_of(head, struct relay_stream, rcu_node);
+
+ ctf_trace_try_destroy(stream->ctf_trace);
+
free(stream->path_name);
free(stream->channel_name);
free(stream);
}
+static
+void deferred_free_session(struct rcu_head *head)
+{
+ struct relay_session *session =
+ caa_container_of(head, struct relay_session, rcu_node);
+ free(session);
+}
+
+/*
+ * Close a given stream. The stream is freed using a call RCU.
+ *
+ * RCU read side lock MUST be acquired. If NO close_stream_check() was called
+ * BEFORE the stream lock MUST be acquired.
+ */
+static void destroy_stream(struct relay_stream *stream)
+{
+ int delret;
+ struct relay_viewer_stream *vstream;
+ struct lttng_ht_iter iter;
+
+ assert(stream);
+
+ delret = close(stream->fd);
+ if (delret < 0) {
+ PERROR("close stream");
+ }
+
+ if (stream->index_fd >= 0) {
+ delret = close(stream->index_fd);
+ if (delret < 0) {
+ PERROR("close stream index_fd");
+ }
+ }
+
+ vstream = live_find_viewer_stream_by_id(stream->stream_handle);
+ if (vstream) {
+ /*
+ * Set the last good value into the viewer stream. This is done
+ * right before the stream gets deleted from the hash table. The
+ * lookup failure on the live thread side of a stream indicates
+ * that the viewer stream index received value should be used.
+ */
+ vstream->total_index_received = stream->total_index_received;
+ }
+
+ /* Cleanup index of that stream. */
+ relay_index_destroy_by_stream_id(stream->stream_handle);
+
+ iter.iter.node = &stream->stream_n.node;
+ delret = lttng_ht_del(relay_streams_ht, &iter);
+ assert(!delret);
+ iter.iter.node = &stream->ctf_trace_node.node;
+ delret = lttng_ht_del(stream->ctf_traces_ht, &iter);
+ assert(!delret);
+ call_rcu(&stream->rcu_node, deferred_free_stream);
+ DBG("Closed tracefile %d from close stream", stream->fd);
+}
+
/*
* relay_delete_session: Free all memory associated with a session and
* close all the FDs
*/
static
-void relay_delete_session(struct relay_command *cmd, struct lttng_ht *streams_ht)
+void relay_delete_session(struct relay_command *cmd,
+ struct lttng_ht *sessions_ht)
{
struct lttng_ht_iter iter;
struct lttng_ht_node_ulong *node;
DBG("Relay deleting session %" PRIu64, cmd->session->id);
rcu_read_lock();
- cds_lfht_for_each_entry(streams_ht->ht, &iter.iter, node, node) {
+ cds_lfht_for_each_entry(relay_streams_ht->ht, &iter.iter, node, node) {
node = lttng_ht_iter_get_node_ulong(&iter);
- if (node) {
- stream = caa_container_of(node,
- struct relay_stream, stream_n);
- if (stream->session == cmd->session) {
- ret = close(stream->fd);
- if (ret < 0) {
- PERROR("close stream fd on delete session");
- }
- ret = lttng_ht_del(streams_ht, &iter);
- assert(!ret);
- call_rcu(&stream->rcu_node,
- deferred_free_stream);
- }
+ if (!node) {
+ continue;
+ }
+ stream = caa_container_of(node, struct relay_stream, stream_n);
+ if (stream->session == cmd->session) {
+ destroy_stream(stream);
}
}
+
+ /* Make this session not visible anymore. */
+ iter.iter.node = &cmd->session->session_n.node;
+ ret = lttng_ht_del(sessions_ht, &iter);
+ assert(!ret);
+ call_rcu(&cmd->session->rcu_node, deferred_free_session);
rcu_read_unlock();
+}
- free(cmd->session);
+/*
+ * Copy index data from the control port to a given index object.
+ */
+static void copy_index_control_data(struct relay_index *index,
+ struct lttcomm_relayd_index *data)
+{
+ assert(index);
+ assert(data);
+
+ /*
+ * The index on disk is encoded in big endian, so we don't need to convert
+ * the data received on the network. The data_offset value is NEVER
+ * modified here and is updated by the data thread.
+ */
+ index->index_data.packet_size = data->packet_size;
+ index->index_data.content_size = data->content_size;
+ index->index_data.timestamp_begin = data->timestamp_begin;
+ index->index_data.timestamp_end = data->timestamp_end;
+ index->index_data.events_discarded = data->events_discarded;
+ index->index_data.stream_id = data->stream_id;
}
/*
*/
static
int relay_create_session(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd)
+ struct relay_command *cmd,
+ struct lttng_ht *sessions_ht)
{
int ret = 0, send_ret;
struct relay_session *session;
session->id = ++last_relay_session_id;
session->sock = cmd->sock;
+ session->minor = cmd->minor;
+ session->major = cmd->major;
cmd->session = session;
reply.session_id = htobe64(session->id);
+ switch (cmd->minor) {
+ case 4: /* LTTng sessiond 2.4 */
+ default:
+ ret = cmd_create_session_2_4(cmd, session);
+ break;
+ }
+
+ lttng_ht_node_init_ulong(&session->session_n,
+ (unsigned long) session->id);
+ lttng_ht_add_unique_ulong(sessions_ht,
+ &session->session_n);
+
DBG("Created session %" PRIu64, session->id);
error:
*/
static
int relay_add_stream(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd, struct lttng_ht *streams_ht)
+ struct relay_command *cmd, struct lttng_ht *sessions_ht)
{
struct relay_session *session = cmd->session;
struct relay_stream *stream = NULL;
stream->stream_handle = ++last_relay_stream_id;
stream->prev_seq = -1ULL;
stream->session = session;
+ stream->index_fd = -1;
+ stream->read_index_fd = -1;
+ stream->ctf_trace = NULL;
+ pthread_mutex_init(&stream->lock, NULL);
ret = utils_mkdir_recursive(stream->path_name, S_IRWXU | S_IRWXG);
if (ret < 0) {
* uses its own credentials for the stream files.
*/
ret = utils_create_stream_file(stream->path_name, stream->channel_name,
- stream->tracefile_size, 0, -1, -1);
+ stream->tracefile_size, 0, relayd_uid, relayd_gid, NULL);
if (ret < 0) {
ERR("Create output file");
goto end;
DBG("Tracefile %s/%s created", stream->path_name, stream->channel_name);
}
+ if (!strncmp(stream->channel_name, DEFAULT_METADATA_NAME, NAME_MAX)) {
+ stream->metadata_flag = 1;
+ /*
+ * When we receive a new metadata stream, we create a new
+ * ctf_trace and we assign this ctf_trace to all streams with
+ * the same path.
+ *
+ * If later on we receive a new stream for the same ctf_trace,
+ * we copy the information from the first hit in the HT to the
+ * new stream.
+ */
+ stream->ctf_trace = ctf_trace_create();
+ if (!stream->ctf_trace) {
+ ret = -1;
+ goto end;
+ }
+ stream->ctf_trace->refcount++;
+ stream->ctf_trace->metadata_stream = stream;
+ }
+ ctf_trace_assign(cmd->ctf_traces_ht, stream);
+ stream->ctf_traces_ht = cmd->ctf_traces_ht;
+
lttng_ht_node_init_ulong(&stream->stream_n,
(unsigned long) stream->stream_handle);
- lttng_ht_add_unique_ulong(streams_ht,
+ lttng_ht_add_unique_ulong(relay_streams_ht,
&stream->stream_n);
- DBG("Relay new stream added %s", stream->channel_name);
+ lttng_ht_node_init_str(&stream->ctf_trace_node, stream->path_name);
+ lttng_ht_add_str(cmd->ctf_traces_ht, &stream->ctf_trace_node);
+
+ DBG("Relay new stream added %s with ID %" PRIu64, stream->channel_name,
+ stream->stream_handle);
end:
reply.handle = htobe64(stream->stream_handle);
*/
static
int relay_close_stream(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd, struct lttng_ht *streams_ht)
+ struct relay_command *cmd)
{
+ int ret, send_ret;
struct relay_session *session = cmd->session;
struct lttcomm_relayd_close_stream stream_info;
struct lttcomm_relayd_generic_reply reply;
struct relay_stream *stream;
- int ret, send_ret;
- struct lttng_ht_iter iter;
DBG("Close stream received");
}
rcu_read_lock();
- stream = relay_stream_from_stream_id(be64toh(stream_info.stream_id),
- streams_ht);
+ stream = relay_stream_find_by_id(be64toh(stream_info.stream_id));
if (!stream) {
ret = -1;
goto end_unlock;
stream->close_flag = 1;
if (close_stream_check(stream)) {
- int delret;
-
- delret = close(stream->fd);
- if (delret < 0) {
- PERROR("close stream");
- }
- iter.iter.node = &stream->stream_n.node;
- delret = lttng_ht_del(streams_ht, &iter);
- assert(!delret);
- call_rcu(&stream->rcu_node,
- deferred_free_stream);
- DBG("Closed tracefile %d from close stream", stream->fd);
+ destroy_stream(stream);
}
end_unlock:
*/
static
int relay_recv_metadata(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd, struct lttng_ht *streams_ht)
+ struct relay_command *cmd)
{
int ret = htobe32(LTTNG_OK);
struct relay_session *session = cmd->session;
metadata_struct = (struct lttcomm_relayd_metadata_payload *) data_buffer;
rcu_read_lock();
- metadata_stream = relay_stream_from_stream_id(
- be64toh(metadata_struct->stream_id), streams_ht);
+ metadata_stream = relay_stream_find_by_id(
+ be64toh(metadata_struct->stream_id));
if (!metadata_stream) {
ret = -1;
goto end_unlock;
if (ret < 0) {
goto end_unlock;
}
+ metadata_stream->ctf_trace->metadata_received +=
+ payload_size + be32toh(metadata_struct->padding_size);
DBG2("Relay metadata written");
*/
static
int relay_send_version(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd, struct lttng_ht *streams_ht)
+ struct relay_command *cmd, struct lttng_ht *sessions_ht)
{
int ret;
struct lttcomm_relayd_version reply, msg;
goto end;
}
- ret = sscanf(VERSION, "%10u.%10u", &reply.major, &reply.minor);
- if (ret < 2) {
- ERR("Error in scanning version");
- ret = -1;
- goto end;
- }
+ reply.major = RELAYD_VERSION_COMM_MAJOR;
+ reply.minor = RELAYD_VERSION_COMM_MINOR;
/* Major versions must be the same */
if (reply.major != be32toh(msg.major)) {
DBG("Incompatible major versions (%u vs %u), deleting session",
reply.major, be32toh(msg.major));
- relay_delete_session(cmd, streams_ht);
+ relay_delete_session(cmd, sessions_ht);
ret = 0;
goto end;
}
*/
static
int relay_data_pending(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd, struct lttng_ht *streams_ht)
+ struct relay_command *cmd)
{
struct relay_session *session = cmd->session;
struct lttcomm_relayd_data_pending msg;
last_net_seq_num = be64toh(msg.last_net_seq_num);
rcu_read_lock();
- stream = relay_stream_from_stream_id(stream_id, streams_ht);
+ stream = relay_stream_find_by_id(stream_id);
if (stream == NULL) {
ret = -1;
goto end_unlock;
*/
static
int relay_quiescent_control(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd, struct lttng_ht *streams_ht)
+ struct relay_command *cmd)
{
int ret;
uint64_t stream_id;
stream_id = be64toh(msg.stream_id);
rcu_read_lock();
- cds_lfht_for_each_entry(streams_ht->ht, &iter.iter, stream, stream_n.node) {
+ cds_lfht_for_each_entry(relay_streams_ht->ht, &iter.iter, stream,
+ stream_n.node) {
if (stream->stream_handle == stream_id) {
stream->data_pending_check_done = 1;
DBG("Relay quiescent control pending flag set to %" PRIu64,
*/
static
int relay_begin_data_pending(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd, struct lttng_ht *streams_ht)
+ struct relay_command *cmd)
{
int ret;
struct lttng_ht_iter iter;
assert(recv_hdr);
assert(cmd);
- assert(streams_ht);
DBG("Init streams for data pending");
* streams to find the one associated with the right session_id.
*/
rcu_read_lock();
- cds_lfht_for_each_entry(streams_ht->ht, &iter.iter, stream, stream_n.node) {
+ cds_lfht_for_each_entry(relay_streams_ht->ht, &iter.iter, stream,
+ stream_n.node) {
if (stream->session->id == session_id) {
stream->data_pending_check_done = 0;
DBG("Set begin data pending flag to stream %" PRIu64,
*/
static
int relay_end_data_pending(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd, struct lttng_ht *streams_ht)
+ struct relay_command *cmd)
{
int ret;
struct lttng_ht_iter iter;
assert(recv_hdr);
assert(cmd);
- assert(streams_ht);
DBG("End data pending command");
/* Iterate over all streams to see if the begin data pending flag is set. */
rcu_read_lock();
- cds_lfht_for_each_entry(streams_ht->ht, &iter.iter, stream, stream_n.node) {
+ cds_lfht_for_each_entry(relay_streams_ht->ht, &iter.iter, stream,
+ stream_n.node) {
if (stream->session->id == session_id &&
!stream->data_pending_check_done) {
is_data_inflight = 1;
}
/*
- * relay_process_control: Process the commands received on the control socket
+ * Receive an index for a specific stream.
+ *
+ * Return 0 on success else a negative value.
+ */
+static
+int relay_recv_index(struct lttcomm_relayd_hdr *recv_hdr,
+ struct relay_command *cmd)
+{
+ int ret, send_ret, index_created = 0;
+ struct relay_session *session = cmd->session;
+ struct lttcomm_relayd_index index_info;
+ struct relay_index *index, *wr_index = NULL;
+ struct lttcomm_relayd_generic_reply reply;
+ struct relay_stream *stream;
+ uint64_t net_seq_num;
+
+ assert(cmd);
+
+ DBG("Relay receiving index");
+
+ if (!session || cmd->version_check_done == 0) {
+ ERR("Trying to close a stream before version check");
+ ret = -1;
+ goto end_no_session;
+ }
+
+ ret = cmd->sock->ops->recvmsg(cmd->sock, &index_info,
+ sizeof(index_info), 0);
+ if (ret < sizeof(index_info)) {
+ if (ret == 0) {
+ /* Orderly shutdown. Not necessary to print an error. */
+ DBG("Socket %d did an orderly shutdown", cmd->sock->fd);
+ } else {
+ ERR("Relay didn't receive valid index struct size : %d", ret);
+ }
+ ret = -1;
+ goto end_no_session;
+ }
+
+ net_seq_num = be64toh(index_info.net_seq_num);
+
+ rcu_read_lock();
+ stream = relay_stream_find_by_id(be64toh(index_info.relay_stream_id));
+ if (!stream) {
+ ret = -1;
+ goto end_rcu_unlock;
+ }
+
+ /* Live beacon handling */
+ if (index_info.packet_size == 0) {
+ DBG("Received live beacon for stream %" PRIu64, stream->stream_handle);
+
+ /*
+ * Only flag a stream inactive when it has already received data.
+ */
+ if (stream->total_index_received > 0) {
+ stream->beacon_ts_end = be64toh(index_info.timestamp_end);
+ }
+ ret = 0;
+ goto end_rcu_unlock;
+ } else {
+ stream->beacon_ts_end = -1ULL;
+ }
+
+ index = relay_index_find(stream->stream_handle, net_seq_num);
+ if (!index) {
+ /* A successful creation will add the object to the HT. */
+ index = relay_index_create(stream->stream_handle, net_seq_num);
+ if (!index) {
+ goto end_rcu_unlock;
+ }
+ index_created = 1;
+ }
+
+ copy_index_control_data(index, &index_info);
+
+ if (index_created) {
+ /*
+ * Try to add the relay index object to the hash table. If an object
+ * already exist, destroy back the index created, set the data in this
+ * object and write it on disk.
+ */
+ relay_index_add(index, &wr_index);
+ if (wr_index) {
+ copy_index_control_data(wr_index, &index_info);
+ free(index);
+ }
+ } else {
+ /* The index already exists so write it on disk. */
+ wr_index = index;
+ }
+
+ /* Do we have a writable ready index to write on disk. */
+ if (wr_index) {
+ /* Starting at 2.4, create the index file if none available. */
+ if (cmd->minor >= 4 && stream->index_fd < 0) {
+ ret = index_create_file(stream->path_name, stream->channel_name,
+ relayd_uid, relayd_gid, stream->tracefile_size,
+ stream->tracefile_count_current);
+ if (ret < 0) {
+ goto end_rcu_unlock;
+ }
+ stream->index_fd = ret;
+ }
+
+ ret = relay_index_write(wr_index->fd, wr_index);
+ if (ret < 0) {
+ goto end_rcu_unlock;
+ }
+ stream->total_index_received++;
+ }
+
+end_rcu_unlock:
+ rcu_read_unlock();
+
+ if (ret < 0) {
+ reply.ret_code = htobe32(LTTNG_ERR_UNK);
+ } else {
+ reply.ret_code = htobe32(LTTNG_OK);
+ }
+ send_ret = cmd->sock->ops->sendmsg(cmd->sock, &reply, sizeof(reply), 0);
+ if (send_ret < 0) {
+ ERR("Relay sending close index id reply");
+ ret = send_ret;
+ }
+
+end_no_session:
+ return ret;
+}
+
+/*
+ * Process the commands received on the control socket
*/
static
int relay_process_control(struct lttcomm_relayd_hdr *recv_hdr,
- struct relay_command *cmd, struct lttng_ht *streams_ht)
+ struct relay_command *cmd, struct relay_local_data *ctx)
{
int ret = 0;
switch (be32toh(recv_hdr->cmd)) {
case RELAYD_CREATE_SESSION:
- ret = relay_create_session(recv_hdr, cmd);
+ ret = relay_create_session(recv_hdr, cmd, ctx->sessions_ht);
break;
case RELAYD_ADD_STREAM:
- ret = relay_add_stream(recv_hdr, cmd, streams_ht);
+ ret = relay_add_stream(recv_hdr, cmd, ctx->sessions_ht);
break;
case RELAYD_START_DATA:
ret = relay_start(recv_hdr, cmd);
break;
case RELAYD_SEND_METADATA:
- ret = relay_recv_metadata(recv_hdr, cmd, streams_ht);
+ ret = relay_recv_metadata(recv_hdr, cmd);
break;
case RELAYD_VERSION:
- ret = relay_send_version(recv_hdr, cmd, streams_ht);
+ ret = relay_send_version(recv_hdr, cmd, ctx->sessions_ht);
break;
case RELAYD_CLOSE_STREAM:
- ret = relay_close_stream(recv_hdr, cmd, streams_ht);
+ ret = relay_close_stream(recv_hdr, cmd);
break;
case RELAYD_DATA_PENDING:
- ret = relay_data_pending(recv_hdr, cmd, streams_ht);
+ ret = relay_data_pending(recv_hdr, cmd);
break;
case RELAYD_QUIESCENT_CONTROL:
- ret = relay_quiescent_control(recv_hdr, cmd, streams_ht);
+ ret = relay_quiescent_control(recv_hdr, cmd);
break;
case RELAYD_BEGIN_DATA_PENDING:
- ret = relay_begin_data_pending(recv_hdr, cmd, streams_ht);
+ ret = relay_begin_data_pending(recv_hdr, cmd);
break;
case RELAYD_END_DATA_PENDING:
- ret = relay_end_data_pending(recv_hdr, cmd, streams_ht);
+ ret = relay_end_data_pending(recv_hdr, cmd);
+ break;
+ case RELAYD_SEND_INDEX:
+ ret = relay_recv_index(recv_hdr, cmd);
break;
case RELAYD_UPDATE_SYNC_INFO:
default:
return ret;
}
+/*
+ * Handle index for a data stream.
+ *
+ * RCU read side lock MUST be acquired.
+ *
+ * Return 0 on success else a negative value.
+ */
+static int handle_index_data(struct relay_stream *stream, uint64_t net_seq_num,
+ int rotate_index)
+{
+ int ret = 0, index_created = 0;
+ uint64_t stream_id, data_offset;
+ struct relay_index *index, *wr_index = NULL;
+
+ assert(stream);
+
+ stream_id = stream->stream_handle;
+ /* Get data offset because we are about to update the index. */
+ data_offset = htobe64(stream->tracefile_size_current);
+
+ /*
+ * Lookup for an existing index for that stream id/sequence number. If on
+ * exists, the control thread already received the data for it thus we need
+ * to write it on disk.
+ */
+ index = relay_index_find(stream_id, net_seq_num);
+ if (!index) {
+ /* A successful creation will add the object to the HT. */
+ index = relay_index_create(stream_id, net_seq_num);
+ if (!index) {
+ ret = -1;
+ goto error;
+ }
+ index_created = 1;
+ }
+
+ if (rotate_index || stream->index_fd < 0) {
+ index->to_close_fd = stream->index_fd;
+ ret = index_create_file(stream->path_name, stream->channel_name,
+ relayd_uid, relayd_gid, stream->tracefile_size,
+ stream->tracefile_count_current);
+ if (ret < 0) {
+ /* This will close the stream's index fd if one. */
+ relay_index_free_safe(index);
+ goto error;
+ }
+ stream->index_fd = ret;
+ }
+ index->fd = stream->index_fd;
+ index->index_data.offset = data_offset;
+
+ if (index_created) {
+ /*
+ * Try to add the relay index object to the hash table. If an object
+ * already exist, destroy back the index created and set the data.
+ */
+ relay_index_add(index, &wr_index);
+ if (wr_index) {
+ /* Copy back data from the created index. */
+ wr_index->fd = index->fd;
+ wr_index->to_close_fd = index->to_close_fd;
+ wr_index->index_data.offset = data_offset;
+ free(index);
+ }
+ } else {
+ /* The index already exists so write it on disk. */
+ wr_index = index;
+ }
+
+ /* Do we have a writable ready index to write on disk. */
+ if (wr_index) {
+ ret = relay_index_write(wr_index->fd, wr_index);
+ if (ret < 0) {
+ goto error;
+ }
+ stream->total_index_received++;
+ }
+
+error:
+ return ret;
+}
+
/*
* relay_process_data: Process the data received on the data socket
*/
static
-int relay_process_data(struct relay_command *cmd, struct lttng_ht *streams_ht)
+int relay_process_data(struct relay_command *cmd)
{
- int ret = 0;
+ int ret = 0, rotate_index = 0;
struct relay_stream *stream;
struct lttcomm_relayd_data_hdr data_hdr;
uint64_t stream_id;
stream_id = be64toh(data_hdr.stream_id);
rcu_read_lock();
- stream = relay_stream_from_stream_id(stream_id, streams_ht);
+ stream = relay_stream_find_by_id(stream_id);
if (!stream) {
ret = -1;
- goto end_unlock;
+ goto end_rcu_unlock;
}
data_size = be32toh(data_hdr.data_size);
ERR("Allocating data buffer");
free(data_buffer);
ret = -1;
- goto end_unlock;
+ goto end_rcu_unlock;
}
data_buffer = tmp_data_ptr;
data_buffer_size = data_size;
DBG("Socket %d did an orderly shutdown", cmd->sock->fd);
}
ret = -1;
- goto end_unlock;
+ goto end_rcu_unlock;
}
+ /* Check if a rotation is needed. */
if (stream->tracefile_size > 0 &&
(stream->tracefile_size_current + data_size) >
stream->tracefile_size) {
- ret = utils_rotate_stream_file(stream->path_name,
- stream->channel_name, stream->tracefile_size,
- stream->tracefile_count, -1, -1,
- stream->fd, &(stream->tracefile_count_current));
+ ret = utils_rotate_stream_file(stream->path_name, stream->channel_name,
+ stream->tracefile_size, stream->tracefile_count,
+ relayd_uid, relayd_gid, stream->fd,
+ &(stream->tracefile_count_current), &stream->fd);
if (ret < 0) {
- ERR("Rotating output file");
- goto end;
+ ERR("Rotating stream output file");
+ goto end_rcu_unlock;
}
- stream->fd = ret;
/* Reset current size because we just perform a stream rotation. */
stream->tracefile_size_current = 0;
+ rotate_index = 1;
}
- stream->tracefile_size_current += data_size;
+
+ /*
+ * Index are handled in protocol version 2.4 and above. Also, snapshot and
+ * index are NOT supported.
+ */
+ if (stream->session->minor >= 4 && !stream->session->snapshot) {
+ ret = handle_index_data(stream, net_seq_num, rotate_index);
+ if (ret < 0) {
+ goto end_rcu_unlock;
+ }
+ }
+
+ /* Write data to stream output fd. */
do {
ret = write(stream->fd, data_buffer, data_size);
} while (ret < 0 && errno == EINTR);
if (ret < 0 || ret != data_size) {
ERR("Relay error writing data to file");
ret = -1;
- goto end_unlock;
+ goto end_rcu_unlock;
}
DBG2("Relay wrote %d bytes to tracefile for stream id %" PRIu64,
ret = write_padding_to_file(stream->fd, be32toh(data_hdr.padding_size));
if (ret < 0) {
- goto end_unlock;
+ goto end_rcu_unlock;
}
+ stream->tracefile_size_current += data_size + be32toh(data_hdr.padding_size);
stream->prev_seq = net_seq_num;
/* Check if we need to close the FD */
if (close_stream_check(stream)) {
- int cret;
- struct lttng_ht_iter iter;
-
- cret = close(stream->fd);
- if (cret < 0) {
- PERROR("close stream process data");
- }
- iter.iter.node = &stream->stream_n.node;
- ret = lttng_ht_del(streams_ht, &iter);
- assert(!ret);
- call_rcu(&stream->rcu_node,
- deferred_free_stream);
- DBG("Closed tracefile %d after recv data", stream->fd);
+ destroy_stream(stream);
}
-end_unlock:
+end_rcu_unlock:
rcu_read_unlock();
end:
return ret;
goto error_read;
}
+ /*
+ * Only used by the control side and the reference is copied inside each
+ * stream from that connection. Thus a destroy HT must be done after every
+ * stream has been destroyed.
+ */
+ if (relay_connection->type == RELAY_CONTROL) {
+ relay_connection->ctf_traces_ht = lttng_ht_new(0,
+ LTTNG_HT_TYPE_STRING);
+ if (!relay_connection->ctf_traces_ht) {
+ goto error_read;
+ }
+ }
+
lttng_ht_node_init_ulong(&relay_connection->sock_n,
(unsigned long) relay_connection->sock->fd);
rcu_read_lock();
static
void relay_del_connection(struct lttng_ht *relay_connections_ht,
- struct lttng_ht *streams_ht, struct lttng_ht_iter *iter,
- struct relay_command *relay_connection)
+ struct lttng_ht_iter *iter, struct relay_command *relay_connection,
+ struct lttng_ht *sessions_ht)
{
int ret;
ret = lttng_ht_del(relay_connections_ht, iter);
assert(!ret);
+
if (relay_connection->type == RELAY_CONTROL) {
- relay_delete_session(relay_connection, streams_ht);
+ relay_delete_session(relay_connection, sessions_ht);
+ lttng_ht_destroy(relay_connection->ctf_traces_ht);
}
- call_rcu(&relay_connection->rcu_node,
- deferred_free_connection);
+ call_rcu(&relay_connection->rcu_node, deferred_free_connection);
}
/*
struct lttng_ht *relay_connections_ht;
struct lttng_ht_node_ulong *node;
struct lttng_ht_iter iter;
- struct lttng_ht *streams_ht;
struct lttcomm_relayd_hdr recv_hdr;
+ struct relay_local_data *relay_ctx = (struct relay_local_data *) data;
+ struct lttng_ht *sessions_ht = relay_ctx->sessions_ht;
DBG("[thread] Relay worker started");
rcu_register_thread();
+ health_register(health_relayd, HEALTH_RELAYD_TYPE_WORKER);
+
+ health_code_update();
+
/* table of connections indexed on socket */
relay_connections_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
if (!relay_connections_ht) {
goto relay_connections_ht_error;
}
- /* tables of streams indexed by stream ID */
- streams_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
- if (!streams_ht) {
- goto streams_ht_error;
+ /* Tables of received indexes indexed by index handle and net_seq_num. */
+ indexes_ht = lttng_ht_new(0, LTTNG_HT_TYPE_TWO_U64);
+ if (!indexes_ht) {
+ goto indexes_ht_error;
}
ret = create_thread_poll_set(&events, 2);
while (1) {
int idx = -1, i, seen_control = 0, last_notdel_data_fd = -1;
+ health_code_update();
+
/* Infinite blocking call, waiting for transmission */
DBG3("Relayd worker thread polling...");
+ health_poll_entry();
ret = lttng_poll_wait(&events, -1);
+ health_poll_exit();
if (ret < 0) {
/*
* Restart interrupted system call.
uint32_t revents = LTTNG_POLL_GETEV(&events, i);
int pollfd = LTTNG_POLL_GETFD(&events, i);
+ health_code_update();
+
/* Thread quit pipe has been closed. Killing thread. */
ret = check_thread_quit_pipe(pollfd, revents);
if (ret) {
ERR("POLL ERROR");
relay_cleanup_poll_connection(&events, pollfd);
relay_del_connection(relay_connections_ht,
- streams_ht, &iter,
- relay_connection);
+ &iter, relay_connection, sessions_ht);
if (last_seen_data_fd == pollfd) {
last_seen_data_fd = last_notdel_data_fd;
}
DBG("Socket %d hung up", pollfd);
relay_cleanup_poll_connection(&events, pollfd);
relay_del_connection(relay_connections_ht,
- streams_ht, &iter,
- relay_connection);
+ &iter, relay_connection, sessions_ht);
if (last_seen_data_fd == pollfd) {
last_seen_data_fd = last_notdel_data_fd;
}
if (ret <= 0) {
relay_cleanup_poll_connection(&events, pollfd);
relay_del_connection(relay_connections_ht,
- streams_ht, &iter,
- relay_connection);
+ &iter, relay_connection, sessions_ht);
DBG("Control connection closed with %d", pollfd);
} else {
if (relay_connection->session) {
relay_connection->session->id);
}
ret = relay_process_control(&recv_hdr,
- relay_connection,
- streams_ht);
+ relay_connection, relay_ctx);
if (ret < 0) {
/* Clear the session on error. */
relay_cleanup_poll_connection(&events, pollfd);
relay_del_connection(relay_connections_ht,
- streams_ht, &iter,
- relay_connection);
+ &iter, relay_connection, sessions_ht);
DBG("Connection closed with %d", pollfd);
}
seen_control = 1;
if (last_seen_data_fd >= 0) {
for (i = 0; i < nb_fd; i++) {
int pollfd = LTTNG_POLL_GETFD(&events, i);
+
+ health_code_update();
+
if (last_seen_data_fd == pollfd) {
idx = i;
break;
uint32_t revents = LTTNG_POLL_GETEV(&events, i);
int pollfd = LTTNG_POLL_GETFD(&events, i);
+ health_code_update();
+
/* Skip the command pipe. It's handled in the first loop. */
if (pollfd == relay_cmd_pipe[0]) {
continue;
continue;
}
- ret = relay_process_data(relay_connection, streams_ht);
+ ret = relay_process_data(relay_connection);
/* connection closed */
if (ret < 0) {
relay_cleanup_poll_connection(&events, pollfd);
relay_del_connection(relay_connections_ht,
- streams_ht, &iter,
- relay_connection);
+ &iter, relay_connection, sessions_ht);
DBG("Data connection closed with %d", pollfd);
/*
* Every goto restart call sets the last seen fd where
last_seen_data_fd = -1;
}
+ /* Normal exit, no error */
+ ret = 0;
+
exit:
error:
lttng_poll_clean(&events);
/* empty the hash table and free the memory */
rcu_read_lock();
cds_lfht_for_each_entry(relay_connections_ht->ht, &iter.iter, node, node) {
+ health_code_update();
+
node = lttng_ht_iter_get_node_ulong(&iter);
if (node) {
relay_connection = caa_container_of(node,
struct relay_command, sock_n);
relay_del_connection(relay_connections_ht,
- streams_ht, &iter,
- relay_connection);
+ &iter, relay_connection, sessions_ht);
}
}
rcu_read_unlock();
error_poll_create:
- lttng_ht_destroy(streams_ht);
-streams_ht_error:
+ lttng_ht_destroy(indexes_ht);
+indexes_ht_error:
lttng_ht_destroy(relay_connections_ht);
relay_connections_ht_error:
/* Close relay cmd pipes */
}
DBG("Worker thread cleanup complete");
free(data_buffer);
- stop_threads();
+ if (err) {
+ health_error();
+ ERR("Health error occurred in %s", __func__);
+ }
+ health_unregister(health_relayd);
rcu_unregister_thread();
+ stop_threads();
return NULL;
}
{
int ret = 0;
void *status;
+ struct relay_local_data *relay_ctx;
/* Create thread quit pipe */
if ((ret = init_thread_quit_pipe()) < 0) {
}
}
- /* Check if daemon is UID = 0 */
- is_root = !getuid();
+ /* We need those values for the file/dir creation. */
+ relayd_uid = getuid();
+ relayd_gid = getgid();
- if (!is_root) {
+ /* Check if daemon is UID = 0 */
+ if (relayd_uid == 0) {
if (control_uri->port < 1024 || data_uri->port < 1024) {
ERR("Need to be root to use ports < 1024");
ret = -1;
/* Set up max poll set size */
lttng_poll_set_max_size();
+ /* Initialize communication library */
+ lttcomm_init();
+
+ relay_ctx = zmalloc(sizeof(struct relay_local_data));
+ if (!relay_ctx) {
+ PERROR("relay_ctx");
+ goto exit;
+ }
+
+ /* tables of sessions indexed by session ID */
+ relay_ctx->sessions_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ if (!relay_ctx->sessions_ht) {
+ goto exit_relay_ctx_sessions;
+ }
+
+ /* tables of streams indexed by stream ID */
+ relay_streams_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ if (!relay_streams_ht) {
+ goto exit_relay_ctx_streams;
+ }
+
+ /* tables of streams indexed by stream ID */
+ viewer_streams_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ if (!viewer_streams_ht) {
+ goto exit_relay_ctx_viewer_streams;
+ }
+
+ /* Initialize thread health monitoring */
+ health_relayd = health_app_create(NR_HEALTH_RELAYD_TYPES);
+ if (!health_relayd) {
+ PERROR("health_app_create error");
+ goto exit_health_app_create;
+ }
+
+ ret = utils_create_pipe(health_quit_pipe);
+ if (ret < 0) {
+ goto error_health_pipe;
+ }
+
+ /* Create thread to manage the client socket */
+ ret = pthread_create(&health_thread, NULL,
+ thread_manage_health, (void *) NULL);
+ if (ret != 0) {
+ PERROR("pthread_create health");
+ goto health_error;
+ }
+
/* Setup the dispatcher thread */
ret = pthread_create(&dispatcher_thread, NULL,
relay_thread_dispatcher, (void *) NULL);
/* Setup the worker thread */
ret = pthread_create(&worker_thread, NULL,
- relay_thread_worker, (void *) NULL);
+ relay_thread_worker, (void *) relay_ctx);
if (ret != 0) {
PERROR("pthread_create worker");
goto exit_worker;
goto exit_listener;
}
-exit_listener:
+ ret = live_start_threads(live_uri, relay_ctx, thread_quit_pipe);
+ if (ret != 0) {
+ ERR("Starting live viewer threads");
+ goto exit_live;
+ }
+
+exit_live:
ret = pthread_join(listener_thread, &status);
if (ret != 0) {
PERROR("pthread_join");
goto error; /* join error, exit without cleanup */
}
-exit_worker:
+exit_listener:
ret = pthread_join(worker_thread, &status);
if (ret != 0) {
PERROR("pthread_join");
goto error; /* join error, exit without cleanup */
}
-exit_dispatcher:
+exit_worker:
ret = pthread_join(dispatcher_thread, &status);
if (ret != 0) {
PERROR("pthread_join");
goto error; /* join error, exit without cleanup */
}
+exit_dispatcher:
+ ret = pthread_join(health_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join health thread");
+ goto error; /* join error, exit without cleanup */
+ }
+
+ /*
+ * Stop live threads only after joining other threads.
+ */
+ live_stop_threads();
+
+health_error:
+ utils_close_pipe(health_quit_pipe);
+
+error_health_pipe:
+ health_app_destroy(health_relayd);
+
+exit_health_app_create:
+ lttng_ht_destroy(viewer_streams_ht);
+
+exit_relay_ctx_viewer_streams:
+ lttng_ht_destroy(relay_streams_ht);
+
+exit_relay_ctx_streams:
+ lttng_ht_destroy(relay_ctx->sessions_ht);
+
+exit_relay_ctx_sessions:
+ free(relay_ctx);
+
exit:
cleanup();
if (!ret) {
fd-limit.c fd-limit.h \
kernel-consumer.c kernel-consumer.h \
consumer.h \
- health.c health.h \
+ health-sessiond.h \
cmd.c cmd.h \
buffer-registry.c buffer-registry.h \
testpoint.h ht-cleanup.c \
- snapshot.c snapshot.h
+ snapshot.c snapshot.h \
+ jul.c jul.h
if HAVE_LIBLTTNG_UST_CTL
lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \
ust-consumer.c ust-consumer.h ust-thread.c \
- ust-metadata.c ust-clock.h
+ ust-metadata.c ust-clock.h jul-thread.c jul-thread.h
endif
# Add main.c at the end for compile order
$(top_builddir)/src/common/libcommon.la \
$(top_builddir)/src/common/compat/libcompat.la \
$(top_builddir)/src/common/relayd/librelayd.la \
- $(top_builddir)/src/common/testpoint/libtestpoint.la
+ $(top_builddir)/src/common/testpoint/libtestpoint.la \
+ $(top_builddir)/src/common/health/libhealth.la
if HAVE_LIBLTTNG_UST_CTL
lttng_sessiond_LDADD += -llttng-ust-ctl
pthread_mutex_lock(&channel->stream_list_lock);
cds_list_add_tail(&stream->lnode, &channel->streams);
+ channel->stream_count++;
pthread_mutex_unlock(&channel->stream_list_lock);
}
/* Wipe stream */
cds_list_for_each_entry_safe(sreg, stmp, ®p->streams, lnode) {
cds_list_del(&sreg->lnode);
+ regp->stream_count--;
buffer_reg_stream_destroy(sreg, domain);
}
uint64_t consumer_key;
/* Stream registry object of this channel registry. */
struct cds_list_head streams;
+ /* Total number of stream in the list. */
+ uint64_t stream_count;
/* Used to ensure mutual exclusion to the stream's list. */
pthread_mutex_t stream_list_lock;
/* Node for hash table usage. */
struct lttng_ht_node_u64 node;
+ /* Size of subbuffers in this channel. */
+ size_t subbuf_size;
union {
/* Original object data that MUST be copied over. */
struct lttng_ust_object_data *ust;
chan->attr.output = DEFAULT_KERNEL_CHANNEL_OUTPUT;
chan->attr.switch_timer_interval = DEFAULT_KERNEL_CHANNEL_SWITCH_TIMER;
chan->attr.read_timer_interval = DEFAULT_KERNEL_CHANNEL_READ_TIMER;
+ chan->attr.live_timer_interval = DEFAULT_KERNEL_CHANNEL_LIVE_TIMER;
break;
case LTTNG_DOMAIN_UST:
switch (type) {
DEFAULT_UST_UID_CHANNEL_SWITCH_TIMER;
chan->attr.read_timer_interval =
DEFAULT_UST_UID_CHANNEL_READ_TIMER;
+ chan->attr.live_timer_interval =
+ DEFAULT_UST_UID_CHANNEL_LIVE_TIMER;
break;
case LTTNG_BUFFER_PER_PID:
default:
DEFAULT_UST_PID_CHANNEL_SWITCH_TIMER;
chan->attr.read_timer_interval =
DEFAULT_UST_PID_CHANNEL_READ_TIMER;
+ chan->attr.live_timer_interval =
+ DEFAULT_UST_UID_CHANNEL_LIVE_TIMER;
break;
}
break;
attr = defattr;
}
+ if (ksession->snapshot_mode) {
+ /* Force channel attribute for snapshot mode. */
+ attr->attr.overwrite = 1;
+ attr->attr.output = LTTNG_EVENT_MMAP;
+ }
+
/* Channel not found, creating it */
ret = kernel_create_channel(ksession, attr);
if (ret < 0) {
attr = defattr;
}
+ if (usess->snapshot_mode) {
+ /* Force channel attribute for snapshot mode. */
+ attr->attr.overwrite = 1;
+ attr->attr.output = LTTNG_EVENT_MMAP;
+ }
+
/*
* Validate UST buffer size and number of buffers: must both be power of 2
* and nonzero. We validate right here for UST, because applications will
#include "channel.h"
#include "consumer.h"
#include "event.h"
-#include "health.h"
+#include "health-sessiond.h"
#include "kernel.h"
#include "kernel-consumer.h"
#include "lttng-sessiond.h"
}
}
+/*
+ * Create a list of JUL domain events.
+ *
+ * Return number of events in list on success or else a negative value.
+ */
+static int list_lttng_jul_events(struct jul_domain *dom,
+ struct lttng_event **events)
+{
+ int i = 0, ret = 0;
+ unsigned int nb_event = 0;
+ struct jul_event *event;
+ struct lttng_event *tmp_events;
+ struct lttng_ht_iter iter;
+
+ assert(dom);
+ assert(events);
+
+ DBG3("Listing JUL events");
+
+ nb_event = lttng_ht_get_count(dom->events);
+ if (nb_event == 0) {
+ ret = nb_event;
+ goto error;
+ }
+
+ tmp_events = zmalloc(nb_event * sizeof(*tmp_events));
+ if (!tmp_events) {
+ PERROR("zmalloc JUL events session");
+ ret = -LTTNG_ERR_FATAL;
+ goto error;
+ }
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(dom->events->ht, &iter.iter, event, node.node) {
+ strncpy(tmp_events[i].name, event->name, sizeof(tmp_events[i].name));
+ tmp_events[i].name[sizeof(tmp_events[i].name) - 1] = '\0';
+ tmp_events[i].enabled = event->enabled;
+ i++;
+ }
+ rcu_read_unlock();
+
+ *events = tmp_events;
+ ret = nb_event;
+
+error:
+ assert(nb_event == i);
+ return ret;
+}
+
/*
* Create a list of ust global domain events.
*/
if (session->consumer_fds_sent == 0 && session->consumer != NULL) {
cds_lfht_for_each_entry(session->consumer->socks->ht, &iter.iter,
socket, node.node) {
- /* Code flow error */
- assert(socket->fd >= 0);
-
pthread_mutex_lock(socket->lock);
ret = kernel_consumer_send_session(socket, session);
pthread_mutex_unlock(socket->lock);
*/
static int send_consumer_relayd_socket(int domain, unsigned int session_id,
struct lttng_uri *relayd_uri, struct consumer_output *consumer,
- struct consumer_socket *consumer_sock)
+ struct consumer_socket *consumer_sock,
+ char *session_name, char *hostname, int session_live_timer)
{
int ret;
struct lttcomm_relayd_sock *rsock = NULL;
/* Send relayd socket to consumer. */
ret = consumer_send_relayd_socket(consumer_sock, rsock, consumer,
- relayd_uri->stype, session_id);
+ relayd_uri->stype, session_id,
+ session_name, hostname, session_live_timer);
if (ret < 0) {
ret = LTTNG_ERR_ENABLE_CONSUMER_FAIL;
goto close_sock;
* session.
*/
static int send_consumer_relayd_sockets(int domain, unsigned int session_id,
- struct consumer_output *consumer, struct consumer_socket *sock)
+ struct consumer_output *consumer, struct consumer_socket *sock,
+ char *session_name, char *hostname, int session_live_timer)
{
int ret = LTTNG_OK;
/* Sending control relayd socket. */
if (!sock->control_sock_sent) {
ret = send_consumer_relayd_socket(domain, session_id,
- &consumer->dst.net.control, consumer, sock);
+ &consumer->dst.net.control, consumer, sock,
+ session_name, hostname, session_live_timer);
if (ret != LTTNG_OK) {
goto error;
}
/* Sending data relayd socket. */
if (!sock->data_sock_sent) {
ret = send_consumer_relayd_socket(domain, session_id,
- &consumer->dst.net.data, consumer, sock);
+ &consumer->dst.net.data, consumer, sock,
+ session_name, hostname, session_live_timer);
if (ret != LTTNG_OK) {
goto error;
}
/* For each consumer socket, send relayd sockets */
cds_lfht_for_each_entry(usess->consumer->socks->ht, &iter.iter,
socket, node.node) {
- /* Code flow error */
- assert(socket->fd >= 0);
-
pthread_mutex_lock(socket->lock);
ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_UST, session->id,
- usess->consumer, socket);
+ usess->consumer, socket,
+ session->name, session->hostname,
+ session->live_timer);
pthread_mutex_unlock(socket->lock);
if (ret != LTTNG_OK) {
goto error;
&& ksess->consumer->enabled) {
cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter,
socket, node.node) {
- /* Code flow error */
- assert(socket->fd >= 0);
-
pthread_mutex_lock(socket->lock);
ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_KERNEL, session->id,
- ksess->consumer, socket);
+ ksess->consumer, socket,
+ session->name, session->hostname,
+ session->live_timer);
pthread_mutex_unlock(socket->lock);
if (ret != LTTNG_OK) {
goto error;
DBG("Enabling channel %s for session %s", attr->name, session->name);
+ rcu_read_lock();
+
/*
* Don't try to enable a channel if the session has been started at
* some point in time before. The tracer does not allow it.
goto error;
}
- rcu_read_lock();
+ /*
+ * If the session is a live session, remove the switch timer, the
+ * live timer does the same thing but sends also synchronisation
+ * beacons for inactive streams.
+ */
+ if (session->live_timer > 0) {
+ attr->attr.live_timer_interval = session->live_timer;
+ attr->attr.switch_timer_interval = 0;
+ }
switch (domain->type) {
case LTTNG_DOMAIN_KERNEL:
kchan = trace_kernel_get_channel_by_name(attr->name,
session->kernel_session);
if (kchan == NULL) {
+ /*
+ * Don't try to create a channel if the session
+ * has been started at some point in time
+ * before. The tracer does not allow it.
+ */
+ if (session->started) {
+ ret = LTTNG_ERR_TRACE_ALREADY_STARTED;
+ goto error;
+ }
ret = channel_kernel_create(session->kernel_session, attr, wpipe);
+ if (attr->name[0] != '\0') {
+ session->kernel_session->has_non_default_channel = 1;
+ }
} else {
ret = channel_kernel_enable(session->kernel_session, kchan);
}
uchan = trace_ust_find_channel_by_name(chan_ht, attr->name);
if (uchan == NULL) {
+ /*
+ * Don't try to create a channel if the session
+ * has been started at some point in time
+ * before. The tracer does not allow it.
+ */
+ if (session->started) {
+ ret = LTTNG_ERR_TRACE_ALREADY_STARTED;
+ goto error;
+ }
ret = channel_ust_create(usess, attr, domain->buf_type);
+ if (attr->name[0] != '\0') {
+ usess->has_non_default_channel = 1;
+ }
} else {
ret = channel_ust_enable(usess, uchan);
}
ksess = session->kernel_session;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (ksess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
kchan = trace_kernel_get_channel_by_name(channel_name, ksess);
if (kchan == NULL) {
ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND;
usess = session->ust_session;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
channel_name);
if (uchan == NULL) {
channel_name);
break;
}
+ case LTTNG_DOMAIN_JUL:
+ {
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(usess);
+
+ ret = event_jul_disable(usess, event_name);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ break;
+ }
#if 0
case LTTNG_DOMAIN_UST_EXEC_NAME:
case LTTNG_DOMAIN_UST_PID:
ksess = session->kernel_session;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (ksess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
kchan = trace_kernel_get_channel_by_name(channel_name, ksess);
if (kchan == NULL) {
ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND;
usess = session->ust_session;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
channel_name);
if (uchan == NULL) {
DBG3("Disable all UST events in channel %s completed", channel_name);
+ break;
+ }
+ case LTTNG_DOMAIN_JUL:
+ {
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(usess);
+
+ ret = event_jul_disable_all(usess);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
break;
}
#if 0
}
chan_kern_created = 1;
}
-
/* Add kernel context to kernel tracer */
ret = context_kernel_add(session->kernel_session, ctx, channel_name);
if (ret != LTTNG_OK) {
case LTTNG_DOMAIN_UST:
{
struct ltt_ust_session *usess = session->ust_session;
+ unsigned int chan_count;
+
assert(usess);
- unsigned int chan_count =
- lttng_ht_get_count(usess->domain_global.channels);
+ chan_count = lttng_ht_get_count(usess->domain_global.channels);
if (chan_count == 0) {
struct lttng_channel *attr;
/* Create default channel */
{
struct ltt_kernel_channel *kchan;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (session->kernel_session->has_non_default_channel
+ && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
kchan = trace_kernel_get_channel_by_name(channel_name,
session->kernel_session);
if (kchan == NULL) {
assert(usess);
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
/* Get channel from global UST domain */
uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
channel_name);
}
break;
}
+ case LTTNG_DOMAIN_JUL:
+ {
+ struct lttng_event uevent;
+ struct lttng_domain tmp_dom;
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(usess);
+
+ /* Create the default JUL tracepoint. */
+ uevent.type = LTTNG_EVENT_TRACEPOINT;
+ uevent.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
+ strncpy(uevent.name, DEFAULT_JUL_EVENT_NAME, sizeof(uevent.name));
+ uevent.name[sizeof(uevent.name) - 1] = '\0';
+
+ /*
+ * The domain type is changed because we are about to enable the
+ * default channel and event for the JUL domain that are hardcoded.
+ * This happens in the UST domain.
+ */
+ memcpy(&tmp_dom, domain, sizeof(tmp_dom));
+ tmp_dom.type = LTTNG_DOMAIN_UST;
+
+ ret = cmd_enable_event(session, &tmp_dom, DEFAULT_JUL_CHANNEL_NAME,
+ &uevent, NULL, wpipe);
+ if (ret != LTTNG_OK && ret != LTTNG_ERR_UST_EVENT_ENABLED) {
+ goto error;
+ }
+
+ /* The wild card * means that everything should be enabled. */
+ if (strncmp(event->name, "*", 1) == 0 && strlen(event->name) == 1) {
+ ret = event_jul_enable_all(usess);
+ } else {
+ ret = event_jul_enable(usess, event);
+ }
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ break;
+ }
#if 0
case LTTNG_DOMAIN_UST_EXEC_NAME:
case LTTNG_DOMAIN_UST_PID:
assert(session->kernel_session);
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (session->kernel_session->has_non_default_channel
+ && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
kchan = trace_kernel_get_channel_by_name(channel_name,
session->kernel_session);
if (kchan == NULL) {
assert(usess);
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
/* Get channel from global UST domain */
uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
channel_name);
goto error;
}
+ break;
+ }
+ case LTTNG_DOMAIN_JUL:
+ {
+ struct lttng_event uevent;
+ struct lttng_domain tmp_dom;
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(usess);
+
+ /* Create the default JUL tracepoint. */
+ uevent.type = LTTNG_EVENT_TRACEPOINT;
+ uevent.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
+ strncpy(uevent.name, DEFAULT_JUL_EVENT_NAME, sizeof(uevent.name));
+ uevent.name[sizeof(uevent.name) - 1] = '\0';
+
+ /*
+ * The domain type is changed because we are about to enable the
+ * default channel and event for the JUL domain that are hardcoded.
+ * This happens in the UST domain.
+ */
+ memcpy(&tmp_dom, domain, sizeof(tmp_dom));
+ tmp_dom.type = LTTNG_DOMAIN_UST;
+
+ ret = cmd_enable_event(session, &tmp_dom, DEFAULT_JUL_CHANNEL_NAME,
+ &uevent, NULL, wpipe);
+ if (ret != LTTNG_OK && ret != LTTNG_ERR_UST_EVENT_ENABLED) {
+ goto error;
+ }
+
+ ret = event_jul_enable_all(usess);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
break;
}
#if 0
goto error;
}
break;
+ case LTTNG_DOMAIN_JUL:
+ nb_events = jul_list_events(events);
+ if (nb_events < 0) {
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+ goto error;
+ }
+ break;
default:
ret = LTTNG_ERR_UND;
goto error;
* Command LTTNG_CREATE_SESSION processed by the client thread.
*/
int cmd_create_session_uri(char *name, struct lttng_uri *uris,
- size_t nb_uri, lttng_sock_cred *creds)
+ size_t nb_uri, lttng_sock_cred *creds, unsigned int live_timer)
{
int ret;
struct ltt_session *session;
session = session_find_by_name(name);
assert(session);
+ session->live_timer = live_timer;
/* Create default consumer output for the session not yet created. */
session->consumer = consumer_create_output(CONSUMER_DST_LOCAL);
if (session->consumer == NULL) {
return ret;
}
+/*
+ * Command LTTNG_CREATE_SESSION_SNAPSHOT processed by the client thread.
+ */
+int cmd_create_session_snapshot(char *name, struct lttng_uri *uris,
+ size_t nb_uri, lttng_sock_cred *creds)
+{
+ int ret;
+ struct ltt_session *session;
+ struct snapshot_output *new_output = NULL;
+
+ assert(name);
+ assert(creds);
+
+ /*
+ * Create session in no output mode with URIs set to NULL. The uris we've
+ * received are for a default snapshot output if one.
+ */
+ ret = cmd_create_session_uri(name, NULL, 0, creds, -1);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* Get the newly created session pointer back. This should NEVER fail. */
+ session = session_find_by_name(name);
+ assert(session);
+
+ /* Flag session for snapshot mode. */
+ session->snapshot_mode = 1;
+
+ /* Skip snapshot output creation if no URI is given. */
+ if (nb_uri == 0) {
+ goto end;
+ }
+
+ new_output = snapshot_output_alloc();
+ if (!new_output) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error_snapshot_alloc;
+ }
+
+ ret = snapshot_output_init_with_uri(DEFAULT_SNAPSHOT_MAX_SIZE, NULL,
+ uris, nb_uri, session->consumer, new_output, &session->snapshot);
+ if (ret < 0) {
+ if (ret == -ENOMEM) {
+ ret = LTTNG_ERR_NOMEM;
+ } else {
+ ret = LTTNG_ERR_INVALID;
+ }
+ goto error_snapshot;
+ }
+
+ rcu_read_lock();
+ snapshot_add_output(&session->snapshot, new_output);
+ rcu_read_unlock();
+
+end:
+ return LTTNG_OK;
+
+error_snapshot:
+ snapshot_output_destroy(new_output);
+error_snapshot_alloc:
+ session_destroy(session);
+error:
+ return ret;
+}
+
/*
* Command LTTNG_DESTROY_SESSION processed by the client thread.
*/
ret = LTTNG_ERR_CONNECT_FAIL;
goto error;
}
+ cdata->cmd_sock = sock;
- socket = consumer_allocate_socket(sock);
+ socket = consumer_allocate_socket(&cdata->cmd_sock);
if (socket == NULL) {
ret = close(sock);
if (ret < 0) {
PERROR("close register consumer");
}
+ cdata->cmd_sock = -1;
ret = LTTNG_ERR_FATAL;
goto error;
}
if (session->ust_session != NULL) {
DBG3("Listing domains found UST global domain");
nb_dom++;
+
+ if (session->ust_session->domain_jul.being_used) {
+ nb_dom++;
+ }
}
*domains = zmalloc(nb_dom * sizeof(struct lttng_domain));
(*domains)[index].type = LTTNG_DOMAIN_UST;
(*domains)[index].buf_type = session->ust_session->buffer_type;
index++;
+
+ if (session->ust_session->domain_jul.being_used) {
+ (*domains)[index].type = LTTNG_DOMAIN_JUL;
+ (*domains)[index].buf_type = session->ust_session->buffer_type;
+ index++;
+ }
}
return nb_dom;
}
break;
}
+ case LTTNG_DOMAIN_JUL:
+ if (session->ust_session) {
+ nb_event = list_lttng_jul_events(
+ &session->ust_session->domain_jul, events);
+ }
+ break;
default:
ret = LTTNG_ERR_UND;
goto error;
strncpy(sessions[i].name, session->name, NAME_MAX);
sessions[i].name[NAME_MAX - 1] = '\0';
sessions[i].enabled = session->enabled;
+ sessions[i].snapshot_mode = session->snapshot_mode;
i++;
}
}
/*
* Command LTTNG_DATA_PENDING returning 0 if the data is NOT pending meaning
- * ready for trace analysis (or anykind of reader) or else 1 for pending data.
+ * ready for trace analysis (or any kind of reader) or else 1 for pending data.
*/
int cmd_data_pending(struct ltt_session *session)
{
DBG("Cmd snapshot add output for session %s", session->name);
/*
- * Persmission denied to create an output if the session is not set in no
- * output mode.
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
*/
if (session->output_traces) {
ret = LTTNG_ERR_EPERM;
goto free_error;
}
- /*
- * Copy sockets so the snapshot output can use them on destroy.
- */
-
- if (session->ust_session) {
- ret = consumer_copy_sockets(new_output->consumer,
- session->ust_session->consumer);
- if (ret < 0) {
- goto free_error;
- }
- new_output->ust_sockets_copied = 1;
- }
- if (session->kernel_session) {
- ret = consumer_copy_sockets(new_output->consumer,
- session->kernel_session->consumer);
- if (ret < 0) {
- goto free_error;
- }
- new_output->kernel_sockets_copied = 1;
- }
-
rcu_read_lock();
snapshot_add_output(&session->snapshot, new_output);
if (id) {
struct lttng_snapshot_output *output)
{
int ret;
- struct snapshot_output *sout;
+ struct snapshot_output *sout = NULL;
assert(session);
assert(output);
- DBG("Cmd snapshot del output id %" PRIu32 " for session %s", output->id,
- session->name);
-
rcu_read_lock();
/*
- * Persmission denied to create an output if the session is not set in no
- * output mode.
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
*/
if (session->output_traces) {
ret = LTTNG_ERR_EPERM;
goto error;
}
- sout = snapshot_find_output_by_id(output->id, &session->snapshot);
+ if (output->id) {
+ DBG("Cmd snapshot del output id %" PRIu32 " for session %s", output->id,
+ session->name);
+ sout = snapshot_find_output_by_id(output->id, &session->snapshot);
+ } else if (*output->name != '\0') {
+ DBG("Cmd snapshot del output name %s for session %s", output->name,
+ session->name);
+ sout = snapshot_find_output_by_name(output->name, &session->snapshot);
+ }
if (!sout) {
ret = LTTNG_ERR_INVALID;
goto error;
DBG("Cmd snapshot list outputs for session %s", session->name);
/*
- * Persmission denied to create an output if the session is not set in no
- * output mode.
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
*/
if (session->output_traces) {
ret = LTTNG_ERR_EPERM;
* Send relayd sockets from snapshot output to consumer. Ignore request if the
* snapshot output is *not* set with a remote destination.
*
- * Return 0 on success or else a negative value.
+ * Return 0 on success or a LTTNG_ERR code.
*/
static int set_relayd_for_snapshot(struct consumer_output *consumer,
struct snapshot_output *snap_output, struct ltt_session *session)
{
- int ret = 0;
+ int ret = LTTNG_OK;
struct lttng_ht_iter iter;
struct consumer_socket *socket;
cds_lfht_for_each_entry(snap_output->consumer->socks->ht, &iter.iter,
socket, node.node) {
ret = send_consumer_relayd_sockets(0, session->id,
- snap_output->consumer, socket);
- if (ret < 0) {
+ snap_output->consumer, socket,
+ session->name, session->hostname,
+ session->live_timer);
+ if (ret != LTTNG_OK) {
rcu_read_unlock();
goto error;
}
/*
* Record a kernel snapshot.
*
- * Return 0 on success or else a negative value.
+ * Return 0 on success or a LTTNG_ERR code.
*/
static int record_kernel_snapshot(struct ltt_kernel_session *ksess,
- struct snapshot_output *output, struct ltt_session *session, int wait)
+ struct snapshot_output *output, struct ltt_session *session,
+ int wait, int nb_streams)
{
int ret;
ret = utils_get_current_time_str("%Y%m%d-%H%M%S", output->datetime,
sizeof(output->datetime));
if (!ret) {
- ret = -EINVAL;
+ ret = LTTNG_ERR_INVALID;
goto error;
}
- if (!output->kernel_sockets_copied) {
- ret = consumer_copy_sockets(output->consumer, ksess->consumer);
- if (ret < 0) {
- goto error;
- }
- output->kernel_sockets_copied = 1;
+ /*
+ * Copy kernel session sockets so we can communicate with the right
+ * consumer for the snapshot record command.
+ */
+ ret = consumer_copy_sockets(output->consumer, ksess->consumer);
+ if (ret < 0) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
}
ret = set_relayd_for_snapshot(ksess->consumer, output, session);
- if (ret < 0) {
- goto error;
+ if (ret != LTTNG_OK) {
+ goto error_snapshot;
}
- ret = kernel_snapshot_record(ksess, output, wait);
- if (ret < 0) {
- goto error;
+ ret = kernel_snapshot_record(ksess, output, wait, nb_streams);
+ if (ret != LTTNG_OK) {
+ goto error_snapshot;
}
+ ret = LTTNG_OK;
+
+error_snapshot:
+ /* Clean up copied sockets so this output can use some other later on. */
+ consumer_destroy_output_sockets(output->consumer);
error:
return ret;
}
/*
* Record a UST snapshot.
*
- * Return 0 on success or else a negative value.
+ * Return 0 on success or a LTTNG_ERR error code.
*/
static int record_ust_snapshot(struct ltt_ust_session *usess,
- struct snapshot_output *output, struct ltt_session *session, int wait)
+ struct snapshot_output *output, struct ltt_session *session,
+ int wait, int nb_streams)
{
int ret;
ret = utils_get_current_time_str("%Y%m%d-%H%M%S", output->datetime,
sizeof(output->datetime));
if (!ret) {
- ret = -EINVAL;
+ ret = LTTNG_ERR_INVALID;
goto error;
}
- if (!output->ust_sockets_copied) {
- ret = consumer_copy_sockets(output->consumer, usess->consumer);
- if (ret < 0) {
- goto error;
- }
- output->ust_sockets_copied = 1;
+ /*
+ * Copy UST session sockets so we can communicate with the right
+ * consumer for the snapshot record command.
+ */
+ ret = consumer_copy_sockets(output->consumer, usess->consumer);
+ if (ret < 0) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
}
ret = set_relayd_for_snapshot(usess->consumer, output, session);
- if (ret < 0) {
- goto error;
+ if (ret != LTTNG_OK) {
+ goto error_snapshot;
}
- ret = ust_app_snapshot_record(usess, output, wait);
+ ret = ust_app_snapshot_record(usess, output, wait, nb_streams);
if (ret < 0) {
- goto error;
+ if (ret == -EINVAL) {
+ ret = LTTNG_ERR_INVALID;
+ goto error_snapshot;
+ }
+
+ ret = LTTNG_ERR_SNAPSHOT_FAIL;
+ goto error_snapshot;
}
+ ret = LTTNG_OK;
+
+error_snapshot:
+ /* Clean up copied sockets so this output can use some other later on. */
+ consumer_destroy_output_sockets(output->consumer);
error:
return ret;
}
+/*
+ * Returns the total number of streams for a session or a negative value
+ * on error.
+ */
+static unsigned int get_total_nb_stream(struct ltt_session *session)
+{
+ unsigned int total_streams = 0;
+
+ if (session->kernel_session) {
+ struct ltt_kernel_session *ksess = session->kernel_session;
+
+ total_streams += ksess->stream_count_global;
+ }
+
+ if (session->ust_session) {
+ struct ltt_ust_session *usess = session->ust_session;
+
+ total_streams += ust_app_get_nb_stream(usess);
+ }
+
+ return total_streams;
+}
+
/*
* Command LTTNG_SNAPSHOT_RECORD from lib lttng ctl.
*
struct lttng_snapshot_output *output, int wait)
{
int ret = LTTNG_OK;
- struct snapshot_output *tmp_sout = NULL;
+ unsigned int use_tmp_output = 0;
+ struct snapshot_output tmp_output;
+ unsigned int nb_streams, snapshot_success = 0;
assert(session);
DBG("Cmd snapshot record for session %s", session->name);
/*
- * Persmission denied to create an output if the session is not set in no
- * output mode.
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
*/
if (session->output_traces) {
ret = LTTNG_ERR_EPERM;
/* Use temporary output for the session. */
if (output && *output->ctrl_url != '\0') {
- tmp_sout = snapshot_output_alloc();
- if (!tmp_sout) {
- ret = LTTNG_ERR_NOMEM;
- goto error;
- }
-
ret = snapshot_output_init(output->max_size, output->name,
output->ctrl_url, output->data_url, session->consumer,
- tmp_sout, NULL);
+ &tmp_output, NULL);
if (ret < 0) {
if (ret == -ENOMEM) {
ret = LTTNG_ERR_NOMEM;
}
goto error;
}
+ /* Use the global session count for the temporary snapshot. */
+ tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
+ use_tmp_output = 1;
}
+ /*
+ * Get the total number of stream of that session which is used by the
+ * maximum size of the snapshot feature.
+ */
+ nb_streams = get_total_nb_stream(session);
+
if (session->kernel_session) {
struct ltt_kernel_session *ksess = session->kernel_session;
- if (tmp_sout) {
- ret = record_kernel_snapshot(ksess, tmp_sout, session, wait);
- if (ret < 0) {
+ if (use_tmp_output) {
+ ret = record_kernel_snapshot(ksess, &tmp_output, session,
+ wait, nb_streams);
+ if (ret != LTTNG_OK) {
goto error;
}
+ snapshot_success = 1;
} else {
struct snapshot_output *sout;
struct lttng_ht_iter iter;
rcu_read_lock();
cds_lfht_for_each_entry(session->snapshot.output_ht->ht,
&iter.iter, sout, node.node) {
- ret = record_kernel_snapshot(ksess, sout, session, wait);
- if (ret < 0) {
+ /*
+ * Make a local copy of the output and assign the possible
+ * temporary value given by the caller.
+ */
+ memset(&tmp_output, 0, sizeof(tmp_output));
+ memcpy(&tmp_output, sout, sizeof(tmp_output));
+
+ /* Use temporary max size. */
+ if (output->max_size != (uint64_t) -1ULL) {
+ tmp_output.max_size = output->max_size;
+ }
+
+ /* Use temporary name. */
+ if (*output->name != '\0') {
+ strncpy(tmp_output.name, output->name,
+ sizeof(tmp_output.name));
+ }
+
+ tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
+
+ ret = record_kernel_snapshot(ksess, &tmp_output,
+ session, wait, nb_streams);
+ if (ret != LTTNG_OK) {
rcu_read_unlock();
goto error;
}
+ snapshot_success = 1;
}
rcu_read_unlock();
}
if (session->ust_session) {
struct ltt_ust_session *usess = session->ust_session;
- if (tmp_sout) {
- ret = record_ust_snapshot(usess, tmp_sout, session, wait);
- if (ret < 0) {
+ if (use_tmp_output) {
+ ret = record_ust_snapshot(usess, &tmp_output, session,
+ wait, nb_streams);
+ if (ret != LTTNG_OK) {
goto error;
}
+ snapshot_success = 1;
} else {
struct snapshot_output *sout;
struct lttng_ht_iter iter;
rcu_read_lock();
cds_lfht_for_each_entry(session->snapshot.output_ht->ht,
&iter.iter, sout, node.node) {
- ret = record_ust_snapshot(usess, sout, session, wait);
- if (ret < 0) {
+ /*
+ * Make a local copy of the output and assign the possible
+ * temporary value given by the caller.
+ */
+ memset(&tmp_output, 0, sizeof(tmp_output));
+ memcpy(&tmp_output, sout, sizeof(tmp_output));
+
+ /* Use temporary max size. */
+ if (output->max_size != (uint64_t) -1ULL) {
+ tmp_output.max_size = output->max_size;
+ }
+
+ /* Use temporary name. */
+ if (*output->name != '\0') {
+ strncpy(tmp_output.name, output->name,
+ sizeof(tmp_output.name));
+ }
+
+ tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
+
+ ret = record_ust_snapshot(usess, &tmp_output, session,
+ wait, nb_streams);
+ if (ret != LTTNG_OK) {
rcu_read_unlock();
goto error;
}
+ snapshot_success = 1;
}
rcu_read_unlock();
}
}
-error:
- if (tmp_sout) {
- snapshot_output_destroy(tmp_sout);
+ if (snapshot_success) {
+ session->snapshot.nb_snapshot++;
+ } else {
+ ret = LTTNG_ERR_SNAPSHOT_FAIL;
}
+
+error:
return ret;
}
/* Session commands */
int cmd_create_session_uri(char *name, struct lttng_uri *uris,
+ size_t nb_uri, lttng_sock_cred *creds, unsigned int live_timer);
+int cmd_create_session_snapshot(char *name, struct lttng_uri *uris,
size_t nb_uri, lttng_sock_cred *creds);
int cmd_destroy_session(struct ltt_session *session, int wpipe);
#include <common/common.h>
#include <common/defaults.h>
#include <common/uri.h>
+#include <common/relayd/relayd.h>
#include "consumer.h"
-#include "health.h"
+#include "health-sessiond.h"
#include "ust-app.h"
#include "utils.h"
+/*
+ * Send a data payload using a given consumer socket of size len.
+ *
+ * The consumer socket lock MUST be acquired before calling this since this
+ * function can change the fd value.
+ *
+ * Return 0 on success else a negative value on error.
+ */
+int consumer_socket_send(struct consumer_socket *socket, void *msg, size_t len)
+{
+ int fd;
+ ssize_t size;
+
+ assert(socket);
+ assert(socket->fd_ptr);
+ assert(msg);
+
+ /* Consumer socket is invalid. Stopping. */
+ fd = *socket->fd_ptr;
+ if (fd < 0) {
+ goto error;
+ }
+
+ size = lttcomm_send_unix_sock(fd, msg, len);
+ if (size < 0) {
+ /* The above call will print a PERROR on error. */
+ DBG("Error when sending data to consumer on sock %d", fd);
+ /*
+ * At this point, the socket is not usable anymore thus closing it and
+ * setting the file descriptor to -1 so it is not reused.
+ */
+
+ /* This call will PERROR on error. */
+ (void) lttcomm_close_unix_sock(fd);
+ *socket->fd_ptr = -1;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
+/*
+ * Receive a data payload using a given consumer socket of size len.
+ *
+ * The consumer socket lock MUST be acquired before calling this since this
+ * function can change the fd value.
+ *
+ * Return 0 on success else a negative value on error.
+ */
+int consumer_socket_recv(struct consumer_socket *socket, void *msg, size_t len)
+{
+ int fd;
+ ssize_t size;
+
+ assert(socket);
+ assert(socket->fd_ptr);
+ assert(msg);
+
+ /* Consumer socket is invalid. Stopping. */
+ fd = *socket->fd_ptr;
+ if (fd < 0) {
+ goto error;
+ }
+
+ size = lttcomm_recv_unix_sock(fd, msg, len);
+ if (size <= 0) {
+ /* The above call will print a PERROR on error. */
+ DBG("Error when receiving data from the consumer socket %d", fd);
+ /*
+ * At this point, the socket is not usable anymore thus closing it and
+ * setting the file descriptor to -1 so it is not reused.
+ */
+
+ /* This call will PERROR on error. */
+ (void) lttcomm_close_unix_sock(fd);
+ *socket->fd_ptr = -1;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return -1;
+}
+
/*
* Receive a reply command status message from the consumer. Consumer socket
* lock MUST be acquired before calling this function.
assert(sock);
- ret = lttcomm_recv_unix_sock(sock->fd, &reply, sizeof(reply));
- if (ret <= 0) {
- if (ret == 0) {
- /* Orderly shutdown. Don't return 0 which means success. */
- ret = -1;
- }
- /* The above call will print a PERROR on error. */
- DBG("Fail to receive status reply on sock %d", sock->fd);
+ ret = consumer_socket_recv(sock, &reply, sizeof(reply));
+ if (ret < 0) {
goto end;
}
assert(stream_count);
assert(key);
- ret = lttcomm_recv_unix_sock(sock->fd, &reply, sizeof(reply));
- if (ret <= 0) {
- if (ret == 0) {
- /* Orderly shutdown. Don't return 0 which means success. */
- ret = -1;
- }
- /* The above call will print a PERROR on error. */
- DBG("Fail to receive status reply on sock %d", sock->fd);
+ ret = consumer_socket_recv(sock, &reply, sizeof(reply));
+ if (ret < 0) {
goto end;
}
assert(consumer);
assert(sock);
- DBG2("Sending destroy relayd command to consumer sock %d", sock->fd);
-
- /* Bail out if consumer is disabled */
- if (!consumer->enabled) {
- ret = LTTNG_OK;
- DBG3("Consumer is disabled");
- goto error;
- }
+ DBG2("Sending destroy relayd command to consumer sock %d", *sock->fd_ptr);
msg.cmd_type = LTTNG_CONSUMER_DESTROY_RELAYD;
msg.u.destroy_relayd.net_seq_idx = consumer->net_seq_index;
pthread_mutex_lock(sock->lock);
- ret = lttcomm_send_unix_sock(sock->fd, &msg, sizeof(msg));
+ ret = consumer_socket_send(sock, &msg, sizeof(msg));
if (ret < 0) {
- /* Indicate that the consumer is probably closing at this point. */
- DBG("send consumer destroy relayd command");
- goto error_send;
+ goto error;
}
/* Don't check the return value. The caller will do it. */
DBG2("Consumer send destroy relayd command done");
-error_send:
- pthread_mutex_unlock(sock->lock);
error:
+ pthread_mutex_unlock(sock->lock);
return ret;
}
socket = consumer_find_socket(data->cmd_sock, output);
rcu_read_unlock();
if (socket == NULL) {
- socket = consumer_allocate_socket(data->cmd_sock);
+ socket = consumer_allocate_socket(&data->cmd_sock);
if (socket == NULL) {
ret = -1;
goto error;
/*
* Allocate a new consumer_socket and return the pointer.
*/
-struct consumer_socket *consumer_allocate_socket(int fd)
+struct consumer_socket *consumer_allocate_socket(int *fd)
{
struct consumer_socket *socket = NULL;
+ assert(fd);
+
socket = zmalloc(sizeof(struct consumer_socket));
if (socket == NULL) {
PERROR("zmalloc consumer socket");
goto error;
}
- socket->fd = fd;
- lttng_ht_node_init_ulong(&socket->node, fd);
+ socket->fd_ptr = fd;
+ lttng_ht_node_init_ulong(&socket->node, *fd);
error:
return socket;
* consumer was registered,
*/
if (sock->registered) {
- DBG3("Consumer socket was registered. Closing fd %d", sock->fd);
- lttcomm_close_unix_sock(sock->fd);
+ DBG3("Consumer socket was registered. Closing fd %d", *sock->fd_ptr);
+ lttcomm_close_unix_sock(*sock->fd_ptr);
}
call_rcu(&sock->node.head, destroy_socket_rcu);
return output;
}
+/*
+ * Iterate over the consumer output socket hash table and destroy them. The
+ * socket file descriptor are only closed if the consumer output was
+ * registered meaning it's an external consumer.
+ */
+void consumer_destroy_output_sockets(struct consumer_output *obj)
+{
+ struct lttng_ht_iter iter;
+ struct consumer_socket *socket;
+
+ if (!obj->socks) {
+ return;
+ }
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(obj->socks->ht, &iter.iter, socket, node.node) {
+ consumer_del_socket(socket, obj);
+ consumer_destroy_socket(socket);
+ }
+ rcu_read_unlock();
+}
+
/*
* Delete the consumer_output object from the list and free the ptr.
*
return;
}
- if (obj->socks) {
- struct lttng_ht_iter iter;
- struct consumer_socket *socket;
-
- rcu_read_lock();
- cds_lfht_for_each_entry(obj->socks->ht, &iter.iter, socket, node.node) {
- consumer_del_socket(socket, obj);
- consumer_destroy_socket(socket);
- }
- rcu_read_unlock();
+ consumer_destroy_output_sockets(obj);
+ if (obj->socks) {
/* Finally destroy HT */
ht_cleanup_push(obj->socks);
}
rcu_read_lock();
cds_lfht_for_each_entry(src->socks->ht, &iter.iter, socket, node.node) {
/* Ignore socket that are already there. */
- copy_sock = consumer_find_socket(socket->fd, dst);
+ copy_sock = consumer_find_socket(*socket->fd_ptr, dst);
if (copy_sock) {
continue;
}
/* Create new socket object. */
- copy_sock = consumer_allocate_socket(socket->fd);
+ copy_sock = consumer_allocate_socket(socket->fd_ptr);
if (copy_sock == NULL) {
rcu_read_unlock();
ret = -ENOMEM;
assert(sock);
assert(nb_fd > 0);
- ret = lttcomm_send_fds_unix_sock(sock->fd, fds, nb_fd);
+ ret = lttcomm_send_fds_unix_sock(*sock->fd_ptr, fds, nb_fd);
if (ret < 0) {
/* The above call will print a PERROR on error. */
- DBG("Error when sending consumer fds on sock %d", sock->fd);
+ DBG("Error when sending consumer fds on sock %d", *sock->fd_ptr);
goto error;
}
assert(msg);
assert(sock);
- assert(sock->fd >= 0);
- ret = lttcomm_send_unix_sock(sock->fd, msg,
- sizeof(struct lttcomm_consumer_msg));
+ ret = consumer_socket_send(sock, msg, sizeof(struct lttcomm_consumer_msg));
if (ret < 0) {
- /* The above call will print a PERROR on error. */
- DBG("Error when sending consumer channel on sock %d", sock->fd);
goto error;
}
assert(msg);
assert(sock);
- assert(sock->fd >= 0);
- ret = lttcomm_send_unix_sock(sock->fd, msg,
- sizeof(struct lttcomm_consumer_msg));
+ ret = consumer_send_msg(sock, msg);
if (ret < 0) {
- /* The above call will print a PERROR on error. */
- DBG("Error when sending consumer channel on sock %d", sock->fd);
goto error;
}
- ret = consumer_recv_status_reply(sock);
-
error:
return ret;
}
int overwrite,
unsigned int switch_timer_interval,
unsigned int read_timer_interval,
+ unsigned int live_timer_interval,
int output,
int type,
uint64_t session_id,
uint64_t tracefile_size,
uint64_t tracefile_count,
uint64_t session_id_per_pid,
- unsigned int monitor)
+ unsigned int monitor,
+ uint32_t ust_app_uid)
{
assert(msg);
msg->u.ask_channel.overwrite = overwrite;
msg->u.ask_channel.switch_timer_interval = switch_timer_interval;
msg->u.ask_channel.read_timer_interval = read_timer_interval;
+ msg->u.ask_channel.live_timer_interval = live_timer_interval;
msg->u.ask_channel.output = output;
msg->u.ask_channel.type = type;
msg->u.ask_channel.session_id = session_id;
msg->u.ask_channel.tracefile_size = tracefile_size;
msg->u.ask_channel.tracefile_count = tracefile_count;
msg->u.ask_channel.monitor = monitor;
+ msg->u.ask_channel.ust_app_uid = ust_app_uid;
memcpy(msg->u.ask_channel.uuid, uuid, sizeof(msg->u.ask_channel.uuid));
int type,
uint64_t tracefile_size,
uint64_t tracefile_count,
- unsigned int monitor)
+ unsigned int monitor,
+ unsigned int live_timer_interval)
{
assert(msg);
msg->u.channel.tracefile_size = tracefile_size;
msg->u.channel.tracefile_count = tracefile_count;
msg->u.channel.monitor = monitor;
+ msg->u.channel.live_timer_interval = live_timer_interval;
strncpy(msg->u.channel.pathname, pathname,
sizeof(msg->u.channel.pathname));
assert(sock);
assert(fds);
- /* Send on socket */
- ret = lttcomm_send_unix_sock(sock->fd, msg,
- sizeof(struct lttcomm_consumer_msg));
- if (ret < 0) {
- /* The above call will print a PERROR on error. */
- DBG("Error when sending consumer stream on sock %d", sock->fd);
- goto error;
- }
-
- ret = consumer_recv_status_reply(sock);
+ ret = consumer_send_msg(sock, msg);
if (ret < 0) {
goto error;
}
*/
int consumer_send_relayd_socket(struct consumer_socket *consumer_sock,
struct lttcomm_relayd_sock *rsock, struct consumer_output *consumer,
- enum lttng_stream_type type, uint64_t session_id)
+ enum lttng_stream_type type, uint64_t session_id,
+ char *session_name, char *hostname, int session_live_timer)
{
int ret;
struct lttcomm_consumer_msg msg;
goto error;
}
+ if (type == LTTNG_STREAM_CONTROL) {
+ ret = relayd_create_session(rsock,
+ &msg.u.relayd_sock.relayd_session_id,
+ session_name, hostname, session_live_timer,
+ consumer->snapshot);
+ if (ret < 0) {
+ /* Close the control socket. */
+ (void) relayd_close(rsock);
+ goto error;
+ }
+ }
+
msg.cmd_type = LTTNG_CONSUMER_ADD_RELAYD_SOCKET;
/*
* Assign network consumer output index using the temporary consumer since
msg.u.relayd_sock.session_id = session_id;
memcpy(&msg.u.relayd_sock.sock, rsock, sizeof(msg.u.relayd_sock.sock));
- DBG3("Sending relayd sock info to consumer on %d", consumer_sock->fd);
- ret = lttcomm_send_unix_sock(consumer_sock->fd, &msg, sizeof(msg));
- if (ret < 0) {
- /* The above call will print a PERROR on error. */
- DBG("Error when sending relayd sockets on sock %d", rsock->sock.fd);
- goto error;
- }
-
- ret = consumer_recv_status_reply(consumer_sock);
+ DBG3("Sending relayd sock info to consumer on %d", *consumer_sock->fd_ptr);
+ ret = consumer_send_msg(consumer_sock, &msg);
if (ret < 0) {
goto error;
}
rcu_read_lock();
cds_lfht_for_each_entry(consumer->socks->ht, &iter.iter, socket,
node.node) {
- /* Code flow error */
- assert(socket->fd >= 0);
-
pthread_mutex_lock(socket->lock);
-
- ret = lttcomm_send_unix_sock(socket->fd, &msg, sizeof(msg));
+ ret = consumer_socket_send(socket, &msg, sizeof(msg));
if (ret < 0) {
- /* The above call will print a PERROR on error. */
- DBG("Error on consumer is data pending on sock %d", socket->fd);
pthread_mutex_unlock(socket->lock);
goto error_unlock;
}
* the reply status message.
*/
- ret = lttcomm_recv_unix_sock(socket->fd, &ret_code, sizeof(ret_code));
- if (ret <= 0) {
- if (ret == 0) {
- /* Orderly shutdown. Don't return 0 which means success. */
- ret = -1;
- }
- /* The above call will print a PERROR on error. */
- DBG("Error on recv consumer is data pending on sock %d", socket->fd);
+ ret = consumer_socket_recv(socket, &ret_code, sizeof(ret_code));
+ if (ret < 0) {
pthread_mutex_unlock(socket->lock);
goto error_unlock;
}
-
pthread_mutex_unlock(socket->lock);
if (ret_code == 1) {
struct lttcomm_consumer_msg msg;
assert(socket);
- assert(socket->fd >= 0);
DBG2("Consumer flush channel key %" PRIu64, key);
struct lttcomm_consumer_msg msg;
assert(socket);
- assert(socket->fd >= 0);
DBG2("Consumer close metadata channel key %" PRIu64, metadata_key);
struct lttcomm_consumer_msg msg;
assert(socket);
- assert(socket->fd >= 0);
DBG2("Consumer setup metadata channel key %" PRIu64, metadata_key);
struct lttcomm_consumer_msg msg;
assert(socket);
- assert(socket->fd >= 0);
- DBG2("Consumer push metadata to consumer socket %d", socket->fd);
+ DBG2("Consumer push metadata to consumer socket %d", *socket->fd_ptr);
msg.cmd_type = LTTNG_CONSUMER_PUSH_METADATA;
msg.u.push_metadata.key = metadata_key;
goto end;
}
- DBG3("Consumer pushing metadata on sock %d of len %zu", socket->fd, len);
+ DBG3("Consumer pushing metadata on sock %d of len %zu", *socket->fd_ptr,
+ len);
- ret = lttcomm_send_unix_sock(socket->fd, metadata_str, len);
+ ret = consumer_socket_send(socket, metadata_str, len);
if (ret < 0) {
goto end;
}
*/
int consumer_snapshot_channel(struct consumer_socket *socket, uint64_t key,
struct snapshot_output *output, int metadata, uid_t uid, gid_t gid,
- const char *session_path, int wait)
+ const char *session_path, int wait, int max_stream_size)
{
int ret;
struct lttcomm_consumer_msg msg;
assert(socket);
- assert(socket->fd >= 0);
assert(output);
assert(output->consumer);
memset(&msg, 0, sizeof(msg));
msg.cmd_type = LTTNG_CONSUMER_SNAPSHOT_CHANNEL;
msg.u.snapshot_channel.key = key;
- msg.u.snapshot_channel.max_size = output->max_size;
+ msg.u.snapshot_channel.max_stream_size = max_stream_size;
msg.u.snapshot_channel.metadata = metadata;
if (output->consumer->type == CONSUMER_DST_NET) {
msg.u.snapshot_channel.relayd_id = output->consumer->net_seq_index;
msg.u.snapshot_channel.use_relayd = 1;
ret = snprintf(msg.u.snapshot_channel.pathname,
- sizeof(msg.u.snapshot_channel.pathname), "%s/%s-%s%s",
- output->consumer->subdir, output->name, output->datetime,
+ sizeof(msg.u.snapshot_channel.pathname),
+ "%s/%s-%s-%" PRIu64 "%s", output->consumer->subdir,
+ output->name, output->datetime, output->nb_snapshot,
session_path);
if (ret < 0) {
ret = -LTTNG_ERR_NOMEM;
}
} else {
ret = snprintf(msg.u.snapshot_channel.pathname,
- sizeof(msg.u.snapshot_channel.pathname), "%s/%s-%s%s",
- output->consumer->dst.trace_path, output->name,
- output->datetime, session_path);
+ sizeof(msg.u.snapshot_channel.pathname),
+ "%s/%s-%s-%" PRIu64 "%s", output->consumer->dst.trace_path,
+ output->name, output->datetime, output->nb_snapshot,
+ session_path);
if (ret < 0) {
ret = -LTTNG_ERR_NOMEM;
goto error;
};
struct consumer_socket {
- /* File descriptor */
- int fd;
+ /*
+ * File descriptor. This is just a reference to the consumer data meaning
+ * that every access must be locked and checked for a possible invalid
+ * value.
+ */
+ int *fd_ptr;
+
/*
* To use this socket (send/recv), this lock MUST be acquired.
*/
int err_sock;
/* These two sockets uses the cmd_unix_sock_path. */
int cmd_sock;
+ /*
+ * The metadata socket object is handled differently and only created
+ * locally in this object thus it's the only reference available in the
+ * session daemon. For that reason, a variable for the fd is required and
+ * the metadata socket fd points to it.
+ */
+ int metadata_fd;
struct consumer_socket metadata_sock;
/* consumer error and command Unix socket path */
char err_unix_sock_path[PATH_MAX];
char cmd_unix_sock_path[PATH_MAX];
- /* communication lock */
+ /*
+ * This lock has two purposes. It protects any change to the consumer
+ * socket and make sure only one thread uses this object for read/write
+ * operations.
+ */
pthread_mutex_t lock;
};
*/
struct lttng_ht *socks;
+ /* Tell if this output is used for snapshot. */
+ unsigned int snapshot:1;
+
union {
char trace_path[PATH_MAX];
struct consumer_net net;
struct consumer_output *consumer);
struct consumer_socket *consumer_find_socket_by_bitness(int bits,
struct consumer_output *consumer);
-struct consumer_socket *consumer_allocate_socket(int fd);
+struct consumer_socket *consumer_allocate_socket(int *fd);
void consumer_add_socket(struct consumer_socket *sock,
struct consumer_output *consumer);
void consumer_del_socket(struct consumer_socket *sock,
void consumer_destroy_socket(struct consumer_socket *sock);
int consumer_copy_sockets(struct consumer_output *dst,
struct consumer_output *src);
+void consumer_destroy_output_sockets(struct consumer_output *obj);
+int consumer_socket_send(struct consumer_socket *socket, void *msg,
+ size_t len);
+int consumer_socket_recv(struct consumer_socket *socket, void *msg,
+ size_t len);
struct consumer_output *consumer_create_output(enum consumer_dst_type type);
struct consumer_output *consumer_copy_output(struct consumer_output *obj);
struct lttcomm_consumer_msg *msg);
int consumer_send_relayd_socket(struct consumer_socket *consumer_sock,
struct lttcomm_relayd_sock *rsock, struct consumer_output *consumer,
- enum lttng_stream_type type, uint64_t session_id);
+ enum lttng_stream_type type, uint64_t session_id,
+ char *session_name, char *hostname, int session_live_timer);
int consumer_send_destroy_relayd(struct consumer_socket *sock,
struct consumer_output *consumer);
int consumer_recv_status_reply(struct consumer_socket *sock);
int overwrite,
unsigned int switch_timer_interval,
unsigned int read_timer_interval,
+ unsigned int live_timer_interval,
int output,
int type,
uint64_t session_id,
uint64_t tracefile_size,
uint64_t tracefile_count,
uint64_t session_id_per_pid,
- unsigned int monitor);
+ unsigned int monitor,
+ uint32_t ust_app_uid);
void consumer_init_stream_comm_msg(struct lttcomm_consumer_msg *msg,
enum lttng_consumer_command cmd,
uint64_t channel_key,
int type,
uint64_t tracefile_size,
uint64_t tracefile_count,
- unsigned int monitor);
+ unsigned int monitor,
+ unsigned int live_timer_interval);
int consumer_is_data_pending(uint64_t session_id,
struct consumer_output *consumer);
int consumer_close_metadata(struct consumer_socket *socket,
/* Snapshot command. */
int consumer_snapshot_channel(struct consumer_socket *socket, uint64_t key,
struct snapshot_output *output, int metadata, uid_t uid, gid_t gid,
- const char *session_path, int wait);
+ const char *session_path, int wait, int max_size_per_stream);
#endif /* _CONSUMER_H */
/* Add ltt UST context node to ltt UST channel */
lttng_ht_add_unique_ulong(uchan->ctx, &uctx->node);
rcu_read_unlock();
+ cds_list_add_tail(&uctx->list, &uchan->ctx_list);
DBG("Context UST %d added to channel %s", uctx->ctx.ctx, uchan->name);
rcu_read_unlock();
return ret;
}
+
+/*
+ * Enable all JUL event for a given UST session.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int event_jul_enable_all(struct ltt_ust_session *usess)
+{
+ int ret;
+ struct jul_event *jevent;
+ struct lttng_event event;
+ struct lttng_ht_iter iter;
+
+ assert(usess);
+
+ DBG("Event JUL enabling ALL events for session %" PRIu64, usess->id);
+
+ /* Create the * wildcard event name for the Java agent. */
+ memset(event.name, 0, sizeof(event.name));
+ strncpy(event.name, "*", sizeof(event.name));
+ event.name[sizeof(event.name) - 1] = '\0';
+
+ /* Enable event on JUL application through TCP socket. */
+ ret = event_jul_enable(usess, &event);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* Flag every event that they are now enabled. */
+ rcu_read_lock();
+ cds_lfht_for_each_entry(usess->domain_jul.events->ht, &iter.iter, jevent,
+ node.node) {
+ jevent->enabled = 1;
+ }
+ rcu_read_unlock();
+
+ ret = LTTNG_OK;
+
+error:
+ return ret;
+}
+
+/*
+ * Enable a single JUL event for a given UST session.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int event_jul_enable(struct ltt_ust_session *usess, struct lttng_event *event)
+{
+ int ret, created = 0;
+ struct jul_event *jevent;
+
+ assert(usess);
+ assert(event);
+
+ DBG("Event JUL enabling %s for session %" PRIu64, event->name, usess->id);
+
+ jevent = jul_find_by_name(event->name, &usess->domain_jul);
+ if (!jevent) {
+ jevent = jul_create_event(event->name);
+ if (!jevent) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ created = 1;
+ }
+
+ /* Already enabled? */
+ if (jevent->enabled) {
+ goto end;
+ }
+
+ ret = jul_enable_event(jevent);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* If the event was created prior to the enable, add it to the domain. */
+ if (created) {
+ jul_add_event(jevent, &usess->domain_jul);
+ }
+
+end:
+ return LTTNG_OK;
+
+error:
+ if (created) {
+ jul_destroy_event(jevent);
+ }
+ return ret;
+}
+
+/*
+ * Disable a single JUL event for a given UST session.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int event_jul_disable(struct ltt_ust_session *usess, char *event_name)
+{
+ int ret;
+ struct jul_event *jevent;
+
+ assert(usess);
+ assert(event_name);
+
+ DBG("Event JUL disabling %s for session %" PRIu64, event_name, usess->id);
+
+ jevent = jul_find_by_name(event_name, &usess->domain_jul);
+ if (!jevent) {
+ ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
+ goto error;
+ }
+
+ /* Already disabled? */
+ if (!jevent->enabled) {
+ goto end;
+ }
+
+ ret = jul_disable_event(jevent);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+end:
+ return LTTNG_OK;
+
+error:
+ return ret;
+}
+/*
+ * Disable all JUL event for a given UST session.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int event_jul_disable_all(struct ltt_ust_session *usess)
+{
+ int ret, do_disable = 0;
+ struct jul_event *jevent;
+ struct lttng_ht_iter iter;
+
+ assert(usess);
+
+ /* Enable event on JUL application through TCP socket. */
+ ret = event_jul_disable(usess, "*");
+ if (ret != LTTNG_OK) {
+ if (ret == LTTNG_ERR_UST_EVENT_NOT_FOUND) {
+ /*
+ * This means that no enable all was done before but still a user
+ * could want to disable everything even though the * wild card
+ * event does not exists.
+ */
+ do_disable = 1;
+ } else {
+ goto error;
+ }
+ }
+
+ /* Flag every event that they are now enabled. */
+ rcu_read_lock();
+ cds_lfht_for_each_entry(usess->domain_jul.events->ht, &iter.iter, jevent,
+ node.node) {
+ if (jevent->enabled && do_disable) {
+ ret = event_jul_disable(usess, jevent->name);
+ if (ret != LTTNG_OK) {
+ rcu_read_unlock();
+ goto error;
+ }
+ }
+ jevent->enabled = 0;
+ }
+ rcu_read_unlock();
+
+ ret = LTTNG_OK;
+
+error:
+ return ret;
+}
int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess,
struct ltt_ust_channel *uchan);
+int event_jul_enable(struct ltt_ust_session *usess, struct lttng_event *event);
+int event_jul_enable_all(struct ltt_ust_session *usess);
+
+int event_jul_disable(struct ltt_ust_session *usess, char *event_name);
+int event_jul_disable_all(struct ltt_ust_session *usess);
+
#endif /* _LTT_EVENT_H */
--- /dev/null
+#ifndef HEALTH_SESSIOND_H
+#define HEALTH_SESSIOND_H
+
+/*
+ * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <lttng/health-internal.h>
+
+enum health_type_sessiond {
+ HEALTH_SESSIOND_TYPE_CMD = 0,
+ HEALTH_SESSIOND_TYPE_APP_MANAGE = 1,
+ HEALTH_SESSIOND_TYPE_APP_REG = 2,
+ HEALTH_SESSIOND_TYPE_KERNEL = 3,
+ HEALTH_SESSIOND_TYPE_CONSUMER = 4,
+ HEALTH_SESSIOND_TYPE_HT_CLEANUP = 5,
+ HEALTH_SESSIOND_TYPE_APP_MANAGE_NOTIFY = 6,
+ HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH = 7,
+
+ NR_HEALTH_SESSIOND_TYPES,
+};
+
+/* Application health monitoring */
+extern struct health_app *health_sessiond;
+
+#endif /* HEALTH_SESSIOND_H */
+++ /dev/null
-/*
- * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License, version 2 only, 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., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#define _GNU_SOURCE
-#include <assert.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-
-#include <common/defaults.h>
-#include <common/error.h>
-
-#include "health.h"
-
-static const struct timespec time_delta = {
- .tv_sec = DEFAULT_HEALTH_CHECK_DELTA_S,
- .tv_nsec = DEFAULT_HEALTH_CHECK_DELTA_NS,
-};
-
-/* Define TLS health state. */
-DEFINE_URCU_TLS(struct health_state, health_state);
-
-/*
- * It ensures that TLS memory used for the node and its container structure
- * don't get reclaimed after the TLS owner thread exits until we have finished
- * using it.
- */
-static pthread_mutex_t health_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static struct health_tls_state_list health_state_list = {
- .head = CDS_LIST_HEAD_INIT(health_state_list.head),
-};
-
-/*
- * This keeps track of the error state for unregistered thread. A thread
- * reporting a health error, normally unregisters and quits. This makes the TLS
- * health state not available to the health_check_state() call so on unregister
- * we update this global error array so we can keep track of which thread was
- * on error if the TLS health state has been removed.
- */
-static enum health_flags global_error_state[HEALTH_NUM_TYPE];
-
-/*
- * Lock health state global list mutex.
- */
-static void state_lock(void)
-{
- pthread_mutex_lock(&health_mutex);
-}
-
-/*
- * Unlock health state global list mutex.
- */
-static void state_unlock(void)
-{
- pthread_mutex_unlock(&health_mutex);
-}
-
-/*
- * Set time difference in res from time_a and time_b.
- */
-static void time_diff(const struct timespec *time_a,
- const struct timespec *time_b, struct timespec *res)
-{
- if (time_a->tv_nsec - time_b->tv_nsec < 0) {
- res->tv_sec = time_a->tv_sec - time_b->tv_sec - 1;
- res->tv_nsec = 1000000000L + time_a->tv_sec - time_b->tv_sec;
- } else {
- res->tv_sec = time_a->tv_sec - time_b->tv_sec;
- res->tv_nsec = time_a->tv_nsec - time_b->tv_nsec;
- }
-}
-
-/*
- * Return true if time_a - time_b > diff, else false.
- */
-static int time_diff_gt(const struct timespec *time_a,
- const struct timespec *time_b, const struct timespec *diff)
-{
- struct timespec res;
-
- time_diff(time_a, time_b, &res);
- time_diff(&res, diff, &res);
-
- if (res.tv_sec > 0) {
- return 1;
- } else if (res.tv_sec == 0 && res.tv_nsec > 0) {
- return 1;
- }
-
- return 0;
-}
-
-/*
- * Validate health state. Checks for the error flag or health conditions.
- *
- * Return 0 if health is bad or else 1.
- */
-static int validate_state(struct health_state *state)
-{
- int retval = 1, ret;
- unsigned long current, last;
- struct timespec current_time;
-
- assert(state);
-
- last = state->last;
- current = uatomic_read(&state->current);
-
- ret = clock_gettime(CLOCK_MONOTONIC, ¤t_time);
- if (ret < 0) {
- PERROR("Error reading time\n");
- /* error */
- retval = 0;
- goto end;
- }
-
- /*
- * Thread is in bad health if flag HEALTH_ERROR is set. It is also in bad
- * health if, after the delta delay has passed, its the progress counter
- * has not moved and it has NOT been waiting for a poll() call.
- */
- if (uatomic_read(&state->flags) & HEALTH_ERROR) {
- retval = 0;
- goto end;
- }
-
- /*
- * Initial condition need to update the last counter and sample time, but
- * should not check health in this initial case, because we don't know how
- * much time has passed.
- */
- if (state->last_time.tv_sec == 0 && state->last_time.tv_nsec == 0) {
- /* update last counter and last sample time */
- state->last = current;
- memcpy(&state->last_time, ¤t_time, sizeof(current_time));
- } else {
- if (time_diff_gt(¤t_time, &state->last_time, &time_delta)) {
- if (current == last && !HEALTH_IS_IN_POLL(current)) {
- /* error */
- retval = 0;
- }
- /* update last counter and last sample time */
- state->last = current;
- memcpy(&state->last_time, ¤t_time, sizeof(current_time));
-
- /* On error, stop right now and notify caller. */
- if (retval == 0) {
- goto end;
- }
- }
- }
-
-end:
- DBG("Health state current %lu, last %lu, ret %d",
- current, last, ret);
- return retval;
-}
-
-/*
- * Check health of a specific health type. Note that if a thread has not yet
- * initialize its health subsystem or has quit, it's considered in a good
- * state.
- *
- * Return 0 if health is bad or else 1.
- */
-int health_check_state(enum health_type type)
-{
- int retval = 1;
- struct health_state *state;
-
- assert(type < HEALTH_NUM_TYPE);
-
- state_lock();
-
- cds_list_for_each_entry(state, &health_state_list.head, node) {
- int ret;
-
- if (state->type != type) {
- continue;
- }
-
- ret = validate_state(state);
- if (!ret) {
- retval = 0;
- goto end;
- }
- }
-
- /* Check the global state since some state might not be visible anymore. */
- if (global_error_state[type] & HEALTH_ERROR) {
- retval = 0;
- }
-
-end:
- state_unlock();
-
- DBG("Health check for type %d is %s", (int) type,
- (retval == 0) ? "BAD" : "GOOD");
- return retval;
-}
-
-/*
- * Init health state.
- */
-void health_register(enum health_type type)
-{
- assert(type < HEALTH_NUM_TYPE);
-
- /* Init TLS state. */
- uatomic_set(&URCU_TLS(health_state).last, 0);
- uatomic_set(&URCU_TLS(health_state).last_time.tv_sec, 0);
- uatomic_set(&URCU_TLS(health_state).last_time.tv_nsec, 0);
- uatomic_set(&URCU_TLS(health_state).current, 0);
- uatomic_set(&URCU_TLS(health_state).flags, 0);
- uatomic_set(&URCU_TLS(health_state).type, type);
-
- /* Add it to the global TLS state list. */
- state_lock();
- cds_list_add(&URCU_TLS(health_state).node, &health_state_list.head);
- state_unlock();
-}
-
-/*
- * Remove node from global list.
- */
-void health_unregister(void)
-{
- state_lock();
- /*
- * On error, set the global_error_state since we are about to remove
- * the node from the global list.
- */
- if (uatomic_read(&URCU_TLS(health_state).flags) & HEALTH_ERROR) {
- uatomic_set(&global_error_state[URCU_TLS(health_state).type],
- HEALTH_ERROR);
- }
- cds_list_del(&URCU_TLS(health_state).node);
- state_unlock();
-}
+++ /dev/null
-/*
- * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License, version 2 only, 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., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef _HEALTH_H
-#define _HEALTH_H
-
-#include <assert.h>
-#include <time.h>
-#include <pthread.h>
-#include <urcu/tls-compat.h>
-#include <urcu/uatomic.h>
-#include <urcu/list.h>
-
-/*
- * These are the value added to the current state depending of the position in
- * the thread where is either waiting on a poll() or running in the code.
- */
-#define HEALTH_POLL_VALUE (1UL << 0)
-#define HEALTH_CODE_VALUE (1UL << 1)
-
-#define HEALTH_IS_IN_POLL(x) ((x) & HEALTH_POLL_VALUE)
-
-enum health_flags {
- HEALTH_ERROR = (1U << 0),
-};
-
-enum health_type {
- HEALTH_TYPE_CMD = 0,
- HEALTH_TYPE_APP_MANAGE = 1,
- HEALTH_TYPE_APP_REG = 2,
- HEALTH_TYPE_KERNEL = 3,
- HEALTH_TYPE_CONSUMER = 4,
- HEALTH_TYPE_HT_CLEANUP = 5,
- HEALTH_TYPE_APP_MANAGE_NOTIFY = 6,
- HEALTH_TYPE_APP_REG_DISPATCH = 7,
-
- HEALTH_NUM_TYPE,
-};
-
-struct health_tls_state_list {
- struct cds_list_head head;
-};
-
-struct health_state {
- /*
- * last counter and last_time are only read and updated by the health_check
- * thread (single updater).
- */
- unsigned long last;
- struct timespec last_time;
-
- /*
- * current and flags are updated by multiple threads concurrently.
- */
- unsigned long current; /* progress counter, updated atomically */
- enum health_flags flags; /* other flags, updated atomically */
- enum health_type type; /* Indicates the nature of the thread. */
- /* Node of the global TLS state list. */
- struct cds_list_head node;
-};
-
-/* Declare TLS health state. */
-extern DECLARE_URCU_TLS(struct health_state, health_state);
-
-/*
- * Update current counter by 1 to indicate that the thread entered or left a
- * blocking state caused by a poll(). If the counter's value is not an even
- * number (meaning a code execution flow), an assert() is raised.
- */
-static inline void health_poll_entry(void)
-{
- /* Code MUST be in code execution state which is an even number. */
- assert(!(uatomic_read(&URCU_TLS(health_state).current)
- & HEALTH_POLL_VALUE));
-
- uatomic_add(&URCU_TLS(health_state).current, HEALTH_POLL_VALUE);
-}
-
-/*
- * Update current counter by 1 indicating the exit of a poll or blocking call.
- * If the counter's value is not an odd number (a poll execution), an assert()
- * is raised.
- */
-static inline void health_poll_exit(void)
-{
- /* Code MUST be in poll execution state which is an odd number. */
- assert(uatomic_read(&URCU_TLS(health_state).current)
- & HEALTH_POLL_VALUE);
-
- uatomic_add(&URCU_TLS(health_state).current, HEALTH_POLL_VALUE);
-}
-
-/*
- * Update current counter by 2 indicates progress in execution of a
- * thread.
- */
-static inline void health_code_update(void)
-{
- uatomic_add(&URCU_TLS(health_state).current, HEALTH_CODE_VALUE);
-}
-
-/*
- * Set health "error" flag.
- */
-static inline void health_error(void)
-{
- uatomic_or(&URCU_TLS(health_state).flags, HEALTH_ERROR);
-}
-
-int health_check_state(enum health_type type);
-void health_register(enum health_type type);
-void health_unregister(void);
-
-#endif /* _HEALTH_H */
#include <common/utils.h>
#include "lttng-sessiond.h"
-#include "health.h"
+#include "health-sessiond.h"
void *thread_ht_cleanup(void *data)
{
rcu_register_thread();
rcu_thread_online();
- health_register(HEALTH_TYPE_HT_CLEANUP);
+ health_register(health_sessiond, HEALTH_SESSIOND_TYPE_HT_CLEANUP);
health_code_update();
health_error();
ERR("Health error occurred in %s", __func__);
}
- health_unregister();
+ health_unregister(health_sessiond);
rcu_thread_offline();
rcu_unregister_thread();
return NULL;
--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+
+#include <common/common.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/uri.h>
+#include <common/utils.h>
+
+#include "fd-limit.h"
+#include "jul-thread.h"
+#include "lttng-sessiond.h"
+#include "session.h"
+#include "utils.h"
+
+/*
+ * Note that there is not port here. It's set after this URI is parsed so we
+ * can let the user define a custom one. However, localhost is ALWAYS the
+ * default listening address.
+ */
+static const char *default_reg_uri = "tcp://localhost";
+
+/*
+ * Update JUL application using the given socket. This is done just after
+ * registration was successful.
+ *
+ * This is a quite heavy call in terms of locking since the session list lock
+ * AND session lock are acquired.
+ */
+static void update_jul_app(int sock)
+{
+ struct ltt_session *session, *stmp;
+ struct ltt_session_list *list;
+
+ list = session_get_list();
+ assert(list);
+
+ session_lock_list();
+ cds_list_for_each_entry_safe(session, stmp, &list->head, list) {
+ session_lock(session);
+ if (session->ust_session) {
+ jul_update(&session->ust_session->domain_jul, sock);
+ }
+ session_unlock(session);
+ }
+ session_unlock_list();
+}
+
+/*
+ * Destroy a JUL application by socket.
+ */
+static void destroy_jul_app(int sock)
+{
+ struct jul_app *app;
+
+ assert(sock >= 0);
+
+ /*
+ * Not finding an application is a very important error that should NEVER
+ * happen. The hash table deletion is ONLY done through this call even on
+ * thread cleanup.
+ */
+ rcu_read_lock();
+ app = jul_find_app_by_sock(sock);
+ assert(app);
+ rcu_read_unlock();
+
+ /* RCU read side lock is taken in this function call. */
+ jul_delete_app(app);
+
+ /* The application is freed in a RCU call but the socket is closed here. */
+ jul_destroy_app(app);
+}
+
+/*
+ * Cleanup remaining JUL apps in the hash table. This should only be called in
+ * the exit path of the thread.
+ */
+static void clean_jul_apps_ht(void)
+{
+ struct lttng_ht_node_ulong *node;
+ struct lttng_ht_iter iter;
+
+ DBG3("[jul-thread] Cleaning JUL apps ht");
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, node, node) {
+ struct jul_app *app;
+
+ app = caa_container_of(node, struct jul_app, node);
+ destroy_jul_app(app->sock->fd);
+ }
+ rcu_read_unlock();
+}
+
+/*
+ * Create and init socket from uri.
+ */
+static struct lttcomm_sock *init_tcp_socket(void)
+{
+ int ret;
+ struct lttng_uri *uri = NULL;
+ struct lttcomm_sock *sock = NULL;
+
+ /*
+ * This should never fail since the URI is hardcoded and the port is set
+ * before this thread is launched.
+ */
+ ret = uri_parse(default_reg_uri, &uri);
+ assert(ret);
+ assert(jul_tcp_port);
+ uri->port = jul_tcp_port;
+
+ sock = lttcomm_alloc_sock_from_uri(uri);
+ uri_free(uri);
+ if (sock == NULL) {
+ ERR("[jul-thread] JUL allocating TCP socket");
+ goto error;
+ }
+
+ ret = lttcomm_create_sock(sock);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = sock->ops->bind(sock);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = sock->ops->listen(sock, -1);
+ if (ret < 0) {
+ goto error;
+ }
+
+ DBG("[jul-thread] Listening on TCP port %u and socket %d", jul_tcp_port,
+ sock->fd);
+
+ return sock;
+
+error:
+ if (sock) {
+ lttcomm_destroy_sock(sock);
+ }
+ return NULL;
+}
+
+/*
+ * Close and destroy the given TCP socket.
+ */
+static void destroy_tcp_socket(struct lttcomm_sock *sock)
+{
+ assert(sock);
+
+ DBG3("[jul-thread] Destroy TCP socket on port %u", jul_tcp_port);
+
+ /* This will return gracefully if fd is invalid. */
+ sock->ops->close(sock);
+ lttcomm_destroy_sock(sock);
+}
+
+/*
+ * Handle a new JUL registration using the reg socket. After that, a new JUL
+ * application is added to the global hash table and attach to an UST app
+ * object.
+ *
+ * Return the new FD created upon accept() on success or else a negative errno
+ * value.
+ */
+static int handle_registration(struct lttcomm_sock *reg_sock)
+{
+ int ret;
+ pid_t pid;
+ ssize_t size;
+ struct jul_app *app;
+ struct jul_register_msg msg;
+ struct lttcomm_sock *new_sock;
+
+ assert(reg_sock);
+
+ new_sock = reg_sock->ops->accept(reg_sock);
+ if (!new_sock) {
+ ret = -ENOTCONN;
+ goto error;
+ }
+
+ size = new_sock->ops->recvmsg(new_sock, &msg, sizeof(msg), 0);
+ if (size < sizeof(msg)) {
+ ret = -errno;
+ goto error_socket;
+ }
+ pid = be32toh(msg.pid);
+
+ DBG2("[jul-thread] New registration for pid %d on socket %d", pid,
+ new_sock->fd);
+
+ app = jul_create_app(pid, new_sock);
+ if (!app) {
+ ret = -ENOMEM;
+ goto error_socket;
+ }
+
+ /*
+ * Add before assigning the socket value to the UST app so it can be found
+ * concurrently.
+ */
+ jul_add_app(app);
+
+ /*
+ * Attach JUL application to a UST app object if one exists.
+ *
+ * FIXME: This implies that the UST app object exists and created before
+ * JUL registration. Must confirm or else JUL app will leak until socket is
+ * closed by the application.
+ */
+ jul_attach_app(app);
+
+ return new_sock->fd;
+
+error_socket:
+ new_sock->ops->close(new_sock);
+ lttcomm_destroy_sock(new_sock);
+error:
+ return ret;
+}
+
+/*
+ * This thread manage application notify communication.
+ */
+void *jul_thread_manage_registration(void *data)
+{
+ int i, ret, pollfd;
+ uint32_t revents, nb_fd;
+ struct lttng_poll_event events;
+ struct lttcomm_sock *reg_sock;
+
+ DBG("[jul-thread] Manage JUL application registration.");
+
+ rcu_register_thread();
+ rcu_thread_online();
+
+ /* JUL initialization call MUST be called before starting the thread. */
+ assert(jul_apps_ht_by_sock);
+
+ /* Create pollset with size 2, quit pipe and socket. */
+ ret = sessiond_set_thread_pollset(&events, 2);
+ if (ret < 0) {
+ goto error_poll_create;
+ }
+
+ reg_sock = init_tcp_socket();
+ if (!reg_sock) {
+ goto error_tcp_socket;
+ }
+
+ /* Add create valid TCP socket to poll set. */
+ ret = lttng_poll_add(&events, reg_sock->fd,
+ LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP);
+ if (ret < 0) {
+ goto error;
+ }
+
+ while (1) {
+ DBG3("[jul-thread] Manage JUL polling on %d fds",
+ LTTNG_POLL_GETNB(&events));
+
+ /* Inifinite blocking call, waiting for transmission */
+restart:
+ ret = lttng_poll_wait(&events, -1);
+ if (ret < 0) {
+ /*
+ * Restart interrupted system call.
+ */
+ if (errno == EINTR) {
+ goto restart;
+ }
+ goto error;
+ }
+ nb_fd = ret;
+ DBG3("[jul-thread] %d fd ready", nb_fd);
+
+ for (i = 0; i < nb_fd; i++) {
+ /* Fetch once the poll data */
+ revents = LTTNG_POLL_GETEV(&events, i);
+ pollfd = LTTNG_POLL_GETFD(&events, i);
+
+ /* Thread quit pipe has been closed. Killing thread. */
+ ret = sessiond_check_thread_quit_pipe(pollfd, revents);
+ if (ret) {
+ goto exit;
+ }
+
+ /*
+ * Check first if this is a POLLERR since POLLIN is also included
+ * in an error value thus checking first.
+ */
+ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+ /* Removing from the poll set */
+ ret = lttng_poll_del(&events, pollfd);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /*
+ * FIXME: Should we try to invalidate the JUL socket in the
+ * associated ust app.
+ */
+ destroy_jul_app(pollfd);
+ } else if (revents & (LPOLLIN)) {
+ int new_fd;
+
+ /* Pollin event of JUL app socket should NEVER happen. */
+ assert(pollfd == reg_sock->fd);
+
+ new_fd = handle_registration(reg_sock);
+ if (new_fd < 0) {
+ WARN("[jul-thread] JUL registration failed. Ignoring.");
+ /* Somehow the communication failed. Just continue. */
+ continue;
+ }
+
+ /* Only add poll error event to only detect shutdown. */
+ ret = lttng_poll_add(&events, new_fd,
+ LPOLLERR | LPOLLHUP | LPOLLRDHUP);
+ if (ret < 0) {
+ destroy_jul_app(new_fd);
+ continue;
+ }
+
+ /* Update newly registered app. */
+ update_jul_app(new_fd);
+ } else {
+ ERR("Unknown poll events %u for sock %d", revents, pollfd);
+ continue;
+ }
+ }
+ }
+
+exit:
+ /* Whatever happens, try to delete it and exit. */
+ (void) lttng_poll_del(&events, reg_sock->fd);
+error:
+ destroy_tcp_socket(reg_sock);
+error_tcp_socket:
+ lttng_poll_clean(&events);
+error_poll_create:
+ DBG("[jul-thread] is cleaning up and stopping.");
+
+ if (jul_apps_ht_by_sock) {
+ clean_jul_apps_ht();
+ lttng_ht_destroy(jul_apps_ht_by_sock);
+ }
+
+ rcu_thread_offline();
+ rcu_unregister_thread();
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _JUL_THREAD_H
+#define _JUL_THREAD_H
+
+#ifdef HAVE_LIBLTTNG_UST_CTL
+
+void *jul_thread_manage_registration(void *data);
+
+#else /* HAVE_LIBLTTNG_UST_CTL */
+
+static inline
+void *jul_thread_manage_registration(void *data)
+{
+ return NULL;
+}
+
+#endif /* HAVE_LIBLTTNG_UST_CTL */
+
+#endif /* _JUL_THREAD_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <urcu/uatomic.h>
+
+#include <common/common.h>
+#include <common/sessiond-comm/jul.h>
+
+#include "jul.h"
+#include "ust-app.h"
+#include "utils.h"
+
+/*
+ * URCU intermediate call to complete destroy a JUL event.
+ */
+static void destroy_event_jul_rcu(struct rcu_head *head)
+{
+ struct lttng_ht_node_str *node =
+ caa_container_of(head, struct lttng_ht_node_str, head);
+ struct jul_event *event =
+ caa_container_of(node, struct jul_event, node);
+
+ free(event);
+}
+
+/*
+ * URCU intermediate call to complete destroy a JUL event.
+ */
+static void destroy_app_jul_rcu(struct rcu_head *head)
+{
+ struct lttng_ht_node_ulong *node =
+ caa_container_of(head, struct lttng_ht_node_ulong, head);
+ struct jul_app *app =
+ caa_container_of(node, struct jul_app, node);
+
+ free(app);
+}
+
+/*
+ * Communication with Java agent call. Send the message header to the given
+ * socket all in big endian.
+ *
+ * Return 0 on success or else a negative errno message of sendmsg() op.
+ */
+static int send_header(struct lttcomm_sock *sock, uint64_t data_size,
+ uint32_t cmd, uint32_t cmd_version)
+{
+ int ret;
+ ssize_t size;
+ struct lttcomm_jul_hdr msg;
+
+ assert(sock);
+
+ msg.data_size = htobe64(data_size);
+ msg.cmd = htobe32(cmd);
+ msg.cmd_version = htobe32(cmd_version);
+
+ size = sock->ops->sendmsg(sock, &msg, sizeof(msg), 0);
+ if (size < sizeof(msg)) {
+ ret = -errno;
+ goto error;
+ }
+ ret = 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Communication call with the Java agent. Send the payload to the given
+ * socket. The header MUST be sent prior to this call.
+ *
+ * Return 0 on success or else a negative errno value of sendmsg() op.
+ */
+static int send_payload(struct lttcomm_sock *sock, void *data,
+ size_t size)
+{
+ int ret;
+ ssize_t len;
+
+ assert(sock);
+ assert(data);
+
+ len = sock->ops->sendmsg(sock, data, size, 0);
+ if (len < size) {
+ ret = -errno;
+ goto error;
+ }
+ ret = 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Communication call with the Java agent. Receive reply from the agent using
+ * the given socket.
+ *
+ * Return 0 on success or else a negative errno value from recvmsg() op.
+ */
+static int recv_reply(struct lttcomm_sock *sock, void *buf, size_t size)
+{
+ int ret;
+ ssize_t len;
+
+ assert(sock);
+ assert(buf);
+
+ len = sock->ops->recvmsg(sock, buf, size, 0);
+ if (len < size) {
+ ret = -errno;
+ goto error;
+ }
+ ret = 0;
+
+error:
+ return ret;
+}
+
+
+/*
+ * Internal call to list events on a given app. Populate events.
+ *
+ * Return number of element in the list or else a negative LTTNG_ERR* code.
+ */
+static ssize_t list_events(struct jul_app *app, struct lttng_event **events)
+{
+ int ret, i, len = 0, offset = 0;
+ uint32_t nb_event;
+ size_t data_size;
+ struct lttng_event *tmp_events = NULL;
+ struct lttcomm_jul_list_reply *reply = NULL;
+ struct lttcomm_jul_list_reply_hdr reply_hdr;
+
+ assert(app);
+ assert(app->sock);
+ assert(events);
+
+ DBG2("JUL listing events for app pid: %d and socket %d", app->pid,
+ app->sock->fd);
+
+ ret = send_header(app->sock, 0, JUL_CMD_LIST, 0);
+ if (ret < 0) {
+ goto error_io;
+ }
+
+ /* Get list header so we know how much we'll receive. */
+ ret = recv_reply(app->sock, &reply_hdr, sizeof(reply_hdr));
+ if (ret < 0) {
+ goto error_io;
+ }
+
+ switch (be32toh(reply_hdr.ret_code)) {
+ case JUL_RET_CODE_SUCCESS:
+ data_size = be32toh(reply_hdr.data_size) + sizeof(*reply);
+ break;
+ default:
+ ERR("Java agent returned an unknown code: %" PRIu32,
+ be32toh(reply_hdr.ret_code));
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+
+ reply = zmalloc(data_size);
+ if (!reply) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ /* Get the list with the appropriate data size. */
+ ret = recv_reply(app->sock, reply, data_size);
+ if (ret < 0) {
+ goto error_io;
+ }
+
+ nb_event = be32toh(reply->nb_event);
+ tmp_events = zmalloc(sizeof(*tmp_events) * nb_event);
+ if (!tmp_events) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ for (i = 0; i < nb_event; i++) {
+ offset += len;
+ strncpy(tmp_events[i].name, reply->payload + offset,
+ sizeof(tmp_events[i].name));
+ tmp_events[i].pid = app->pid;
+ tmp_events[i].enabled = -1;
+ len = strlen(reply->payload + offset) + 1;
+ }
+
+ *events = tmp_events;
+
+ free(reply);
+ return nb_event;
+
+error_io:
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+error:
+ free(reply);
+ free(tmp_events);
+ return -ret;
+
+}
+
+/*
+ * Internal enable JUL event call on a JUL application. This function
+ * communicates with the Java agent to enable a given event (Logger name).
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+static int enable_event(struct jul_app *app, struct jul_event *event)
+{
+ int ret;
+ uint64_t data_size;
+ struct lttcomm_jul_enable msg;
+ struct lttcomm_jul_generic_reply reply;
+
+ assert(app);
+ assert(app->sock);
+ assert(event);
+
+ DBG2("JUL enabling event %s for app pid: %d and socket %d", event->name,
+ app->pid, app->sock->fd);
+
+ data_size = sizeof(msg);
+
+ ret = send_header(app->sock, data_size, JUL_CMD_ENABLE, 0);
+ if (ret < 0) {
+ goto error_io;
+ }
+
+ strncpy(msg.name, event->name, sizeof(msg.name));
+ ret = send_payload(app->sock, &msg, sizeof(msg));
+ if (ret < 0) {
+ goto error_io;
+ }
+
+ ret = recv_reply(app->sock, &reply, sizeof(reply));
+ if (ret < 0) {
+ goto error_io;
+ }
+
+ switch (be32toh(reply.ret_code)) {
+ case JUL_RET_CODE_SUCCESS:
+ break;
+ case JUL_RET_CODE_UNKNOWN_NAME:
+ ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
+ goto error;
+ default:
+ ERR("Java agent returned an unknown code: %" PRIu32,
+ be32toh(reply.ret_code));
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+
+ return LTTNG_OK;
+
+error_io:
+ ret = LTTNG_ERR_UST_ENABLE_FAIL;
+error:
+ return ret;
+}
+
+/*
+ * Internal disable JUL event call on a JUL application. This function
+ * communicates with the Java agent to disable a given event (Logger name).
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+static int disable_event(struct jul_app *app, struct jul_event *event)
+{
+ int ret;
+ uint64_t data_size;
+ struct lttcomm_jul_disable msg;
+ struct lttcomm_jul_generic_reply reply;
+
+ assert(app);
+ assert(app->sock);
+ assert(event);
+
+ DBG2("JUL disabling event %s for app pid: %d and socket %d", event->name,
+ app->pid, app->sock->fd);
+
+ data_size = sizeof(msg);
+
+ ret = send_header(app->sock, data_size, JUL_CMD_DISABLE, 0);
+ if (ret < 0) {
+ goto error_io;
+ }
+
+ strncpy(msg.name, event->name, sizeof(msg.name));
+ ret = send_payload(app->sock, &msg, sizeof(msg));
+ if (ret < 0) {
+ goto error_io;
+ }
+
+ ret = recv_reply(app->sock, &reply, sizeof(reply));
+ if (ret < 0) {
+ goto error_io;
+ }
+
+ switch (be32toh(reply.ret_code)) {
+ case JUL_RET_CODE_SUCCESS:
+ break;
+ case JUL_RET_CODE_UNKNOWN_NAME:
+ ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
+ goto error;
+ default:
+ ERR("Java agent returned an unknown code: %" PRIu32,
+ be32toh(reply.ret_code));
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+
+ return LTTNG_OK;
+
+error_io:
+ ret = LTTNG_ERR_UST_DISABLE_FAIL;
+error:
+ return ret;
+}
+
+/*
+ * Enable JUL event on every JUL applications registered with the session
+ * daemon.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int jul_enable_event(struct jul_event *event)
+{
+ int ret;
+ struct jul_app *app;
+ struct lttng_ht_iter iter;
+
+ assert(event);
+
+ rcu_read_lock();
+
+ cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, app,
+ node.node) {
+ /* Enable event on JUL application through TCP socket. */
+ ret = enable_event(app, event);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ event->enabled = 1;
+ ret = LTTNG_OK;
+
+error:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Disable JUL event on every JUL applications registered with the session
+ * daemon.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int jul_disable_event(struct jul_event *event)
+{
+ int ret;
+ struct jul_app *app;
+ struct lttng_ht_iter iter;
+
+ assert(event);
+
+ rcu_read_lock();
+
+ cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, app,
+ node.node) {
+ /* Enable event on JUL application through TCP socket. */
+ ret = disable_event(app, event);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ event->enabled = 0;
+ ret = LTTNG_OK;
+
+error:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Ask every java agent for the list of possible event (logger name). Events is
+ * allocated with the events of every JUL application.
+ *
+ * Return the number of events or else a negative value.
+ */
+int jul_list_events(struct lttng_event **events)
+{
+ int ret;
+ size_t nbmem, count = 0;
+ struct jul_app *app;
+ struct lttng_event *tmp_events = NULL;
+ struct lttng_ht_iter iter;
+
+ assert(events);
+
+ nbmem = UST_APP_EVENT_LIST_SIZE;
+ tmp_events = zmalloc(nbmem * sizeof(*tmp_events));
+ if (!tmp_events) {
+ PERROR("zmalloc jul list events");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, app,
+ node.node) {
+ ssize_t nb_ev;
+ struct lttng_event *jul_events;
+
+ nb_ev = list_events(app, &jul_events);
+ if (nb_ev < 0) {
+ ret = nb_ev;
+ rcu_read_unlock();
+ goto error;
+ }
+
+ if (count >= nbmem) {
+ /* In case the realloc fails, we free the memory */
+ void *ptr;
+
+ DBG2("Reallocating JUL event list from %zu to %zu entries", nbmem,
+ 2 * nbmem);
+ nbmem *= 2;
+ ptr = realloc(tmp_events, nbmem * sizeof(*tmp_events));
+ if (!ptr) {
+ PERROR("realloc JUL events");
+ ret = -ENOMEM;
+ rcu_read_unlock();
+ goto error;
+ }
+ tmp_events = ptr;
+ }
+ memcpy(tmp_events + (count * sizeof(*tmp_events)), jul_events,
+ nb_ev * sizeof(*tmp_events));
+ free(jul_events);
+ count += nb_ev;
+ }
+ rcu_read_unlock();
+
+ ret = count;
+ *events = tmp_events;
+ return ret;
+
+error:
+ free(tmp_events);
+ return ret;
+}
+
+/*
+ * Create a JUL app object using the given PID.
+ *
+ * Return newly allocated object or else NULL on error.
+ */
+struct jul_app *jul_create_app(pid_t pid, struct lttcomm_sock *sock)
+{
+ struct jul_app *app;
+
+ assert(sock);
+
+ app = zmalloc(sizeof(*app));
+ if (!app) {
+ PERROR("zmalloc JUL create");
+ goto error;
+ }
+
+ app->pid = pid;
+ app->sock = sock;
+ /* Flag it invalid until assignation. */
+ app->ust_app_sock = -1;
+ lttng_ht_node_init_ulong(&app->node, (unsigned long) app->sock->fd);
+
+error:
+ return app;
+}
+
+/*
+ * Lookup JUL app by socket in the global hash table.
+ *
+ * RCU read side lock MUST be acquired.
+ *
+ * Return object if found else NULL.
+ */
+struct jul_app *jul_find_app_by_sock(int sock)
+{
+ struct lttng_ht_node_ulong *node;
+ struct lttng_ht_iter iter;
+ struct jul_app *app;
+
+ assert(sock >= 0);
+
+ lttng_ht_lookup(jul_apps_ht_by_sock, (void *)((unsigned long) sock), &iter);
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (node == NULL) {
+ goto error;
+ }
+ app = caa_container_of(node, struct jul_app, node);
+
+ DBG3("JUL app pid %d found by sock %d.", app->pid, sock);
+ return app;
+
+error:
+ DBG3("JUL app NOT found by sock %d.", sock);
+ return NULL;
+}
+
+/*
+ * Add JUL application object to a given hash table.
+ */
+void jul_add_app(struct jul_app *app)
+{
+ assert(app);
+
+ DBG3("JUL adding app sock: %d and pid: %d to ht", app->sock->fd, app->pid);
+
+ rcu_read_lock();
+ lttng_ht_add_unique_ulong(jul_apps_ht_by_sock, &app->node);
+ rcu_read_unlock();
+}
+
+/*
+ * Attach a given JUL application to an UST app object. This is done by copying
+ * the socket fd value into the ust app obj. atomically.
+ */
+void jul_attach_app(struct jul_app *japp)
+{
+ struct ust_app *uapp;
+
+ assert(japp);
+
+ rcu_read_lock();
+ uapp = ust_app_find_by_pid(japp->pid);
+ if (!uapp) {
+ goto end;
+ }
+
+ uatomic_set(&uapp->jul_app_sock, japp->sock->fd);
+
+ DBG3("JUL app pid: %d, sock: %d attached to UST app.", japp->pid,
+ japp->sock->fd);
+
+end:
+ rcu_read_unlock();
+ return;
+}
+
+/*
+ * Remove JUL app. reference from an UST app object and set it to NULL.
+ */
+void jul_detach_app(struct jul_app *japp)
+{
+ struct ust_app *uapp;
+
+ assert(japp);
+
+ rcu_read_lock();
+
+ if (japp->ust_app_sock < 0) {
+ goto end;
+ }
+
+ uapp = ust_app_find_by_sock(japp->ust_app_sock);
+ if (!uapp) {
+ goto end;
+ }
+
+ uapp->jul_app_sock = -1;
+
+end:
+ rcu_read_unlock();
+ return;
+}
+
+/*
+ * Delete JUL application from the global hash table.
+ */
+void jul_delete_app(struct jul_app *app)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ assert(app);
+
+ DBG3("JUL deleting app pid: %d and sock: %d", app->pid, app->sock->fd);
+
+ iter.iter.node = &app->node.node;
+ rcu_read_lock();
+ ret = lttng_ht_del(jul_apps_ht_by_sock, &iter);
+ rcu_read_unlock();
+ assert(!ret);
+}
+
+/*
+ * Destroy a JUL application object by detaching it from its corresponding UST
+ * app if one, closing the socket and freeing the memory.
+ */
+void jul_destroy_app(struct jul_app *app)
+{
+ assert(app);
+
+ if (app->sock) {
+ app->sock->ops->close(app->sock);
+ lttcomm_destroy_sock(app->sock);
+ }
+
+ call_rcu(&app->node.head, destroy_app_jul_rcu);
+}
+
+/*
+ * Initialize an already allocated JUL domain object.
+ *
+ * Return 0 on success or else a negative errno value.
+ */
+int jul_init_domain(struct jul_domain *dom)
+{
+ int ret;
+
+ assert(dom);
+
+ dom->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ if (!dom->events) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Create a newly allocated JUL event data structure. If name is valid, it's
+ * copied into the created event.
+ *
+ * Return a new object else NULL on error.
+ */
+struct jul_event *jul_create_event(const char *name)
+{
+ struct jul_event *event;
+
+ DBG3("JUL create new event with name %s", name);
+
+ event = zmalloc(sizeof(*event));
+ if (!event) {
+ goto error;
+ }
+
+ if (name) {
+ strncpy(event->name, name, sizeof(event->name));
+ event->name[sizeof(event->name) - 1] = '\0';
+ lttng_ht_node_init_str(&event->node, event->name);
+ }
+
+error:
+ return event;
+}
+
+/*
+ * Unique add of a JUL event to a given domain.
+ */
+void jul_add_event(struct jul_event *event, struct jul_domain *dom)
+{
+ assert(event);
+ assert(dom);
+ assert(dom->events);
+
+ DBG3("JUL adding event %s to domain", event->name);
+
+ rcu_read_lock();
+ lttng_ht_add_unique_str(dom->events, &event->node);
+ rcu_read_unlock();
+ dom->being_used = 1;
+}
+
+/*
+ * Find a JUL event in the given domain using name.
+ *
+ * RCU read side lock MUST be acquired.
+ *
+ * Return object if found else NULL.
+ */
+struct jul_event *jul_find_by_name(const char *name, struct jul_domain *dom)
+{
+ struct lttng_ht_node_str *node;
+ struct lttng_ht_iter iter;
+
+ assert(name);
+ assert(dom);
+ assert(dom->events);
+
+ lttng_ht_lookup(dom->events, (void *)name, &iter);
+ node = lttng_ht_iter_get_node_str(&iter);
+ if (node == NULL) {
+ goto error;
+ }
+
+ DBG3("JUL found by name %s in domain.", name);
+ return caa_container_of(node, struct jul_event, node);
+
+error:
+ DBG3("JUL NOT found by name %s in domain.", name);
+ return NULL;
+}
+
+/*
+ * Delete JUL event from given domain. Events hash table MUST be initialized.
+ */
+void jul_delete_event(struct jul_event *event, struct jul_domain *dom)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ assert(event);
+ assert(dom);
+ assert(dom->events);
+
+ DBG3("JUL deleting event %s from domain", event->name);
+
+ iter.iter.node = &event->node.node;
+ rcu_read_lock();
+ ret = lttng_ht_del(dom->events, &iter);
+ rcu_read_unlock();
+ assert(!ret);
+}
+
+/*
+ * Free given JUl event. After this call, the pointer is not usable anymore.
+ */
+void jul_destroy_event(struct jul_event *event)
+{
+ assert(event);
+
+ free(event);
+}
+
+/*
+ * Destroy a JUL domain completely. Note that the given pointer is NOT freed
+ * thus a reference can be passed to this function.
+ */
+void jul_destroy_domain(struct jul_domain *dom)
+{
+ struct lttng_ht_node_str *node;
+ struct lttng_ht_iter iter;
+
+ assert(dom);
+
+ DBG3("JUL destroy domain");
+
+ /*
+ * Just ignore if no events hash table exists. This is possible if for
+ * instance a JUL domain object was allocated but not initialized.
+ */
+ if (!dom->events) {
+ return;
+ }
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(dom->events->ht, &iter.iter, node, node) {
+ int ret;
+
+ ret = lttng_ht_del(dom->events, &iter);
+ assert(!ret);
+ call_rcu(&node->head, destroy_event_jul_rcu);
+ }
+ rcu_read_unlock();
+
+ lttng_ht_destroy(dom->events);
+}
+
+/*
+ * Initialize JUL subsystem.
+ */
+int jul_init(void)
+{
+ jul_apps_ht_by_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ if (!jul_apps_ht_by_sock) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Update a JUL application (given socket) using the given domain.
+ *
+ * Note that this function is most likely to be used with a tracing session
+ * thus the caller should make sure to hold the appropriate lock(s).
+ */
+void jul_update(struct jul_domain *domain, int sock)
+{
+ int ret;
+ struct jul_app *app;
+ struct jul_event *event;
+ struct lttng_ht_iter iter;
+
+ assert(domain);
+ assert(sock >= 0);
+
+ DBG("JUL updating app socket %d", sock);
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(domain->events->ht, &iter.iter, event, node.node) {
+ /* Skip event if disabled. */
+ if (!event->enabled) {
+ continue;
+ }
+
+ app = jul_find_app_by_sock(sock);
+ /*
+ * We are in the registration path thus if the application is gone,
+ * there is a serious code flow error.
+ */
+ assert(app);
+
+ ret = enable_event(app, event);
+ if (ret != LTTNG_OK) {
+ DBG2("JUL update unable to enable event %s on app pid: %d sock %d",
+ event->name, app->pid, app->sock->fd);
+ /* Let's try the others here and don't assume the app is dead. */
+ continue;
+ }
+ }
+ rcu_read_unlock();
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _JUL_H
+#define _JUL_H
+
+#define _GNU_SOURCE
+#include <inttypes.h>
+
+#include <common/hashtable/hashtable.h>
+#include <lttng/lttng.h>
+
+/*
+ * Hash table that contains the JUL app created upon registration indexed by
+ * socket.
+ */
+struct lttng_ht *jul_apps_ht_by_sock;
+
+/*
+ * Registration message payload from a JUL application. The PID is used to find
+ * back the corresponding UST app object so both socket can be linked.
+ */
+struct jul_register_msg {
+ uint32_t pid;
+};
+
+/*
+ * JUL application object created after a successful registration. This object
+ * is kept inside an UST app.
+ */
+struct jul_app {
+ /*
+ * PID sent during registration of a JUL application.
+ */
+ pid_t pid;
+
+ /*
+ * JUL TCP socket that was created upon registration.
+ */
+ struct lttcomm_sock *sock;
+
+ /*
+ * Associated UST app. socket. To get a reference to the ust application
+ * object corresponding to that socket, a lookup MUST be done each time
+ * since there is important synchronization issue for the lockless hash
+ * table shared accross multiple threads.
+ */
+ int ust_app_sock;
+
+ /* Initialized with the JUL sock value. */
+ struct lttng_ht_node_ulong node;
+};
+
+/*
+ * Java Util Logging event representation.
+ */
+struct jul_event {
+ /*
+ * Name of the event which is directly mapped to a Logger object name in
+ * the JUL API.
+ */
+ char name[LTTNG_SYMBOL_NAME_LEN];
+
+ /*
+ * Tells if the event is enabled or not on the JUL Agent.
+ */
+ unsigned int enabled:1;
+
+ /*
+ * Hash table nodes of the JUL domain. Indexed by name string.
+ */
+ struct lttng_ht_node_str node;
+};
+
+/*
+ * Top level data structure in a UST session containing JUL event name created
+ * for it.
+ */
+struct jul_domain {
+ /*
+ * This indicates if that domain is being used meaning if at least one
+ * event has been at some point in time added to it. This is used so when
+ * listing domains for a session, we can tell or not if the JUL is actually
+ * enabled.
+ */
+ unsigned int being_used:1;
+ /*
+ * Contains JUL event indexed by name.
+ */
+ struct lttng_ht *events;
+};
+
+/* Initialize JUL domain subsystem. */
+int jul_init(void);
+
+/* Initialize an already allocated JUL domain. */
+int jul_init_domain(struct jul_domain *dom);
+void jul_destroy_domain(struct jul_domain *dom);
+
+/* JUL event API. */
+struct jul_event *jul_create_event(const char *name);
+void jul_add_event(struct jul_event *event, struct jul_domain *dom);
+struct jul_event *jul_find_by_name(const char *name, struct jul_domain *dom);
+void jul_delete_event(struct jul_event *event, struct jul_domain *dom);
+void jul_destroy_event(struct jul_event *event);
+
+/* JUL app API. */
+struct jul_app *jul_create_app(pid_t pid, struct lttcomm_sock *sock);
+void jul_add_app(struct jul_app *app);
+void jul_delete_app(struct jul_app *app);
+struct jul_app *jul_find_app_by_sock(int sock);
+void jul_attach_app(struct jul_app *japp);
+void jul_detach_app(struct jul_app *app);
+void jul_destroy_app(struct jul_app *app);
+
+/* JUL action API */
+int jul_enable_event(struct jul_event *event);
+int jul_disable_event(struct jul_event *event);
+void jul_update(struct jul_domain *domain, int sock);
+int jul_list_events(struct lttng_event **events);
+
+#endif /* _JUL_H */
#include <common/defaults.h>
#include "consumer.h"
-#include "health.h"
+#include "health-sessiond.h"
#include "kernel-consumer.h"
static char *create_channel_path(struct consumer_output *consumer,
CONSUMER_CHANNEL_TYPE_DATA,
channel->channel->attr.tracefile_size,
channel->channel->attr.tracefile_count,
- monitor);
+ monitor,
+ channel->channel->attr.live_timer_interval);
health_code_update();
DEFAULT_KERNEL_CHANNEL_OUTPUT,
CONSUMER_CHANNEL_TYPE_METADATA,
0, 0,
- monitor);
+ monitor, 0);
health_code_update();
assert(channel);
assert(socket);
- assert(socket->fd >= 0);
DBG("Sending kernel consumer destroy channel key %d", channel->fd);
assert(metadata);
assert(socket);
- assert(socket->fd >= 0);
DBG("Sending kernel consumer destroy channel key %d", metadata->fd);
goto error;
}
- DBG3("Kernel create channel %s with attr: %d, %" PRIu64 ", %" PRIu64 ", %u, %u, %d",
+ DBG3("Kernel create channel %s with attr: %d, %" PRIu64 ", %" PRIu64 ", %u, %u, %d, %d",
chan->name, lkc->channel->attr.overwrite,
lkc->channel->attr.subbuf_size, lkc->channel->attr.num_subbuf,
lkc->channel->attr.switch_timer_interval, lkc->channel->attr.read_timer_interval,
- lkc->channel->attr.output);
+ lkc->channel->attr.live_timer_interval, lkc->channel->attr.output);
/* Kernel tracer channel creation */
ret = kernctl_create_channel(session->fd, &lkc->channel->attr);
/*
* Take a snapshot for a given kernel session.
*
- * Return 0 on success or else a negative value.
+ * Return 0 on success or else return a LTTNG_ERR code.
*/
int kernel_snapshot_record(struct ltt_kernel_session *ksess,
- struct snapshot_output *output, int wait)
+ struct snapshot_output *output, int wait, unsigned int nb_streams)
{
- int ret, saved_metadata_fd;
+ int err, ret, saved_metadata_fd;
struct consumer_socket *socket;
struct lttng_ht_iter iter;
struct ltt_kernel_metadata *saved_metadata;
+ uint64_t max_size_per_stream = 0;
assert(ksess);
assert(ksess->consumer);
goto error_open_stream;
}
+ if (output->max_size > 0 && nb_streams > 0) {
+ max_size_per_stream = output->max_size / nb_streams;
+ }
+
/* Send metadata to consumer and snapshot everything. */
cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter,
socket, node.node) {
struct consumer_output *saved_output;
struct ltt_kernel_channel *chan;
- /* Code flow error */
- assert(socket->fd >= 0);
/*
* Temporarly switch consumer output for our snapshot output. As long
/* For each channel, ask the consumer to snapshot it. */
cds_list_for_each_entry(chan, &ksess->channel_list.head, list) {
+ if (max_size_per_stream &&
+ chan->channel->attr.subbuf_size > max_size_per_stream) {
+ ret = LTTNG_ERR_INVALID;
+ DBG3("Kernel snapshot record maximum stream size %" PRIu64
+ " is smaller than subbuffer size of %" PRIu64,
+ max_size_per_stream, chan->channel->attr.subbuf_size);
+ (void) kernel_consumer_destroy_metadata(socket,
+ ksess->metadata);
+ goto error_consumer;
+ }
+
pthread_mutex_lock(socket->lock);
ret = consumer_snapshot_channel(socket, chan->fd, output, 0,
- ksess->uid, ksess->gid, DEFAULT_KERNEL_TRACE_DIR, wait);
+ ksess->uid, ksess->gid,
+ DEFAULT_KERNEL_TRACE_DIR, wait,
+ max_size_per_stream);
pthread_mutex_unlock(socket->lock);
if (ret < 0) {
ret = LTTNG_ERR_KERN_CONSUMER_FAIL;
+ (void) kernel_consumer_destroy_metadata(socket,
+ ksess->metadata);
goto error_consumer;
}
}
/* Snapshot metadata, */
pthread_mutex_lock(socket->lock);
ret = consumer_snapshot_channel(socket, ksess->metadata->fd, output,
- 1, ksess->uid, ksess->gid, DEFAULT_KERNEL_TRACE_DIR, wait);
+ 1, ksess->uid, ksess->gid,
+ DEFAULT_KERNEL_TRACE_DIR, wait, max_size_per_stream);
pthread_mutex_unlock(socket->lock);
if (ret < 0) {
ret = LTTNG_ERR_KERN_CONSUMER_FAIL;
error_consumer:
/* Close newly opened metadata stream. It's now on the consumer side. */
- ret = close(ksess->metadata_stream_fd);
- if (ret < 0) {
+ err = close(ksess->metadata_stream_fd);
+ if (err < 0) {
PERROR("close snapshot kernel");
}
void kernel_destroy_session(struct ltt_kernel_session *ksess);
void kernel_destroy_channel(struct ltt_kernel_channel *kchan);
int kernel_snapshot_record(struct ltt_kernel_session *ksess,
- struct snapshot_output *output, int wait);
+ struct snapshot_output *output, int wait, unsigned int nb_streams);
int init_kernel_workarounds(void);
*/
extern long page_size;
+/*
+ * Global set once in main(). JUL TCP port for registration.
+ */
+extern unsigned int jul_tcp_port;
+
int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size);
int sessiond_check_thread_quit_pipe(int fd, uint32_t events);
#define LTTNG_UST_COMM_MAGIC 0xC57C57C5
/* Version for ABI between liblttng-ust, sessiond, consumerd */
-#define LTTNG_UST_ABI_MAJOR_VERSION 4
+#define LTTNG_UST_ABI_MAJOR_VERSION 5
#define LTTNG_UST_ABI_MINOR_VERSION 0
enum lttng_ust_instrumentation {
LTTNG_UST_CONTEXT_VPID = 1,
LTTNG_UST_CONTEXT_PTHREAD_ID = 2,
LTTNG_UST_CONTEXT_PROCNAME = 3,
+ LTTNG_UST_CONTEXT_IP = 4,
};
#define LTTNG_UST_CONTEXT_PADDING1 16
#include "ust-consumer.h"
#include "utils.h"
#include "fd-limit.h"
-#include "health.h"
+#include "health-sessiond.h"
#include "testpoint.h"
#include "ust-thread.h"
+#include "jul-thread.h"
#define CONSUMERD_FILE "lttng-consumerd"
-/* Const values */
-const char default_tracing_group[] = DEFAULT_TRACING_GROUP;
-
const char *progname;
-const char *opt_tracing_group;
+static const char *tracing_group_name = DEFAULT_TRACING_GROUP;
static const char *opt_pidfile;
static int opt_sig_parent;
static int opt_verbose_consumer;
.cmd_unix_sock_path = DEFAULT_KCONSUMERD_CMD_SOCK_PATH,
.err_sock = -1,
.cmd_sock = -1,
- .metadata_sock.fd = -1,
.pid_mutex = PTHREAD_MUTEX_INITIALIZER,
.lock = PTHREAD_MUTEX_INITIALIZER,
.cond = PTHREAD_COND_INITIALIZER,
.cmd_unix_sock_path = DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH,
.err_sock = -1,
.cmd_sock = -1,
- .metadata_sock.fd = -1,
.pid_mutex = PTHREAD_MUTEX_INITIALIZER,
.lock = PTHREAD_MUTEX_INITIALIZER,
.cond = PTHREAD_COND_INITIALIZER,
.cmd_unix_sock_path = DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH,
.err_sock = -1,
.cmd_sock = -1,
- .metadata_sock.fd = -1,
.pid_mutex = PTHREAD_MUTEX_INITIALIZER,
.lock = PTHREAD_MUTEX_INITIALIZER,
.cond = PTHREAD_COND_INITIALIZER,
static pthread_t dispatch_thread;
static pthread_t health_thread;
static pthread_t ht_cleanup_thread;
+static pthread_t jul_reg_thread;
/*
* UST registration command queue. This queue is tied with a futex and uses a N
/* Set in main() with the current page size. */
long page_size;
+/* Application health monitoring */
+struct health_app *health_sessiond;
+
+/* JUL TCP port for registration. Used by the JUL thread. */
+unsigned int jul_tcp_port = DEFAULT_JUL_TCP_PORT;
+
static
void setup_consumerd_path(void)
{
return 0;
}
-/*
- * Return group ID of the tracing group or -1 if not found.
- */
-static gid_t allowed_group(void)
-{
- struct group *grp;
-
- if (opt_tracing_group) {
- grp = getgrnam(opt_tracing_group);
- } else {
- grp = getgrnam(default_tracing_group);
- }
- if (!grp) {
- return -1;
- } else {
- return grp->gr_gid;
- }
-}
-
/*
* Init thread quit pipe.
*
if (ustconsumer32_data.err_sock >= 0) {
ret = close(ustconsumer32_data.err_sock);
if (ret < 0) {
- PERROR("UST consumer32 err_sock close");
+ PERROR("UST consumerd32 err_sock close");
}
}
if (ustconsumer64_data.err_sock >= 0) {
ret = close(ustconsumer64_data.err_sock);
if (ret < 0) {
- PERROR("UST consumer64 err_sock close");
+ PERROR("UST consumerd64 err_sock close");
}
}
if (kconsumer_data.cmd_sock >= 0) {
if (ustconsumer32_data.cmd_sock >= 0) {
ret = close(ustconsumer32_data.cmd_sock);
if (ret < 0) {
- PERROR("UST consumer32 cmd_sock close");
+ PERROR("UST consumerd32 cmd_sock close");
}
}
if (ustconsumer64_data.cmd_sock >= 0) {
ret = close(ustconsumer64_data.cmd_sock);
if (ret < 0) {
- PERROR("UST consumer64 cmd_sock close");
+ PERROR("UST consumerd64 cmd_sock close");
}
}
}
static void cleanup(void)
{
int ret;
- char *cmd = NULL;
struct ltt_session *sess, *stmp;
+ char path[PATH_MAX];
DBG("Cleaning up");
- /* First thing first, stop all threads */
+ /*
+ * Close the thread quit pipe. It has already done its job,
+ * since we are now called.
+ */
utils_close_pipe(thread_quit_pipe);
/*
}
}
- DBG("Removing %s directory", rundir);
- ret = asprintf(&cmd, "rm -rf %s", rundir);
- if (ret < 0) {
- ERR("asprintf failed. Something is really wrong!");
- }
+ DBG("Removing sessiond and consumerd content of directory %s", rundir);
+
+ /* sessiond */
+ snprintf(path, PATH_MAX,
+ "%s/%s",
+ rundir, DEFAULT_LTTNG_SESSIOND_PIDFILE);
+ DBG("Removing %s", path);
+ (void) unlink(path);
+
+ /* kconsumerd */
+ snprintf(path, PATH_MAX,
+ DEFAULT_KCONSUMERD_ERR_SOCK_PATH,
+ rundir);
+ DBG("Removing %s", path);
+ (void) unlink(path);
+
+ snprintf(path, PATH_MAX,
+ DEFAULT_KCONSUMERD_PATH,
+ rundir);
+ DBG("Removing directory %s", path);
+ (void) rmdir(path);
+
+ /* ust consumerd 32 */
+ snprintf(path, PATH_MAX,
+ DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH,
+ rundir);
+ DBG("Removing %s", path);
+ (void) unlink(path);
+
+ snprintf(path, PATH_MAX,
+ DEFAULT_USTCONSUMERD32_PATH,
+ rundir);
+ DBG("Removing directory %s", path);
+ (void) rmdir(path);
+
+ /* ust consumerd 64 */
+ snprintf(path, PATH_MAX,
+ DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH,
+ rundir);
+ DBG("Removing %s", path);
+ (void) unlink(path);
+
+ snprintf(path, PATH_MAX,
+ DEFAULT_USTCONSUMERD64_PATH,
+ rundir);
+ DBG("Removing directory %s", path);
+ (void) rmdir(path);
- /* Remove lttng run directory */
- ret = system(cmd);
- if (ret < 0) {
- ERR("Unable to clean %s", rundir);
- }
- free(cmd);
free(rundir);
DBG("Cleaning up all sessions");
if (ret < 0) {
goto error;
}
+ /* Update the stream global counter */
+ ksess->stream_count_global += ret;
/*
* Have we already sent fds to the consumer? If yes, it means
rcu_read_lock();
cds_lfht_for_each_entry(ksess->consumer->socks->ht,
&iter.iter, socket, node.node) {
- /* Code flow error */
- assert(socket->fd >= 0);
-
pthread_mutex_lock(socket->lock);
ret = kernel_consumer_send_channel_stream(socket,
channel, ksess,
{
struct ltt_session *sess, *stmp;
+ /* Consumer is in an ERROR state. Stop any application update. */
+ if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) {
+ /* Stop the update process since the consumer is dead. */
+ return;
+ }
+
/* For all tracing session(s) */
cds_list_for_each_entry_safe(sess, stmp, &session_list_ptr->head, list) {
session_lock(sess);
DBG("[thread] Thread manage kernel started");
- health_register(HEALTH_TYPE_KERNEL);
+ health_register(health_sessiond, HEALTH_SESSIOND_TYPE_KERNEL);
/*
* This first step of the while is to clean this structure which could free
WARN("Kernel thread died unexpectedly. "
"Kernel tracing can continue but CPU hotplug is disabled.");
}
- health_unregister();
+ health_unregister(health_sessiond);
DBG("Kernel thread dying");
return NULL;
}
DBG("[thread] Manage consumer started");
- health_register(HEALTH_TYPE_CONSUMER);
+ health_register(health_sessiond, HEALTH_SESSIOND_TYPE_CONSUMER);
health_code_update();
/* Connect both socket, command and metadata. */
consumer_data->cmd_sock =
lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path);
- consumer_data->metadata_sock.fd =
+ consumer_data->metadata_fd =
lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path);
- if (consumer_data->cmd_sock < 0 ||
- consumer_data->metadata_sock.fd < 0) {
+ if (consumer_data->cmd_sock < 0
+ || consumer_data->metadata_fd < 0) {
PERROR("consumer connect cmd socket");
/* On error, signal condition and quit. */
signal_consumer_condition(consumer_data, -1);
goto error;
}
+ consumer_data->metadata_sock.fd_ptr = &consumer_data->metadata_fd;
/* Create metadata socket lock. */
consumer_data->metadata_sock.lock = zmalloc(sizeof(pthread_mutex_t));
if (consumer_data->metadata_sock.lock == NULL) {
signal_consumer_condition(consumer_data, 1);
DBG("Consumer command socket ready (fd: %d", consumer_data->cmd_sock);
DBG("Consumer metadata socket ready (fd: %d)",
- consumer_data->metadata_sock.fd);
+ consumer_data->metadata_fd);
} else {
ERR("consumer error when waiting for SOCK_READY : %s",
lttcomm_get_readable_code(-code));
}
/* Add metadata socket that is successfully connected. */
- ret = lttng_poll_add(&events, consumer_data->metadata_sock.fd,
+ ret = lttng_poll_add(&events, consumer_data->metadata_fd,
LPOLLIN | LPOLLRDHUP);
if (ret < 0) {
goto error;
lttcomm_get_readable_code(-code));
goto exit;
- } else if (pollfd == consumer_data->metadata_sock.fd) {
+ } else if (pollfd == consumer_data->metadata_fd) {
/* UST metadata requests */
ret = ust_consumer_metadata_request(
&consumer_data->metadata_sock);
exit:
error:
+ /*
+ * We lock here because we are about to close the sockets and some other
+ * thread might be using them so get exclusive access which will abort all
+ * other consumer command by other threads.
+ */
+ pthread_mutex_lock(&consumer_data->lock);
+
/* Immediately set the consumerd state to stopped */
if (consumer_data->type == LTTNG_CONSUMER_KERNEL) {
uatomic_set(&kernel_consumerd_state, CONSUMER_ERROR);
if (ret) {
PERROR("close");
}
+ consumer_data->err_sock = -1;
}
if (consumer_data->cmd_sock >= 0) {
ret = close(consumer_data->cmd_sock);
if (ret) {
PERROR("close");
}
+ consumer_data->cmd_sock = -1;
}
- if (consumer_data->metadata_sock.fd >= 0) {
- ret = close(consumer_data->metadata_sock.fd);
+ if (*consumer_data->metadata_sock.fd_ptr >= 0) {
+ ret = close(*consumer_data->metadata_sock.fd_ptr);
if (ret) {
PERROR("close");
}
}
- /* Cleanup metadata socket mutex. */
- pthread_mutex_destroy(consumer_data->metadata_sock.lock);
- free(consumer_data->metadata_sock.lock);
if (sock >= 0) {
ret = close(sock);
unlink(consumer_data->err_unix_sock_path);
unlink(consumer_data->cmd_unix_sock_path);
consumer_data->pid = 0;
+ pthread_mutex_unlock(&consumer_data->lock);
+
+ /* Cleanup metadata socket mutex. */
+ pthread_mutex_destroy(consumer_data->metadata_sock.lock);
+ free(consumer_data->metadata_sock.lock);
lttng_poll_clean(&events);
error_poll:
health_error();
ERR("Health error occurred in %s", __func__);
}
- health_unregister();
+ health_unregister(health_sessiond);
DBG("consumer thread cleanup completed");
return NULL;
rcu_register_thread();
rcu_thread_online();
- health_register(HEALTH_TYPE_APP_MANAGE);
+ health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_MANAGE);
if (testpoint(thread_manage_apps)) {
goto error_testpoint;
goto error;
}
- /* Set socket timeout for both receiving and ending */
- (void) lttcomm_setsockopt_rcv_timeout(sock,
- app_socket_timeout);
- (void) lttcomm_setsockopt_snd_timeout(sock,
- app_socket_timeout);
-
DBG("Apps with sock %d added to poll set", sock);
health_code_update();
health_error();
ERR("Health error occurred in %s", __func__);
}
- health_unregister();
+ health_unregister(health_sessiond);
DBG("Application communication apps thread cleanup complete");
rcu_thread_offline();
rcu_unregister_thread();
* Send a socket to a thread This is called from the dispatch UST registration
* thread once all sockets are set for the application.
*
+ * The sock value can be invalid, we don't really care, the thread will handle
+ * it and make the necessary cleanup if so.
+ *
* On success, return 0 else a negative value being the errno message of the
* write().
*/
{
int ret;
- /* Sockets MUST be set or else this should not have been called. */
- assert(fd >= 0);
- assert(sock >= 0);
+ /*
+ * It's possible that the FD is set as invalid with -1 concurrently just
+ * before calling this function being a shutdown state of the thread.
+ */
+ if (fd < 0) {
+ ret = -EBADF;
+ goto error;
+ }
do {
ret = write(fd, &sock, sizeof(sock));
.count = 0,
};
- health_register(HEALTH_TYPE_APP_REG_DISPATCH);
+ health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH);
health_code_update();
if (ret < 0) {
rcu_read_unlock();
session_unlock_list();
- /* No notify thread, stop the UST tracing. */
+ /*
+ * No notify thread, stop the UST tracing. However, this is
+ * not an internal error of the this thread thus setting
+ * the health error code to a normal exit.
+ */
+ err = 0;
goto error;
}
if (ret < 0) {
rcu_read_unlock();
session_unlock_list();
- /* No apps. thread, stop the UST tracing. */
+ /*
+ * No apps. thread, stop the UST tracing. However, this is
+ * not an internal error of the this thread thus setting
+ * the health error code to a normal exit.
+ */
+ err = 0;
goto error;
}
health_error();
ERR("Health error occurred in %s", __func__);
}
- health_unregister();
+ health_unregister(health_sessiond);
return NULL;
}
DBG("[thread] Manage application registration started");
- health_register(HEALTH_TYPE_APP_REG);
+ health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG);
if (testpoint(thread_registration_apps)) {
goto error_testpoint;
goto error;
}
+ /*
+ * Set socket timeout for both receiving and ending.
+ * app_socket_timeout is in seconds, whereas
+ * lttcomm_setsockopt_rcv_timeout and
+ * lttcomm_setsockopt_snd_timeout expect msec as
+ * parameter.
+ */
+ (void) lttcomm_setsockopt_rcv_timeout(sock,
+ app_socket_timeout * 1000);
+ (void) lttcomm_setsockopt_snd_timeout(sock,
+ app_socket_timeout * 1000);
+
/*
* Set the CLOEXEC flag. Return code is useless because
* either way, the show must go on.
error_create_poll:
error_testpoint:
DBG("UST Registration thread cleanup complete");
- health_unregister();
+ health_unregister(health_sessiond);
return NULL;
}
"lttng-consumerd", verbosity, "-k",
"--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path,
"--consumerd-err-sock", consumer_data->err_unix_sock_path,
+ "--group", tracing_group_name,
NULL);
break;
case LTTNG_CONSUMER64_UST:
ret = execl(consumerd64_bin, "lttng-consumerd", verbosity, "-u",
"--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path,
"--consumerd-err-sock", consumer_data->err_unix_sock_path,
+ "--group", tracing_group_name,
NULL);
if (consumerd64_libdir[0] != '\0') {
free(tmpnew);
ret = execl(consumerd32_bin, "lttng-consumerd", verbosity, "-u",
"--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path,
"--consumerd-err-sock", consumer_data->err_unix_sock_path,
+ "--group", tracing_group_name,
NULL);
if (consumerd32_libdir[0] != '\0') {
free(tmpnew);
return ret;
}
-/*
- * Compute health status of each consumer. If one of them is zero (bad
- * state), we return 0.
- */
-static int check_consumer_health(void)
-{
- int ret;
-
- ret = health_check_state(HEALTH_TYPE_CONSUMER);
-
- DBG3("Health consumer check %d", ret);
-
- return ret;
-}
-
/*
* Setup necessary data for kernel tracer action.
*/
consumer = session->kernel_session->consumer;
dir_name = DEFAULT_KERNEL_TRACE_DIR;
break;
+ case LTTNG_DOMAIN_JUL:
case LTTNG_DOMAIN_UST:
DBG3("Copying tracing session consumer output in UST session");
if (session->ust_session->consumer) {
assert(session->consumer);
switch (domain->type) {
+ case LTTNG_DOMAIN_JUL:
case LTTNG_DOMAIN_UST:
break;
default:
lus->uid = session->uid;
lus->gid = session->gid;
lus->output_traces = session->output_traces;
+ lus->snapshot_mode = session->snapshot_mode;
+ lus->live_timer_interval = session->live_timer;
session->ust_session = lus;
/* Copy session output to the newly created UST session */
session->kernel_session->uid = session->uid;
session->kernel_session->gid = session->gid;
session->kernel_session->output_traces = session->output_traces;
+ session->kernel_session->snapshot_mode = session->snapshot_mode;
return LTTNG_OK;
switch (cmd_ctx->lsm->cmd_type) {
case LTTNG_CREATE_SESSION:
+ case LTTNG_CREATE_SESSION_SNAPSHOT:
+ case LTTNG_CREATE_SESSION_LIVE:
case LTTNG_DESTROY_SESSION:
case LTTNG_LIST_SESSIONS:
case LTTNG_LIST_DOMAINS:
/* Commands that DO NOT need a session. */
switch (cmd_ctx->lsm->cmd_type) {
case LTTNG_CREATE_SESSION:
+ case LTTNG_CREATE_SESSION_SNAPSHOT:
+ case LTTNG_CREATE_SESSION_LIVE:
case LTTNG_CALIBRATE:
case LTTNG_LIST_SESSIONS:
case LTTNG_LIST_TRACEPOINTS:
}
break;
+ case LTTNG_DOMAIN_JUL:
case LTTNG_DOMAIN_UST:
{
+ if (!ust_app_supported()) {
+ ret = LTTNG_ERR_NO_UST;
+ goto error;
+ }
/* Consumer is in an ERROR state. Report back to client */
if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) {
ret = LTTNG_ERR_NO_USTCONSUMERD;
if (cmd_ctx->lsm->cmd_type == LTTNG_START_TRACE ||
cmd_ctx->lsm->cmd_type == LTTNG_STOP_TRACE) {
switch (cmd_ctx->lsm->domain.type) {
+ case LTTNG_DOMAIN_JUL:
case LTTNG_DOMAIN_UST:
if (uatomic_read(&ust_consumerd_state) != CONSUMER_STARTED) {
ret = LTTNG_ERR_NO_USTCONSUMERD;
}
ret = cmd_create_session_uri(cmd_ctx->lsm->session.name, uris, nb_uri,
- &cmd_ctx->creds);
+ &cmd_ctx->creds, 0);
free(uris);
cmd_ctx->lsm->u.snapshot_record.wait);
break;
}
+ case LTTNG_CREATE_SESSION_SNAPSHOT:
+ {
+ size_t nb_uri, len;
+ struct lttng_uri *uris = NULL;
+
+ nb_uri = cmd_ctx->lsm->u.uri.size;
+ len = nb_uri * sizeof(struct lttng_uri);
+
+ if (nb_uri > 0) {
+ uris = zmalloc(len);
+ if (uris == NULL) {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+
+ /* Receive variable len data */
+ DBG("Waiting for %zu URIs from client ...", nb_uri);
+ ret = lttcomm_recv_unix_sock(sock, uris, len);
+ if (ret <= 0) {
+ DBG("No URIs received from client... continuing");
+ *sock_error = 1;
+ ret = LTTNG_ERR_SESSION_FAIL;
+ free(uris);
+ goto error;
+ }
+
+ if (nb_uri == 1 && uris[0].dtype != LTTNG_DST_PATH) {
+ DBG("Creating session with ONE network URI is a bad call");
+ ret = LTTNG_ERR_SESSION_FAIL;
+ free(uris);
+ goto error;
+ }
+ }
+
+ ret = cmd_create_session_snapshot(cmd_ctx->lsm->session.name, uris,
+ nb_uri, &cmd_ctx->creds);
+ free(uris);
+ break;
+ }
+ case LTTNG_CREATE_SESSION_LIVE:
+ {
+ size_t nb_uri, len;
+ struct lttng_uri *uris = NULL;
+
+ nb_uri = cmd_ctx->lsm->u.uri.size;
+ len = nb_uri * sizeof(struct lttng_uri);
+
+ if (nb_uri > 0) {
+ uris = zmalloc(len);
+ if (uris == NULL) {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+
+ /* Receive variable len data */
+ DBG("Waiting for %zu URIs from client ...", nb_uri);
+ ret = lttcomm_recv_unix_sock(sock, uris, len);
+ if (ret <= 0) {
+ DBG("No URIs received from client... continuing");
+ *sock_error = 1;
+ ret = LTTNG_ERR_SESSION_FAIL;
+ free(uris);
+ goto error;
+ }
+
+ if (nb_uri == 1 && uris[0].dtype != LTTNG_DST_PATH) {
+ DBG("Creating session with ONE network URI is a bad call");
+ ret = LTTNG_ERR_SESSION_FAIL;
+ free(uris);
+ goto error;
+ }
+ }
+
+ ret = cmd_create_session_uri(cmd_ctx->lsm->session.name, uris,
+ nb_uri, &cmd_ctx->creds, cmd_ctx->lsm->u.session_live.timer_interval);
+ free(uris);
+ break;
+ }
default:
ret = LTTNG_ERR_UND;
break;
int sock = -1, new_sock = -1, ret, i, pollfd, err = -1;
uint32_t revents, nb_fd;
struct lttng_poll_event events;
- struct lttcomm_health_msg msg;
- struct lttcomm_health_data reply;
+ struct health_comm_msg msg;
+ struct health_comm_reply reply;
DBG("[thread] Manage health check started");
goto error;
}
+ if (is_root) {
+ /* lttng health client socket path permissions */
+ ret = chown(health_unix_sock_path, 0,
+ utils_get_group_id(tracing_group_name));
+ if (ret < 0) {
+ ERR("Unable to set group on %s", health_unix_sock_path);
+ PERROR("chown");
+ ret = -1;
+ goto error;
+ }
+
+ ret = chmod(health_unix_sock_path,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ if (ret < 0) {
+ ERR("Unable to set permissions on %s", health_unix_sock_path);
+ PERROR("chmod");
+ ret = -1;
+ goto error;
+ }
+ }
+
/*
* Set the CLOEXEC flag. Return code is useless because either way, the
* show must go on.
rcu_thread_online();
- switch (msg.component) {
- case LTTNG_HEALTH_CMD:
- reply.ret_code = health_check_state(HEALTH_TYPE_CMD);
- break;
- case LTTNG_HEALTH_APP_MANAGE:
- reply.ret_code = health_check_state(HEALTH_TYPE_APP_MANAGE);
- break;
- case LTTNG_HEALTH_APP_REG:
- reply.ret_code = health_check_state(HEALTH_TYPE_APP_REG);
- break;
- case LTTNG_HEALTH_KERNEL:
- reply.ret_code = health_check_state(HEALTH_TYPE_KERNEL);
- break;
- case LTTNG_HEALTH_CONSUMER:
- reply.ret_code = check_consumer_health();
- break;
- case LTTNG_HEALTH_HT_CLEANUP:
- reply.ret_code = health_check_state(HEALTH_TYPE_HT_CLEANUP);
- break;
- case LTTNG_HEALTH_APP_MANAGE_NOTIFY:
- reply.ret_code = health_check_state(HEALTH_TYPE_APP_MANAGE_NOTIFY);
- break;
- case LTTNG_HEALTH_APP_REG_DISPATCH:
- reply.ret_code = health_check_state(HEALTH_TYPE_APP_REG_DISPATCH);
- break;
- case LTTNG_HEALTH_ALL:
- reply.ret_code =
- health_check_state(HEALTH_TYPE_APP_MANAGE) &&
- health_check_state(HEALTH_TYPE_APP_REG) &&
- health_check_state(HEALTH_TYPE_CMD) &&
- health_check_state(HEALTH_TYPE_KERNEL) &&
- check_consumer_health() &&
- health_check_state(HEALTH_TYPE_HT_CLEANUP) &&
- health_check_state(HEALTH_TYPE_APP_MANAGE_NOTIFY) &&
- health_check_state(HEALTH_TYPE_APP_REG_DISPATCH);
- break;
- default:
- reply.ret_code = LTTNG_ERR_UND;
- break;
- }
-
- /*
- * Flip ret value since 0 is a success and 1 indicates a bad health for
- * the client where in the sessiond it is the opposite. Again, this is
- * just to make things easier for us poor developer which enjoy a lot
- * lazyness.
- */
- if (reply.ret_code == 0 || reply.ret_code == 1) {
- reply.ret_code = !reply.ret_code;
+ reply.ret_code = 0;
+ for (i = 0; i < NR_HEALTH_SESSIOND_TYPES; i++) {
+ /*
+ * health_check_state returns 0 if health is
+ * bad.
+ */
+ if (!health_check_state(health_sessiond, i)) {
+ reply.ret_code |= 1ULL << i;
+ }
}
- DBG2("Health check return value %d", reply.ret_code);
+ DBG2("Health check return value %" PRIx64, reply.ret_code);
ret = send_unix_sock(new_sock, (void *) &reply, sizeof(reply));
if (ret < 0) {
rcu_register_thread();
- health_register(HEALTH_TYPE_CMD);
+ health_register(health_sessiond, HEALTH_SESSIOND_TYPE_CMD);
if (testpoint(thread_manage_clients)) {
goto error_testpoint;
ERR("Health error occurred in %s", __func__);
}
- health_unregister();
+ health_unregister(health_sessiond);
DBG("Client thread dying");
fprintf(stderr, " -p, --pidfile FILE Write a pid to FILE name overriding the default value.\n");
fprintf(stderr, " --verbose-consumer Verbose mode for consumer. Activate DBG() macro.\n");
fprintf(stderr, " --no-kernel Disable kernel tracer\n");
+ fprintf(stderr, " --jul-tcp-port JUL application registration TCP port\n");
}
/*
{ "verbose-consumer", 0, 0, 'Z' },
{ "no-kernel", 0, 0, 'N' },
{ "pidfile", 1, 0, 'p' },
+ { "jul-tcp-port", 1, 0, 'J' },
{ NULL, 0, 0, 0 }
};
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "dhqvVSN" "a:c:g:s:C:E:D:F:Z:u:t:p:",
+ c = getopt_long(argc, argv, "dhqvVSN" "a:c:g:s:C:E:D:F:Z:u:t:p:J:",
long_options, &option_index);
if (c == -1) {
break;
opt_daemon = 1;
break;
case 'g':
- opt_tracing_group = optarg;
+ tracing_group_name = optarg;
break;
case 'h':
usage();
case 'p':
opt_pidfile = optarg;
break;
+ case 'J': /* JUL TCP port. */
+ {
+ unsigned long v;
+
+ errno = 0;
+ v = strtoul(optarg, NULL, 0);
+ if (errno != 0 || !isdigit(optarg[0])) {
+ ERR("Wrong value in --jul-tcp-port parameter: %s", optarg);
+ return -1;
+ }
+ if (v == 0 || v >= 65535) {
+ ERR("Port overflow in --jul-tcp-port parameter: %s", optarg);
+ return -1;
+ }
+ jul_tcp_port = (uint32_t) v;
+ DBG3("JUL TCP port set to non default: %u", jul_tcp_port);
+ break;
+ }
default:
/* Unknown option or other error.
* Error is printed by getopt, just return */
int ret;
gid_t gid;
- ret = allowed_group();
- if (ret < 0) {
- WARN("No tracing group detected");
- ret = 0;
- goto end;
- }
-
- gid = ret;
+ gid = utils_get_group_id(tracing_group_name);
/* Set lttng run dir */
ret = chown(rundir, 0, gid);
PERROR("chown");
}
- /* Ensure tracing group can search the run dir */
- ret = chmod(rundir, S_IRWXU | S_IXGRP | S_IXOTH);
+ /*
+ * Ensure all applications and tracing group can search the run
+ * dir. Allow everyone to read the directory, since it does not
+ * buy us anything to hide its content.
+ */
+ ret = chmod(rundir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (ret < 0) {
ERR("Unable to set permissions on %s", rundir);
PERROR("chmod");
}
/* kconsumer error socket path */
- ret = chown(kconsumer_data.err_unix_sock_path, 0, gid);
+ ret = chown(kconsumer_data.err_unix_sock_path, 0, 0);
if (ret < 0) {
ERR("Unable to set group on %s", kconsumer_data.err_unix_sock_path);
PERROR("chown");
}
/* 64-bit ustconsumer error socket path */
- ret = chown(ustconsumer64_data.err_unix_sock_path, 0, gid);
+ ret = chown(ustconsumer64_data.err_unix_sock_path, 0, 0);
if (ret < 0) {
ERR("Unable to set group on %s", ustconsumer64_data.err_unix_sock_path);
PERROR("chown");
}
/* 32-bit ustconsumer compat32 error socket path */
- ret = chown(ustconsumer32_data.err_unix_sock_path, 0, gid);
+ ret = chown(ustconsumer32_data.err_unix_sock_path, 0, 0);
if (ret < 0) {
ERR("Unable to set group on %s", ustconsumer32_data.err_unix_sock_path);
PERROR("chown");
DBG("All permissions are set");
-end:
return ret;
}
int ret;
char path[PATH_MAX];
- switch (consumer_data->type) {
+ switch (consumer_data->type) {
case LTTNG_CONSUMER_KERNEL:
snprintf(path, PATH_MAX, DEFAULT_KCONSUMERD_PATH, rundir);
break;
DBG2("Creating consumer directory: %s", path);
- ret = mkdir(path, S_IRWXU);
+ ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP);
if (ret < 0) {
if (errno != EEXIST) {
PERROR("mkdir");
}
ret = -1;
}
+ if (is_root) {
+ ret = chown(path, 0, utils_get_group_id(tracing_group_name));
+ if (ret < 0) {
+ ERR("Unable to set group on %s", path);
+ PERROR("chown");
+ goto error;
+ }
+ }
/* Create the kconsumerd error unix socket */
consumer_data->err_sock =
goto error;
}
+ /*
+ * Set the CLOEXEC flag. Return code is useless because either way, the
+ * show must go on.
+ */
+ ret = utils_set_fd_cloexec(consumer_data->err_sock);
+ if (ret < 0) {
+ PERROR("utils_set_fd_cloexec");
+ /* continue anyway */
+ }
+
/* File permission MUST be 660 */
ret = chmod(consumer_data->err_unix_sock_path,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
*/
ust_app_ht_alloc();
+ /* Initialize JUL domain subsystem. */
+ if ((ret = jul_init()) < 0) {
+ /* ENOMEM at this point. */
+ goto error;
+ }
+
/* After this point, we can safely call cleanup() with "goto exit" */
/*
write_pidfile();
+ /* Initialize communication library */
+ lttcomm_init();
+ /* This is to get the TCP timeout value. */
+ lttcomm_inet_init();
+
+ /*
+ * Initialize the health check subsystem. This call should set the
+ * appropriate time values.
+ */
+ health_sessiond = health_app_create(NR_HEALTH_SESSIOND_TYPES);
+ if (!health_sessiond) {
+ PERROR("health_app_create error");
+ goto exit_health_sessiond_cleanup;
+ }
+
/* Create thread to manage the client socket */
ret = pthread_create(&ht_cleanup_thread, NULL,
thread_ht_cleanup, (void *) NULL);
ust_thread_manage_notify, (void *) NULL);
if (ret != 0) {
PERROR("pthread_create apps");
- goto exit_apps;
+ goto exit_apps_notify;
+ }
+
+ /* Create JUL registration thread. */
+ ret = pthread_create(&jul_reg_thread, NULL,
+ jul_thread_manage_registration, (void *) NULL);
+ if (ret != 0) {
+ PERROR("pthread_create apps");
+ goto exit_jul_reg;
}
/* Don't start this thread if kernel tracing is not requested nor root */
}
exit_kernel:
+ ret = pthread_join(jul_reg_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join JUL");
+ goto error; /* join error, exit without cleanup */
+ }
+
+exit_jul_reg:
+ ret = pthread_join(apps_notify_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join apps notify");
+ goto error; /* join error, exit without cleanup */
+ }
+
+exit_apps_notify:
ret = pthread_join(apps_thread, &status);
if (ret != 0) {
- PERROR("pthread_join");
+ PERROR("pthread_join apps");
goto error; /* join error, exit without cleanup */
}
+
exit_apps:
ret = pthread_join(reg_apps_thread, &status);
if (ret != 0) {
goto error; /* join error, exit without cleanup */
}
exit_ht_cleanup:
+ health_app_destroy(health_sessiond);
+exit_health_sessiond_cleanup:
exit:
/*
* cleanup() is called when no other thread is running.
{ "lttng-probe-jbd2", 0 },
{ "lttng-probe-kmem", 0 },
{ "lttng-probe-kvm", 0 },
+ { "lttng-probe-kvm-x86", 0 },
+ { "lttng-probe-kvm-x86-mmu", 0 },
{ "lttng-probe-lock", 0 },
{ "lttng-probe-module", 0 },
{ "lttng-probe-napi", 0 },
goto error;
}
+ ret = gethostname(new_session->hostname, sizeof(new_session->hostname));
+ if (ret < 0) {
+ if (errno == ENAMETOOLONG) {
+ new_session->hostname[sizeof(new_session->hostname) - 1] = '\0';
+ } else {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ }
+
/* Init kernel session */
new_session->kernel_session = NULL;
new_session->ust_session = NULL;
#ifndef _LTT_SESSION_H
#define _LTT_SESSION_H
+#include <limits.h>
#include <urcu/list.h>
#include <common/hashtable/hashtable.h>
*/
struct ltt_session {
char name[NAME_MAX];
+ char hostname[HOST_NAME_MAX]; /* Local hostname. */
struct ltt_kernel_session *kernel_session;
struct ltt_ust_session *ust_session;
/*
struct snapshot snapshot;
/* Indicate if the session has to output the traces or not. */
unsigned int output_traces;
+ /*
+ * This session is in snapshot mode. This means that every channel enabled
+ * will be set in overwrite mode and mmap. It is considered exclusively for
+ * snapshot purposes.
+ */
+ unsigned int snapshot_mode;
+ /*
+ * Timer set when the session is created for live reading.
+ */
+ int live_timer;
};
/* Prototypes */
}
/*
- * Initialize a snapshot output object using the given parameters. The name
- * value and url can be NULL.
+ * Initialized snapshot output with the given values.
*
* Return 0 on success or else a negative value.
*/
-int snapshot_output_init(uint64_t max_size, const char *name,
- const char *ctrl_url, const char *data_url,
+static int output_init(uint64_t max_size, const char *name,
+ struct lttng_uri *uris, size_t nb_uri,
struct consumer_output *consumer, struct snapshot_output *output,
struct snapshot *snapshot)
{
- int ret = 0, nb_uri, i;
- struct lttng_uri *uris = NULL;
+ int ret = 0, i;
assert(output);
- DBG2("Snapshot output initializing with max size %" PRIu64 ", name %s "
- "ctrl URL %s, data URL %s", max_size, name, ctrl_url, data_url);
+ memset(output, 0, sizeof(struct snapshot_output));
+ if (max_size == (uint64_t) -1ULL) {
+ max_size = 0;
+ }
output->max_size = max_size;
+
if (snapshot) {
output->id = get_next_output_id(snapshot);
}
goto end;
}
- /* Create an array of URIs from URLs. */
- nb_uri = uri_parse_str_urls(ctrl_url, data_url, &uris);
- if (nb_uri < 0) {
- ret = nb_uri;
- goto error;
- }
-
output->consumer = consumer_copy_output(consumer);
if (!output->consumer) {
ret = -ENOMEM;
goto error;
}
+ output->consumer->snapshot = 1;
/* No URL given. */
if (nb_uri == 0) {
error:
end:
+ return ret;
+}
+
+/*
+ * Initialize a snapshot output object using the given parameters and URI(s).
+ * The name value and uris can be NULL.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int snapshot_output_init_with_uri(uint64_t max_size, const char *name,
+ struct lttng_uri *uris, size_t nb_uri,
+ struct consumer_output *consumer, struct snapshot_output *output,
+ struct snapshot *snapshot)
+{
+ return output_init(max_size, name, uris, nb_uri, consumer, output,
+ snapshot);
+}
+
+/*
+ * Initialize a snapshot output object using the given parameters. The name
+ * value and url can be NULL.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int snapshot_output_init(uint64_t max_size, const char *name,
+ const char *ctrl_url, const char *data_url,
+ struct consumer_output *consumer, struct snapshot_output *output,
+ struct snapshot *snapshot)
+{
+ int ret = 0, nb_uri;
+ struct lttng_uri *uris = NULL;
+
+ /* Create an array of URIs from URLs. */
+ nb_uri = uri_parse_str_urls(ctrl_url, data_url, &uris);
+ if (nb_uri < 0) {
+ ret = nb_uri;
+ goto error;
+ }
+
+ ret = output_init(max_size, name, uris, nb_uri, consumer, output,
+ snapshot);
+
+error:
free(uris);
return ret;
}
free(obj);
}
+/*
+ * RCU read side lock MUST be acquired before calling this since the returned
+ * pointer is in a RCU hash table.
+ *
+ * Return the reference on success or else NULL.
+ */
+struct snapshot_output *snapshot_find_output_by_name(const char *name,
+ struct snapshot *snapshot)
+{
+ struct lttng_ht_iter iter;
+ struct snapshot_output *output = NULL;
+
+ assert(snapshot);
+ assert(name);
+
+ cds_lfht_for_each_entry(snapshot->output_ht->ht, &iter.iter, output,
+ node.node) {
+ if (!strncmp(output->name, name, strlen(name))) {
+ return output;
+ }
+ }
+
+ /* Not found */
+ return NULL;
+}
+
/*
* RCU read side lock MUST be acquired before calling this since the returned
* pointer is in a RCU hash table.
struct snapshot_output {
uint32_t id;
uint64_t max_size;
+ /* Number of snapshot taken with that output. */
+ uint64_t nb_snapshot;
char name[NAME_MAX];
struct consumer_output *consumer;
int kernel_sockets_copied;
struct snapshot {
unsigned long next_output_id;
size_t nb_output;
+ /*
+ * Number of snapshot taken for that object. This value is used with a
+ * temporary output of a snapshot record.
+ */
+ uint64_t nb_snapshot;
struct lttng_ht *output_ht;
};
const char *ctrl_url, const char *data_url,
struct consumer_output *consumer, struct snapshot_output *output,
struct snapshot *snapshot);
+int snapshot_output_init_with_uri(uint64_t max_size, const char *name,
+ struct lttng_uri *uris, size_t nb_uri,
+ struct consumer_output *consumer, struct snapshot_output *output,
+ struct snapshot *snapshot);
struct snapshot_output *snapshot_find_output_by_id(uint32_t id,
struct snapshot *snapshot);
+struct snapshot_output *snapshot_find_output_by_name(const char *name,
+ struct snapshot *snapshot);
#endif /* SNAPSHOT_H */
assert(session);
assert(name);
+ /*
+ * If we receive an empty string for channel name, it means the
+ * default channel name is requested.
+ */
+ if (name[0] == '\0')
+ name = DEFAULT_CHANNEL_NAME;
+
DBG("Trying to find channel %s", name);
cds_list_for_each_entry(chan, &session->channel_list.head, list) {
}
memcpy(lkc->channel, chan, sizeof(struct lttng_channel));
+ /*
+ * If we receive an empty string for channel name, it means the
+ * default channel name is requested.
+ */
+ if (chan->name[0] == '\0') {
+ strncpy(lkc->channel->name, DEFAULT_CHANNEL_NAME,
+ sizeof(lkc->channel->name));
+ }
+ lkc->channel->name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+
lkc->fd = -1;
lkc->stream_count = 0;
lkc->event_count = 0;
attr->instrumentation = LTTNG_KERNEL_KRETPROBE;
attr->u.kretprobe.addr = ev->attr.probe.addr;
attr->u.kretprobe.offset = ev->attr.probe.offset;
- attr->u.kretprobe.offset = ev->attr.probe.offset;
strncpy(attr->u.kretprobe.symbol_name,
ev->attr.probe.symbol_name, LTTNG_KERNEL_SYM_NAME_LEN);
attr->u.kretprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
unsigned int started;
/* Tell or not if the session has to output the traces. */
unsigned int output_traces;
+ unsigned int snapshot_mode;
+ unsigned int has_non_default_channel;
};
/*
struct lttng_ht_node_str *node;
struct lttng_ht_iter iter;
+ /*
+ * If we receive an empty string for channel name, it means the
+ * default channel name is requested.
+ */
+ if (name[0] == '\0')
+ name = DEFAULT_CHANNEL_NAME;
+
lttng_ht_lookup(ht, (void *)name, &iter);
node = lttng_ht_iter_get_node_str(&iter);
if (node == NULL) {
*/
struct ltt_ust_session *trace_ust_create_session(uint64_t session_id)
{
+ int ret;
struct ltt_ust_session *lus;
/* Allocate a new ltt ust session */
* during the session lifetime which is at the first enable channel and
* only before start. The flag buffer_type_changed indicates the status.
*/
- lus->buffer_type = LTTNG_BUFFER_PER_PID;
+ lus->buffer_type = LTTNG_BUFFER_PER_UID;
/* Once set to 1, the buffer_type is immutable for the session. */
lus->buffer_type_changed = 0;
/* Init it in case it get used after allocation. */
/* Alloc UST global domain channels' HT */
lus->domain_global.channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ ret = jul_init_domain(&lus->domain_jul);
+ if (ret < 0) {
+ goto error_consumer;
+ }
lus->consumer = consumer_create_output(CONSUMER_DST_LOCAL);
if (lus->consumer == NULL) {
error_consumer:
ht_cleanup_push(lus->domain_global.channels);
+ jul_destroy_domain(&lus->domain_jul);
free(lus);
error:
return NULL;
break;
}
- /* Copy channel name */
- strncpy(luc->name, chan->name, sizeof(luc->name));
+ /*
+ * If we receive an empty string for channel name, it means the
+ * default channel name is requested.
+ */
+ if (chan->name[0] == '\0') {
+ strncpy(luc->name, DEFAULT_CHANNEL_NAME, sizeof(luc->name));
+ } else {
+ /* Copy channel name */
+ strncpy(luc->name, chan->name, sizeof(luc->name));
+ }
luc->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
/* Init node */
lttng_ht_node_init_str(&luc->node, luc->name);
+ CDS_INIT_LIST_HEAD(&luc->ctx_list);
+
/* Alloc hash tables */
luc->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
luc->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
case LTTNG_EVENT_CONTEXT_PROCNAME:
utype = LTTNG_UST_CONTEXT_PROCNAME;
break;
+ case LTTNG_EVENT_CONTEXT_IP:
+ utype = LTTNG_UST_CONTEXT_IP;
+ break;
default:
ERR("Invalid UST context");
return NULL;
uctx->ctx.ctx = utype;
lttng_ht_node_init_ulong(&uctx->node, (unsigned long) uctx->ctx.ctx);
+ CDS_INIT_LIST_HEAD(&uctx->list);
return uctx;
int ret;
struct lttng_ht_node_ulong *node;
struct lttng_ht_iter iter;
+ struct ltt_ust_context *ctx;
assert(ht);
rcu_read_lock();
cds_lfht_for_each_entry(ht->ht, &iter.iter, node, node) {
+ /* Remove from ordered list. */
+ ctx = caa_container_of(node, struct ltt_ust_context, node);
+ cds_list_del(&ctx->list);
+ /* Remove from channel's hash table. */
ret = lttng_ht_del(ht, &iter);
if (!ret) {
call_rcu(&node->head, destroy_context_rcu);
/* Cleaning up UST domain */
destroy_domain_global(&session->domain_global);
+ jul_destroy_domain(&session->domain_jul);
/* Cleanup UID buffer registry object(s). */
cds_list_for_each_entry_safe(reg, sreg, &session->buffer_reg_uid_list,
#include <common/defaults.h>
#include "consumer.h"
+#include "jul.h"
#include "ust-ctl.h"
struct ltt_ust_ht_key {
struct ltt_ust_context {
struct lttng_ust_context ctx;
struct lttng_ht_node_ulong node;
+ struct cds_list_head list;
};
/* UST event */
char name[LTTNG_UST_SYM_NAME_LEN];
struct lttng_ust_channel_attr attr;
struct lttng_ht *ctx;
+ struct cds_list_head ctx_list;
struct lttng_ht *events;
struct lttng_ht_node_str node;
uint64_t tracefile_size;
uint64_t id; /* Unique identifier of session */
int start_trace;
struct ltt_ust_domain_global domain_global;
+ struct jul_domain domain_jul;
/* UID/GID of the user owning the session */
uid_t uid;
gid_t gid;
uint64_t used_channel_id;
/* Tell or not if the session has to output the traces. */
unsigned int output_traces;
+ unsigned int snapshot_mode;
+ unsigned int has_non_default_channel;
+ unsigned int live_timer_interval; /* usec */
};
/*
#include "buffer-registry.h"
#include "fd-limit.h"
-#include "health.h"
+#include "health-sessiond.h"
#include "ust-app.h"
#include "ust-consumer.h"
#include "ust-ctl.h"
/* Wipe context */
cds_lfht_for_each_entry(ua_chan->ctx->ht, &iter.iter, ua_ctx, node.node) {
+ cds_list_del(&ua_ctx->list);
ret = lttng_ht_del(ua_chan->ctx, &iter);
assert(!ret);
delete_ust_app_ctx(sock, ua_ctx);
lttng_ht_node_init_str(&ua_chan->node, ua_chan->name);
CDS_INIT_LIST_HEAD(&ua_chan->streams.head);
+ CDS_INIT_LIST_HEAD(&ua_chan->ctx_list);
/* Copy attributes */
if (attr) {
goto error;
}
+ CDS_INIT_LIST_HEAD(&ua_ctx->list);
+
if (uctx) {
memcpy(&ua_ctx->ctx, uctx, sizeof(ua_ctx->ctx));
}
* Find an ust_app using the sock and return it. RCU read side lock must be
* held before calling this helper function.
*/
-static
-struct ust_app *find_app_by_sock(int sock)
+struct ust_app *ust_app_find_by_sock(int sock)
{
struct lttng_ht_node_ulong *node;
struct lttng_ht_iter iter;
ERR("UST app create channel context failed for app (pid: %d) "
"with ret %d", app->pid, ret);
} else {
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ ret = 0;
DBG3("UST app disable event failed. Application is dead.");
}
goto error;
ERR("UST app event %s filter failed for app (pid: %d) "
"with ret %d", ua_event->attr.name, app->pid, ret);
} else {
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ ret = 0;
DBG3("UST app filter event failed. Application is dead.");
}
goto error;
"and session handle %d with ret %d",
ua_event->attr.name, app->pid, ua_sess->handle, ret);
} else {
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ ret = 0;
DBG3("UST app disable event failed. Application is dead.");
}
goto error;
"and session handle %d with ret %d",
ua_chan->name, app->pid, ua_sess->handle, ret);
} else {
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ ret = 0;
DBG3("UST app disable channel failed. Application is dead.");
}
goto error;
"and session handle %d with ret %d",
ua_chan->name, app->pid, ua_sess->handle, ret);
} else {
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ ret = 0;
DBG3("UST app enable channel failed. Application is dead.");
}
goto error;
"and session handle %d with ret %d",
ua_event->attr.name, app->pid, ua_sess->handle, ret);
} else {
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ ret = 0;
DBG3("UST app enable event failed. Application is dead.");
}
goto error;
ERR("Error ustctl create event %s for app pid: %d with ret %d",
ua_event->attr.name, app->pid, ret);
} else {
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ ret = 0;
DBG3("UST app create event failed. Application is dead.");
}
goto error;
ua_chan->enabled = uchan->enabled;
ua_chan->tracing_channel_id = uchan->id;
- cds_lfht_for_each_entry(uchan->ctx->ht, &iter.iter, uctx, node.node) {
+ cds_list_for_each_entry(uctx, &uchan->ctx_list, list) {
ua_ctx = alloc_ust_app_ctx(&uctx->ctx);
if (ua_ctx == NULL) {
continue;
lttng_ht_node_init_ulong(&ua_ctx->node,
(unsigned long) ua_ctx->ctx.ctx);
lttng_ht_add_unique_ulong(ua_chan->ctx, &ua_ctx->node);
+ cds_list_add_tail(&ua_ctx->list, &ua_chan->ctx_list);
}
/* Copy all events from ltt ust channel to ust app channel */
/* There is only one consumer object per session possible. */
ua_sess->consumer = usess->consumer;
ua_sess->output_traces = usess->output_traces;
+ ua_sess->live_timer_interval = usess->live_timer_interval;
switch (ua_sess->buffer_type) {
case LTTNG_BUFFER_PER_PID:
app->pid, ret);
} else {
DBG("UST app creating session failed. Application is dead");
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally. This will get flagged ENOTCONN and the
+ * caller will handle it.
+ */
+ ret = 0;
}
delete_ust_app_session(-1, ua_sess, app);
if (ret != -ENOMEM) {
lttng_ht_node_init_ulong(&ua_ctx->node, (unsigned long) ua_ctx->ctx.ctx);
lttng_ht_add_unique_ulong(ua_chan->ctx, &ua_ctx->node);
+ cds_list_add_tail(&ua_ctx->list, &ua_chan->ctx_list);
ret = create_ust_channel_context(ua_chan, ua_ctx, app);
if (ret < 0) {
}
assert(reg_chan);
reg_chan->consumer_key = ua_chan->key;
+ reg_chan->subbuf_size = ua_chan->attr.subbuf_size;
/* Create and add a channel registry to session. */
ret = ust_registry_channel_add(reg_sess->reg.ust,
app->sock, ret);
} else {
DBG3("UST app tp list get failed. Application is dead");
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally. Continue normal execution.
+ */
+ break;
}
goto rcu_error;
}
app->sock, ret);
} else {
DBG3("UST app tp list field failed. Application is dead");
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ break;
}
goto rcu_error;
}
app->pid, ret);
} else {
DBG("UST app start session failed. Application is dead.");
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto end;
}
goto error_unlock;
}
app->pid, ret);
} else {
DBG("UST app stop session failed. Application is dead.");
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ goto end_unlock;
}
goto error_rcu_unlock;
}
(void) push_metadata(registry, ua_sess->consumer);
}
+end_unlock:
pthread_mutex_unlock(&ua_sess->lock);
end_no_session:
rcu_read_unlock();
} else {
DBG3("UST app failed to flush %s. Application is dead.",
ua_chan->name);
- /* No need to continue. */
- break;
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
}
/* Continuing flushing all buffers */
continue;
}
}
- /* Flush buffers */
+ /* Flush buffers and push metadata (for UID buffers). */
switch (usess->buffer_type) {
case LTTNG_BUFFER_PER_UID:
{
/* Flush all per UID buffers associated to that session. */
cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) {
+ struct ust_registry_session *ust_session_reg;
struct buffer_reg_channel *reg_chan;
struct consumer_socket *socket;
*/
(void) consumer_flush_channel(socket, reg_chan->consumer_key);
}
+
+ ust_session_reg = reg->registry->reg.ust;
+ if (!ust_session_reg->metadata_closed) {
+ /* Push metadata. */
+ (void) push_metadata(ust_session_reg, usess->consumer);
+ }
}
+
break;
}
case LTTNG_BUFFER_PER_PID:
void ust_app_global_update(struct ltt_ust_session *usess, int sock)
{
int ret = 0;
- struct lttng_ht_iter iter, uiter, iter_ctx;
+ struct lttng_ht_iter iter, uiter;
struct ust_app *app;
struct ust_app_session *ua_sess = NULL;
struct ust_app_channel *ua_chan;
rcu_read_lock();
- app = find_app_by_sock(sock);
+ app = ust_app_find_by_sock(sock);
if (app == NULL) {
/*
* Application can be unregistered before so this is possible hence
}
}
- cds_lfht_for_each_entry(ua_chan->ctx->ht, &iter_ctx.iter, ua_ctx,
- node.node) {
+ /*
+ * Add context using the list so they are enabled in the same order the
+ * user added them.
+ */
+ cds_list_for_each_entry(ua_ctx, &ua_chan->ctx_list, list) {
ret = create_ust_channel_context(ua_chan, ua_ctx, app);
if (ret < 0) {
goto error_unlock;
*/
ret_code = ust_registry_create_event(registry, chan_reg_key,
sobjd, cobjd, name, sig, nr_fields, fields, loglevel,
- model_emf_uri, ua_sess->buffer_type, &event_id);
+ model_emf_uri, ua_sess->buffer_type, &event_id,
+ app);
/*
* The return value is returned to ustctl so in case of an error, the
* Return 0 on success or else a negative value.
*/
int ust_app_snapshot_record(struct ltt_ust_session *usess,
- struct snapshot_output *output, int wait)
+ struct snapshot_output *output, int wait, unsigned int nb_streams)
{
int ret = 0;
struct lttng_ht_iter iter;
struct ust_app *app;
+ char pathname[PATH_MAX];
+ uint64_t max_stream_size = 0;
assert(usess);
assert(output);
rcu_read_lock();
- cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
- struct consumer_socket *socket;
- struct lttng_ht_iter chan_iter;
- struct ust_app_channel *ua_chan;
- struct ust_app_session *ua_sess;
- struct ust_registry_session *registry;
+ /*
+ * Compute the maximum size of a single stream if a max size is asked by
+ * the caller.
+ */
+ if (output->max_size > 0 && nb_streams > 0) {
+ max_stream_size = output->max_size / nb_streams;
+ }
- ua_sess = lookup_session_by_app(usess, app);
- if (!ua_sess) {
- /* Session not associated with this app. */
- continue;
- }
+ switch (usess->buffer_type) {
+ case LTTNG_BUFFER_PER_UID:
+ {
+ struct buffer_reg_uid *reg;
- /* Get the right consumer socket for the application. */
- socket = consumer_find_socket_by_bitness(app->bits_per_long,
- output->consumer);
- if (!socket) {
- ret = -EINVAL;
- goto error;
- }
+ cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) {
+ struct buffer_reg_channel *reg_chan;
+ struct consumer_socket *socket;
+
+ /* Get consumer socket to use to push the metadata.*/
+ socket = consumer_find_socket_by_bitness(reg->bits_per_long,
+ usess->consumer);
+ if (!socket) {
+ ret = -EINVAL;
+ goto error;
+ }
- cds_lfht_for_each_entry(ua_sess->channels->ht, &chan_iter.iter,
- ua_chan, node.node) {
- ret = consumer_snapshot_channel(socket, ua_chan->key, output, 0,
- ua_sess->euid, ua_sess->egid, ua_sess->path, wait);
+ memset(pathname, 0, sizeof(pathname));
+ ret = snprintf(pathname, sizeof(pathname),
+ DEFAULT_UST_TRACE_DIR "/" DEFAULT_UST_TRACE_UID_PATH,
+ reg->uid, reg->bits_per_long);
if (ret < 0) {
+ PERROR("snprintf snapshot path");
goto error;
}
- }
- registry = get_session_registry(ua_sess);
- assert(registry);
- ret = consumer_snapshot_channel(socket, registry->metadata_key, output,
- 1, ua_sess->euid, ua_sess->egid, ua_sess->path, wait);
- if (ret < 0) {
- goto error;
+ /* Add the UST default trace dir to path. */
+ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter,
+ reg_chan, node.node) {
+
+ /*
+ * Make sure the maximum stream size is not lower than the
+ * subbuffer size or else it's an error since we won't be able to
+ * snapshot anything.
+ */
+ if (max_stream_size &&
+ reg_chan->subbuf_size > max_stream_size) {
+ ret = -EINVAL;
+ DBG3("UST app snapshot record maximum stream size %" PRIu64
+ " is smaller than subbuffer size of %zu",
+ max_stream_size, reg_chan->subbuf_size);
+ goto error;
+ }
+ ret = consumer_snapshot_channel(socket, reg_chan->consumer_key, output, 0,
+ usess->uid, usess->gid, pathname, wait,
+ max_stream_size);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+ ret = consumer_snapshot_channel(socket, reg->registry->reg.ust->metadata_key, output,
+ 1, usess->uid, usess->gid, pathname, wait,
+ max_stream_size);
+ if (ret < 0) {
+ goto error;
+ }
}
+ break;
+ }
+ case LTTNG_BUFFER_PER_PID:
+ {
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ struct consumer_socket *socket;
+ struct lttng_ht_iter chan_iter;
+ struct ust_app_channel *ua_chan;
+ struct ust_app_session *ua_sess;
+ struct ust_registry_session *registry;
+
+ ua_sess = lookup_session_by_app(usess, app);
+ if (!ua_sess) {
+ /* Session not associated with this app. */
+ continue;
+ }
+
+ /* Get the right consumer socket for the application. */
+ socket = consumer_find_socket_by_bitness(app->bits_per_long,
+ output->consumer);
+ if (!socket) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Add the UST default trace dir to path. */
+ memset(pathname, 0, sizeof(pathname));
+ ret = snprintf(pathname, sizeof(pathname), DEFAULT_UST_TRACE_DIR "/%s",
+ ua_sess->path);
+ if (ret < 0) {
+ PERROR("snprintf snapshot path");
+ goto error;
+ }
+
+ cds_lfht_for_each_entry(ua_sess->channels->ht, &chan_iter.iter,
+ ua_chan, node.node) {
+ /*
+ * Make sure the maximum stream size is not lower than the
+ * subbuffer size or else it's an error since we won't be able to
+ * snapshot anything.
+ */
+ if (max_stream_size &&
+ ua_chan->attr.subbuf_size > max_stream_size) {
+ ret = -EINVAL;
+ DBG3("UST app snapshot record maximum stream size %" PRIu64
+ " is smaller than subbuffer size of %" PRIu64,
+ max_stream_size, ua_chan->attr.subbuf_size);
+ goto error;
+ }
+ ret = consumer_snapshot_channel(socket, ua_chan->key, output, 0,
+ ua_sess->euid, ua_sess->egid, pathname, wait,
+ max_stream_size);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ registry = get_session_registry(ua_sess);
+ assert(registry);
+ ret = consumer_snapshot_channel(socket, registry->metadata_key, output,
+ 1, ua_sess->euid, ua_sess->egid, pathname, wait,
+ max_stream_size);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+ break;
+ }
+ default:
+ assert(0);
+ break;
}
error:
rcu_read_unlock();
return ret;
}
+
+/*
+ * Return the number of streams for a UST session.
+ */
+unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess)
+{
+ unsigned int ret = 0;
+ struct ust_app *app;
+ struct lttng_ht_iter iter;
+
+ assert(usess);
+
+ switch (usess->buffer_type) {
+ case LTTNG_BUFFER_PER_UID:
+ {
+ struct buffer_reg_uid *reg;
+
+ cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) {
+ struct buffer_reg_channel *reg_chan;
+
+ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter,
+ reg_chan, node.node) {
+ ret += reg_chan->stream_count;
+ }
+ }
+ break;
+ }
+ case LTTNG_BUFFER_PER_PID:
+ {
+ rcu_read_lock();
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ struct ust_app_channel *ua_chan;
+ struct ust_app_session *ua_sess;
+ struct lttng_ht_iter chan_iter;
+
+ ua_sess = lookup_session_by_app(usess, app);
+ if (!ua_sess) {
+ /* Session not associated with this app. */
+ continue;
+ }
+
+ cds_lfht_for_each_entry(ua_sess->channels->ht, &chan_iter.iter,
+ ua_chan, node.node) {
+ ret += ua_chan->streams.count;
+ }
+ }
+ rcu_read_unlock();
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+
+ return ret;
+}
#include <stdint.h>
#include <common/compat/uuid.h>
+
+#include "jul.h"
#include "trace-ust.h"
#include "ust-registry.h"
struct lttng_ust_context ctx;
struct lttng_ust_object_data *obj;
struct lttng_ht_node_ulong node;
+ struct cds_list_head list;
};
struct ust_app_event {
struct ust_app_stream_list streams;
/* Session pointer that owns this object. */
struct ust_app_session *session;
+ /*
+ * Contexts are kept in a hash table for fast lookup and in an ordered list
+ * so we are able to enable them on the tracer side in the same order the
+ * user added them.
+ */
struct lttng_ht *ctx;
+ struct cds_list_head ctx_list;
+
struct lttng_ht *events;
uint64_t tracefile_size;
uint64_t tracefile_count;
struct rcu_head rcu_head;
/* If the channel's streams have to be outputed or not. */
unsigned int output_traces;
+ unsigned int live_timer_interval; /* usec */
};
/*
* Hash table containing ust_app_channel indexed by channel objd.
*/
struct lttng_ht *ust_objd;
+ /*
+ * If this application is of the JUL domain and this is non negative then a
+ * lookup MUST be done to acquire a read side reference to the
+ * corresponding JUL app object. If the lookup fails, this should be set to
+ * a negative value indicating that the JUL application is gone.
+ */
+ int jul_app_sock;
};
#ifdef HAVE_LIBLTTNG_UST_CTL
struct consumer_socket *socket, int send_zero_data);
void ust_app_destroy(struct ust_app *app);
int ust_app_snapshot_record(struct ltt_ust_session *usess,
- struct snapshot_output *output, int wait);
+ struct snapshot_output *output, int wait, unsigned int nb_streams);
+unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess);
+struct ust_app *ust_app_find_by_sock(int sock);
+
+static inline
+int ust_app_supported(void)
+{
+ return 1;
+}
#else /* HAVE_LIBLTTNG_UST_CTL */
}
static inline
int ust_app_snapshot_record(struct ltt_ust_session *usess,
- struct snapshot_output *output, int wait)
+ struct snapshot_output *output, int wait, unsigned int nb_stream)
+{
+ return 0;
+}
+static inline
+unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess)
{
return 0;
}
+static inline
+int ust_app_supported(void)
+{
+ return 0;
+}
+static inline
+struct ust_app *ust_app_find_by_sock(int sock)
+{
+ return NULL;
+}
+static inline
+struct ust_app *ust_app_find_by_pid(pid_t pid)
+{
+ return NULL;
+}
+
#endif /* HAVE_LIBLTTNG_UST_CTL */
#endif /* _LTT_UST_APP_H */
#include <common/defaults.h>
#include "consumer.h"
-#include "health.h"
+#include "health-sessiond.h"
#include "ust-consumer.h"
#include "buffer-registry.h"
#include "session.h"
ua_chan->attr.overwrite,
ua_chan->attr.switch_timer_interval,
ua_chan->attr.read_timer_interval,
+ ua_sess->live_timer_interval,
(int) ua_chan->attr.output,
(int) ua_chan->attr.type,
ua_sess->tracing_id,
ua_chan->tracefile_size,
ua_chan->tracefile_count,
ua_sess->id,
- ua_sess->output_traces);
+ ua_sess->output_traces,
+ ua_sess->uid);
health_code_update();
- ret = lttcomm_send_unix_sock(socket->fd, &msg, sizeof(msg));
+ ret = consumer_socket_send(socket, &msg, sizeof(msg));
if (ret < 0) {
goto error;
}
assert(ua_chan);
assert(consumer);
assert(socket);
- assert(socket->fd >= 0);
assert(registry);
if (!consumer->enabled) {
assert(ua_chan);
assert(socket);
- assert(socket->fd >= 0);
msg.cmd_type = LTTNG_CONSUMER_GET_CHANNEL;
msg.u.get_channel.key = ua_chan->key;
}
/* First, get the channel from consumer. */
- ret = ustctl_recv_channel_from_consumer(socket->fd, &ua_chan->obj);
+ ret = ustctl_recv_channel_from_consumer(*socket->fd_ptr, &ua_chan->obj);
if (ret < 0) {
if (ret != -EPIPE) {
ERR("Error recv channel from consumer %d with ret %d",
- socket->fd, ret);
+ *socket->fd_ptr, ret);
} else {
DBG3("UST app recv channel from consumer. Consumer is dead.");
}
}
/* Stream object is populated by this call if successful. */
- ret = ustctl_recv_stream_from_consumer(socket->fd, &stream->obj);
+ ret = ustctl_recv_stream_from_consumer(*socket->fd_ptr, &stream->obj);
if (ret < 0) {
free(stream);
if (ret == -LTTNG_UST_ERR_NOENT) {
}
if (ret != -EPIPE) {
ERR("Recv stream from consumer %d with ret %d",
- socket->fd, ret);
+ *socket->fd_ptr, ret);
} else {
DBG3("UST app recv stream from consumer. Consumer is dead.");
}
assert(ua_chan);
assert(socket);
- assert(socket->fd >= 0);
msg.cmd_type = LTTNG_CONSUMER_DESTROY_CHANNEL;
msg.u.destroy_channel.key = ua_chan->key;
health_code_update();
/* Wait for a metadata request */
- ret = lttcomm_recv_unix_sock(socket->fd, &request, sizeof(request));
- if (ret <= 0) {
- ERR("Consumer closed the metadata socket");
- ret = -1;
+ ret = consumer_socket_recv(socket, &request, sizeof(request));
+ if (ret < 0) {
goto end;
}
#include <lttng/lttng.h>
#include "ust-registry.h"
+#include "ust-app.h"
#include "utils.h"
/*
return hash_key_u64(&xored_key, seed);
}
+/*
+ * Return negative value on error, 0 if OK.
+ *
+ * TODO: we could add stricter verification of more types to catch
+ * errors in liblttng-ust implementation earlier than consumption by the
+ * trace reader.
+ */
+static
+int validate_event_field(struct ustctl_field *field,
+ const char *event_name,
+ struct ust_app *app)
+{
+ switch(field->type.atype) {
+ case ustctl_atype_integer:
+ case ustctl_atype_enum:
+ case ustctl_atype_array:
+ case ustctl_atype_sequence:
+ case ustctl_atype_string:
+ break;
+
+ case ustctl_atype_float:
+ switch (field->type.u.basic._float.mant_dig) {
+ case 0:
+ WARN("UST application '%s' (pid: %d) has unknown float mantissa '%u' "
+ "in field '%s', rejecting event '%s'",
+ app->name, app->pid,
+ field->type.u.basic._float.mant_dig,
+ field->name,
+ event_name);
+ return -EINVAL;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ return -ENOENT;
+ }
+ return 0;
+}
+
+static
+int validate_event_fields(size_t nr_fields, struct ustctl_field *fields,
+ const char *event_name, struct ust_app *app)
+{
+ unsigned int i;
+
+ for (i = 0; i < nr_fields; i++) {
+ if (validate_event_field(&fields[i], event_name, app) < 0)
+ return -EINVAL;
+ }
+ return 0;
+}
+
/*
* Allocate event and initialize it. This does NOT set a valid event id from a
* registry.
*/
static struct ust_registry_event *alloc_event(int session_objd,
int channel_objd, char *name, char *sig, size_t nr_fields,
- struct ustctl_field *fields, int loglevel, char *model_emf_uri)
+ struct ustctl_field *fields, int loglevel, char *model_emf_uri,
+ struct ust_app *app)
{
struct ust_registry_event *event = NULL;
+ /*
+ * Ensure that the field content is valid.
+ */
+ if (validate_event_fields(nr_fields, fields, name, app) < 0) {
+ return NULL;
+ }
+
event = zmalloc(sizeof(*event));
if (!event) {
PERROR("zmalloc ust registry event");
int ust_registry_create_event(struct ust_registry_session *session,
uint64_t chan_key, int session_objd, int channel_objd, char *name,
char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel,
- char *model_emf_uri, int buffer_type, uint32_t *event_id_p)
+ char *model_emf_uri, int buffer_type, uint32_t *event_id_p,
+ struct ust_app *app)
{
int ret;
uint32_t event_id;
}
event = alloc_event(session_objd, channel_objd, name, sig, nr_fields,
- fields, loglevel, model_emf_uri);
+ fields, loglevel, model_emf_uri, app);
if (!event) {
ret = -ENOMEM;
goto error_free;
int ust_registry_create_event(struct ust_registry_session *session,
uint64_t chan_key, int session_objd, int channel_objd, char *name,
char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel,
- char *model_emf_uri, int buffer_type, uint32_t *event_id_p);
+ char *model_emf_uri, int buffer_type, uint32_t *event_id_p,
+ struct ust_app *app);
struct ust_registry_event *ust_registry_find_event(
struct ust_registry_channel *chan, char *name, char *sig);
void ust_registry_destroy_event(struct ust_registry_channel *chan,
#include "fd-limit.h"
#include "lttng-sessiond.h"
#include "ust-thread.h"
-#include "health.h"
+#include "health-sessiond.h"
/*
* This thread manage application notify communication.
rcu_register_thread();
rcu_thread_online();
- health_register(HEALTH_TYPE_APP_MANAGE_NOTIFY);
+ health_register(health_sessiond,
+ HEALTH_SESSIOND_TYPE_APP_MANAGE_NOTIFY);
health_code_update();
health_error();
ERR("Health error occurred in %s", __func__);
}
- health_unregister();
+ health_unregister(health_sessiond);
rcu_thread_offline();
rcu_unregister_thread();
return NULL;
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src \
-DINSTALL_BIN_PATH=\""$(bindir)"\"
+AUTOMAKE_OPTIONS = subdir-objects
+
bin_PROGRAMS = lttng
lttng_SOURCES = command.h conf.c conf.h commands/start.c \
CONTEXT_VPPID = 9,
CONTEXT_PTHREAD_ID = 10,
CONTEXT_HOSTNAME = 11,
+ CONTEXT_IP = 12,
};
/*
{ "ppid", CONTEXT_PPID },
{ "vppid", CONTEXT_VPPID },
{ "hostname", CONTEXT_HOSTNAME },
+ { "ip", CONTEXT_IP },
/* Perf options */
PERF_HW(cpu-cycles, CPU_CYCLES),
PERF_HW(cycles, CPU_CYCLES),
} else if (opt_userspace) {
dom.type = LTTNG_DOMAIN_UST;
} else {
- ERR("Please specify a tracer (-k/--kernel or -u/--userspace)");
+ print_missing_domain();
ret = CMD_ERROR;
goto error;
}
ret = CMD_ERROR;
goto end;
} else {
- cds_list_add(&type->list, &ctx_type_list.head);
+ cds_list_add_tail(&type->list, &ctx_type_list.head);
}
break;
case OPT_USERSPACE:
* tracer anymore.
*/
{"function:entry", 0, POPT_ARG_NONE, 0, OPT_FUNCTION_ENTRY, 0, 0},
-#endif
{"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0},
+#endif
{"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL},
{0, 0, 0, 0, 0, 0, 0}
};
} else if (opt_userspace) {
dom.type = LTTNG_DOMAIN_UST;
} else {
- ERR("Please specify a tracer (-k/--kernel or -u/--userspace)");
+ print_missing_domain();
ret = CMD_ERROR;
goto error;
}
#define _GNU_SOURCE
#include <assert.h>
+#include <ctype.h>
#include <popt.h>
#include <stdio.h>
#include <stdlib.h>
#include <common/sessiond-comm/sessiond-comm.h>
#include <common/uri.h>
#include <common/utils.h>
+#include <lttng/snapshot.h>
static char *opt_output_path;
static char *opt_session_name;
static char *opt_data_url;
static int opt_no_consumer;
static int opt_no_output;
+static int opt_snapshot;
+static unsigned int opt_live_timer;
static int opt_disable_consumer;
enum {
OPT_HELP = 1,
OPT_LIST_OPTIONS,
+ OPT_LIVE_TIMER,
};
static struct poptOption long_options[] = {
{"no-output", 0, POPT_ARG_VAL, &opt_no_output, 1, 0, 0},
{"no-consumer", 0, POPT_ARG_VAL, &opt_no_consumer, 1, 0, 0},
{"disable-consumer", 0, POPT_ARG_VAL, &opt_disable_consumer, 1, 0, 0},
+ {"snapshot", 0, POPT_ARG_VAL, &opt_snapshot, 1, 0, 0},
+ {"live", 0, POPT_ARG_INT, 0, OPT_LIVE_TIMER, 0, 0},
{0, 0, 0, 0, 0, 0, 0}
};
* why this declaration exists and used ONLY in for this command.
*/
extern int _lttng_create_session_ext(const char *name, const char *url,
- const char *datetime);
+ const char *datetime, int live_timer);
/*
* usage
fprintf(ofp, " -h, --help Show this help\n");
fprintf(ofp, " --list-options Simple listing of options\n");
fprintf(ofp, " -o, --output PATH Specify output path for traces\n");
- fprintf(ofp, " --no-output Traces will not be outputed\n");
+ fprintf(ofp, " --no-output Traces will not be outputted\n");
+ fprintf(ofp, " --snapshot Set the session in snapshot mode.\n");
+ fprintf(ofp, " Created in no-output mode and uses the URL,\n");
+ fprintf(ofp, " if one, as the default snapshot output.\n");
+ fprintf(ofp, " Every channel will be set in overwrite mode\n");
+ fprintf(ofp, " and with mmap output (splice not supported).\n");
+ fprintf(ofp, " --live USEC Set the session in live-reading mode.\n");
+ fprintf(ofp, " The delay parameter in micro-seconds is the\n");
+ fprintf(ofp, " maximum time the user can wait for the data\n");
+ fprintf(ofp, " to be flushed. Requires a network URL (-U or -C/-D)\n");
+ fprintf(ofp, " and a lttng-relayd listening.\n");
fprintf(ofp, "\n");
fprintf(ofp, "Extended Options:\n");
fprintf(ofp, "\n");
return ret;
}
+static int add_snapshot_output(const char *session_name, const char *ctrl_url,
+ const char *data_url)
+{
+ int ret;
+ struct lttng_snapshot_output *output = NULL;
+
+ assert(session_name);
+
+ output = lttng_snapshot_output_create();
+ if (!output) {
+ ret = CMD_FATAL;
+ goto error_create;
+ }
+
+ if (ctrl_url) {
+ ret = lttng_snapshot_output_set_ctrl_url(ctrl_url, output);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ if (data_url) {
+ ret = lttng_snapshot_output_set_data_url(data_url, output);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ /* This call, if successful, populates the id of the output object. */
+ ret = lttng_snapshot_add_output(session_name, output);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ lttng_snapshot_output_destroy(output);
+error_create:
+ return ret;
+}
+
/*
* Create a tracing session.
* If no name is specified, a default name is generated.
print_str_url = alloc_url + strlen("file://");
}
} else {
- /* No output means --no-output. */
+ /* No output means --no-output or --snapshot mode. */
url = NULL;
}
goto error;
}
- ret = _lttng_create_session_ext(session_name, url, datetime);
+ if ((opt_live_timer && !opt_url) && (opt_live_timer && !opt_data_url)) {
+ ERR("You need a network URL (-U or -C/-D) to use live tracing.");
+ ret = CMD_ERROR;
+ goto error;
+ }
+
+ if (opt_snapshot && opt_live_timer) {
+ ERR("Snapshot and live modes are mutually exclusive.");
+ ret = CMD_ERROR;
+ goto error;
+ }
+
+ if (opt_snapshot) {
+ /* No output by default. */
+ const char *snapshot_url = NULL;
+
+ if (opt_url) {
+ snapshot_url = url;
+ } else if (!opt_data_url && !opt_ctrl_url) {
+ /* This is the session path that we need to use as output. */
+ snapshot_url = url;
+ }
+ ret = lttng_create_session_snapshot(session_name, snapshot_url);
+ } else if (opt_live_timer) {
+ ret = lttng_create_session_live(session_name, url, opt_live_timer);
+ } else {
+ ret = _lttng_create_session_ext(session_name, url, datetime, -1);
+ }
if (ret < 0) {
/* Don't set ret so lttng can interpret the sessiond error. */
switch (-ret) {
}
if (opt_ctrl_url && opt_data_url) {
- /* Setting up control URI (-C or/and -D opt) */
- ret = set_consumer_url(session_name, opt_ctrl_url, opt_data_url);
+ if (opt_snapshot) {
+ ret = add_snapshot_output(session_name, opt_ctrl_url,
+ opt_data_url);
+ } else {
+ /* Setting up control URI (-C or/and -D opt) */
+ ret = set_consumer_url(session_name, opt_ctrl_url, opt_data_url);
+ }
if (ret < 0) {
/* Destroy created session because the URL are not valid. */
lttng_destroy_session(session_name);
}
MSG("Session %s created.", session_name);
- if (print_str_url) {
+ if (print_str_url && !opt_snapshot) {
MSG("Traces will be written in %s", print_str_url);
+ } else if (opt_snapshot) {
+ if (print_str_url) {
+ MSG("Default snapshot output set to: %s", print_str_url);
+ }
+ MSG("Snapshot mode set. Every channel enabled for that session will "
+ "be set in overwrite mode and mmap output.");
}
/* Init lttng session config */
int cmd_create(int argc, const char **argv)
{
int opt, ret = CMD_SUCCESS;
+ char *opt_arg = NULL;
static poptContext pc;
pc = poptGetContext(NULL, argc, argv, long_options, 0);
case OPT_LIST_OPTIONS:
list_cmd_options(stdout, long_options);
goto end;
+ case OPT_LIVE_TIMER:
+ {
+ unsigned long v;
+
+ errno = 0;
+ opt_arg = poptGetOptArg(pc);
+ v = strtoul(opt_arg, NULL, 0);
+ if (errno != 0 || !isdigit(opt_arg[0])) {
+ ERR("Wrong value in --live parameter: %s", opt_arg);
+ ret = CMD_ERROR;
+ goto end;
+ }
+ if (v != (uint32_t) v) {
+ ERR("32-bit overflow in --live parameter: %s", opt_arg);
+ ret = CMD_ERROR;
+ goto end;
+ }
+ opt_live_timer = (uint32_t) v;
+ DBG("Session live timer interval set to %d", opt_live_timer);
+ break;
+ }
default:
usage(stderr);
ret = CMD_UNDEFINED;
*/
static void usage(FILE *ofp)
{
- fprintf(ofp, "usage: lttng disable-channel NAME[,NAME2,...] [-k|-u] [OPTIONS]\n");
+ fprintf(ofp, "usage: lttng disable-channel NAME[,NAME2,...] (-k | -u) [OPTIONS]\n");
fprintf(ofp, "\n");
fprintf(ofp, "Options:\n");
fprintf(ofp, " -h, --help Show this help\n");
} else if (opt_userspace) {
dom.type = LTTNG_DOMAIN_UST;
} else {
- ERR("Please specify a tracer (-k/--kernel or -u/--userspace)");
+ print_missing_domain();
ret = CMD_ERROR;
goto error;
}
warn = 1;
} else {
MSG("%s channel %s disabled for session %s",
- opt_kernel ? "Kernel" : "UST", channel_name, session_name);
+ get_domain_str(dom.type), channel_name, session_name);
}
/* Next channel */
static char *opt_session_name;
static int opt_userspace;
static int opt_disable_all;
+static int opt_jul;
#if 0
/* Not implemented yet */
static char *opt_cmd_name;
{"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0},
{"all-events", 'a', POPT_ARG_VAL, &opt_disable_all, 1, 0, 0},
{"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0},
+ {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0},
{"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0},
#if 0
/* Not implemented yet */
*/
static void usage(FILE *ofp)
{
- fprintf(ofp, "usage: lttng disable-event NAME[,NAME2,...] [-k|-u] [OPTIONS]\n");
+ fprintf(ofp, "usage: lttng disable-event NAME[,NAME2,...] (-k | -u) [OPTIONS]\n");
fprintf(ofp, "\n");
fprintf(ofp, "Options:\n");
fprintf(ofp, " -h, --help Show this help\n");
fprintf(ofp, " -a, --all-events Disable all tracepoints\n");
fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n");
fprintf(ofp, " -u, --userspace Apply to the user-space tracer\n");
+ fprintf(ofp, " -j, --jul Apply for Java application using JUL\n");
fprintf(ofp, "\n");
}
+static
+const char *print_channel_name(const char *name)
+{
+ return name ? : DEFAULT_CHANNEL_NAME;
+}
+
+static
+const char *print_raw_channel_name(const char *name)
+{
+ return name ? : "<default>";
+}
+
/*
* disable_events
*
*/
static int disable_events(char *session_name)
{
- int err, ret = CMD_SUCCESS, warn = 0;
+ int ret = CMD_SUCCESS, warn = 0;
char *event_name, *channel_name = NULL;
struct lttng_domain dom;
dom.type = LTTNG_DOMAIN_KERNEL;
} else if (opt_userspace) {
dom.type = LTTNG_DOMAIN_UST;
+ } else if (opt_jul) {
+ dom.type = LTTNG_DOMAIN_JUL;
} else {
- ERR("Please specify a tracer (-k/--kernel or -u/--userspace)");
+ print_missing_domain();
ret = CMD_ERROR;
goto error;
}
- /* Get channel name */
- if (opt_channel_name == NULL) {
- err = asprintf(&channel_name, DEFAULT_CHANNEL_NAME);
- if (err < 0) {
- ret = CMD_FATAL;
- goto error;
- }
- } else {
- channel_name = opt_channel_name;
- }
+ channel_name = opt_channel_name;
handle = lttng_create_handle(session_name, &dom);
if (handle == NULL) {
}
MSG("All %s events are disabled in channel %s",
- opt_kernel ? "kernel" : "UST", channel_name);
+ get_domain_str(dom.type), print_channel_name(channel_name));
goto end;
}
ret = lttng_disable_event(handle, event_name, channel_name);
if (ret < 0) {
ERR("Event %s: %s (channel %s, session %s)", event_name,
- lttng_strerror(ret), channel_name, session_name);
+ lttng_strerror(ret),
+ ret == -LTTNG_ERR_NEED_CHANNEL_NAME
+ ? print_raw_channel_name(channel_name)
+ : print_channel_name(channel_name),
+ session_name);
warn = 1;
} else {
MSG("%s event %s disabled in channel %s for session %s",
- opt_kernel ? "kernel" : "UST", event_name, channel_name,
+ get_domain_str(dom.type), event_name,
+ print_channel_name(channel_name),
session_name);
}
if (warn) {
ret = CMD_WARNING;
}
- if (opt_channel_name == NULL) {
- free(channel_name);
- }
lttng_destroy_handle(handle);
return ret;
*/
static void usage(FILE *ofp)
{
- fprintf(ofp, "usage: lttng enable-channel NAME[,NAME2,...] [-u|-k] [OPTIONS]\n");
+ fprintf(ofp, "usage: lttng enable-channel NAME[,NAME2,...] (-u | -k) [OPTIONS]\n");
fprintf(ofp, "\n");
fprintf(ofp, "Options:\n");
fprintf(ofp, " -h, --help Show this help\n");
DEFAULT_UST_PID_CHANNEL_OUTPUT == LTTNG_EVENT_MMAP ? output_mmap : output_splice,
DEFAULT_KERNEL_CHANNEL_OUTPUT == LTTNG_EVENT_MMAP ? output_mmap : output_splice,
DEFAULT_METADATA_OUTPUT == LTTNG_EVENT_MMAP ? output_mmap : output_splice);
- fprintf(ofp, " --buffers-uid Use per UID buffer (-u only)\n");
- fprintf(ofp, " --buffers-pid Use per PID buffer (-u only)\n");
+ fprintf(ofp, " --buffers-uid Use per UID buffer (-u/-j only)\n");
+ fprintf(ofp, " --buffers-pid Use per PID buffer (-u/-j only)\n");
fprintf(ofp, " --buffers-global Use shared buffer for the whole system (-k only)\n");
fprintf(ofp, " -C, --tracefile-size SIZE\n");
fprintf(ofp, " Maximum size of each tracefile within a stream (in bytes). 0 means unlimited.\n");
}
} else if (opt_userspace) {
dom.type = LTTNG_DOMAIN_UST;
- if (opt_buffer_uid) {
- dom.buf_type = LTTNG_BUFFER_PER_UID;
+ if (opt_buffer_pid) {
+ dom.buf_type = LTTNG_BUFFER_PER_PID;
} else {
if (opt_buffer_global) {
ERR("Buffer type not supported for domain -u");
ret = CMD_ERROR;
goto error;
}
- dom.buf_type = LTTNG_BUFFER_PER_PID;
+ dom.buf_type = LTTNG_BUFFER_PER_UID;
}
} else {
- ERR("Please specify a tracer (-k/--kernel or -u/--userspace)");
+ print_missing_domain();
ret = CMD_ERROR;
goto error;
}
switch (-ret) {
case LTTNG_ERR_KERN_CHAN_EXIST:
case LTTNG_ERR_UST_CHAN_EXIST:
+ case LTTNG_ERR_CHAN_EXIST:
WARN("Channel %s: %s (session %s)", channel_name,
lttng_strerror(ret), session_name);
goto error;
warn = 1;
} else {
MSG("%s channel %s enabled for session %s",
- opt_kernel ? "Kernel" : "UST", channel_name,
- session_name);
+ get_domain_str(dom.type), channel_name, session_name);
}
/* Next event */
static int opt_kernel;
static char *opt_session_name;
static int opt_userspace;
+static int opt_jul;
static int opt_enable_all;
static char *opt_probe;
static char *opt_function;
{"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0},
{"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0},
{"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0},
+ {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0},
{"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT, 0, 0},
{"probe", 0, POPT_ARG_STRING, &opt_probe, OPT_PROBE, 0, 0},
{"function", 0, POPT_ARG_STRING, &opt_function, OPT_FUNCTION, 0, 0},
fprintf(ofp, " -a, --all Enable all tracepoints and syscalls\n");
fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n");
fprintf(ofp, " -u, --userspace Apply to the user-space tracer\n");
+ fprintf(ofp, " -j, --jul Apply for Java application using JUL\n");
fprintf(ofp, "\n");
fprintf(ofp, "Event options:\n");
fprintf(ofp, " --tracepoint Tracepoint event (default)\n");
fprintf(ofp, " e.g.:\n");
fprintf(ofp, " \"*\"\n");
fprintf(ofp, " \"app_component:na*\"\n");
- fprintf(ofp, " --probe [addr | symbol | symbol+offset]\n");
+ fprintf(ofp, " --probe (addr | symbol | symbol+offset)\n");
fprintf(ofp, " Dynamic probe.\n");
fprintf(ofp, " Addr and offset can be octal (0NNN...),\n");
fprintf(ofp, " decimal (NNN...) or hexadecimal (0xNNN...)\n");
- fprintf(ofp, " --function [addr | symbol | symbol+offset]\n");
+ fprintf(ofp, " --function (addr | symbol | symbol+offset)\n");
fprintf(ofp, " Dynamic function entry/return probe.\n");
fprintf(ofp, " Addr and offset can be octal (0NNN...),\n");
fprintf(ofp, " decimal (NNN...) or hexadecimal (0xNNN...)\n");
fprintf(ofp, " TRACE_DEBUG_LINE = 13\n");
fprintf(ofp, " TRACE_DEBUG = 14\n");
fprintf(ofp, " (shortcuts such as \"system\" are allowed)\n");
- fprintf(ofp, " --filter \'expression\'\n");
+ fprintf(ofp, " -f, --filter \'expression\'\n");
fprintf(ofp, " Filter expression on event fields and context.\n");
fprintf(ofp, " Event recording depends on evaluation.\n");
fprintf(ofp, " Only specify on first activation of\n");
fprintf(ofp, " Expression examples:.\n");
fprintf(ofp, " \n");
fprintf(ofp, " 'intfield > 500 && intfield < 503'\n");
- fprintf(ofp, " '(stringfield == \"test\" || intfield != 10) && intfield > 33'\n");
+ fprintf(ofp, " '(strfield == \"test\" || intfield != 10) && intfield > 33'\n");
fprintf(ofp, " 'doublefield > 1.1 && intfield < 5.3'\n");
fprintf(ofp, " \n");
fprintf(ofp, " Wildcards are allowed at the end of strings:\n");
}
}
+static
+const char *print_channel_name(const char *name)
+{
+ return name ? : DEFAULT_CHANNEL_NAME;
+}
+
+static
+const char *print_raw_channel_name(const char *name)
+{
+ return name ? : "<default>";
+}
+
/*
* Enabling event using the lttng API.
*/
static int enable_events(char *session_name)
{
- int err, ret = CMD_SUCCESS, warn = 0;
+ int ret = CMD_SUCCESS, warn = 0;
char *event_name, *channel_name = NULL;
struct lttng_event ev;
struct lttng_domain dom;
} else if (opt_userspace) {
dom.type = LTTNG_DOMAIN_UST;
/* Default. */
- dom.buf_type = LTTNG_BUFFER_PER_PID;
+ dom.buf_type = LTTNG_BUFFER_PER_UID;
+ } else if (opt_jul) {
+ dom.type = LTTNG_DOMAIN_JUL;
+ /* Default. */
+ dom.buf_type = LTTNG_BUFFER_PER_UID;
} else {
- ERR("Please specify a tracer (-k/--kernel or -u/--userspace)");
+ print_missing_domain();
ret = CMD_ERROR;
goto error;
}
- if (opt_channel_name == NULL) {
- err = asprintf(&channel_name, DEFAULT_CHANNEL_NAME);
- if (err < 0) {
- ret = CMD_FATAL;
- goto error;
- }
- } else {
- channel_name = opt_channel_name;
- }
+ channel_name = opt_channel_name;
handle = lttng_create_handle(session_name, &dom);
if (handle == NULL) {
switch (-ret) {
case LTTNG_ERR_KERN_EVENT_EXIST:
WARN("Kernel events already enabled (channel %s, session %s)",
- channel_name, session_name);
+ print_channel_name(channel_name), session_name);
break;
default:
ERR("Events: %s (channel %s, session %s)",
- lttng_strerror(ret), channel_name, session_name);
+ lttng_strerror(ret),
+ ret == -LTTNG_ERR_NEED_CHANNEL_NAME
+ ? print_raw_channel_name(channel_name)
+ : print_channel_name(channel_name),
+ session_name);
break;
}
goto end;
case LTTNG_EVENT_TRACEPOINT:
if (opt_loglevel) {
MSG("All %s tracepoints are enabled in channel %s for loglevel %s",
- opt_kernel ? "kernel" : "UST", channel_name,
+ get_domain_str(dom.type),
+ print_channel_name(channel_name),
opt_loglevel);
} else {
MSG("All %s tracepoints are enabled in channel %s",
- opt_kernel ? "kernel" : "UST", channel_name);
+ get_domain_str(dom.type),
+ print_channel_name(channel_name));
}
break;
case LTTNG_EVENT_SYSCALL:
if (opt_kernel) {
MSG("All kernel system calls are enabled in channel %s",
- channel_name);
+ print_channel_name(channel_name));
}
break;
case LTTNG_EVENT_ALL:
if (opt_loglevel) {
MSG("All %s events are enabled in channel %s for loglevel %s",
- opt_kernel ? "kernel" : "UST", channel_name,
+ get_domain_str(dom.type),
+ print_channel_name(channel_name),
opt_loglevel);
} else {
MSG("All %s events are enabled in channel %s",
- opt_kernel ? "kernel" : "UST", channel_name);
+ get_domain_str(dom.type),
+ print_channel_name(channel_name));
}
break;
default:
if (ret < 0) {
switch (-ret) {
case LTTNG_ERR_FILTER_EXIST:
- WARN("Filter on events is already enabled"
+ WARN("Filter on all events is already enabled"
" (channel %s, session %s)",
- channel_name, session_name);
+ print_channel_name(channel_name), session_name);
break;
- case LTTNG_ERR_FILTER_INVAL:
- case LTTNG_ERR_FILTER_NOMEM:
default:
- ERR("%s", lttng_strerror(ret));
+ ERR("All events: %s (channel %s, session %s, filter \'%s\')",
+ lttng_strerror(ret),
+ ret == -LTTNG_ERR_NEED_CHANNEL_NAME
+ ? print_raw_channel_name(channel_name)
+ : print_channel_name(channel_name),
+ session_name, opt_filter);
break;
}
goto error;
/* Kernel tracer action */
if (opt_kernel) {
DBG("Enabling kernel event %s for channel %s",
- event_name, channel_name);
+ event_name,
+ print_channel_name(channel_name));
switch (opt_event_type) {
case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */
#endif
DBG("Enabling UST event %s for channel %s, loglevel %s", event_name,
- channel_name, opt_loglevel ? : "<all>");
+ print_channel_name(channel_name), opt_loglevel ? : "<all>");
switch (opt_event_type) {
case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */
} else {
ev.loglevel = -1;
}
+ } else if (opt_jul) {
+ if (opt_event_type != LTTNG_EVENT_ALL &&
+ opt_event_type != LTTNG_EVENT_TRACEPOINT) {
+ ERR("Event type not supported for JUL domain.");
+ ret = CMD_UNSUPPORTED;
+ goto error;
+ }
+ ev.type = LTTNG_EVENT_TRACEPOINT;
+ strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN);
+ ev.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
} else {
- ERR("Please specify a tracer (-k/--kernel or -u/--userspace)");
+ print_missing_domain();
ret = CMD_ERROR;
goto error;
}
switch (-ret) {
case LTTNG_ERR_KERN_EVENT_EXIST:
WARN("Kernel event %s already enabled (channel %s, session %s)",
- event_name, channel_name, session_name);
+ event_name,
+ print_channel_name(channel_name), session_name);
break;
default:
ERR("Event %s: %s (channel %s, session %s)", event_name,
- lttng_strerror(ret), channel_name, session_name);
+ lttng_strerror(ret),
+ ret == -LTTNG_ERR_NEED_CHANNEL_NAME
+ ? print_raw_channel_name(channel_name)
+ : print_channel_name(channel_name),
+ session_name);
break;
}
warn = 1;
} else {
MSG("%s event %s created in channel %s",
- opt_kernel ? "kernel": "UST", event_name, channel_name);
+ get_domain_str(dom.type), event_name,
+ print_channel_name(channel_name));
}
}
case LTTNG_ERR_FILTER_EXIST:
WARN("Filter on event %s is already enabled"
" (channel %s, session %s)",
- event_name, channel_name, session_name);
+ event_name,
+ print_channel_name(channel_name), session_name);
break;
- case LTTNG_ERR_FILTER_INVAL:
- case LTTNG_ERR_FILTER_NOMEM:
- ERR("%s", lttng_strerror(ret));
default:
- ERR("Setting filter for event %s: '%s'", ev.name,
- opt_filter);
+ ERR("Event %s: %s (channel %s, session %s, filter \'%s\')", ev.name,
+ lttng_strerror(ret),
+ ret == -LTTNG_ERR_NEED_CHANNEL_NAME
+ ? print_raw_channel_name(channel_name)
+ : print_channel_name(channel_name),
+ session_name, opt_filter);
break;
}
goto error;
if (warn) {
ret = CMD_WARNING;
}
- if (opt_channel_name == NULL) {
- free(channel_name);
- }
lttng_destroy_handle(handle);
return ret;
static int opt_userspace;
static int opt_kernel;
+static int opt_jul;
static char *opt_channel;
static int opt_domain;
static int opt_fields;
/* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
{"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0},
{"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0},
+ {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0},
#if 0
/* Not implemented yet */
{"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0},
*/
static void usage(FILE *ofp)
{
- fprintf(ofp, "usage: lttng list [OPTIONS] [SESSION [<OPTIONS>]]\n");
+ fprintf(ofp, "usage: lttng list [OPTIONS] [SESSION [SESSION OPTIONS]]\n");
fprintf(ofp, "\n");
fprintf(ofp, "With no arguments, list available tracing session(s)\n");
fprintf(ofp, "\n");
fprintf(ofp, " --list-options Simple listing of options\n");
fprintf(ofp, " -k, --kernel Select kernel domain\n");
fprintf(ofp, " -u, --userspace Select user-space domain.\n");
+ fprintf(ofp, " -j, --jul Apply for Java application using JUL\n");
fprintf(ofp, " -f, --fields List event fields.\n");
#if 0
fprintf(ofp, " -p, --pid PID List user-space events by PID\n");
const char *active_string(int value)
{
switch (value) {
- case 0: return " [inactive]";
- case 1: return " [active]";
+ case 0: return "inactive";
+ case 1: return "active";
case -1: return "";
default: return NULL;
}
}
+static const char *snapshot_string(int value)
+{
+ switch (value) {
+ case 1:
+ return " snapshot";
+ default:
+ return "";
+ }
+}
+
static
const char *enabled_string(int value)
{
field_type(field), field->nowrite ? " [no write]" : "");
}
+static int list_jul_events(void)
+{
+ int i, size;
+ struct lttng_domain domain;
+ struct lttng_handle *handle;
+ struct lttng_event *event_list;
+ pid_t cur_pid = 0;
+ char *cmdline = NULL;
+
+ DBG("Getting JUL tracing events");
+
+ memset(&domain, 0, sizeof(domain));
+ domain.type = LTTNG_DOMAIN_JUL;
+
+ handle = lttng_create_handle(NULL, &domain);
+ if (handle == NULL) {
+ goto error;
+ }
+
+ size = lttng_list_tracepoints(handle, &event_list);
+ if (size < 0) {
+ ERR("Unable to list JUL events: %s", lttng_strerror(size));
+ lttng_destroy_handle(handle);
+ return size;
+ }
+
+ MSG("JUL events (Logger name):\n-------------------------");
+
+ if (size == 0) {
+ MSG("None");
+ }
+
+ for (i = 0; i < size; i++) {
+ if (cur_pid != event_list[i].pid) {
+ cur_pid = event_list[i].pid;
+ cmdline = get_cmdline_by_pid(cur_pid);
+ MSG("\nPID: %d - Name: %s", cur_pid, cmdline);
+ free(cmdline);
+ }
+ MSG("%s- %s", indent6, event_list[i].name);
+ }
+
+ MSG("");
+
+ free(event_list);
+ lttng_destroy_handle(handle);
+
+ return CMD_SUCCESS;
+
+error:
+ lttng_destroy_handle(handle);
+ return -1;
+}
+
/*
* Ask session daemon for all user space tracepoints available.
*/
return -1;
}
+/*
+ * List JUL events for a specific session using the handle.
+ *
+ * Return CMD_SUCCESS on success else a negative value.
+ */
+static int list_session_jul_events(void)
+{
+ int ret, count, i;
+ struct lttng_event *events = NULL;
+
+ count = lttng_list_events(handle, "", &events);
+ if (count < 0) {
+ ret = count;
+ ERR("%s", lttng_strerror(ret));
+ goto error;
+ }
+
+ MSG("Events (Logger name):\n---------------------");
+ if (count == 0) {
+ MSG("%sNone\n", indent6);
+ goto end;
+ }
+
+ for (i = 0; i < count; i++) {
+ MSG("%s- %s%s", indent4, events[i].name,
+ enabled_string(events[i].enabled));
+ }
+
+ MSG("");
+
+end:
+ free(events);
+ ret = CMD_SUCCESS;
+
+error:
+ return ret;
+}
+
/*
* List events of channel of session and domain.
*/
if (session_name != NULL) {
if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) {
session_found = 1;
- MSG("Tracing session %s:%s", session_name, active_string(sessions[i].enabled));
+ MSG("Tracing session %s: [%s%s]", session_name,
+ active_string(sessions[i].enabled),
+ snapshot_string(sessions[i].snapshot_mode));
MSG("%sTrace path: %s\n", indent4, sessions[i].path);
break;
}
} else {
- MSG(" %d) %s (%s)%s", i + 1, sessions[i].name, sessions[i].path,
- active_string(sessions[i].enabled));
+ MSG(" %d) %s (%s) [%s%s]", i + 1, sessions[i].name, sessions[i].path,
+ active_string(sessions[i].enabled),
+ snapshot_string(sessions[i].snapshot_mode));
}
}
case LTTNG_DOMAIN_UST:
MSG(" - UST global");
break;
+ case LTTNG_DOMAIN_JUL:
+ MSG(" - JUL (Java Util Logging)");
+ break;
default:
break;
}
} else if (opt_userspace) {
DBG2("Listing userspace global domain");
domain.type = LTTNG_DOMAIN_UST;
+ } else if (opt_jul) {
+ DBG2("Listing JUL domain");
+ domain.type = LTTNG_DOMAIN_JUL;
}
- if (opt_kernel || opt_userspace) {
+ if (opt_kernel || opt_userspace || opt_jul) {
handle = lttng_create_handle(session_name, &domain);
if (handle == NULL) {
ret = CMD_FATAL;
}
if (session_name == NULL) {
- if (!opt_kernel && !opt_userspace) {
+ if (!opt_kernel && !opt_userspace && !opt_jul) {
ret = list_sessions(NULL);
if (ret != 0) {
goto end;
goto end;
}
}
+ if (opt_jul) {
+ ret = list_jul_events();
+ if (ret < 0) {
+ ret = CMD_ERROR;
+ goto end;
+ }
+ }
} else {
/* List session attributes */
ret = list_sessions(session_name);
domains[i].buf_type ==
LTTNG_BUFFER_PER_PID ? "per PID" : "per UID");
break;
+ case LTTNG_DOMAIN_JUL:
+ MSG("=== Domain: JUL (Java Util Logging) ===\n");
+ break;
default:
MSG("=== Domain: Unimplemented ===\n");
break;
goto end;
}
+ if (domains[i].type == LTTNG_DOMAIN_JUL) {
+ ret = list_session_jul_events();
+ if (ret < 0) {
+ goto end;
+ }
+ continue;
+ }
+
ret = list_channels(opt_channel);
if (ret < 0) {
goto end;
*/
static void usage(FILE *ofp)
{
- fprintf(ofp, "usage: lttng set-session NAME\n");
+ fprintf(ofp, "usage: lttng set-session NAME [OPTIONS]\n");
fprintf(ofp, "\n");
fprintf(ofp, "Options:\n");
fprintf(ofp, " -h, --help Show this help\n");
#include <sys/types.h>
#include <unistd.h>
+#include <common/utils.h>
#include <lttng/snapshot.h>
#include "../command.h"
OPT_HELP = 1,
OPT_LIST_OPTIONS,
OPT_MAX_SIZE,
+ OPT_LIST_COMMANDS,
};
static struct poptOption snapshot_opts[] = {
{"ctrl-url", 'C', POPT_ARG_STRING, &opt_ctrl_url, 0, 0, 0},
{"data-url", 'D', POPT_ARG_STRING, &opt_data_url, 0, 0, 0},
{"name", 'n', POPT_ARG_STRING, &opt_output_name, 0, 0, 0},
- {"max-size", 'm', POPT_ARG_DOUBLE, 0, OPT_MAX_SIZE, 0, 0},
+ {"max-size", 'm', POPT_ARG_STRING, 0, OPT_MAX_SIZE, 0, 0},
{"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL},
+ {"list-commands", 0, POPT_ARG_NONE, NULL, OPT_LIST_COMMANDS},
{0, 0, 0, 0, 0, 0, 0}
};
*/
static void usage(FILE *ofp)
{
- fprintf(ofp, "usage: lttng snapshot [ACTION]\n");
+ fprintf(ofp, "usage: lttng snapshot [OPTION] ACTION\n");
fprintf(ofp, "\n");
fprintf(ofp, "Actions:\n");
fprintf(ofp, " add-output [-m <SIZE>] [-s <NAME>] [-n <NAME>] <URL> | -C <URL> -D <URL>\n");
fprintf(ofp, " Setup and add an snapshot output for a session.\n");
fprintf(ofp, "\n");
- fprintf(ofp, " del-output ID [-s <NAME>]\n");
+ fprintf(ofp, " del-output ID | NAME [-s <NAME>]\n");
fprintf(ofp, " Delete an output for a session using the ID.\n");
fprintf(ofp, "\n");
fprintf(ofp, " list-output [-s <NAME>]\n");
fprintf(ofp, " record [-m <SIZE>] [-s <NAME>] [-n <NAME>] [<URL> | -C <URL> -D <URL>]\n");
fprintf(ofp, " Snapshot a session's buffer(s) for all domains. If an URL is\n");
fprintf(ofp, " specified, it is used instead of a previously added output.\n");
- fprintf(ofp, " The snapshot is saved in the session directory in snapshot/ with\n");
- fprintf(ofp, " the top directory being NAME or the default: snapshot-ID/\n");
+ fprintf(ofp, " Specifying only a name or/a size will override the current output value.\n");
+ fprintf(ofp, " For instance, you can record a snapshot with a custom maximum size\n");
+ fprintf(ofp, " or with a different name.\n");
fprintf(ofp, "\n");
fprintf(ofp, "Options:\n");
fprintf(ofp, " -h, --help Show this help\n");
fprintf(ofp, " --list-options Simple listing of options\n");
fprintf(ofp, " -s, --session NAME Apply to session name\n");
fprintf(ofp, " -n, --name NAME Name of the output or snapshot\n");
- fprintf(ofp, " -m, --max-size SIZE Maximum bytes size of the snapshot\n");
+ fprintf(ofp, " -m, --max-size SIZE Maximum bytes size of the snapshot {+k,+M,+G}\n");
fprintf(ofp, " -C, --ctrl-url URL Set control path URL. (Must use -D also)\n");
fprintf(ofp, " -D, --data-url URL Set data path URL. (Must use -C also)\n");
fprintf(ofp, "\n");
/*
* Delete output by ID.
*/
-static int del_output(uint32_t id)
+static int del_output(uint32_t id, const char *name)
{
int ret;
struct lttng_snapshot_output *output = NULL;
goto error;
}
- ret = lttng_snapshot_output_set_id(id, output);
+ if (name) {
+ ret = lttng_snapshot_output_set_name(name, output);
+ } else if (id != UINT32_MAX) {
+ ret = lttng_snapshot_output_set_id(id, output);
+ } else {
+ ret = CMD_ERROR;
+ goto error;
+ }
if (ret < 0) {
ret = CMD_FATAL;
goto error;
goto error;
}
- MSG("Snapshot output id %" PRIu32 " successfully deleted for session %s",
- id, current_session_name);
+ if (id != UINT32_MAX) {
+ MSG("Snapshot output id %" PRIu32 " successfully deleted for session %s",
+ id, current_session_name);
+ } else {
+ MSG("Snapshot output %s successfully deleted for session %s",
+ name, current_session_name);
+ }
error:
lttng_snapshot_output_destroy(output);
static int cmd_del_output(int argc, const char **argv)
{
int ret = CMD_SUCCESS;
+ char *name;
+ long id;
if (argc < 2) {
usage(stderr);
goto end;
}
- ret = del_output(atoi(argv[1]));
+ errno = 0;
+ id = strtol(argv[1], &name, 10);
+ if (id == 0 && errno == 0) {
+ ret = del_output(UINT32_MAX, name);
+ } else if (errno == 0 && *name == '\0') {
+ ret = del_output(id, NULL);
+ } else {
+ ERR("Argument %s not recognized", argv[1]);
+ ret = -1;
+ goto end;
+ }
end:
return ret;
int ret;
struct lttng_snapshot_output *output = NULL;
- if (url || (opt_ctrl_url && opt_data_url)) {
- output = create_output_from_args(url);
- if (!output) {
- ret = CMD_FATAL;
- goto error;
- }
+ output = create_output_from_args(url);
+ if (!output) {
+ ret = CMD_FATAL;
+ goto error;
}
ret = lttng_snapshot_record(current_session_name, output, 0);
} else if (opt_ctrl_url) {
MSG("Snapshot written to ctrl: %s, data: %s", opt_ctrl_url,
opt_data_url);
- } else {
- MSG("Snapshot written in session directory.");
}
error:
case OPT_LIST_OPTIONS:
list_cmd_options(stdout, snapshot_opts);
goto end;
+ case OPT_LIST_COMMANDS:
+ list_commands(actions, stdout);
+ goto end;
case OPT_MAX_SIZE:
{
- long long int val;
- char *endptr;
+ uint64_t val;
const char *opt = poptGetOptArg(pc);
- val = strtoll(opt, &endptr, 10);
- if ((errno == ERANGE && (val == LLONG_MAX || val == LONG_MIN))
- || (errno != 0 && val == 0)) {
+ if (utils_parse_size_suffix((char *) opt, &val) < 0) {
ERR("Unable to handle max-size value %s", opt);
ret = CMD_ERROR;
goto end;
}
- if (endptr == opt) {
- ERR("No digits were found in %s", opt);
- ret = CMD_ERROR;
- goto end;
- }
opt_max_size = val;
break;
}
}
-/*
- * list_commands
- *
- * List commands line by line. This is mostly for bash auto completion and to
- * avoid difficult parsing.
- */
-static void list_commands(FILE *ofp)
-{
- int i = 0;
- struct cmd_struct *cmd = NULL;
-
- cmd = &commands[i];
- while (cmd->name != NULL) {
- fprintf(ofp, "%s\n", cmd->name);
- i++;
- cmd = &commands[i];
- }
-}
-
/*
* clean_exit
*/
ret = 0;
goto end;
case OPT_DUMP_COMMANDS:
- list_commands(stdout);
+ list_commands(commands, stdout);
ret = 0;
goto end;
default:
*/
#define _GNU_SOURCE
+#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include "conf.h"
#include "utils.h"
+#include "command.h"
+
+static const char *str_kernel = "Kernel";
+static const char *str_ust = "UST";
+static const char *str_jul = "JUL";
/*
* get_session_name
return NULL;
}
+/*
+ * list_commands
+ *
+ * List commands line by line. This is mostly for bash auto completion and to
+ * avoid difficult parsing.
+ */
+void list_commands(struct cmd_struct *commands, FILE *ofp)
+{
+ int i = 0;
+ struct cmd_struct *cmd = NULL;
+
+ cmd = &commands[i];
+ while (cmd->name != NULL) {
+ fprintf(ofp, "%s\n", cmd->name);
+ i++;
+ cmd = &commands[i];
+ }
+}
/*
* list_cmd_options
return fls_ulong(x - 1);
}
+
+const char *get_domain_str(enum lttng_domain_type domain)
+{
+ const char *str_dom;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_KERNEL:
+ str_dom = str_kernel;
+ break;
+ case LTTNG_DOMAIN_UST:
+ str_dom = str_ust;
+ break;
+ case LTTNG_DOMAIN_JUL:
+ str_dom = str_jul;
+ break;
+ default:
+ /* Should not have an unknown domain or else define it. */
+ assert(0);
+ }
+
+ return str_dom;
+}
#include <popt.h>
+#include <lttng/lttng.h>
+
+struct cmd_struct;
+
char *get_session_name(void);
+void list_commands(struct cmd_struct *commands, FILE *ofp);
void list_cmd_options(FILE *ofp, struct poptOption *options);
/*
*/
int get_count_order_ulong(unsigned long x);
+const char *get_domain_str(enum lttng_domain_type domain);
+
+static inline
+void print_missing_domain(void)
+{
+ ERR("Please specify a domain (-k/-u/-j).");
+}
+
#endif /* _LTTNG_UTILS_H */
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
-SUBDIRS = compat hashtable kernel-ctl sessiond-comm relayd \
- kernel-consumer ust-consumer testpoint
+SUBDIRS = compat health hashtable kernel-ctl sessiond-comm relayd \
+ kernel-consumer ust-consumer testpoint index
AM_CFLAGS = -fno-strict-aliasing
}
if (cache->max_offset == cache->total_bytes_written) {
- offset = cache->rb_pushed;
- len = cache->total_bytes_written - cache->rb_pushed;
- ret = lttng_ustconsumer_push_metadata(channel, cache->data, offset,
- len);
- if (ret < 0) {
- ERR("Pushing metadata");
- goto end;
+ char dummy = 'c';
+
+ cache->contiguous = cache->max_offset;
+ if (channel->monitor) {
+ ret = write(channel->metadata_stream->ust_metadata_poll_pipe[1],
+ &dummy, 1);
+ if (ret < 1) {
+ ERR("Wakeup UST metadata pipe");
+ goto end;
+ }
}
- cache->rb_pushed += len;
}
end:
DBG("Destroying metadata cache");
- if (channel->metadata_cache->max_offset >
- channel->metadata_cache->rb_pushed) {
- ERR("Destroying a cache not entirely commited");
- }
-
pthread_mutex_destroy(&channel->metadata_cache->lock);
free(channel->metadata_cache->data);
free(channel->metadata_cache);
* Return 0 if everything has been flushed, 1 if there is data not flushed.
*/
int consumer_metadata_cache_flushed(struct lttng_consumer_channel *channel,
- uint64_t offset)
+ uint64_t offset, int timer)
{
- int ret;
- struct consumer_metadata_cache *cache;
+ int ret = 0;
+ struct lttng_consumer_stream *metadata_stream;
assert(channel);
assert(channel->metadata_cache);
- cache = channel->metadata_cache;
-
- pthread_mutex_lock(&consumer_data.lock);
+ /*
+ * If not called from a timer handler, we have to take the
+ * channel lock to be mutually exclusive with channel teardown.
+ * Timer handler does not need to take this lock because it is
+ * already synchronized by timer stop (and, more importantly,
+ * taking this lock in a timer handler would cause a deadlock).
+ */
+ if (!timer) {
+ pthread_mutex_lock(&channel->lock);
+ }
+ pthread_mutex_lock(&channel->timer_lock);
pthread_mutex_lock(&channel->metadata_cache->lock);
- if (cache->rb_pushed >= offset) {
- ret = 0;
- } else if (!channel->metadata_stream) {
+ metadata_stream = channel->metadata_stream;
+
+ if (!metadata_stream) {
/*
* Having no metadata stream means the channel is being destroyed so there
* is no cache to flush anymore.
*/
ret = 0;
+ } else if (metadata_stream->ust_metadata_pushed >= offset) {
+ ret = 0;
} else if (channel->metadata_stream->endpoint_status !=
CONSUMER_ENDPOINT_ACTIVE) {
/* An inactive endpoint means we don't have to flush anymore. */
}
pthread_mutex_unlock(&channel->metadata_cache->lock);
- pthread_mutex_unlock(&consumer_data.lock);
+ pthread_mutex_unlock(&channel->timer_lock);
+ if (!timer) {
+ pthread_mutex_unlock(&channel->lock);
+ }
return ret;
}
char *data;
uint64_t cache_alloc_size;
/*
- * How many bytes from the cache were already sent to the ring buffer.
+ * How many bytes from the cache are written contiguously.
*/
- uint64_t rb_pushed;
+ uint64_t contiguous;
/*
* How many bytes are written in the buffer (excluding the wholes).
*/
int consumer_metadata_cache_allocate(struct lttng_consumer_channel *channel);
void consumer_metadata_cache_destroy(struct lttng_consumer_channel *channel);
int consumer_metadata_cache_flushed(struct lttng_consumer_channel *channel,
- uint64_t offset);
+ uint64_t offset, int timer);
#endif /* CONSUMER_METADATA_CACHE_H */
#include <unistd.h>
#include <common/common.h>
+#include <common/index/index.h>
+#include <common/kernel-consumer/kernel-consumer.h>
#include <common/relayd/relayd.h>
#include <common/ust-consumer/ust-consumer.h>
stream->out_fd = -1;
}
+ if (stream->index_fd >= 0) {
+ ret = close(stream->index_fd);
+ if (ret) {
+ PERROR("close stream index_fd");
+ }
+ stream->index_fd = -1;
+ }
+
/* Check and cleanup relayd if needed. */
rcu_read_lock();
relayd = consumer_find_relayd(stream->net_seq_idx);
*/
if (stream->globally_visible) {
pthread_mutex_lock(&consumer_data.lock);
+ pthread_mutex_lock(&stream->chan->lock);
pthread_mutex_lock(&stream->lock);
/* Remove every reference of the stream in the consumer. */
consumer_stream_delete(stream, ht);
consumer_data.need_update = 1;
pthread_mutex_unlock(&stream->lock);
+ pthread_mutex_unlock(&stream->chan->lock);
pthread_mutex_unlock(&consumer_data.lock);
} else {
/*
/* Free stream within a RCU call. */
consumer_stream_free(stream);
}
+
+/*
+ * Write index of a specific stream either on the relayd or local disk.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int consumer_stream_write_index(struct lttng_consumer_stream *stream,
+ struct lttng_packet_index *index)
+{
+ int ret;
+ struct consumer_relayd_sock_pair *relayd;
+
+ assert(stream);
+ assert(index);
+
+ rcu_read_lock();
+ relayd = consumer_find_relayd(stream->net_seq_idx);
+ if (relayd) {
+ ret = relayd_send_index(&relayd->control_sock, index,
+ stream->relayd_stream_id, stream->next_net_seq_num - 1);
+ } else {
+ ret = index_write(stream->index_fd, index,
+ sizeof(struct lttng_packet_index));
+ }
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Synchronize the metadata using a given session ID. A successful acquisition
+ * of a metadata stream will trigger a request to the session daemon and a
+ * snapshot so the metadata thread can consume it.
+ *
+ * This function call is a rendez-vous point between the metadata thread and
+ * the data thread.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int consumer_stream_sync_metadata(struct lttng_consumer_local_data *ctx,
+ uint64_t session_id)
+{
+ int ret;
+ struct lttng_consumer_stream *metadata = NULL, *stream = NULL;
+ struct lttng_ht_iter iter;
+ struct lttng_ht *ht;
+
+ assert(ctx);
+
+ /* Ease our life a bit. */
+ ht = consumer_data.stream_list_ht;
+
+ rcu_read_lock();
+
+ /* Search the metadata associated with the session id of the given stream. */
+
+ cds_lfht_for_each_entry_duplicate(ht->ht,
+ ht->hash_fct(&session_id, lttng_ht_seed), ht->match_fct,
+ &session_id, &iter.iter, stream, node_session_id.node) {
+ if (stream->metadata_flag) {
+ metadata = stream;
+ break;
+ }
+ }
+ if (!metadata) {
+ ret = 0;
+ goto end_unlock_rcu;
+ }
+
+ /*
+ * In UST, since we have to write the metadata from the cache packet
+ * by packet, we might need to start this procedure multiple times
+ * until all the metadata from the cache has been extracted.
+ */
+ do {
+ /*
+ * Steps :
+ * - Lock the metadata stream
+ * - Check if metadata stream node was deleted before locking.
+ * - if yes, release and return success
+ * - Check if new metadata is ready (flush + snapshot pos)
+ * - If nothing : release and return.
+ * - Lock the metadata_rdv_lock
+ * - Unlock the metadata stream
+ * - cond_wait on metadata_rdv to wait the wakeup from the
+ * metadata thread
+ * - Unlock the metadata_rdv_lock
+ */
+ pthread_mutex_lock(&metadata->lock);
+
+ /*
+ * There is a possibility that we were able to acquire a reference on the
+ * stream from the RCU hash table but between then and now, the node might
+ * have been deleted just before the lock is acquired. Thus, after locking,
+ * we make sure the metadata node has not been deleted which means that the
+ * buffers are closed.
+ *
+ * In that case, there is no need to sync the metadata hence returning a
+ * success return code.
+ */
+ ret = cds_lfht_is_node_deleted(&metadata->node.node);
+ if (ret) {
+ ret = 0;
+ goto end_unlock_mutex;
+ }
+
+ switch (ctx->type) {
+ case LTTNG_CONSUMER_KERNEL:
+ /*
+ * Empty the metadata cache and flush the current stream.
+ */
+ ret = lttng_kconsumer_sync_metadata(metadata);
+ break;
+ case LTTNG_CONSUMER32_UST:
+ case LTTNG_CONSUMER64_UST:
+ /*
+ * Ask the sessiond if we have new metadata waiting and update the
+ * consumer metadata cache.
+ */
+ ret = lttng_ustconsumer_sync_metadata(ctx, metadata);
+ break;
+ default:
+ assert(0);
+ ret = -1;
+ break;
+ }
+ /*
+ * Error or no new metadata, we exit here.
+ */
+ if (ret <= 0 || ret == ENODATA) {
+ goto end_unlock_mutex;
+ }
+
+ /*
+ * At this point, new metadata have been flushed, so we wait on the
+ * rendez-vous point for the metadata thread to wake us up when it
+ * finishes consuming the metadata and continue execution.
+ */
+
+ pthread_mutex_lock(&metadata->metadata_rdv_lock);
+
+ /*
+ * Release metadata stream lock so the metadata thread can process it.
+ */
+ pthread_mutex_unlock(&metadata->lock);
+
+ /*
+ * Wait on the rendez-vous point. Once woken up, it means the metadata was
+ * consumed and thus synchronization is achieved.
+ */
+ pthread_cond_wait(&metadata->metadata_rdv, &metadata->metadata_rdv_lock);
+ pthread_mutex_unlock(&metadata->metadata_rdv_lock);
+ } while (ret == EAGAIN);
+
+ ret = 0;
+ goto end_unlock_rcu;
+
+end_unlock_mutex:
+ pthread_mutex_unlock(&metadata->lock);
+end_unlock_rcu:
+ rcu_read_unlock();
+ return ret;
+}
*/
void consumer_stream_destroy_buffers(struct lttng_consumer_stream *stream);
+/*
+ * Write index of a specific stream either on the relayd or local disk.
+ */
+int consumer_stream_write_index(struct lttng_consumer_stream *stream,
+ struct lttng_packet_index *index);
+
+int consumer_stream_sync_metadata(struct lttng_consumer_local_data *ctx,
+ uint64_t session_id);
+
#endif /* LTTNG_CONSUMER_STREAM_H */
#include <inttypes.h>
#include <signal.h>
+#include <bin/lttng-consumerd/health-consumerd.h>
#include <common/common.h>
+#include <common/kernel-ctl/kernel-ctl.h>
+#include <common/kernel-consumer/kernel-consumer.h>
+#include <common/consumer-stream.h>
#include "consumer-timer.h"
#include "ust-consumer/ust-consumer.h"
}
ret = sigaddset(mask, LTTNG_CONSUMER_SIG_SWITCH);
if (ret) {
- PERROR("sigaddset");
+ PERROR("sigaddset switch");
}
ret = sigaddset(mask, LTTNG_CONSUMER_SIG_TEARDOWN);
if (ret) {
- PERROR("sigaddset");
+ PERROR("sigaddset teardown");
+ }
+ ret = sigaddset(mask, LTTNG_CONSUMER_SIG_LIVE);
+ if (ret) {
+ PERROR("sigaddset live");
}
}
/*
* Execute action on a timer switch.
+ *
+ * Beware: metadata_switch_timer() should *never* take a mutex also held
+ * while consumer_timer_switch_stop() is called. It would result in
+ * deadlocks.
*/
static void metadata_switch_timer(struct lttng_consumer_local_data *ctx,
int sig, siginfo_t *si, void *uc)
switch (ctx->type) {
case LTTNG_CONSUMER32_UST:
case LTTNG_CONSUMER64_UST:
- ret = lttng_ustconsumer_request_metadata(ctx, channel);
+ /*
+ * Locks taken by lttng_ustconsumer_request_metadata():
+ * - metadata_socket_lock
+ * - Calling lttng_ustconsumer_recv_metadata():
+ * - channel->metadata_cache->lock
+ * - Calling consumer_metadata_cache_flushed():
+ * - channel->timer_lock
+ * - channel->metadata_cache->lock
+ *
+ * Ensure that neither consumer_data.lock nor
+ * channel->lock are taken within this function, since
+ * they are held while consumer_timer_switch_stop() is
+ * called.
+ */
+ ret = lttng_ustconsumer_request_metadata(ctx, channel, 1, 1);
if (ret < 0) {
channel->switch_timer_error = 1;
}
}
}
+static int send_empty_index(struct lttng_consumer_stream *stream, uint64_t ts)
+{
+ int ret;
+ struct lttng_packet_index index;
+
+ memset(&index, 0, sizeof(index));
+ index.timestamp_end = htobe64(ts);
+ ret = consumer_stream_write_index(stream, &index);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+static int check_kernel_stream(struct lttng_consumer_stream *stream)
+{
+ uint64_t ts;
+ int ret;
+
+ /*
+ * While holding the stream mutex, try to take a snapshot, if it
+ * succeeds, it means that data is ready to be sent, just let the data
+ * thread handle that. Otherwise, if the snapshot returns EAGAIN, it
+ * means that there is no data to read after the flush, so we can
+ * safely send the empty index.
+ */
+ pthread_mutex_lock(&stream->lock);
+ ret = kernctl_get_current_timestamp(stream->wait_fd, &ts);
+ if (ret < 0) {
+ ERR("Failed to get the current timestamp");
+ goto error_unlock;
+ }
+ ret = kernctl_buffer_flush(stream->wait_fd);
+ if (ret < 0) {
+ ERR("Failed to flush kernel stream");
+ goto error_unlock;
+ }
+ ret = kernctl_snapshot(stream->wait_fd);
+ if (ret < 0) {
+ if (errno != EAGAIN) {
+ ERR("Taking kernel snapshot");
+ ret = -1;
+ goto error_unlock;
+ }
+ DBG("Stream %" PRIu64 " empty, sending beacon", stream->key);
+ ret = send_empty_index(stream, ts);
+ if (ret < 0) {
+ goto error_unlock;
+ }
+ }
+ ret = 0;
+
+error_unlock:
+ pthread_mutex_unlock(&stream->lock);
+ return ret;
+}
+
+static int check_ust_stream(struct lttng_consumer_stream *stream)
+{
+ uint64_t ts;
+ int ret;
+
+ assert(stream);
+ assert(stream->ustream);
+ /*
+ * While holding the stream mutex, try to take a snapshot, if it
+ * succeeds, it means that data is ready to be sent, just let the data
+ * thread handle that. Otherwise, if the snapshot returns EAGAIN, it
+ * means that there is no data to read after the flush, so we can
+ * safely send the empty index.
+ */
+ pthread_mutex_lock(&stream->lock);
+ ret = cds_lfht_is_node_deleted(&stream->node.node);
+ if (ret) {
+ goto error_unlock;
+ }
+
+ ret = lttng_ustconsumer_get_current_timestamp(stream, &ts);
+ if (ret < 0) {
+ ERR("Failed to get the current timestamp");
+ goto error_unlock;
+ }
+ lttng_ustconsumer_flush_buffer(stream, 1);
+ ret = lttng_ustconsumer_take_snapshot(stream);
+ if (ret < 0) {
+ if (ret != -EAGAIN) {
+ ERR("Taking UST snapshot");
+ ret = -1;
+ goto error_unlock;
+ }
+ DBG("Stream %" PRIu64 " empty, sending beacon", stream->key);
+ ret = send_empty_index(stream, ts);
+ if (ret < 0) {
+ goto error_unlock;
+ }
+ }
+ ret = 0;
+
+error_unlock:
+ pthread_mutex_unlock(&stream->lock);
+ return ret;
+}
+
+/*
+ * Execute action on a live timer
+ */
+static void live_timer(struct lttng_consumer_local_data *ctx,
+ int sig, siginfo_t *si, void *uc)
+{
+ int ret;
+ struct lttng_consumer_channel *channel;
+ struct lttng_consumer_stream *stream;
+ struct lttng_ht *ht;
+ struct lttng_ht_iter iter;
+
+ channel = si->si_value.sival_ptr;
+ assert(channel);
+
+ if (channel->switch_timer_error) {
+ goto error;
+ }
+ ht = consumer_data.stream_per_chan_id_ht;
+
+ DBG("Live timer for channel %" PRIu64, channel->key);
+
+ rcu_read_lock();
+ switch (ctx->type) {
+ case LTTNG_CONSUMER32_UST:
+ case LTTNG_CONSUMER64_UST:
+ cds_lfht_for_each_entry_duplicate(ht->ht,
+ ht->hash_fct(&channel->key, lttng_ht_seed),
+ ht->match_fct, &channel->key, &iter.iter,
+ stream, node_channel_id.node) {
+ ret = check_ust_stream(stream);
+ if (ret < 0) {
+ goto error_unlock;
+ }
+ }
+ break;
+ case LTTNG_CONSUMER_KERNEL:
+ cds_lfht_for_each_entry_duplicate(ht->ht,
+ ht->hash_fct(&channel->key, lttng_ht_seed),
+ ht->match_fct, &channel->key, &iter.iter,
+ stream, node_channel_id.node) {
+ ret = check_kernel_stream(stream);
+ if (ret < 0) {
+ goto error_unlock;
+ }
+ }
+ break;
+ case LTTNG_CONSUMER_UNKNOWN:
+ assert(0);
+ break;
+ }
+
+error_unlock:
+ rcu_read_unlock();
+
+error:
+ return;
+}
+
static
void consumer_timer_signal_thread_qs(unsigned int signr)
{
channel->switch_timer_enabled = 0;
}
+/*
+ * Set the timer for the live mode.
+ */
+void consumer_timer_live_start(struct lttng_consumer_channel *channel,
+ int live_timer_interval)
+{
+ int ret;
+ struct sigevent sev;
+ struct itimerspec its;
+
+ assert(channel);
+ assert(channel->key);
+
+ if (live_timer_interval == 0) {
+ return;
+ }
+
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = LTTNG_CONSUMER_SIG_LIVE;
+ sev.sigev_value.sival_ptr = channel;
+ ret = timer_create(CLOCKID, &sev, &channel->live_timer);
+ if (ret == -1) {
+ PERROR("timer_create");
+ }
+ channel->live_timer_enabled = 1;
+
+ its.it_value.tv_sec = live_timer_interval / 1000000;
+ its.it_value.tv_nsec = live_timer_interval % 1000000;
+ its.it_interval.tv_sec = its.it_value.tv_sec;
+ its.it_interval.tv_nsec = its.it_value.tv_nsec;
+
+ ret = timer_settime(channel->live_timer, 0, &its, NULL);
+ if (ret == -1) {
+ PERROR("timer_settime");
+ }
+}
+
+/*
+ * Stop and delete timer.
+ */
+void consumer_timer_live_stop(struct lttng_consumer_channel *channel)
+{
+ int ret;
+
+ assert(channel);
+
+ ret = timer_delete(channel->live_timer);
+ if (ret == -1) {
+ PERROR("timer_delete");
+ }
+
+ consumer_timer_signal_thread_qs(LTTNG_CONSUMER_SIG_LIVE);
+
+ channel->live_timer = 0;
+ channel->live_timer_enabled = 0;
+}
+
/*
* Block the RT signals for the entire process. It must be called from the
* consumer main before creating the threads
}
/*
- * This thread is the sighandler for signals LTTNG_CONSUMER_SIG_SWITCH and
- * LTTNG_CONSUMER_SIG_TEARDOWN that are emitted by the periodic timer to check
- * if new metadata is available.
+ * This thread is the sighandler for signals LTTNG_CONSUMER_SIG_SWITCH,
+ * LTTNG_CONSUMER_SIG_TEARDOWN and LTTNG_CONSUMER_SIG_LIVE.
*/
-void *consumer_timer_metadata_thread(void *data)
+void *consumer_timer_thread(void *data)
{
int signr;
sigset_t mask;
siginfo_t info;
struct lttng_consumer_local_data *ctx = data;
+ health_register(health_consumerd, HEALTH_CONSUMERD_TYPE_METADATA_TIMER);
+
+ health_code_update();
+
/* Only self thread will receive signal mask. */
setmask(&mask);
CMM_STORE_SHARED(timer_signal.tid, pthread_self());
while (1) {
+ health_code_update();
+
+ health_poll_entry();
signr = sigwaitinfo(&mask, &info);
+ health_poll_exit();
if (signr == -1) {
if (errno != EINTR) {
PERROR("sigwaitinfo");
CMM_STORE_SHARED(timer_signal.qs_done, 1);
cmm_smp_mb();
DBG("Signal timer metadata thread teardown");
+ } else if (signr == LTTNG_CONSUMER_SIG_LIVE) {
+ live_timer(ctx, info.si_signo, &info, NULL);
} else {
ERR("Unexpected signal %d\n", info.si_signo);
}
}
+ /* Currently never reached */
+ health_unregister(health_consumerd);
+
+ /* Never return */
return NULL;
}
#define LTTNG_CONSUMER_SIG_SWITCH SIGRTMIN + 10
#define LTTNG_CONSUMER_SIG_TEARDOWN SIGRTMIN + 11
+#define LTTNG_CONSUMER_SIG_LIVE SIGRTMIN + 12
#define CLOCKID CLOCK_MONOTONIC
void consumer_timer_switch_start(struct lttng_consumer_channel *channel,
unsigned int switch_timer_interval);
void consumer_timer_switch_stop(struct lttng_consumer_channel *channel);
-void *consumer_timer_metadata_thread(void *data);
+void consumer_timer_live_start(struct lttng_consumer_channel *channel,
+ int live_timer_interval);
+void consumer_timer_live_stop(struct lttng_consumer_channel *channel);
+void *consumer_timer_thread(void *data);
void consumer_signal_init(void);
#endif /* CONSUMER_TIMER_H */
#include <inttypes.h>
#include <signal.h>
+#include <bin/lttng-consumerd/health-consumerd.h>
#include <common/common.h>
#include <common/utils.h>
#include <common/compat/poll.h>
+#include <common/index/index.h>
#include <common/kernel-ctl/kernel-ctl.h>
#include <common/sessiond-comm/relayd.h>
#include <common/sessiond-comm/sessiond-comm.h>
#include <common/kernel-consumer/kernel-consumer.h>
#include <common/relayd/relayd.h>
#include <common/ust-consumer/ust-consumer.h>
+#include <common/consumer-timer.h>
#include "consumer.h"
#include "consumer-stream.h"
(void) lttng_pipe_write(pipe, &null_stream, sizeof(null_stream));
}
+static void notify_health_quit_pipe(int *pipe)
+{
+ int ret;
+
+ do {
+ ret = write(pipe[1], "4", 1);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0 || ret != 1) {
+ PERROR("write consumer health quit");
+ }
+}
+
static void notify_channel_pipe(struct lttng_consumer_local_data *ctx,
struct lttng_consumer_channel *chan,
uint64_t key,
DBG("Consumer delete channel key %" PRIu64, channel->key);
pthread_mutex_lock(&consumer_data.lock);
+ pthread_mutex_lock(&channel->lock);
/* Delete streams that might have been left in the stream list. */
cds_list_for_each_entry_safe(stream, stmp, &channel->streams.head,
consumer_stream_destroy(stream, NULL);
}
+ if (channel->live_timer_enabled == 1) {
+ consumer_timer_live_stop(channel);
+ }
+
switch (consumer_data.type) {
case LTTNG_CONSUMER_KERNEL:
break;
call_rcu(&channel->node.head, free_channel_rcu);
end:
+ pthread_mutex_unlock(&channel->lock);
pthread_mutex_unlock(&consumer_data.lock);
}
consumer_stream_destroy(stream, ht);
}
+/*
+ * XXX naming of del vs destroy is all mixed up.
+ */
+void consumer_del_stream_for_data(struct lttng_consumer_stream *stream)
+{
+ consumer_stream_destroy(stream, data_ht);
+}
+
+void consumer_del_stream_for_metadata(struct lttng_consumer_stream *stream)
+{
+ consumer_stream_destroy(stream, metadata_ht);
+}
+
struct lttng_consumer_stream *consumer_allocate_stream(uint64_t channel_key,
uint64_t stream_key,
enum lttng_consumer_stream_state state,
stream->key = stream_key;
stream->out_fd = -1;
stream->out_fd_offset = 0;
+ stream->output_written = 0;
stream->state = state;
stream->uid = uid;
stream->gid = gid;
stream->net_seq_idx = relayd_id;
stream->session_id = session_id;
stream->monitor = monitor;
+ stream->endpoint_status = CONSUMER_ENDPOINT_ACTIVE;
+ stream->index_fd = -1;
pthread_mutex_init(&stream->lock, NULL);
/* If channel is the metadata, flag this stream as metadata. */
stream->metadata_flag = 1;
/* Metadata is flat out. */
strncpy(stream->name, DEFAULT_METADATA_NAME, sizeof(stream->name));
+ /* Live rendez-vous point. */
+ pthread_cond_init(&stream->metadata_rdv, NULL);
+ pthread_mutex_init(&stream->metadata_rdv_lock, NULL);
} else {
/* Format stream name to <channel_name>_<cpu_number> */
ret = snprintf(stream->name, sizeof(stream->name), "%s_%d",
/*
* Add a stream to the global list protected by a mutex.
*/
-static int add_stream(struct lttng_consumer_stream *stream,
- struct lttng_ht *ht)
+int consumer_add_data_stream(struct lttng_consumer_stream *stream)
{
+ struct lttng_ht *ht = data_ht;
int ret = 0;
assert(stream);
DBG3("Adding consumer stream %" PRIu64, stream->key);
pthread_mutex_lock(&consumer_data.lock);
+ pthread_mutex_lock(&stream->chan->lock);
+ pthread_mutex_lock(&stream->chan->timer_lock);
pthread_mutex_lock(&stream->lock);
rcu_read_lock();
rcu_read_unlock();
pthread_mutex_unlock(&stream->lock);
+ pthread_mutex_unlock(&stream->chan->timer_lock);
+ pthread_mutex_unlock(&stream->chan->lock);
pthread_mutex_unlock(&consumer_data.lock);
return ret;
}
+void consumer_del_data_stream(struct lttng_consumer_stream *stream)
+{
+ consumer_del_stream(stream, data_ht);
+}
+
/*
* Add relayd socket to global consumer data hashtable. RCU read side lock MUST
* be acquired before calling this.
if (ret < 0) {
goto end;
}
+
uatomic_inc(&relayd->refcount);
stream->sent_to_relayd = 1;
} else {
uint64_t tracefile_size,
uint64_t tracefile_count,
uint64_t session_id_per_pid,
- unsigned int monitor)
+ unsigned int monitor,
+ unsigned int live_timer_interval)
{
struct lttng_consumer_channel *channel;
channel->tracefile_size = tracefile_size;
channel->tracefile_count = tracefile_count;
channel->monitor = monitor;
+ channel->live_timer_interval = live_timer_interval;
+ pthread_mutex_init(&channel->lock, NULL);
+ pthread_mutex_init(&channel->timer_lock, NULL);
/*
* In monitor mode, the streams associated with the channel will be put in
struct lttng_ht_iter iter;
pthread_mutex_lock(&consumer_data.lock);
+ pthread_mutex_lock(&channel->lock);
+ pthread_mutex_lock(&channel->timer_lock);
rcu_read_lock();
lttng_ht_lookup(consumer_data.channel_ht, &channel->key, &iter);
end:
rcu_read_unlock();
+ pthread_mutex_unlock(&channel->timer_lock);
+ pthread_mutex_unlock(&channel->lock);
pthread_mutex_unlock(&consumer_data.lock);
if (!ret && channel->wait_fd != -1 &&
struct lttng_consumer_local_data *ctx),
int (*recv_channel)(struct lttng_consumer_channel *channel),
int (*recv_stream)(struct lttng_consumer_stream *stream),
- int (*update_stream)(int stream_key, uint32_t state))
+ int (*update_stream)(uint64_t stream_key, uint32_t state))
{
int ret;
struct lttng_consumer_local_data *ctx;
ctx->consumer_error_socket = -1;
ctx->consumer_metadata_socket = -1;
+ pthread_mutex_init(&ctx->metadata_socket_lock, NULL);
/* assign the callbacks */
ctx->on_buffer_ready = buffer_ready;
ctx->on_recv_channel = recv_channel;
ssize_t lttng_consumer_on_read_subbuffer_mmap(
struct lttng_consumer_local_data *ctx,
struct lttng_consumer_stream *stream, unsigned long len,
- unsigned long padding)
+ unsigned long padding,
+ struct lttng_packet_index *index)
{
unsigned long mmap_offset;
void *mmap_base;
if (stream->net_seq_idx != (uint64_t) -1ULL) {
relayd = consumer_find_relayd(stream->net_seq_idx);
if (relayd == NULL) {
+ ret = -EPIPE;
goto end;
}
}
case LTTNG_CONSUMER_KERNEL:
mmap_base = stream->mmap_base;
ret = kernctl_get_mmap_read_offset(stream->wait_fd, &mmap_offset);
+ if (ret != 0) {
+ PERROR("tracer ctl get_mmap_read_offset");
+ written = -errno;
+ goto end;
+ }
break;
case LTTNG_CONSUMER32_UST:
case LTTNG_CONSUMER64_UST:
mmap_base = lttng_ustctl_get_mmap_base(stream);
if (!mmap_base) {
ERR("read mmap get mmap base for stream %s", stream->name);
- written = -1;
+ written = -EPERM;
goto end;
}
ret = lttng_ustctl_get_mmap_read_offset(stream, &mmap_offset);
-
+ if (ret != 0) {
+ PERROR("tracer ctl get_mmap_read_offset");
+ written = ret;
+ goto end;
+ }
break;
default:
ERR("Unknown consumer_data type");
assert(0);
}
- if (ret != 0) {
- errno = -ret;
- PERROR("tracer ctl get_mmap_read_offset");
- written = ret;
- goto end;
- }
/* Handle stream on the relayd if the output is on the network */
if (relayd) {
ret = utils_rotate_stream_file(stream->chan->pathname,
stream->name, stream->chan->tracefile_size,
stream->chan->tracefile_count, stream->uid, stream->gid,
- stream->out_fd, &(stream->tracefile_count_current));
+ stream->out_fd, &(stream->tracefile_count_current),
+ &stream->out_fd);
if (ret < 0) {
ERR("Rotating output file");
goto end;
}
- outfd = stream->out_fd = ret;
+ outfd = stream->out_fd;
+
+ if (stream->index_fd >= 0) {
+ ret = index_create_file(stream->chan->pathname,
+ stream->name, stream->uid, stream->gid,
+ stream->chan->tracefile_size,
+ stream->tracefile_count_current);
+ if (ret < 0) {
+ goto end;
+ }
+ stream->index_fd = ret;
+ }
+
/* Reset current size because we just perform a rotation. */
stream->tracefile_size_current = 0;
+ stream->out_fd_offset = 0;
+ orig_offset = 0;
}
stream->tracefile_size_current += len;
+ if (index) {
+ index->offset = htobe64(stream->out_fd_offset);
+ }
}
while (len > 0) {
*/
DBG("Error in file write mmap");
if (written == 0) {
- written = ret;
+ written = -errno;
}
/* Socket operation failed. We consider the relayd dead */
if (errno == EPIPE || errno == EINVAL) {
SYNC_FILE_RANGE_WRITE);
stream->out_fd_offset += ret;
}
+ stream->output_written += ret;
written += ret;
}
lttng_consumer_sync_trace_file(stream, orig_offset);
ssize_t lttng_consumer_on_read_subbuffer_splice(
struct lttng_consumer_local_data *ctx,
struct lttng_consumer_stream *stream, unsigned long len,
- unsigned long padding)
+ unsigned long padding,
+ struct lttng_packet_index *index)
{
ssize_t ret = 0, written = 0, ret_splice = 0;
loff_t offset = 0;
if (stream->net_seq_idx != (uint64_t) -1ULL) {
relayd = consumer_find_relayd(stream->net_seq_idx);
if (relayd == NULL) {
+ ret = -EPIPE;
goto end;
}
}
ret = utils_rotate_stream_file(stream->chan->pathname,
stream->name, stream->chan->tracefile_size,
stream->chan->tracefile_count, stream->uid, stream->gid,
- stream->out_fd, &(stream->tracefile_count_current));
+ stream->out_fd, &(stream->tracefile_count_current),
+ &stream->out_fd);
if (ret < 0) {
ERR("Rotating output file");
goto end;
}
- outfd = stream->out_fd = ret;
+ outfd = stream->out_fd;
+
+ if (stream->index_fd >= 0) {
+ ret = index_create_file(stream->chan->pathname,
+ stream->name, stream->uid, stream->gid,
+ stream->chan->tracefile_size,
+ stream->tracefile_count_current);
+ if (ret < 0) {
+ goto end;
+ }
+ stream->index_fd = ret;
+ }
+
/* Reset current size because we just perform a rotation. */
stream->tracefile_size_current = 0;
+ stream->out_fd_offset = 0;
+ orig_offset = 0;
}
stream->tracefile_size_current += len;
+ index->offset = htobe64(stream->out_fd_offset);
}
while (len > 0) {
SYNC_FILE_RANGE_WRITE);
stream->out_fd_offset += ret_splice;
}
+ stream->output_written += ret_splice;
written += ret_splice;
}
lttng_consumer_sync_trace_file(stream, orig_offset);
}
pthread_mutex_lock(&consumer_data.lock);
+ pthread_mutex_lock(&stream->chan->lock);
pthread_mutex_lock(&stream->lock);
switch (consumer_data.type) {
break;
case LTTNG_CONSUMER32_UST:
case LTTNG_CONSUMER64_UST:
+ if (stream->monitor) {
+ /* close the write-side in close_metadata */
+ ret = close(stream->ust_metadata_poll_pipe[0]);
+ if (ret < 0) {
+ PERROR("Close UST metadata read-side poll pipe");
+ }
+ }
lttng_ustconsumer_del_stream(stream);
break;
default:
end:
/*
* Nullify the stream reference so it is not used after deletion. The
- * consumer data lock MUST be acquired before being able to check for a
- * NULL pointer value.
+ * channel lock MUST be acquired before being able to check for
+ * a NULL pointer value.
*/
stream->chan->metadata_stream = NULL;
pthread_mutex_unlock(&stream->lock);
+ pthread_mutex_unlock(&stream->chan->lock);
pthread_mutex_unlock(&consumer_data.lock);
if (free_chan) {
* Action done with the metadata stream when adding it to the consumer internal
* data structures to handle it.
*/
-static int add_metadata_stream(struct lttng_consumer_stream *stream,
- struct lttng_ht *ht)
+int consumer_add_metadata_stream(struct lttng_consumer_stream *stream)
{
+ struct lttng_ht *ht = metadata_ht;
int ret = 0;
struct lttng_ht_iter iter;
struct lttng_ht_node_u64 *node;
DBG3("Adding metadata stream %" PRIu64 " to hash table", stream->key);
pthread_mutex_lock(&consumer_data.lock);
+ pthread_mutex_lock(&stream->chan->lock);
+ pthread_mutex_lock(&stream->chan->timer_lock);
pthread_mutex_lock(&stream->lock);
/*
rcu_read_unlock();
pthread_mutex_unlock(&stream->lock);
+ pthread_mutex_unlock(&stream->chan->lock);
+ pthread_mutex_unlock(&stream->chan->timer_lock);
pthread_mutex_unlock(&consumer_data.lock);
return ret;
}
*/
void *consumer_thread_metadata_poll(void *data)
{
- int ret, i, pollfd;
+ int ret, i, pollfd, err = -1;
uint32_t revents, nb_fd;
struct lttng_consumer_stream *stream = NULL;
struct lttng_ht_iter iter;
rcu_register_thread();
+ health_register(health_consumerd, HEALTH_CONSUMERD_TYPE_METADATA);
+
+ health_code_update();
+
metadata_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
if (!metadata_ht) {
/* ENOMEM at this point. Better to bail out. */
DBG("Metadata main loop started");
while (1) {
+ health_code_update();
+
/* Only the metadata pipe is set */
if (LTTNG_POLL_GETNB(&events) == 0 && consumer_quit == 1) {
+ err = 0; /* All is OK */
goto end;
}
restart:
DBG("Metadata poll wait with %d fd(s)", LTTNG_POLL_GETNB(&events));
+ health_poll_entry();
ret = lttng_poll_wait(&events, -1);
+ health_poll_exit();
DBG("Metadata event catched in thread");
if (ret < 0) {
if (errno == EINTR) {
/* From here, the event is a metadata wait fd */
for (i = 0; i < nb_fd; i++) {
+ health_code_update();
+
revents = LTTNG_POLL_GETEV(&events, i);
pollfd = LTTNG_POLL_GETFD(&events, i);
- /* Just don't waste time if no returned events for the fd */
- if (!revents) {
- continue;
- }
-
if (pollfd == lttng_pipe_get_readfd(ctx->consumer_metadata_pipe)) {
if (revents & (LPOLLERR | LPOLLHUP )) {
DBG("Metadata thread pipe hung up");
pipe_len = lttng_pipe_read(ctx->consumer_metadata_pipe,
&stream, sizeof(stream));
if (pipe_len < 0) {
- ERR("read metadata stream, ret: %ld", pipe_len);
+ ERR("read metadata stream, ret: %zd", pipe_len);
/*
* Continue here to handle the rest of the streams.
*/
DBG("Adding metadata stream %d to poll set",
stream->wait_fd);
- ret = add_metadata_stream(stream, metadata_ht);
- if (ret) {
- ERR("Unable to add metadata stream");
- /* Stream was not setup properly. Continuing. */
- consumer_del_metadata_stream(stream, NULL);
- continue;
- }
-
/* Add metadata stream to the global poll events list */
lttng_poll_add(&events, stream->wait_fd,
LPOLLIN | LPOLLPRI);
/* We just flushed the stream now read it. */
do {
+ health_code_update();
+
len = ctx->on_buffer_ready(stream, ctx);
/*
* We don't check the return value here since if we get
DBG("Metadata available on fd %d", pollfd);
assert(stream->wait_fd == pollfd);
- len = ctx->on_buffer_ready(stream, ctx);
+ do {
+ health_code_update();
+
+ len = ctx->on_buffer_ready(stream, ctx);
+ /*
+ * We don't check the return value here since if we get
+ * a negative len, it means an error occured thus we
+ * simply remove it from the poll set and free the
+ * stream.
+ */
+ } while (len > 0);
+
/* It's ok to have an unavailable sub-buffer */
if (len < 0 && len != -EAGAIN && len != -ENODATA) {
/* Clean up stream from consumer and free it. */
lttng_poll_del(&events, stream->wait_fd);
consumer_del_metadata_stream(stream, metadata_ht);
- } else if (len > 0) {
- stream->data_read = 1;
}
}
}
}
+ /* All is OK */
+ err = 0;
error:
end:
DBG("Metadata poll thread exiting");
end_poll:
destroy_stream_ht(metadata_ht);
end_ht:
+ if (err) {
+ health_error();
+ ERR("Health error occurred in %s", __func__);
+ }
+ health_unregister(health_consumerd);
rcu_unregister_thread();
return NULL;
}
*/
void *consumer_thread_data_poll(void *data)
{
- int num_rdy, num_hup, high_prio, ret, i;
+ int num_rdy, num_hup, high_prio, ret, i, err = -1;
struct pollfd *pollfd = NULL;
/* local view of the streams */
struct lttng_consumer_stream **local_stream = NULL, *new_stream = NULL;
rcu_register_thread();
+ health_register(health_consumerd, HEALTH_CONSUMERD_TYPE_DATA);
+
+ health_code_update();
+
data_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
if (data_ht == NULL) {
/* ENOMEM at this point. Better to bail out. */
}
while (1) {
+ health_code_update();
+
high_prio = 0;
num_hup = 0;
/* No FDs and consumer_quit, consumer_cleanup the thread */
if (nb_fd == 0 && consumer_quit == 1) {
+ err = 0; /* All is OK */
goto end;
}
/* poll on the array of fds */
restart:
DBG("polling on %d fd", nb_fd + 1);
+ health_poll_entry();
num_rdy = poll(pollfd, nb_fd + 1, -1);
+ health_poll_exit();
DBG("poll num_rdy : %d", num_rdy);
if (num_rdy == -1) {
/*
pipe_readlen = lttng_pipe_read(ctx->consumer_data_pipe,
&new_stream, sizeof(new_stream));
if (pipe_readlen < 0) {
- ERR("Consumer data pipe ret %ld", pipe_readlen);
+ ERR("Consumer data pipe ret %zd", pipe_readlen);
/* Continue so we can at least handle the current stream(s). */
continue;
}
continue;
}
- ret = add_stream(new_stream, data_ht);
- if (ret) {
- ERR("Consumer add stream %" PRIu64 " failed. Continuing",
- new_stream->key);
- /*
- * At this point, if the add_stream fails, it is not in the
- * hash table thus passing the NULL value here.
- */
- consumer_del_stream(new_stream, NULL);
- }
-
/* Continue to update the local streams and handle prio ones */
continue;
}
/* Take care of high priority channels first. */
for (i = 0; i < nb_fd; i++) {
+ health_code_update();
+
if (local_stream[i] == NULL) {
continue;
}
/* Take care of low priority channels. */
for (i = 0; i < nb_fd; i++) {
+ health_code_update();
+
if (local_stream[i] == NULL) {
continue;
}
/* Handle hangup and errors */
for (i = 0; i < nb_fd; i++) {
+ health_code_update();
+
if (local_stream[i] == NULL) {
continue;
}
}
}
}
+ /* All is OK */
+ err = 0;
end:
DBG("polling thread exiting");
free(pollfd);
destroy_data_stream_ht(data_ht);
+ if (err) {
+ health_error();
+ ERR("Health error occurred in %s", __func__);
+ }
+ health_unregister(health_consumerd);
+
rcu_unregister_thread();
return NULL;
}
*/
void *consumer_thread_channel_poll(void *data)
{
- int ret, i, pollfd;
+ int ret, i, pollfd, err = -1;
uint32_t revents, nb_fd;
struct lttng_consumer_channel *chan = NULL;
struct lttng_ht_iter iter;
rcu_register_thread();
+ health_register(health_consumerd, HEALTH_CONSUMERD_TYPE_CHANNEL);
+
+ health_code_update();
+
channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
if (!channel_ht) {
/* ENOMEM at this point. Better to bail out. */
DBG("Channel main loop started");
while (1) {
+ health_code_update();
+
/* Only the channel pipe is set */
if (LTTNG_POLL_GETNB(&events) == 0 && consumer_quit == 1) {
+ err = 0; /* All is OK */
goto end;
}
restart:
DBG("Channel poll wait with %d fd(s)", LTTNG_POLL_GETNB(&events));
+ health_poll_entry();
ret = lttng_poll_wait(&events, -1);
+ health_poll_exit();
DBG("Channel event catched in thread");
if (ret < 0) {
if (errno == EINTR) {
/* From here, the event is a channel wait fd */
for (i = 0; i < nb_fd; i++) {
+ health_code_update();
+
revents = LTTNG_POLL_GETEV(&events, i);
pollfd = LTTNG_POLL_GETFD(&events, i);
/* Delete streams that might have been left in the stream list. */
cds_list_for_each_entry_safe(stream, stmp, &chan->streams.head,
send_node) {
+ health_code_update();
+
cds_list_del(&stream->send_node);
lttng_ustconsumer_del_stream(stream);
uatomic_sub(&stream->chan->refcount, 1);
}
}
+ /* All is OK */
+ err = 0;
end:
lttng_poll_clean(&events);
end_poll:
destroy_channel_ht(channel_ht);
end_ht:
DBG("Channel poll thread exiting");
+ if (err) {
+ health_error();
+ ERR("Health error occurred in %s", __func__);
+ }
+ health_unregister(health_consumerd);
rcu_unregister_thread();
return NULL;
}
*/
void *consumer_thread_sessiond_poll(void *data)
{
- int sock = -1, client_socket, ret;
+ int sock = -1, client_socket, ret, err = -1;
/*
* structure to poll for incoming data on communication socket avoids
* making blocking sockets.
rcu_register_thread();
+ health_register(health_consumerd, HEALTH_CONSUMERD_TYPE_SESSIOND);
+
+ health_code_update();
+
DBG("Creating command socket %s", ctx->consumer_command_sock_path);
unlink(ctx->consumer_command_sock_path);
client_socket = lttcomm_create_unix_sock(ctx->consumer_command_sock_path);
consumer_sockpoll[1].events = POLLIN | POLLPRI;
while (1) {
- if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) {
+ health_code_update();
+
+ health_poll_entry();
+ ret = lttng_consumer_poll_socket(consumer_sockpoll);
+ health_poll_exit();
+ if (ret < 0) {
goto end;
}
DBG("Incoming command on sock");
* ERR() here.
*/
DBG("Communication interrupted on command socket");
+ err = 0;
goto end;
}
if (consumer_quit) {
DBG("consumer_thread_receive_fds received quit from signal");
+ err = 0; /* All is OK */
goto end;
}
DBG("received command on sock");
}
+ /* All is OK */
+ err = 0;
+
end:
DBG("Consumer thread sessiond poll exiting");
notify_channel_pipe(ctx, NULL, -1, CONSUMER_CHANNEL_QUIT);
+ notify_health_quit_pipe(health_quit_pipe);
+
/* Cleaning up possibly open sockets. */
if (sock >= 0) {
ret = close(sock);
}
}
+ if (err) {
+ health_error();
+ ERR("Health error occurred in %s", __func__);
+ }
+ health_unregister(health_consumerd);
+
rcu_unregister_thread();
return NULL;
}
ssize_t ret;
pthread_mutex_lock(&stream->lock);
+ if (stream->metadata_flag) {
+ pthread_mutex_lock(&stream->metadata_rdv_lock);
+ }
switch (consumer_data.type) {
case LTTNG_CONSUMER_KERNEL:
break;
}
+ if (stream->metadata_flag) {
+ pthread_cond_broadcast(&stream->metadata_rdv);
+ pthread_mutex_unlock(&stream->metadata_rdv_lock);
+ }
pthread_mutex_unlock(&stream->lock);
return ret;
}
int consumer_add_relayd_socket(uint64_t net_seq_idx, int sock_type,
struct lttng_consumer_local_data *ctx, int sock,
struct pollfd *consumer_sockpoll,
- struct lttcomm_relayd_sock *relayd_sock, unsigned int sessiond_id)
+ struct lttcomm_relayd_sock *relayd_sock, uint64_t sessiond_id,
+ uint64_t relayd_session_id)
{
int fd = -1, ret = -1, relayd_created = 0;
enum lttng_error_code ret_code = LTTNG_OK;
ret_code = LTTCOMM_CONSUMERD_ENOMEM;
goto error;
} else {
- relayd->sessiond_session_id = (uint64_t) sessiond_id;
+ relayd->sessiond_session_id = sessiond_id;
relayd_created = 1;
}
relayd->control_sock.major = relayd_sock->major;
relayd->control_sock.minor = relayd_sock->minor;
- /*
- * Create a session on the relayd and store the returned id. Lock the
- * control socket mutex if the relayd was NOT created before.
- */
- if (!relayd_created) {
- pthread_mutex_lock(&relayd->ctrl_sock_mutex);
- }
- ret = relayd_create_session(&relayd->control_sock,
- &relayd->relayd_session_id);
- if (!relayd_created) {
- pthread_mutex_unlock(&relayd->ctrl_sock_mutex);
- }
- if (ret < 0) {
- /*
- * Close all sockets of a relayd object. It will be freed if it was
- * created at the error code path or else it will be garbage
- * collect.
- */
- (void) relayd_close(&relayd->control_sock);
- (void) relayd_close(&relayd->data_sock);
- ret_code = LTTCOMM_CONSUMERD_RELAYD_FAIL;
- goto error;
- }
+ relayd->relayd_session_id = relayd_session_id;
break;
case LTTNG_STREAM_DATA:
*/
ret = cds_lfht_is_node_deleted(&stream->node.node);
if (!ret) {
+ /*
+ * An empty output file is not valid. We need at least one packet
+ * generated per stream, even if it contains no event, so it
+ * contains at least one packet header.
+ */
+ if (stream->output_written == 0) {
+ pthread_mutex_unlock(&stream->lock);
+ goto data_pending;
+ }
/* Check the stream if there is data in the buffers. */
ret = data_pending(stream);
if (ret == 1) {
return lttcomm_send_unix_sock(sock, &msg, sizeof(msg));
}
+
+/*
+ * Using a maximum stream size with the produced and consumed position of a
+ * stream, computes the new consumed position to be as close as possible to the
+ * maximum possible stream size.
+ *
+ * If maximum stream size is lower than the possible buffer size (produced -
+ * consumed), the consumed_pos given is returned untouched else the new value
+ * is returned.
+ */
+unsigned long consumer_get_consumed_maxsize(unsigned long consumed_pos,
+ unsigned long produced_pos, uint64_t max_stream_size)
+{
+ if (max_stream_size && max_stream_size < (produced_pos - consumed_pos)) {
+ /* Offset from the produced position to get the latest buffers. */
+ return produced_pos - max_stream_size;
+ }
+
+ return consumed_pos;
+}
#include <common/compat/uuid.h>
#include <common/sessiond-comm/sessiond-comm.h>
#include <common/pipe.h>
+#include <common/index/lttng-index.h>
/* Commands for consumer */
enum lttng_consumer_command {
char pathname[PATH_MAX];
/* Channel name. */
char name[LTTNG_SYMBOL_NAME_LEN];
- /* UID and GID of the channel. */
+ /* UID and GID of the session owning this channel. */
uid_t uid;
gid_t gid;
/* Relayd id of the channel. -1ULL if it does not apply. */
enum consumer_channel_type type;
/* For UST */
+ uid_t ust_app_uid; /* Application UID. */
struct ustctl_consumer_channel *uchan;
unsigned char uuid[UUID_STR_LEN];
/*
/* Metadata cache is metadata channel */
struct consumer_metadata_cache *metadata_cache;
- /* For metadata periodical flush */
+ /* For UST metadata periodical flush */
int switch_timer_enabled;
timer_t switch_timer;
int switch_timer_error;
+ /* For the live mode */
+ int live_timer_enabled;
+ timer_t live_timer;
+ int live_timer_error;
+
/* On-disk circular buffer */
uint64_t tracefile_size;
uint64_t tracefile_count;
* monitor list of the channel.
*/
unsigned int monitor;
+
+ /*
+ * Channel lock.
+ *
+ * This lock protects against concurrent update of channel.
+ *
+ * This is nested INSIDE the consumer data lock.
+ * This is nested OUTSIDE the channel timer lock.
+ * This is nested OUTSIDE the metadata cache lock.
+ * This is nested OUTSIDE stream lock.
+ * This is nested OUTSIDE consumer_relayd_sock_pair lock.
+ */
+ pthread_mutex_t lock;
+
+ /*
+ * Channel teardown lock.
+ *
+ * This lock protect against teardown of channel. It is _never_
+ * taken by the timer handler.
+ *
+ * This is nested INSIDE the consumer data lock.
+ * This is nested INSIDE the channel lock.
+ * This is nested OUTSIDE the metadata cache lock.
+ * This is nested OUTSIDE stream lock.
+ * This is nested OUTSIDE consumer_relayd_sock_pair lock.
+ */
+ pthread_mutex_t timer_lock;
+
+ /* Timer value in usec for live streaming. */
+ unsigned int live_timer_interval;
};
/*
int out_fd; /* output file to write the data */
/* Write position in the output file descriptor */
off_t out_fd_offset;
+ /* Amount of bytes written to the output */
+ uint64_t output_written;
enum lttng_consumer_stream_state state;
int shm_fd_is_copy;
int data_read;
*
* This is nested INSIDE the consumer_data lock.
* This is nested INSIDE the metadata cache lock.
+ * This is nested INSIDE the channel lock.
+ * This is nested INSIDE the channel timer lock.
* This is nested OUTSIDE consumer_relayd_sock_pair lock.
*/
pthread_mutex_t lock;
* acquired in the destroy path.
*/
unsigned int globally_visible;
+ /*
+ * Pipe to wake up the metadata poll thread when the UST metadata
+ * cache is updated.
+ */
+ int ust_metadata_poll_pipe[2];
+ /*
+ * How much metadata was read from the metadata cache and sent
+ * to the channel.
+ */
+ uint64_t ust_metadata_pushed;
+ /*
+ * FD of the index file for this stream.
+ */
+ int index_fd;
+
+ /*
+ * Rendez-vous point between data and metadata stream in live mode.
+ */
+ pthread_cond_t metadata_rdv;
+ pthread_mutex_t metadata_rdv_lock;
};
/*
* == 0 (success, FD is left to library)
* < 0 (error)
*/
- int (*on_update_stream)(int sessiond_key, uint32_t state);
+ int (*on_update_stream)(uint64_t sessiond_key, uint32_t state);
enum lttng_consumer_type type;
/* socket to communicate errors with sessiond */
int consumer_error_socket;
- /* socket to ask metadata to sessiond */
+ /* socket to ask metadata to sessiond. */
int consumer_metadata_socket;
+ /*
+ * Protect consumer_metadata_socket.
+ *
+ * This is nested OUTSIDE the metadata cache lock.
+ */
+ pthread_mutex_t metadata_socket_lock;
/* socket to exchange commands with sessiond */
char *consumer_command_sock_path;
/* communication with splice */
uint64_t tracefile_size,
uint64_t tracefile_count,
uint64_t session_id_per_pid,
- unsigned int monitor);
+ unsigned int monitor,
+ unsigned int live_timer_interval);
void consumer_del_stream(struct lttng_consumer_stream *stream,
struct lttng_ht *ht);
void consumer_del_metadata_stream(struct lttng_consumer_stream *stream,
struct lttng_consumer_local_data *ctx),
int (*recv_channel)(struct lttng_consumer_channel *channel),
int (*recv_stream)(struct lttng_consumer_stream *stream),
- int (*update_stream)(int sessiond_key, uint32_t state));
+ int (*update_stream)(uint64_t sessiond_key, uint32_t state));
void lttng_consumer_destroy(struct lttng_consumer_local_data *ctx);
ssize_t lttng_consumer_on_read_subbuffer_mmap(
struct lttng_consumer_local_data *ctx,
struct lttng_consumer_stream *stream, unsigned long len,
- unsigned long padding);
+ unsigned long padding,
+ struct lttng_packet_index *index);
ssize_t lttng_consumer_on_read_subbuffer_splice(
struct lttng_consumer_local_data *ctx,
struct lttng_consumer_stream *stream, unsigned long len,
- unsigned long padding);
+ unsigned long padding,
+ struct lttng_packet_index *index);
int lttng_consumer_take_snapshot(struct lttng_consumer_stream *stream);
int lttng_consumer_get_produced_snapshot(struct lttng_consumer_stream *stream,
unsigned long *pos);
int consumer_add_relayd_socket(uint64_t net_seq_idx, int sock_type,
struct lttng_consumer_local_data *ctx, int sock,
struct pollfd *consumer_sockpoll, struct lttcomm_relayd_sock *relayd_sock,
- unsigned int sessiond_id);
+ uint64_t sessiond_id, uint64_t relayd_session_id);
void consumer_flag_relayd_for_destroy(
struct consumer_relayd_sock_pair *relayd);
int consumer_data_pending(uint64_t id);
void notify_thread_del_channel(struct lttng_consumer_local_data *ctx,
uint64_t key);
void consumer_destroy_relayd(struct consumer_relayd_sock_pair *relayd);
+unsigned long consumer_get_consumed_maxsize(unsigned long consumed_pos,
+ unsigned long produced_pos, uint64_t max_stream_size);
+int consumer_add_data_stream(struct lttng_consumer_stream *stream);
+void consumer_del_stream_for_data(struct lttng_consumer_stream *stream);
+int consumer_add_metadata_stream(struct lttng_consumer_stream *stream);
+void consumer_del_stream_for_metadata(struct lttng_consumer_stream *stream);
+int consumer_create_index_file(struct lttng_consumer_stream *stream);
#endif /* LIB_CONSUMER_H */
#ifndef _DEFAULTS_H
#define _DEFAULTS_H
+#include <config.h>
+
/* Default unix group name for tracing. */
#define DEFAULT_TRACING_GROUP "tracing"
#define DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH DEFAULT_USTCONSUMERD32_PATH "/command"
#define DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH DEFAULT_USTCONSUMERD32_PATH "/error"
+/* Relayd path */
+#define DEFAULT_RELAYD_RUNDIR "%s"
+#define DEFAULT_RELAYD_PATH DEFAULT_RELAYD_RUNDIR "/relayd"
+
/* Default lttng run directory */
#define DEFAULT_LTTNG_HOME_ENV_VAR "LTTNG_HOME"
#define DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR "HOME"
-#define DEFAULT_LTTNG_RUNDIR "/var/run/lttng"
+#define DEFAULT_LTTNG_RUNDIR CONFIG_LTTNG_SYSTEM_RUNDIR
#define DEFAULT_LTTNG_HOME_RUNDIR "%s/.lttng"
#define DEFAULT_LTTNG_SESSIOND_PIDFILE "lttng-sessiond.pid"
/* Default unix socket path */
#define DEFAULT_GLOBAL_CLIENT_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/client-lttng-sessiond"
#define DEFAULT_HOME_CLIENT_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/client-lttng-sessiond"
-#define DEFAULT_GLOBAL_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/health.sock"
-#define DEFAULT_HOME_HEALTH_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/health.sock"
+#define DEFAULT_GLOBAL_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/sessiond-health"
+#define DEFAULT_HOME_HEALTH_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/sessiond-health"
+
+/* Default consumer health unix socket path */
+#define DEFAULT_GLOBAL_USTCONSUMER32_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/ustconsumerd32/health"
+#define DEFAULT_HOME_USTCONSUMER32_HEALTH_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/ustconsumerd32/health"
+#define DEFAULT_GLOBAL_USTCONSUMER64_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/ustconsumerd64/health"
+#define DEFAULT_HOME_USTCONSUMER64_HEALTH_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/ustconsumerd64/health"
+#define DEFAULT_GLOBAL_KCONSUMER_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/kconsumerd/health"
+#define DEFAULT_HOME_KCONSUMER_HEALTH_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/kconsumerd/health"
+
+/* Default relay health unix socket path */
+#define DEFAULT_GLOBAL_RELAY_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/relayd/health-%d"
+#define DEFAULT_HOME_RELAY_HEALTH_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/relayd/health-%d"
#define DEFAULT_GLOBAL_APPS_UNIX_SOCK \
DEFAULT_LTTNG_RUNDIR "/" LTTNG_UST_SOCK_FILENAME
/* Default channel attributes */
#define DEFAULT_CHANNEL_NAME "channel0"
+/* Default JUL domain channel name. */
+#define DEFAULT_JUL_CHANNEL_NAME "lttng_jul_channel"
+/* Default JUL tracepoint name. This is a wildcard for the JUL domain. */
+#define DEFAULT_JUL_EVENT_NAME "lttng_jul*"
+/* JUL default channel name. */
#define DEFAULT_CHANNEL_OVERWRITE 0
#define DEFAULT_CHANNEL_TRACEFILE_SIZE 0
#define DEFAULT_CHANNEL_TRACEFILE_COUNT 0
/* Must always be a power of 2 */
#define _DEFAULT_CHANNEL_SUBBUF_NUM 4
#define _DEFAULT_CHANNEL_SWITCH_TIMER 0 /* usec */
+#define _DEFAULT_CHANNEL_LIVE_TIMER 0 /* usec */
#define _DEFAULT_CHANNEL_READ_TIMER 200000 /* usec */
#define _DEFAULT_CHANNEL_OUTPUT LTTNG_EVENT_MMAP
#define DEFAULT_KERNEL_CHANNEL_OUTPUT LTTNG_EVENT_SPLICE
#define DEFAULT_KERNEL_CHANNEL_SWITCH_TIMER _DEFAULT_CHANNEL_SWITCH_TIMER
#define DEFAULT_KERNEL_CHANNEL_READ_TIMER _DEFAULT_CHANNEL_READ_TIMER
+#define DEFAULT_KERNEL_CHANNEL_LIVE_TIMER _DEFAULT_CHANNEL_LIVE_TIMER
/* User space defaults */
/* Timers in usec. */
#define DEFAULT_UST_PID_CHANNEL_SWITCH_TIMER _DEFAULT_CHANNEL_SWITCH_TIMER
#define DEFAULT_UST_UID_CHANNEL_SWITCH_TIMER _DEFAULT_CHANNEL_SWITCH_TIMER
+#define DEFAULT_UST_PID_CHANNEL_LIVE_TIMER _DEFAULT_CHANNEL_LIVE_TIMER
+#define DEFAULT_UST_UID_CHANNEL_LIVE_TIMER _DEFAULT_CHANNEL_LIVE_TIMER
#define DEFAULT_UST_PID_CHANNEL_READ_TIMER 0 /* usec */
#define DEFAULT_UST_UID_CHANNEL_READ_TIMER 0 /* usec */
/* Default network ports for trace streaming support */
#define DEFAULT_NETWORK_CONTROL_PORT 5342
#define DEFAULT_NETWORK_DATA_PORT 5343
+#define DEFAULT_NETWORK_VIEWER_PORT 5344
+
+/* JUL registration TCP port. */
+#define DEFAULT_JUL_TCP_PORT 5345
/*
* If a thread stalls for this amount of time, it will be considered bogus (bad
*/
#define DEFAULT_METADATA_AVAILABILITY_WAIT_TIME 200000 /* usec */
+/*
+ * The usual value for the maximum TCP SYN retries time and TCP FIN timeout is
+ * 180 and 60 seconds on most Linux system and the default value since kernel
+ * 2.2 thus using the highest value. See tcp(7) for more details.
+ */
+#define DEFAULT_INET_TCP_TIMEOUT 180 /* sec */
+
/*
* Default receiving and sending timeout for an application socket.
*/
#define DEFAULT_UST_STREAM_FD_NUM 2 /* Number of fd per UST stream. */
#define DEFAULT_SNAPSHOT_NAME "snapshot"
+#define DEFAULT_SNAPSHOT_MAX_SIZE 0 /* Unlimited. */
+
+/* Suffix of an index file. */
+#define DEFAULT_INDEX_FILE_SUFFIX ".idx"
+#define DEFAULT_INDEX_DIR "index"
extern size_t default_channel_subbuf_size;
extern size_t default_metadata_subbuf_size;
[ ERROR_INDEX(LTTNG_ERR_UST_EVENT_NOT_FOUND)] = "UST event not found",
[ ERROR_INDEX(LTTNG_ERR_UST_CONTEXT_EXIST)] = "UST context already exist",
[ ERROR_INDEX(LTTNG_ERR_UST_CONTEXT_INVAL)] = "UST invalid context",
- [ ERROR_INDEX(LTTNG_ERR_NEED_ROOT_SESSIOND) ] = "Tracing the kernel requires a root lttng-sessiond daemon or \"tracing\" group user membership",
+ [ ERROR_INDEX(LTTNG_ERR_NEED_ROOT_SESSIOND) ] = "Tracing the kernel requires a root lttng-sessiond daemon, as well as \"tracing\" group membership or root user ID for the lttng client.",
+ [ ERROR_INDEX(LTTNG_ERR_NO_UST) ] = "LTTng-UST tracer is not supported. Please rebuild lttng-tools with lttng-ust support enabled.",
[ ERROR_INDEX(LTTNG_ERR_TRACE_ALREADY_STARTED) ] = "Tracing already started",
[ ERROR_INDEX(LTTNG_ERR_TRACE_ALREADY_STOPPED) ] = "Tracing already stopped",
[ ERROR_INDEX(LTTNG_ERR_KERN_EVENT_ENOSYS) ] = "Kernel event type not supported",
+ [ ERROR_INDEX(LTTNG_ERR_NEED_CHANNEL_NAME) ] = "Non-default channel exists within session: channel name needs to be specified with '-c name'",
[ ERROR_INDEX(LTTNG_ERR_INVALID) ] = "Invalid parameter",
[ ERROR_INDEX(LTTNG_ERR_NO_USTCONSUMERD) ] = "No UST consumer detected",
[ ERROR_INDEX(LTTNG_ERR_NO_KERNCONSUMERD) ] = "No kernel consumer detected",
[ ERROR_INDEX(LTTNG_ERR_NOMEM)] = "Not enough memory",
[ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST) ] = "Snapshot output already exists",
[ ERROR_INDEX(LTTNG_ERR_START_SESSION_ONCE) ] = "Session needs to be started once",
+ [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_FAIL) ] = "Snapshot record failed",
+ [ ERROR_INDEX(LTTNG_ERR_CHAN_EXIST) ] = "Channel already exists",
/* Last element */
[ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code"
return hash_match_key_u64(&match_node->key, (void *) key);
}
+/*
+ * Match function for two uint64_t node.
+ */
+static int match_two_u64(struct cds_lfht_node *node, const void *key)
+{
+ struct lttng_ht_node_two_u64 *match_node =
+ caa_container_of(node, struct lttng_ht_node_two_u64, node);
+
+ return hash_match_key_two_u64((void *) &match_node->key, (void *) key);
+}
+
/*
* Return an allocated lttng hashtable.
*/
ht->match_fct = match_u64;
ht->hash_fct = hash_key_u64;
break;
+ case LTTNG_HT_TYPE_TWO_U64:
+ ht->match_fct = match_two_u64;
+ ht->hash_fct = hash_key_two_u64;
+ break;
default:
ERR("Unknown lttng hashtable type %d", type);
lttng_ht_destroy(ht);
cds_lfht_node_init(&node->node);
}
+/*
+ * Init lttng ht node with two uint64_t.
+ */
+void lttng_ht_node_init_two_u64(struct lttng_ht_node_two_u64 *node,
+ uint64_t key1, uint64_t key2)
+{
+ assert(node);
+
+ node->key.key1 = key1;
+ node->key.key2 = key2;
+ cds_lfht_node_init(&node->node);
+}
+
/*
* Free lttng ht node string.
*/
free(node);
}
+/*
+ * Free lttng ht node two uint64_t.
+ */
+void lttng_ht_node_free_two_u64(struct lttng_ht_node_two_u64 *node)
+{
+ assert(node);
+ free(node);
+}
+
/*
* Lookup function in hashtable.
*/
assert(node_ptr == &node->node);
}
+/*
+ * Add string node to hashtable.
+ */
+void lttng_ht_add_str(struct lttng_ht *ht,
+ struct lttng_ht_node_str *node)
+{
+ assert(ht);
+ assert(ht->ht);
+ assert(node);
+
+ cds_lfht_add(ht->ht, ht->hash_fct(node->key, lttng_ht_seed),
+ &node->node);
+}
+
/*
* Add unsigned long node to hashtable.
*/
assert(node_ptr == &node->node);
}
+/*
+ * Add unique two uint64_t node to hashtable.
+ */
+void lttng_ht_add_unique_two_u64(struct lttng_ht *ht,
+ struct lttng_ht_node_two_u64 *node)
+{
+ struct cds_lfht_node *node_ptr;
+ assert(ht);
+ assert(ht->ht);
+ assert(node);
+
+ node_ptr = cds_lfht_add_unique(ht->ht,
+ ht->hash_fct((void *) &node->key, lttng_ht_seed), ht->match_fct,
+ (void *) &node->key, &node->node);
+ assert(node_ptr == &node->node);
+}
+
/*
* Add replace unsigned long node to hashtable.
*/
return caa_container_of(node, struct lttng_ht_node_u64, node);
}
+/*
+ * Return lttng ht stream and index id node from iterator.
+ */
+struct lttng_ht_node_two_u64 *lttng_ht_iter_get_node_two_u64(
+ struct lttng_ht_iter *iter)
+{
+ struct cds_lfht_node *node;
+
+ assert(iter);
+ node = cds_lfht_iter_get_node(&iter->iter);
+ if (!node) {
+ return NULL;
+ }
+ return caa_container_of(node, struct lttng_ht_node_two_u64, node);
+}
+
/*
* lib constructor
*/
LTTNG_HT_TYPE_STRING,
LTTNG_HT_TYPE_ULONG,
LTTNG_HT_TYPE_U64,
+ LTTNG_HT_TYPE_TWO_U64,
};
struct lttng_ht {
struct rcu_head head;
};
+struct lttng_ht_two_u64 {
+ uint64_t key1;
+ uint64_t key2;
+};
+
+struct lttng_ht_node_two_u64 {
+ struct lttng_ht_two_u64 key;
+ struct cds_lfht_node node;
+ struct rcu_head head;
+};
+
/* Hashtable new and destroy */
extern struct lttng_ht *lttng_ht_new(unsigned long size, int type);
extern void lttng_ht_destroy(struct lttng_ht *ht);
unsigned long key);
extern void lttng_ht_node_init_u64(struct lttng_ht_node_u64 *node,
uint64_t key);
+extern void lttng_ht_node_init_two_u64(struct lttng_ht_node_two_u64 *node,
+ uint64_t key1, uint64_t key2);
extern void lttng_ht_node_free_str(struct lttng_ht_node_str *node);
extern void lttng_ht_node_free_ulong(struct lttng_ht_node_ulong *node);
extern void lttng_ht_node_free_u64(struct lttng_ht_node_u64 *node);
+extern void lttng_ht_node_free_two_u64(struct lttng_ht_node_two_u64 *node);
extern void lttng_ht_lookup(struct lttng_ht *ht, void *key,
struct lttng_ht_iter *iter);
struct lttng_ht_node_ulong *node);
extern void lttng_ht_add_unique_u64(struct lttng_ht *ht,
struct lttng_ht_node_u64 *node);
+extern void lttng_ht_add_unique_two_u64(struct lttng_ht *ht,
+ struct lttng_ht_node_two_u64 *node);
extern struct lttng_ht_node_ulong *lttng_ht_add_replace_ulong(
struct lttng_ht *ht, struct lttng_ht_node_ulong *node);
extern struct lttng_ht_node_u64 *lttng_ht_add_replace_u64(
struct lttng_ht *ht, struct lttng_ht_node_u64 *node);
+extern void lttng_ht_add_str(struct lttng_ht *ht,
+ struct lttng_ht_node_str *node);
extern void lttng_ht_add_ulong(struct lttng_ht *ht,
struct lttng_ht_node_ulong *node);
extern void lttng_ht_add_u64(struct lttng_ht *ht,
struct lttng_ht_iter *iter);
extern struct lttng_ht_node_u64 *lttng_ht_iter_get_node_u64(
struct lttng_ht_iter *iter);
+extern struct lttng_ht_node_two_u64 *lttng_ht_iter_get_node_two_u64(
+ struct lttng_ht_iter *iter);
#endif /* _LTT_HT_H */
static long nr_cpus_mask = -1;
static long split_count_mask = -1;
+static int split_count_order = -1;
#if defined(HAVE_SYSCONF)
static void ht_init_nr_cpus_mask(void)
split_count_mask = DEFAULT_SPLIT_COUNT_MASK;
else
split_count_mask = nr_cpus_mask;
+ split_count_order =
+ cds_lfht_get_count_order_ulong(split_count_mask + 1);
}
assert(split_count_mask >= 0);
* Use bucket-local length for small table expand and for
* environments lacking per-cpu data support.
*/
- if (count >= (1UL << COUNT_COMMIT_ORDER))
+ if (count >= (1UL << (COUNT_COMMIT_ORDER + split_count_order)))
return;
if (chain_len > 100)
dbg_printf("WARNING: large chain length: %u.\n",
chain_len);
- if (chain_len >= CHAIN_LEN_RESIZE_THRESHOLD)
- cds_lfht_resize_lazy_grow(ht, size,
- cds_lfht_get_count_order_u32(chain_len - (CHAIN_LEN_TARGET - 1)));
+ if (chain_len >= CHAIN_LEN_RESIZE_THRESHOLD) {
+ int growth;
+
+ /*
+ * Ideal growth calculated based on chain length.
+ */
+ growth = cds_lfht_get_count_order_u32(chain_len
+ - (CHAIN_LEN_TARGET - 1));
+ if ((ht->flags & CDS_LFHT_ACCOUNTING)
+ && (size << growth)
+ >= (1UL << (COUNT_COMMIT_ORDER
+ + split_count_order))) {
+ /*
+ * If ideal growth expands the hash table size
+ * beyond the "small hash table" sizes, use the
+ * maximum small hash table size to attempt
+ * expanding the hash table. This only applies
+ * when node accounting is available, otherwise
+ * the chain length is used to expand the hash
+ * table in every case.
+ */
+ growth = COUNT_COMMIT_ORDER + split_count_order
+ - cds_lfht_get_count_order_ulong(size);
+ if (growth <= 0)
+ return;
+ }
+ cds_lfht_resize_lazy_grow(ht, size, growth);
+ }
}
static
#include "utils.h"
#include <common/compat/endian.h> /* attempt to define endianness */
#include <common/common.h>
+#include <common/hashtable/hashtable.h>
/*
* My best guess at if you are big-endian or little-endian. This may
return hashlittle(key, strlen((char *) key), seed);
}
+/*
+ * Hash function for two uint64_t.
+ */
+LTTNG_HIDDEN
+unsigned long hash_key_two_u64(void *key, unsigned long seed)
+{
+ struct lttng_ht_two_u64 *k = (struct lttng_ht_two_u64 *) key;
+
+ return hash_key_u64(&k->key1, seed) ^ hash_key_u64(&k->key2, seed);
+}
+
/*
* Hash function compare for number value.
*/
return 0;
}
+
+/*
+ * Hash function compare two uint64_t.
+ */
+LTTNG_HIDDEN
+int hash_match_key_two_u64(void *key1, void *key2)
+{
+ struct lttng_ht_two_u64 *k1 = (struct lttng_ht_two_u64 *) key1;
+ struct lttng_ht_two_u64 *k2 = (struct lttng_ht_two_u64 *) key2;
+
+ if (hash_match_key_u64(&k1->key1, &k2->key1) &&
+ hash_match_key_u64(&k1->key2, &k2->key2)) {
+ return 1;
+ }
+
+ return 0;
+}
unsigned long hash_key_ulong(void *_key, unsigned long seed);
unsigned long hash_key_u64(void *_key, unsigned long seed);
unsigned long hash_key_str(void *key, unsigned long seed);
+unsigned long hash_key_two_u64(void *key, unsigned long seed);
int hash_match_key_ulong(void *key1, void *key2);
int hash_match_key_u64(void *key1, void *key2);
int hash_match_key_str(void *key1, void *key2);
+int hash_match_key_two_u64(void *key1, void *key2);
#endif /* _LTT_HT_UTILS_H */
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
+
+noinst_LTLIBRARIES = libhealth.la
+
+libhealth_la_SOURCES = health.c
--- /dev/null
+/*
+ * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
+ * Copyright (C) 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <common/defaults.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/sessiond-comm/inet.h>
+
+#include <lttng/health-internal.h>
+
+/*
+ * An application-specific error state for unregistered thread keeps
+ * track of thread errors. A thread reporting a health error, normally
+ * unregisters and quits. This makes the TLS health state not available
+ * to the health_check_state() call so on unregister we update this
+ * global error array so we can keep track of which thread was on error
+ * if the TLS health state has been removed.
+ */
+struct health_app {
+ /* List of health state, for each application thread */
+ struct cds_list_head list;
+ /*
+ * This lock ensures that TLS memory used for the node and its
+ * container structure don't get reclaimed after the TLS owner
+ * thread exits until we have finished using it.
+ */
+ pthread_mutex_t lock;
+ int nr_types;
+ struct timespec time_delta;
+ /* Health flags containing thread type error state */
+ enum health_flags *flags;
+};
+
+/* Define TLS health state. */
+DEFINE_URCU_TLS(struct health_state, health_state);
+
+/*
+ * Initialize health check subsytem.
+ */
+static
+void health_init(struct health_app *ha)
+{
+ /*
+ * Get the maximum value between the default delta value and the TCP
+ * timeout with a safety net of the default health check delta.
+ */
+ ha->time_delta.tv_sec = max_t(unsigned long,
+ lttcomm_inet_tcp_timeout + DEFAULT_HEALTH_CHECK_DELTA_S,
+ ha->time_delta.tv_sec);
+ DBG("Health check time delta in seconds set to %lu",
+ ha->time_delta.tv_sec);
+}
+
+struct health_app *health_app_create(int nr_types)
+{
+ struct health_app *ha;
+
+ ha = zmalloc(sizeof(*ha));
+ if (!ha) {
+ return NULL;
+ }
+ ha->flags = zmalloc(sizeof(*ha->flags) * nr_types);
+ if (!ha->flags) {
+ goto error_flags;
+ }
+ CDS_INIT_LIST_HEAD(&ha->list);
+ pthread_mutex_init(&ha->lock, NULL);
+ ha->nr_types = nr_types;
+ ha->time_delta.tv_sec = DEFAULT_HEALTH_CHECK_DELTA_S;
+ ha->time_delta.tv_nsec = DEFAULT_HEALTH_CHECK_DELTA_NS;
+ health_init(ha);
+ return ha;
+
+error_flags:
+ free(ha);
+ return NULL;
+}
+
+void health_app_destroy(struct health_app *ha)
+{
+ free(ha->flags);
+ free(ha);
+}
+
+/*
+ * Lock health state global list mutex.
+ */
+static void state_lock(struct health_app *ha)
+{
+ pthread_mutex_lock(&ha->lock);
+}
+
+/*
+ * Unlock health state global list mutex.
+ */
+static void state_unlock(struct health_app *ha)
+{
+ pthread_mutex_unlock(&ha->lock);
+}
+
+/*
+ * Set time difference in res from time_a and time_b.
+ */
+static void time_diff(const struct timespec *time_a,
+ const struct timespec *time_b, struct timespec *res)
+{
+ if (time_a->tv_nsec - time_b->tv_nsec < 0) {
+ res->tv_sec = time_a->tv_sec - time_b->tv_sec - 1;
+ res->tv_nsec = 1000000000L + time_a->tv_sec - time_b->tv_sec;
+ } else {
+ res->tv_sec = time_a->tv_sec - time_b->tv_sec;
+ res->tv_nsec = time_a->tv_nsec - time_b->tv_nsec;
+ }
+}
+
+/*
+ * Return true if time_a - time_b > diff, else false.
+ */
+static int time_diff_gt(const struct timespec *time_a,
+ const struct timespec *time_b, const struct timespec *diff)
+{
+ struct timespec res;
+
+ time_diff(time_a, time_b, &res);
+ time_diff(&res, diff, &res);
+
+ if (res.tv_sec > 0) {
+ return 1;
+ } else if (res.tv_sec == 0 && res.tv_nsec > 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Validate health state. Checks for the error flag or health conditions.
+ *
+ * Return 0 if health is bad or else 1.
+ */
+static int validate_state(struct health_app *ha, struct health_state *state)
+{
+ int retval = 1, ret;
+ unsigned long current, last;
+ struct timespec current_time;
+
+ assert(state);
+
+ last = state->last;
+ current = uatomic_read(&state->current);
+
+ ret = clock_gettime(CLOCK_MONOTONIC, ¤t_time);
+ if (ret < 0) {
+ PERROR("Error reading time\n");
+ /* error */
+ retval = 0;
+ goto end;
+ }
+
+ /*
+ * Thread is in bad health if flag HEALTH_ERROR is set. It is also in bad
+ * health if, after the delta delay has passed, its the progress counter
+ * has not moved and it has NOT been waiting for a poll() call.
+ */
+ if (uatomic_read(&state->flags) & HEALTH_ERROR) {
+ retval = 0;
+ goto end;
+ }
+
+ /*
+ * Initial condition need to update the last counter and sample time, but
+ * should not check health in this initial case, because we don't know how
+ * much time has passed.
+ */
+ if (state->last_time.tv_sec == 0 && state->last_time.tv_nsec == 0) {
+ /* update last counter and last sample time */
+ state->last = current;
+ memcpy(&state->last_time, ¤t_time, sizeof(current_time));
+ } else {
+ if (time_diff_gt(¤t_time, &state->last_time,
+ &ha->time_delta)) {
+ if (current == last && !HEALTH_IS_IN_POLL(current)) {
+ /* error */
+ retval = 0;
+ }
+ /* update last counter and last sample time */
+ state->last = current;
+ memcpy(&state->last_time, ¤t_time, sizeof(current_time));
+
+ /* On error, stop right now and notify caller. */
+ if (retval == 0) {
+ goto end;
+ }
+ }
+ }
+
+end:
+ DBG("Health state current %lu, last %lu, ret %d",
+ current, last, ret);
+ return retval;
+}
+
+/*
+ * Check health of a specific health type. Note that if a thread has not yet
+ * initialize its health subsystem or has quit, it's considered in a good
+ * state.
+ *
+ * Return 0 if health is bad or else 1.
+ */
+int health_check_state(struct health_app *ha, int type)
+{
+ int retval = 1;
+ struct health_state *state;
+
+ assert(type < ha->nr_types);
+
+ state_lock(ha);
+
+ cds_list_for_each_entry(state, &ha->list, node) {
+ int ret;
+
+ if (state->type != type) {
+ continue;
+ }
+
+ ret = validate_state(ha, state);
+ if (!ret) {
+ retval = 0;
+ goto end;
+ }
+ }
+
+ /* Check the global state since some state might not be visible anymore. */
+ if (ha->flags[type] & HEALTH_ERROR) {
+ retval = 0;
+ }
+
+end:
+ state_unlock(ha);
+
+ DBG("Health check for type %d is %s", (int) type,
+ (retval == 0) ? "BAD" : "GOOD");
+ return retval;
+}
+
+/*
+ * Init health state.
+ */
+void health_register(struct health_app *ha, int type)
+{
+ assert(type < ha->nr_types);
+
+ /* Init TLS state. */
+ uatomic_set(&URCU_TLS(health_state).last, 0);
+ uatomic_set(&URCU_TLS(health_state).last_time.tv_sec, 0);
+ uatomic_set(&URCU_TLS(health_state).last_time.tv_nsec, 0);
+ uatomic_set(&URCU_TLS(health_state).current, 0);
+ uatomic_set(&URCU_TLS(health_state).flags, 0);
+ uatomic_set(&URCU_TLS(health_state).type, type);
+
+ /* Add it to the global TLS state list. */
+ state_lock(ha);
+ cds_list_add(&URCU_TLS(health_state).node, &ha->list);
+ state_unlock(ha);
+}
+
+/*
+ * Remove node from global list.
+ */
+void health_unregister(struct health_app *ha)
+{
+ state_lock(ha);
+ /*
+ * On error, set the global_error_state since we are about to remove
+ * the node from the global list.
+ */
+ if (uatomic_read(&URCU_TLS(health_state).flags) & HEALTH_ERROR) {
+ uatomic_set(&ha->flags[URCU_TLS(health_state).type],
+ HEALTH_ERROR);
+ }
+ cds_list_del(&URCU_TLS(health_state).node);
+ state_unlock(ha);
+}
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
+
+noinst_LTLIBRARIES = libindex.la
+
+libindex_la_SOURCES = index.c index.h lttng-index.h
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <sys/stat.h>
+
+#include <common/common.h>
+#include <common/defaults.h>
+#include <common/utils.h>
+
+#include "index.h"
+
+/*
+ * Create the index file associated with a trace file.
+ *
+ * Return fd on success, a negative value on error.
+ */
+int index_create_file(char *path_name, char *stream_name, int uid, int gid,
+ uint64_t size, uint64_t count)
+{
+ int ret, fd = -1;
+ struct lttng_packet_index_file_hdr hdr;
+ char fullpath[PATH_MAX];
+
+ ret = snprintf(fullpath, sizeof(fullpath), "%s/" DEFAULT_INDEX_DIR,
+ path_name);
+ if (ret < 0) {
+ PERROR("snprintf index path");
+ goto error;
+ }
+
+ /* Create index directory if necessary. */
+ ret = run_as_mkdir(fullpath, S_IRWXU | S_IRWXG, uid, gid);
+ if (ret < 0) {
+ if (ret != -EEXIST) {
+ ERR("Index trace directory creation error");
+ goto error;
+ }
+ }
+
+ ret = utils_create_stream_file(fullpath, stream_name, size, count, uid,
+ gid, DEFAULT_INDEX_FILE_SUFFIX);
+ if (ret < 0) {
+ goto error;
+ }
+ fd = ret;
+
+ memcpy(hdr.magic, INDEX_MAGIC, sizeof(hdr.magic));
+ hdr.index_major = htobe32(INDEX_MAJOR);
+ hdr.index_minor = htobe32(INDEX_MINOR);
+
+ do {
+ ret = write(fd, &hdr, sizeof(hdr));
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ PERROR("write index header");
+ goto error;
+ }
+
+ return fd;
+
+error:
+ if (fd >= 0) {
+ int close_ret;
+
+ close_ret = close(fd);
+ if (close_ret < 0) {
+ PERROR("close index fd");
+ }
+ }
+ return ret;
+}
+
+/*
+ * Write index values to the given fd of size len.
+ *
+ * Return 0 on success or else a negative value on error.
+ */
+int index_write(int fd, struct lttng_packet_index *index, size_t len)
+{
+ int ret;
+
+ assert(fd >= 0);
+ assert(index);
+
+ do {
+ ret = write(fd, index, len);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ PERROR("writing index file");
+ }
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _INDEX_H
+#define _INDEX_H
+
+#include <inttypes.h>
+
+#include "lttng-index.h"
+
+int index_create_file(char *path_name, char *stream_name, int uid, int gid,
+ uint64_t size, uint64_t count);
+int index_write(int fd, struct lttng_packet_index *index, size_t len);
+
+#endif /* _INDEX_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 - Julien Desfossez <jdesfossez@efficios.com>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * David Goulet <dgoulet@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef LTTNG_INDEX_H
+#define LTTNG_INDEX_H
+
+#include <limits.h>
+
+#define INDEX_MAGIC "CTFIDX"
+#define INDEX_MAJOR 1
+#define INDEX_MINOR 0
+
+/*
+ * Header at the beginning of each index file.
+ * All integer fields are stored in big endian.
+ */
+struct lttng_packet_index_file_hdr {
+ char magic[6];
+ uint32_t index_major;
+ uint32_t index_minor;
+} __attribute__((__packed__));
+
+/*
+ * Packet index generated for each trace packet store in a trace file.
+ * All integer fields are stored in big endian.
+ */
+struct lttng_packet_index {
+ uint64_t offset; /* offset of the packet in the file, in bytes */
+ uint64_t packet_size; /* packet size, in bits */
+ uint64_t content_size; /* content size, in bits */
+ uint64_t timestamp_begin;
+ uint64_t timestamp_end;
+ uint64_t events_discarded;
+ uint64_t stream_id;
+} __attribute__((__packed__));
+
+#endif /* LTTNG_INDEX_H */
#include <unistd.h>
#include <sys/stat.h>
+#include <bin/lttng-consumerd/health-consumerd.h>
#include <common/common.h>
#include <common/kernel-ctl/kernel-ctl.h>
#include <common/sessiond-comm/sessiond-comm.h>
#include <common/relayd/relayd.h>
#include <common/utils.h>
#include <common/consumer-stream.h>
+#include <common/index/index.h>
+#include <common/consumer-timer.h>
#include "kernel-consumer.h"
ret = kernctl_snapshot(infd);
if (ret != 0) {
- errno = -ret;
perror("Getting sub-buffer snapshot.");
+ ret = -errno;
}
return ret;
ret = kernctl_snapshot_get_produced(infd, pos);
if (ret != 0) {
- errno = -ret;
perror("kernctl_snapshot_get_produced");
+ ret = -errno;
}
return ret;
ret = kernctl_snapshot_get_consumed(infd, pos);
if (ret != 0) {
- errno = -ret;
perror("kernctl_snapshot_get_consumed");
+ ret = -errno;
}
return ret;
* Returns 0 on success, < 0 on error
*/
int lttng_kconsumer_snapshot_channel(uint64_t key, char *path,
- uint64_t relayd_id, struct lttng_consumer_local_data *ctx)
+ uint64_t relayd_id, uint64_t max_stream_size,
+ struct lttng_consumer_local_data *ctx)
{
int ret;
unsigned long consumed_pos, produced_pos;
struct lttng_consumer_channel *channel;
struct lttng_consumer_stream *stream;
- DBG("Kernel consumer snapshot channel %lu", key);
+ DBG("Kernel consumer snapshot channel %" PRIu64, key);
rcu_read_lock();
channel = consumer_find_channel(key);
if (!channel) {
- ERR("No channel found for key %lu", key);
+ ERR("No channel found for key %" PRIu64, key);
ret = -1;
goto end;
}
}
cds_list_for_each_entry(stream, &channel->streams.head, send_node) {
+
+ health_code_update();
+
/*
* Lock stream because we are about to change its state.
*/
ret = utils_create_stream_file(path, stream->name,
stream->chan->tracefile_size,
stream->tracefile_count_current,
- stream->uid, stream->gid);
+ stream->uid, stream->gid, NULL);
if (ret < 0) {
ERR("utils_create_stream_file");
goto end_unlock;
stream->out_fd = ret;
stream->tracefile_size_current = 0;
- DBG("Kernel consumer snapshot stream %s/%s (%lu)", path,
- stream->name, stream->key);
+ DBG("Kernel consumer snapshot stream %s/%s (%" PRIu64 ")",
+ path, stream->name, stream->key);
}
ret = kernctl_buffer_flush(stream->wait_fd);
if (ret < 0) {
ERR("Failed to flush kernel stream");
+ ret = -errno;
goto end_unlock;
}
&stream->max_sb_size);
if (ret < 0) {
ERR("Getting kernel max_sb_size");
+ ret = -errno;
goto end_unlock;
}
}
+ /*
+ * The original value is sent back if max stream size is larger than
+ * the possible size of the snapshot. Also, we asume that the session
+ * daemon should never send a maximum stream size that is lower than
+ * subbuffer size.
+ */
+ consumed_pos = consumer_get_consumed_maxsize(consumed_pos,
+ produced_pos, max_stream_size);
+
while (consumed_pos < produced_pos) {
ssize_t read_len;
unsigned long len, padded_len;
+ health_code_update();
+
DBG("Kernel consumer taking snapshot at pos %lu", consumed_pos);
ret = kernctl_get_subbuf(stream->wait_fd, &consumed_pos);
if (ret < 0) {
if (errno != EAGAIN) {
PERROR("kernctl_get_subbuf snapshot");
+ ret = -errno;
goto end_unlock;
}
DBG("Kernel consumer get subbuf failed. Skipping it.");
ret = kernctl_get_subbuf_size(stream->wait_fd, &len);
if (ret < 0) {
ERR("Snapshot kernctl_get_subbuf_size");
+ ret = -errno;
goto error_put_subbuf;
}
ret = kernctl_get_padded_subbuf_size(stream->wait_fd, &padded_len);
if (ret < 0) {
ERR("Snapshot kernctl_get_padded_subbuf_size");
+ ret = -errno;
goto error_put_subbuf;
}
read_len = lttng_consumer_on_read_subbuffer_mmap(ctx, stream, len,
- padded_len - len);
+ padded_len - len, NULL);
/*
* We write the padded len in local tracefiles but the data len
* when using a relay. Display the error but continue processing
ret = kernctl_put_subbuf(stream->wait_fd);
if (ret < 0) {
ERR("Snapshot kernctl_put_subbuf");
+ ret = -errno;
goto end_unlock;
}
consumed_pos += stream->max_sb_size;
error_put_subbuf:
ret = kernctl_put_subbuf(stream->wait_fd);
if (ret < 0) {
+ ret = -errno;
ERR("Snapshot kernctl_put_subbuf error path");
}
end_unlock:
ret = utils_create_stream_file(path, metadata_stream->name,
metadata_stream->chan->tracefile_size,
metadata_stream->tracefile_count_current,
- metadata_stream->uid, metadata_stream->gid);
+ metadata_stream->uid, metadata_stream->gid, NULL);
if (ret < 0) {
goto error;
}
}
do {
+ health_code_update();
+
ret_read = lttng_kconsumer_read_subbuffer(metadata_stream, ctx);
if (ret_read < 0) {
- if (ret_read != -EPERM) {
- ERR("Kernel snapshot reading metadata subbuffer (ret: %ld)",
+ if (ret_read != -EAGAIN) {
+ ERR("Kernel snapshot reading metadata subbuffer (ret: %zd)",
ret_read);
goto error;
}
enum lttng_error_code ret_code = LTTNG_OK;
struct lttcomm_consumer_msg msg;
+ health_code_update();
+
ret = lttcomm_recv_unix_sock(sock, &msg, sizeof(msg));
if (ret != sizeof(msg)) {
if (ret > 0) {
}
return ret;
}
+
+ health_code_update();
+
if (msg.cmd_type == LTTNG_CONSUMER_STOP) {
/*
* Notify the session daemon that the command is completed.
return -ENOENT;
}
+ health_code_update();
+
/* relayd needs RCU read-side protection */
rcu_read_lock();
/* Session daemon status message are handled in the following call. */
ret = consumer_add_relayd_socket(msg.u.relayd_sock.net_index,
msg.u.relayd_sock.type, ctx, sock, consumer_sockpoll,
- &msg.u.relayd_sock.sock, msg.u.relayd_sock.session_id);
+ &msg.u.relayd_sock.sock, msg.u.relayd_sock.session_id,
+ msg.u.relayd_sock.relayd_session_id);
goto end_nosignal;
}
case LTTNG_CONSUMER_ADD_CHANNEL:
struct lttng_consumer_channel *new_channel;
int ret_recv;
+ health_code_update();
+
/* First send a status message before receiving the fds. */
ret = consumer_send_status_msg(sock, ret_code);
if (ret < 0) {
/* Somehow, the session daemon is not responding anymore. */
goto error_fatal;
}
+
+ health_code_update();
+
DBG("consumer_add_channel %" PRIu64, msg.u.channel.channel_key);
new_channel = consumer_allocate_channel(msg.u.channel.channel_key,
msg.u.channel.session_id, msg.u.channel.pathname,
msg.u.channel.relayd_id, msg.u.channel.output,
msg.u.channel.tracefile_size,
msg.u.channel.tracefile_count, 0,
- msg.u.channel.monitor);
+ msg.u.channel.monitor,
+ msg.u.channel.live_timer_interval);
if (new_channel == NULL) {
lttng_consumer_send_error(ctx, LTTCOMM_CONSUMERD_OUTFD_ERROR);
goto end_nosignal;
goto end_nosignal;
};
+ health_code_update();
+
if (ctx->on_recv_channel != NULL) {
ret_recv = ctx->on_recv_channel(new_channel);
if (ret_recv == 0) {
} else {
ret = consumer_add_channel(new_channel, ctx);
}
+ if (CONSUMER_CHANNEL_TYPE_DATA) {
+ consumer_timer_live_start(new_channel,
+ msg.u.channel.live_timer_interval);
+ }
+
+ health_code_update();
/* If we received an error in add_channel, we need to report it. */
if (ret < 0) {
ret_code = LTTNG_ERR_KERN_CHAN_NOT_FOUND;
}
+ health_code_update();
+
/* First send a status message before receiving the fds. */
ret = consumer_send_status_msg(sock, ret_code);
if (ret < 0) {
/* Somehow, the session daemon is not responding anymore. */
goto error_fatal;
}
+
+ health_code_update();
+
if (ret_code != LTTNG_OK) {
/* Channel was not found. */
goto end_nosignal;
}
/* Blocking call */
- if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) {
+ health_poll_entry();
+ ret = lttng_consumer_poll_socket(consumer_sockpoll);
+ health_poll_exit();
+ if (ret < 0) {
rcu_read_unlock();
return -EINTR;
}
+ health_code_update();
+
/* Get stream file descriptor from socket */
ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1);
if (ret != sizeof(fd)) {
return ret;
}
+ health_code_update();
+
/*
* Send status code to session daemon only if the recv works. If the
* above recv() failed, the session daemon is notified through the
goto end_nosignal;
}
+ health_code_update();
+
new_stream = consumer_allocate_stream(channel->key,
fd,
LTTNG_CONSUMER_ACTIVE_STREAM,
*/
new_stream->hangup_flush_done = 0;
+ health_code_update();
+
if (ctx->on_recv_stream) {
ret = ctx->on_recv_stream(new_stream);
if (ret < 0) {
}
}
+ health_code_update();
+
if (new_stream->metadata_flag) {
channel->metadata_stream = new_stream;
}
/* Get the right pipe where the stream will be sent. */
if (new_stream->metadata_flag) {
+ ret = consumer_add_metadata_stream(new_stream);
+ if (ret) {
+ ERR("Consumer add metadata stream %" PRIu64 " failed. Continuing",
+ new_stream->key);
+ consumer_stream_free(new_stream);
+ goto end_nosignal;
+ }
stream_pipe = ctx->consumer_metadata_pipe;
} else {
+ ret = consumer_add_data_stream(new_stream);
+ if (ret) {
+ ERR("Consumer add stream %" PRIu64 " failed. Continuing",
+ new_stream->key);
+ consumer_stream_free(new_stream);
+ goto end_nosignal;
+ }
stream_pipe = ctx->consumer_data_pipe;
}
+ /* Vitible to other threads */
+ new_stream->globally_visible = 1;
+
+ health_code_update();
+
ret = lttng_pipe_write(stream_pipe, &new_stream, sizeof(new_stream));
if (ret < 0) {
ERR("Consumer write %s stream to pipe %d",
new_stream->metadata_flag ? "metadata" : "data",
lttng_pipe_get_writefd(stream_pipe));
- consumer_stream_free(new_stream);
+ if (new_stream->metadata_flag) {
+ consumer_del_stream_for_metadata(new_stream);
+ } else {
+ consumer_del_stream_for_data(new_stream);
+ }
goto end_nosignal;
}
- /* Successfully sent to the right thread. */
- new_stream->globally_visible = 1;
DBG("Kernel consumer ADD_STREAM %s (fd: %d) with relayd id %" PRIu64,
new_stream->name, fd, new_stream->relayd_stream_id);
consumer_flag_relayd_for_destroy(relayd);
}
+ health_code_update();
+
ret = consumer_send_status_msg(sock, ret_code);
if (ret < 0) {
/* Somehow, the session daemon is not responding anymore. */
ret = consumer_data_pending(id);
+ health_code_update();
+
/* Send back returned value to session daemon */
ret = lttcomm_send_unix_sock(sock, &ret, sizeof(ret));
if (ret < 0) {
} else {
ret = lttng_kconsumer_snapshot_channel(msg.u.snapshot_channel.key,
msg.u.snapshot_channel.pathname,
- msg.u.snapshot_channel.relayd_id, ctx);
+ msg.u.snapshot_channel.relayd_id,
+ msg.u.snapshot_channel.max_stream_size,
+ ctx);
if (ret < 0) {
ERR("Snapshot channel failed");
ret_code = LTTNG_ERR_KERN_CHAN_FAIL;
}
}
+ health_code_update();
+
ret = consumer_send_status_msg(sock, ret_code);
if (ret < 0) {
/* Somehow, the session daemon is not responding anymore. */
ret_code = LTTNG_ERR_KERN_CHAN_NOT_FOUND;
}
+ health_code_update();
+
ret = consumer_send_status_msg(sock, ret_code);
if (ret < 0) {
/* Somehow, the session daemon is not responding anymore. */
goto end_nosignal;
}
+ health_code_update();
+
/*
* This command should ONLY be issued for channel with streams set in
* no monitor mode.
* Return 1 to indicate success since the 0 value can be a socket
* shutdown during the recv() or send() call.
*/
+ health_code_update();
return 1;
error_fatal:
return -1;
}
+/*
+ * Populate index values of a kernel stream. Values are set in big endian order.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int get_index_values(struct lttng_packet_index *index, int infd)
+{
+ int ret;
+
+ ret = kernctl_get_timestamp_begin(infd, &index->timestamp_begin);
+ if (ret < 0) {
+ PERROR("kernctl_get_timestamp_begin");
+ goto error;
+ }
+ index->timestamp_begin = htobe64(index->timestamp_begin);
+
+ ret = kernctl_get_timestamp_end(infd, &index->timestamp_end);
+ if (ret < 0) {
+ PERROR("kernctl_get_timestamp_end");
+ goto error;
+ }
+ index->timestamp_end = htobe64(index->timestamp_end);
+
+ ret = kernctl_get_events_discarded(infd, &index->events_discarded);
+ if (ret < 0) {
+ PERROR("kernctl_get_events_discarded");
+ goto error;
+ }
+ index->events_discarded = htobe64(index->events_discarded);
+
+ ret = kernctl_get_content_size(infd, &index->content_size);
+ if (ret < 0) {
+ PERROR("kernctl_get_content_size");
+ goto error;
+ }
+ index->content_size = htobe64(index->content_size);
+
+ ret = kernctl_get_packet_size(infd, &index->packet_size);
+ if (ret < 0) {
+ PERROR("kernctl_get_packet_size");
+ goto error;
+ }
+ index->packet_size = htobe64(index->packet_size);
+
+ ret = kernctl_get_stream_id(infd, &index->stream_id);
+ if (ret < 0) {
+ PERROR("kernctl_get_stream_id");
+ goto error;
+ }
+ index->stream_id = htobe64(index->stream_id);
+
+error:
+ return ret;
+}
+/*
+ * Sync metadata meaning request them to the session daemon and snapshot to the
+ * metadata thread can consumer them.
+ *
+ * Metadata stream lock MUST be acquired.
+ *
+ * Return 0 if new metadatda is available, EAGAIN if the metadata stream
+ * is empty or a negative value on error.
+ */
+int lttng_kconsumer_sync_metadata(struct lttng_consumer_stream *metadata)
+{
+ int ret;
+
+ assert(metadata);
+
+ ret = kernctl_buffer_flush(metadata->wait_fd);
+ if (ret < 0) {
+ ERR("Failed to flush kernel stream");
+ goto end;
+ }
+
+ ret = kernctl_snapshot(metadata->wait_fd);
+ if (ret < 0) {
+ if (errno != EAGAIN) {
+ ERR("Sync metadata, taking kernel snapshot failed.");
+ goto end;
+ }
+ DBG("Sync metadata, no new kernel metadata");
+ /* No new metadata, exit. */
+ ret = ENODATA;
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
/*
* Consume data on a file descriptor and write it on a trace file.
*/
struct lttng_consumer_local_data *ctx)
{
unsigned long len, subbuf_size, padding;
- int err;
+ int err, write_index = 1;
ssize_t ret = 0;
int infd = stream->wait_fd;
+ struct lttng_packet_index index;
DBG("In read_subbuffer (infd : %d)", infd);
+
/* Get the next subbuffer */
err = kernctl_get_next_subbuf(infd);
if (err != 0) {
- ret = err;
/*
* This is a debug message even for single-threaded consumer,
* because poll() have more relaxed criterions than get subbuf,
*/
DBG("Reserving sub buffer failed (everything is normal, "
"it is due to concurrency)");
+ ret = -errno;
goto end;
}
/* Get the full subbuffer size including padding */
err = kernctl_get_padded_subbuf_size(infd, &len);
if (err != 0) {
- errno = -err;
perror("Getting sub-buffer len failed.");
- ret = err;
+ ret = -errno;
goto end;
}
+ if (!stream->metadata_flag) {
+ ret = get_index_values(&index, infd);
+ if (ret < 0) {
+ goto end;
+ }
+ } else {
+ write_index = 0;
+ }
+
switch (stream->chan->output) {
case CONSUMER_CHANNEL_SPLICE:
/*
/* splice the subbuffer to the tracefile */
ret = lttng_consumer_on_read_subbuffer_splice(ctx, stream, subbuf_size,
- padding);
+ padding, &index);
/*
* XXX: Splice does not support network streaming so the return value
* is simply checked against subbuf_size and not like the mmap() op.
*/
ERR("Error splicing to tracefile (ret: %zd != len: %lu)",
ret, subbuf_size);
+ write_index = 0;
}
break;
case CONSUMER_CHANNEL_MMAP:
/* Get subbuffer size without padding */
err = kernctl_get_subbuf_size(infd, &subbuf_size);
if (err != 0) {
- errno = -err;
perror("Getting sub-buffer len failed.");
- ret = err;
+ ret = -errno;
goto end;
}
/* write the subbuffer to the tracefile */
ret = lttng_consumer_on_read_subbuffer_mmap(ctx, stream, subbuf_size,
- padding);
+ padding, &index);
/*
* The mmap operation should write subbuf_size amount of data when
* network streaming or the full padding (len) size when we are _not_
ERR("Error writing to tracefile "
"(ret: %zd != len: %lu != subbuf_size: %lu)",
ret, len, subbuf_size);
+ write_index = 0;
}
break;
default:
ERR("Unknown output method");
- ret = -1;
+ ret = -EPERM;
}
err = kernctl_put_next_subbuf(infd);
if (err != 0) {
- errno = -err;
if (errno == EFAULT) {
perror("Error in unreserving sub buffer\n");
} else if (errno == EIO) {
/* Should never happen with newer LTTng versions */
perror("Reader has been pushed by the writer, last sub-buffer corrupted.");
}
+ ret = -errno;
+ goto end;
+ }
+
+ /* Write index if needed. */
+ if (!write_index) {
+ goto end;
+ }
+
+ if (stream->chan->live_timer_interval && !stream->metadata_flag) {
+ /*
+ * In live, block until all the metadata is sent.
+ */
+ err = consumer_stream_sync_metadata(ctx, stream->session_id);
+ if (err < 0) {
+ goto end;
+ }
+ }
- ret = -err;
+ err = consumer_stream_write_index(stream, &index);
+ if (err < 0) {
goto end;
}
if (stream->net_seq_idx == (uint64_t) -1ULL && stream->chan->monitor) {
ret = utils_create_stream_file(stream->chan->pathname, stream->name,
stream->chan->tracefile_size, stream->tracefile_count_current,
- stream->uid, stream->gid);
+ stream->uid, stream->gid, NULL);
if (ret < 0) {
goto error;
}
stream->out_fd = ret;
stream->tracefile_size_current = 0;
+
+ if (!stream->metadata_flag) {
+ ret = index_create_file(stream->chan->pathname,
+ stream->name, stream->uid, stream->gid,
+ stream->chan->tracefile_size,
+ stream->tracefile_count_current);
+ if (ret < 0) {
+ goto error;
+ }
+ stream->index_fd = ret;
+ }
}
if (stream->output == LTTNG_EVENT_MMAP) {
ret = kernctl_get_mmap_len(stream->wait_fd, &mmap_len);
if (ret != 0) {
- errno = -ret;
PERROR("kernctl_get_mmap_len");
+ ret = -errno;
goto error_close_fd;
}
stream->mmap_len = (size_t) mmap_len;
assert(stream);
+ if (stream->endpoint_status != CONSUMER_ENDPOINT_ACTIVE) {
+ ret = 0;
+ goto end;
+ }
+
ret = kernctl_get_next_subbuf(stream->wait_fd);
if (ret == 0) {
/* There is still data so let's put back this subbuffer. */
int lttng_kconsumer_take_snapshot(struct lttng_consumer_stream *stream);
int lttng_kconsumer_get_produced_snapshot(struct lttng_consumer_stream *stream,
unsigned long *pos);
+int lttng_kconsumer_get_consumed_snapshot(struct lttng_consumer_stream *stream,
+ unsigned long *pos);
int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
int sock, struct pollfd *consumer_sockpoll);
ssize_t lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
struct lttng_consumer_local_data *ctx);
int lttng_kconsumer_on_recv_stream(struct lttng_consumer_stream *stream);
int lttng_kconsumer_data_pending(struct lttng_consumer_stream *stream);
+int lttng_kconsumer_sync_metadata(struct lttng_consumer_stream *metadata);
#endif /* _LTTNG_KCONSUMER_H */
{
return ioctl(fd, RING_BUFFER_PUT_SUBBUF);
}
+
+/* Returns the timestamp begin of the current sub-buffer. */
+int kernctl_get_timestamp_begin(int fd, uint64_t *timestamp_begin)
+{
+ return ioctl(fd, LTTNG_RING_BUFFER_GET_TIMESTAMP_BEGIN, timestamp_begin);
+}
+
+/* Returns the timestamp end of the current sub-buffer. */
+int kernctl_get_timestamp_end(int fd, uint64_t *timestamp_end)
+{
+ return ioctl(fd, LTTNG_RING_BUFFER_GET_TIMESTAMP_END, timestamp_end);
+}
+
+/* Returns the number of discarded events in the current sub-buffer. */
+int kernctl_get_events_discarded(int fd, uint64_t *events_discarded)
+{
+ return ioctl(fd, LTTNG_RING_BUFFER_GET_EVENTS_DISCARDED, events_discarded);
+}
+
+/* Returns the content size in the current sub-buffer. */
+int kernctl_get_content_size(int fd, uint64_t *content_size)
+{
+ return ioctl(fd, LTTNG_RING_BUFFER_GET_CONTENT_SIZE, content_size);
+}
+
+/* Returns the packet size in the current sub-buffer. */
+int kernctl_get_packet_size(int fd, uint64_t *packet_size)
+{
+ return ioctl(fd, LTTNG_RING_BUFFER_GET_PACKET_SIZE, packet_size);
+}
+
+/* Returns the stream id of the current sub-buffer. */
+int kernctl_get_stream_id(int fd, uint64_t *stream_id)
+{
+ return ioctl(fd, LTTNG_RING_BUFFER_GET_STREAM_ID, stream_id);
+}
+
+/* Returns the current timestamp. */
+int kernctl_get_current_timestamp(int fd, uint64_t *ts)
+{
+ return ioctl(fd, LTTNG_RING_BUFFER_GET_CURRENT_TIMESTAMP, ts);
+}
int kernctl_buffer_flush(int fd);
+/* index */
+int kernctl_get_timestamp_begin(int fd, uint64_t *timestamp_begin);
+int kernctl_get_timestamp_end(int fd, uint64_t *timestamp_end);
+int kernctl_get_events_discarded(int fd, uint64_t *events_discarded);
+int kernctl_get_content_size(int fd, uint64_t *content_size);
+int kernctl_get_packet_size(int fd, uint64_t *packet_size);
+int kernctl_get_stream_id(int fd, uint64_t *stream_id);
+int kernctl_get_current_timestamp(int fd, uint64_t *ts);
+
#endif /* _LTTNG_KERNEL_CTL_H */
/* flush the current sub-buffer */
#define RING_BUFFER_FLUSH _IO(0xF6, 0x0C)
+/* returns the timestamp begin of the current sub-buffer */
+#define LTTNG_RING_BUFFER_GET_TIMESTAMP_BEGIN _IOR(0xF6, 0x20, uint64_t)
+/* returns the timestamp end of the current sub-buffer */
+#define LTTNG_RING_BUFFER_GET_TIMESTAMP_END _IOR(0xF6, 0x21, uint64_t)
+/* returns the number of events discarded */
+#define LTTNG_RING_BUFFER_GET_EVENTS_DISCARDED _IOR(0xF6, 0x22, uint64_t)
+/* returns the packet payload size */
+#define LTTNG_RING_BUFFER_GET_CONTENT_SIZE _IOR(0xF6, 0x23, uint64_t)
+/* returns the actual packet size */
+#define LTTNG_RING_BUFFER_GET_PACKET_SIZE _IOR(0xF6, 0x24, uint64_t)
+/* returns the stream id */
+#define LTTNG_RING_BUFFER_GET_STREAM_ID _IOR(0xF6, 0x25, uint64_t)
+/* returns the current timestamp */
+#define LTTNG_RING_BUFFER_GET_CURRENT_TIMESTAMP _IOR(0xF6, 0x26, uint64_t)
+
/* Old ABI (without support for 32/64 bits compat) */
/* LTTng file descriptor ioctl */
#define LTTNG_KERNEL_OLD_SESSION _IO(0xF6, 0x40)
#include <common/common.h>
#include <common/defaults.h>
#include <common/sessiond-comm/relayd.h>
+#include <common/index/lttng-index.h>
#include "relayd.h"
return ret;
}
+/*
+ * Starting at 2.4, RELAYD_CREATE_SESSION takes additional parameters to
+ * support the live reading capability.
+ */
+static int relayd_create_session_2_4(struct lttcomm_relayd_sock *rsock,
+ uint64_t *session_id, char *session_name, char *hostname,
+ int session_live_timer, unsigned int snapshot)
+{
+ int ret;
+ struct lttcomm_relayd_create_session_2_4 msg;
+
+ strncpy(msg.session_name, session_name, sizeof(msg.session_name));
+ strncpy(msg.hostname, hostname, sizeof(msg.hostname));
+ msg.live_timer = htobe32(session_live_timer);
+ msg.snapshot = htobe32(snapshot);
+
+ /* Send command */
+ ret = send_command(rsock, RELAYD_CREATE_SESSION, &msg, sizeof(msg), 0);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * RELAYD_CREATE_SESSION from 2.1 to 2.3.
+ */
+static int relayd_create_session_2_1(struct lttcomm_relayd_sock *rsock,
+ uint64_t *session_id)
+{
+ int ret;
+
+ /* Send command */
+ ret = send_command(rsock, RELAYD_CREATE_SESSION, NULL, 0, 0);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
/*
* Send a RELAYD_CREATE_SESSION command to the relayd with the given socket and
* set session_id of the relayd if we have a successful reply from the relayd.
* On success, return 0 else a negative value which is either an errno error or
* a lttng error code from the relayd.
*/
-int relayd_create_session(struct lttcomm_relayd_sock *rsock, uint64_t *session_id)
+int relayd_create_session(struct lttcomm_relayd_sock *rsock, uint64_t *session_id,
+ char *session_name, char *hostname, int session_live_timer,
+ unsigned int snapshot)
{
int ret;
struct lttcomm_relayd_status_session reply;
DBG("Relayd create session");
- /* Send command */
- ret = send_command(rsock, RELAYD_CREATE_SESSION, NULL, 0, 0);
+ switch(rsock->minor) {
+ case 1:
+ case 2:
+ case 3:
+ ret = relayd_create_session_2_1(rsock, session_id);
+ break;
+ case 4:
+ default:
+ ret = relayd_create_session_2_4(rsock, session_id, session_name,
+ hostname, session_live_timer, snapshot);
+ break;
+ }
+
if (ret < 0) {
goto error;
}
error:
return ret;
}
+
+/*
+ * Send index to the relayd.
+ */
+int relayd_send_index(struct lttcomm_relayd_sock *rsock,
+ struct lttng_packet_index *index, uint64_t relay_stream_id,
+ uint64_t net_seq_num)
+{
+ int ret;
+ struct lttcomm_relayd_index msg;
+ struct lttcomm_relayd_generic_reply reply;
+
+ /* Code flow error. Safety net. */
+ assert(rsock);
+
+ if (rsock->minor < 4) {
+ DBG("Not sending indexes before protocol 2.4");
+ ret = 0;
+ goto error;
+ }
+
+ DBG("Relayd sending index for stream ID %" PRIu64, relay_stream_id);
+
+ msg.relay_stream_id = htobe64(relay_stream_id);
+ msg.net_seq_num = htobe64(net_seq_num);
+
+ /* The index is already in big endian. */
+ msg.packet_size = index->packet_size;
+ msg.content_size = index->content_size;
+ msg.timestamp_begin = index->timestamp_begin;
+ msg.timestamp_end = index->timestamp_end;
+ msg.events_discarded = index->events_discarded;
+ msg.stream_id = index->stream_id;
+
+ /* Send command */
+ ret = send_command(rsock, RELAYD_SEND_INDEX, &msg, sizeof(msg), 0);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* Receive response */
+ ret = recv_reply(rsock, (void *) &reply, sizeof(reply));
+ if (ret < 0) {
+ goto error;
+ }
+
+ reply.ret_code = be32toh(reply.ret_code);
+
+ /* Return session id or negative ret code. */
+ if (reply.ret_code != LTTNG_OK) {
+ ret = -1;
+ ERR("Relayd send index replied error %d", reply.ret_code);
+ } else {
+ /* Success */
+ ret = 0;
+ }
+
+error:
+ return ret;
+}
int relayd_connect(struct lttcomm_relayd_sock *sock);
int relayd_close(struct lttcomm_relayd_sock *sock);
-int relayd_create_session(struct lttcomm_relayd_sock *sock, uint64_t *session_id);
+int relayd_create_session(struct lttcomm_relayd_sock *sock, uint64_t *session_id,
+ char *session_name, char *hostname, int session_live_timer,
+ unsigned int snapshot);
int relayd_add_stream(struct lttcomm_relayd_sock *sock, const char *channel_name,
const char *pathname, uint64_t *stream_id,
uint64_t tracefile_size, uint64_t tracefile_count);
int relayd_begin_data_pending(struct lttcomm_relayd_sock *sock, uint64_t id);
int relayd_end_data_pending(struct lttcomm_relayd_sock *sock, uint64_t id,
unsigned int *is_data_inflight);
+int relayd_send_index(struct lttcomm_relayd_sock *rsock,
+ struct lttng_packet_index *index, uint64_t relay_stream_id,
+ uint64_t net_seq_num);
#endif /* _RELAYD_H */
static
int _mkdir(void *_data)
{
+ int ret;
struct run_as_mkdir_data *data = _data;
- return mkdir(data->path, data->mode);
+
+ ret = mkdir(data->path, data->mode);
+ if (ret < 0) {
+ ret = -errno;
+ }
+
+ return ret;
}
static
ret = setegid(data->gid);
if (ret < 0) {
PERROR("setegid");
- return EXIT_FAILURE;
+ sendret.i = -1;
+ goto write_return;
}
}
if (data->uid != geteuid()) {
ret = seteuid(data->uid);
if (ret < 0) {
PERROR("seteuid");
- return EXIT_FAILURE;
+ sendret.i = -1;
+ goto write_return;
}
}
/*
*/
umask(0);
sendret.i = (*data->cmd)(data->data);
+
+write_return:
/* send back return value */
writeleft = sizeof(sendret);
index = 0;
libsessiond_comm_la_SOURCES = sessiond-comm.c sessiond-comm.h \
unix.c unix.h inet.c inet.h inet6.c inet6.h \
- relayd.h
+ relayd.h jul.h
+libsessiond_comm_la_LIBADD = -lrt
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <poll.h>
#include <common/common.h>
#include "inet.h"
+#define MSEC_PER_SEC 1000
+#define NSEC_PER_MSEC 1000000
+#define RECONNECT_DELAY 200 /* ms */
+
/*
* INET protocol operations.
*/
.sendmsg = lttcomm_sendmsg_inet_sock,
};
+unsigned long lttcomm_inet_tcp_timeout;
+
/*
* Creates an PF_INET socket.
*/
int lttcomm_create_inet_sock(struct lttcomm_sock *sock, int type, int proto)
{
int val = 1, ret;
+ unsigned long timeout;
/* Create server socket */
if ((sock->fd = socket(PF_INET, type, proto)) < 0) {
PERROR("setsockopt inet");
goto error;
}
+ timeout = lttcomm_get_network_timeout();
+ if (timeout) {
+ ret = lttcomm_setsockopt_rcv_timeout(sock->fd, timeout);
+ if (ret) {
+ goto error;
+ }
+ ret = lttcomm_setsockopt_snd_timeout(sock->fd, timeout);
+ if (ret) {
+ goto error;
+ }
+ }
return 0;
return ret;
}
+static
+int connect_no_timeout(struct lttcomm_sock *sock)
+{
+ return connect(sock->fd, (struct sockaddr *) &sock->sockaddr.addr.sin,
+ sizeof(sock->sockaddr.addr.sin));
+}
+
+/*
+ * Return time_a - time_b in milliseconds.
+ */
+static
+unsigned long time_diff_ms(struct timespec *time_a,
+ struct timespec *time_b)
+{
+ time_t sec_diff;
+ long nsec_diff;
+ unsigned long result_ms;
+
+ sec_diff = time_a->tv_sec - time_b->tv_sec;
+ nsec_diff = time_a->tv_nsec - time_b->tv_nsec;
+
+ result_ms = sec_diff * MSEC_PER_SEC;
+ result_ms += nsec_diff / NSEC_PER_MSEC;
+ return result_ms;
+}
+
+static
+int connect_with_timeout(struct lttcomm_sock *sock)
+{
+ unsigned long timeout = lttcomm_get_network_timeout();
+ int ret, flags, connect_ret;
+ struct timespec orig_time, cur_time;
+
+ ret = fcntl(sock->fd, F_GETFL, 0);
+ if (ret == -1) {
+ PERROR("fcntl");
+ return -1;
+ }
+ flags = ret;
+
+ /* Set socket to nonblock */
+ ret = fcntl(sock->fd, F_SETFL, flags | O_NONBLOCK);
+ if (ret == -1) {
+ PERROR("fcntl");
+ return -1;
+ }
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &orig_time);
+ if (ret == -1) {
+ PERROR("clock_gettime");
+ return -1;
+ }
+
+ connect_ret = connect(sock->fd,
+ (struct sockaddr *) &sock->sockaddr.addr.sin,
+ sizeof(sock->sockaddr.addr.sin));
+ if (connect_ret == -1 && errno != EAGAIN
+ && errno != EWOULDBLOCK
+ && errno != EINPROGRESS) {
+ goto error;
+ } else if (!connect_ret) {
+ /* Connect succeeded */
+ goto success;
+ }
+
+ /*
+ * Perform poll loop following EINPROGRESS recommendation from
+ * connect(2) man page.
+ */
+ do {
+ struct pollfd fds;
+
+ fds.fd = sock->fd;
+ fds.events = POLLOUT;
+ fds.revents = 0;
+ ret = poll(&fds, 1, RECONNECT_DELAY);
+ if (ret < 0) {
+ goto error;
+ } else if (ret > 0) {
+ int optval;
+ socklen_t optval_len = sizeof(optval);
+
+ if (!(fds.revents & POLLOUT)) {
+ /* Either hup or error */
+ errno = EPIPE;
+ goto error;
+ }
+ /* got something */
+ ret = getsockopt(sock->fd, SOL_SOCKET,
+ SO_ERROR, &optval, &optval_len);
+ if (ret) {
+ goto error;
+ }
+ if (!optval) {
+ connect_ret = 0;
+ goto success;
+ } else {
+ goto error;
+ }
+ }
+ /* ret == 0: timeout */
+ ret = clock_gettime(CLOCK_MONOTONIC, &cur_time);
+ if (ret == -1) {
+ PERROR("clock_gettime");
+ connect_ret = ret;
+ goto error;
+ }
+ } while (time_diff_ms(&cur_time, &orig_time) < timeout);
+
+ /* Timeout */
+ errno = ETIMEDOUT;
+ connect_ret = -1;
+
+success:
+ /* Restore initial flags */
+ ret = fcntl(sock->fd, F_SETFL, flags);
+ if (ret == -1) {
+ PERROR("fcntl");
+ /* Continue anyway */
+ }
+error:
+ return connect_ret;
+}
+
/*
* Connect PF_INET socket.
*/
{
int ret, closeret;
- ret = connect(sock->fd, (struct sockaddr *) &sock->sockaddr.addr.sin,
- sizeof(sock->sockaddr.addr.sin));
+ if (lttcomm_get_network_timeout()) {
+ ret = connect_with_timeout(sock);
+ } else {
+ ret = connect_no_timeout(sock);
+ }
if (ret < 0) {
PERROR("connect");
goto error_connect;
return ret;
}
+
+/*
+ * Return value read from /proc or else 0 if value is not found.
+ */
+static unsigned long read_proc_value(const char *path)
+{
+ int ret, fd;
+ long r_val;
+ unsigned long val = 0;
+ char buf[64];
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ goto error;
+ }
+
+ ret = read(fd, buf, sizeof(buf));
+ if (ret < 0) {
+ PERROR("read proc failed");
+ goto error_close;
+ }
+
+ errno = 0;
+ r_val = strtol(buf, NULL, 10);
+ if (errno != 0 || r_val < -1L) {
+ val = 0;
+ goto error_close;
+ } else {
+ if (r_val > 0) {
+ val = r_val;
+ }
+ }
+
+error_close:
+ ret = close(fd);
+ if (ret) {
+ PERROR("close /proc value");
+ }
+error:
+ return val;
+}
+
+LTTNG_HIDDEN
+void lttcomm_inet_init(void)
+{
+ unsigned long syn_retries, fin_timeout, syn_timeout, env;
+
+ env = lttcomm_get_network_timeout();
+ if (env) {
+ lttcomm_inet_tcp_timeout = env;
+ goto end;
+ }
+
+ /* Assign default value and see if we can change it. */
+ lttcomm_inet_tcp_timeout = DEFAULT_INET_TCP_TIMEOUT;
+
+ syn_retries = read_proc_value(LTTCOMM_INET_PROC_SYN_RETRIES_PATH);
+ fin_timeout = read_proc_value(LTTCOMM_INET_PROC_FIN_TIMEOUT_PATH);
+
+ syn_timeout = syn_retries * LTTCOMM_INET_SYN_TIMEOUT_FACTOR;
+
+ /*
+ * Get the maximum between the two possible timeout value and use that to
+ * get the maximum with the default timeout.
+ */
+ lttcomm_inet_tcp_timeout = max_t(unsigned long,
+ max_t(unsigned long, syn_timeout, fin_timeout),
+ lttcomm_inet_tcp_timeout);
+
+end:
+ DBG("TCP inet operation timeout set to %lu sec", lttcomm_inet_tcp_timeout);
+}
#include "sessiond-comm.h"
+/* See man tcp(7) for more detail about this value. */
+#define LTTCOMM_INET_PROC_SYN_RETRIES_PATH "/proc/sys/net/ipv4/tcp_syn_retries"
+#define LTTCOMM_INET_PROC_FIN_TIMEOUT_PATH "/proc/sys/net/ipv4/tcp_fin_timeout"
+
+/*
+ * The timeout value of a connect() is computed with an algorithm inside the
+ * kernel using the defined TCP SYN retries so the end value in time is
+ * approximative. According to tcp(7) man page, a value of 5 is roughly 180
+ * seconds of timeout. With that information, we've computed a factor of 36
+ * (180/5) by considering that it grows linearly. This is of course uncertain
+ * but this is the best approximation we can do at runtime.
+ */
+#define LTTCOMM_INET_SYN_TIMEOUT_FACTOR 36
+
+/*
+ * Maximum timeout value in seconds of a TCP connection for both send/recv and
+ * connect operations.
+ */
+extern unsigned long lttcomm_inet_tcp_timeout;
+
/* Stub */
struct lttcomm_sock;
extern ssize_t lttcomm_sendmsg_inet_sock(struct lttcomm_sock *sock, void *buf,
size_t len, int flags);
+/* Initialize inet communication layer. */
+extern void lttcomm_inet_init(void);
+
#endif /* _LTTCOMM_INET_H */
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <poll.h>
#include <common/common.h>
#include "inet6.h"
+#define MSEC_PER_SEC 1000
+#define NSEC_PER_MSEC 1000000
+#define RECONNECT_DELAY 200 /* ms */
+
/*
* INET protocol operations.
*/
int lttcomm_create_inet6_sock(struct lttcomm_sock *sock, int type, int proto)
{
int val = 1, ret;
+ unsigned long timeout;
/* Create server socket */
if ((sock->fd = socket(PF_INET6, type, proto)) < 0) {
PERROR("setsockopt inet6");
goto error;
}
+ timeout = lttcomm_get_network_timeout();
+ if (timeout) {
+ ret = lttcomm_setsockopt_rcv_timeout(sock->fd, timeout);
+ if (ret) {
+ goto error;
+ }
+ ret = lttcomm_setsockopt_snd_timeout(sock->fd, timeout);
+ if (ret) {
+ goto error;
+ }
+ }
return 0;
return ret;
}
+static
+int connect_no_timeout(struct lttcomm_sock *sock)
+{
+ return connect(sock->fd, (struct sockaddr *) &sock->sockaddr.addr.sin6,
+ sizeof(sock->sockaddr.addr.sin6));
+}
+
+/*
+ * Return time_a - time_b in milliseconds.
+ */
+static
+unsigned long time_diff_ms(struct timespec *time_a,
+ struct timespec *time_b)
+{
+ time_t sec_diff;
+ long nsec_diff;
+ unsigned long result_ms;
+
+ sec_diff = time_a->tv_sec - time_b->tv_sec;
+ nsec_diff = time_a->tv_nsec - time_b->tv_nsec;
+
+ result_ms = sec_diff * MSEC_PER_SEC;
+ result_ms += nsec_diff / NSEC_PER_MSEC;
+ return result_ms;
+}
+
+static
+int connect_with_timeout(struct lttcomm_sock *sock)
+{
+ unsigned long timeout = lttcomm_get_network_timeout();
+ int ret, flags, connect_ret;
+ struct timespec orig_time, cur_time;
+
+ ret = fcntl(sock->fd, F_GETFL, 0);
+ if (ret == -1) {
+ PERROR("fcntl");
+ return -1;
+ }
+ flags = ret;
+
+ /* Set socket to nonblock */
+ ret = fcntl(sock->fd, F_SETFL, flags | O_NONBLOCK);
+ if (ret == -1) {
+ PERROR("fcntl");
+ return -1;
+ }
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &orig_time);
+ if (ret == -1) {
+ PERROR("clock_gettime");
+ return -1;
+ }
+
+ connect_ret = connect(sock->fd,
+ (struct sockaddr *) &sock->sockaddr.addr.sin6,
+ sizeof(sock->sockaddr.addr.sin6));
+ if (connect_ret == -1 && errno != EAGAIN
+ && errno != EWOULDBLOCK
+ && errno != EINPROGRESS) {
+ goto error;
+ } else if (!connect_ret) {
+ /* Connect succeeded */
+ goto success;
+ }
+
+ /*
+ * Perform poll loop following EINPROGRESS recommendation from
+ * connect(2) man page.
+ */
+ do {
+ struct pollfd fds;
+
+ fds.fd = sock->fd;
+ fds.events = POLLOUT;
+ fds.revents = 0;
+ ret = poll(&fds, 1, RECONNECT_DELAY);
+ if (ret < 0) {
+ goto error;
+ } else if (ret > 0) {
+ int optval;
+ socklen_t optval_len = sizeof(optval);
+
+ if (!(fds.revents & POLLOUT)) {
+ /* Either hup or error */
+ errno = EPIPE;
+ goto error;
+ }
+ /* got something */
+ ret = getsockopt(sock->fd, SOL_SOCKET,
+ SO_ERROR, &optval, &optval_len);
+ if (ret) {
+ goto error;
+ }
+ if (!optval) {
+ connect_ret = 0;
+ goto success;
+ } else {
+ goto error;
+ }
+ }
+ /* ret == 0: timeout */
+ ret = clock_gettime(CLOCK_MONOTONIC, &cur_time);
+ if (ret == -1) {
+ PERROR("clock_gettime");
+ connect_ret = ret;
+ goto error;
+ }
+ } while (time_diff_ms(&cur_time, &orig_time) < timeout);
+
+ /* Timeout */
+ errno = ETIMEDOUT;
+ connect_ret = -1;
+
+success:
+ /* Restore initial flags */
+ ret = fcntl(sock->fd, F_SETFL, flags);
+ if (ret == -1) {
+ PERROR("fcntl");
+ /* Continue anyway */
+ }
+error:
+ return connect_ret;
+}
+
/*
* Connect PF_INET socket.
*/
{
int ret, closeret;
- ret = connect(sock->fd, (struct sockaddr *) &sock->sockaddr.addr.sin6,
- sizeof(sock->sockaddr.addr.sin6));
+ if (lttcomm_get_network_timeout()) {
+ ret = connect_with_timeout(sock);
+ } else {
+ ret = connect_no_timeout(sock);
+ }
if (ret < 0) {
PERROR("connect inet6");
goto error_connect;
--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _JUL_COMM
+#define _JUL_COMM
+
+#define _GNU_SOURCE
+#include <stdint.h>
+
+#include <lttng/lttng.h>
+
+/*
+ * Command value pass in the header.
+ */
+enum lttcomm_jul_command {
+ JUL_CMD_LIST = 1,
+ JUL_CMD_ENABLE = 2,
+ JUL_CMD_DISABLE = 3,
+};
+
+/*
+ * Return code from the Java agent.
+ */
+enum lttcomm_jul_ret_code {
+ JUL_RET_CODE_SUCCESS = 1,
+ JUL_RET_CODE_INVALID = 2,
+ JUL_RET_CODE_UNKNOWN_NAME = 3,
+};
+
+/*
+ * JUL application communication header.
+ */
+struct lttcomm_jul_hdr {
+ uint64_t data_size; /* data size following this header */
+ uint32_t cmd; /* Enum of JUL command. */
+ uint32_t cmd_version; /* command version */
+} LTTNG_PACKED;
+
+/*
+ * Enable event command payload.
+ */
+struct lttcomm_jul_enable {
+ char name[LTTNG_SYMBOL_NAME_LEN];
+} LTTNG_PACKED;
+
+/*
+ * Disable event command payload.
+ */
+struct lttcomm_jul_disable {
+ char name[LTTNG_SYMBOL_NAME_LEN];
+} LTTNG_PACKED;
+
+/*
+ * Generic reply coming from the Java Agent.
+ */
+struct lttcomm_jul_generic_reply {
+ uint32_t ret_code;
+} LTTNG_PACKED;
+
+/*
+ * List command reply header.
+ */
+struct lttcomm_jul_list_reply_hdr {
+ uint32_t ret_code;
+ uint32_t data_size;
+} LTTNG_PACKED;
+
+/*
+ * List command reply payload coming from the Java Agent.
+ */
+struct lttcomm_jul_list_reply {
+ uint32_t nb_event;
+ /* List of event name each of them ending by a NULL byte. */
+ char payload[];
+} LTTNG_PACKED;
+
+#endif /* _JUL_COMM */
#include <lttng/lttng.h>
#include <common/defaults.h>
+#include <common/index/lttng-index.h>
+#include <config.h>
-#define RELAYD_VERSION_COMM_MAJOR 2
-#define RELAYD_VERSION_COMM_MINOR 2
+#define RELAYD_VERSION_COMM_MAJOR VERSION_MAJOR
+#define RELAYD_VERSION_COMM_MINOR VERSION_MINOR
/*
* lttng-relayd communication header.
uint64_t stream_id;
} LTTNG_PACKED;
+/*
+ * Index data.
+ */
+struct lttcomm_relayd_index {
+ uint64_t relay_stream_id;
+ uint64_t net_seq_num;
+ uint64_t packet_size;
+ uint64_t content_size;
+ uint64_t timestamp_begin;
+ uint64_t timestamp_end;
+ uint64_t events_discarded;
+ uint64_t stream_id;
+} LTTNG_PACKED;
+
+/*
+ * Create session in 2.4 adds additionnal parameters for live reading.
+ */
+struct lttcomm_relayd_create_session_2_4 {
+ char session_name[NAME_MAX];
+ char hostname[HOST_NAME_MAX];
+ uint32_t live_timer;
+ uint32_t snapshot;
+} LTTNG_PACKED;
+
#endif /* _RELAYD_COMM */
/* For Inet6 socket */
#include "inet6.h"
+#define NETWORK_TIMEOUT_ENV "LTTNG_NETWORK_SOCKET_TIMEOUT"
+
static struct lttcomm_net_family net_families[] = {
{ LTTCOMM_INET, lttcomm_create_inet_sock },
{ LTTCOMM_INET6, lttcomm_create_inet6_sock },
[ LTTCOMM_ERR_INDEX(LTTCOMM_NR) ] = "Unknown error code"
};
+static unsigned long network_timeout;
+
/*
* Return ptr to string representing a human readable error code from the
* lttcomm_return_code enum.
error:
return NULL;
}
+
+/*
+ * Set socket receiving timeout.
+ */
+LTTNG_HIDDEN
+int lttcomm_setsockopt_rcv_timeout(int sock, unsigned int msec)
+{
+ int ret;
+ struct timeval tv;
+
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
+
+ ret = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (ret < 0) {
+ PERROR("setsockopt SO_RCVTIMEO");
+ }
+
+ return ret;
+}
+
+/*
+ * Set socket sending timeout.
+ */
+LTTNG_HIDDEN
+int lttcomm_setsockopt_snd_timeout(int sock, unsigned int msec)
+{
+ int ret;
+ struct timeval tv;
+
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
+
+ ret = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (ret < 0) {
+ PERROR("setsockopt SO_SNDTIMEO");
+ }
+
+ return ret;
+}
+
+LTTNG_HIDDEN
+void lttcomm_init(void)
+{
+ const char *env;
+
+ env = getenv(NETWORK_TIMEOUT_ENV);
+ if (env) {
+ long timeout;
+
+ errno = 0;
+ timeout = strtol(env, NULL, 0);
+ if (errno != 0 || timeout < -1L) {
+ PERROR("Network timeout");
+ } else {
+ if (timeout > 0) {
+ network_timeout = timeout;
+ }
+ }
+ }
+}
+
+LTTNG_HIDDEN
+unsigned long lttcomm_get_network_timeout(void)
+{
+ return network_timeout;
+}
LTTNG_ENABLE_CONSUMER = 20,
LTTNG_SET_CONSUMER_URI = 21,
LTTNG_ENABLE_EVENT_WITH_FILTER = 22,
- LTTNG_HEALTH_CHECK = 23,
+ /* Unused */
LTTNG_DATA_PENDING = 24,
LTTNG_SNAPSHOT_ADD_OUTPUT = 25,
LTTNG_SNAPSHOT_DEL_OUTPUT = 26,
LTTNG_SNAPSHOT_LIST_OUTPUT = 27,
LTTNG_SNAPSHOT_RECORD = 28,
+ LTTNG_CREATE_SESSION_SNAPSHOT = 29,
+ LTTNG_CREATE_SESSION_LIVE = 30,
};
enum lttcomm_relayd_command {
RELAYD_QUIESCENT_CONTROL = 9,
RELAYD_BEGIN_DATA_PENDING = 10,
RELAYD_END_DATA_PENDING = 11,
+ RELAYD_ADD_INDEX = 12,
+ RELAYD_SEND_INDEX = 13,
+ RELAYD_CLOSE_INDEX = 14,
+ /* Live-reading commands. */
+ RELAYD_LIST_SESSIONS = 15,
};
/*
uint32_t wait;
struct lttng_snapshot_output output;
} LTTNG_PACKED snapshot_record;
+ struct {
+ uint32_t nb_uri;
+ unsigned int timer_interval; /* usec */
+ } LTTNG_PACKED session_live;
} u;
} LTTNG_PACKED;
uint32_t id;
} LTTNG_PACKED;
-struct lttcomm_health_msg {
- uint32_t component;
- uint32_t cmd;
-} LTTNG_PACKED;
-
-struct lttcomm_health_data {
- uint32_t ret_code;
-} LTTNG_PACKED;
-
/*
* lttcomm_consumer_msg is the message sent from sessiond to consumerd
* to either add a channel, add a stream, update a stream, or stop
uint32_t tracefile_count; /* number of tracefiles */
/* If the channel's streams have to be monitored or not. */
uint32_t monitor;
+ /* timer to check the streams usage in live mode (usec). */
+ unsigned int live_timer_interval;
} LTTNG_PACKED channel; /* Only used by Kernel. */
struct {
uint64_t stream_key;
struct lttcomm_relayd_sock sock;
/* Tracing session id associated to the relayd. */
uint64_t session_id;
+ /* Relayd session id, only used with control socket. */
+ uint64_t relayd_session_id;
} LTTNG_PACKED relayd_sock;
struct {
uint64_t net_seq_idx;
int32_t overwrite; /* 1: overwrite, 0: discard */
uint32_t switch_timer_interval; /* usec */
uint32_t read_timer_interval; /* usec */
+ unsigned int live_timer_interval; /* usec */
int32_t output; /* splice, mmap */
int32_t type; /* metadata or per_cpu */
uint64_t session_id; /* Tracing session id */
uint64_t session_id_per_pid; /* Per-pid session ID. */
/* Tells the consumer if the stream should be or not monitored. */
uint32_t monitor;
+ /*
+ * For UST per UID buffers, this is the application UID of the
+ * channel. This can be different from the user UID requesting the
+ * channel creation and used for the rights on the stream file
+ * because the application can be in the tracing for instance.
+ */
+ uint32_t ust_app_uid;
} LTTNG_PACKED ask_channel;
struct {
uint64_t key;
uint32_t metadata; /* This a metadata snapshot. */
uint64_t relayd_id; /* Relayd id if apply. */
uint64_t key;
- uint64_t max_size;
+ uint64_t max_stream_size;
} LTTNG_PACKED snapshot_channel;
} u;
} LTTNG_PACKED;
extern struct lttcomm_relayd_sock *lttcomm_alloc_relayd_sock(
struct lttng_uri *uri, uint32_t major, uint32_t minor);
+extern int lttcomm_setsockopt_rcv_timeout(int sock, unsigned int msec);
+extern int lttcomm_setsockopt_snd_timeout(int sock, unsigned int msec);
+
+extern void lttcomm_init(void);
+/* Get network timeout, in milliseconds */
+extern unsigned long lttcomm_get_network_timeout(void);
+
#endif /* _LTTNG_SESSIOND_COMM_H */
struct msghdr msg;
struct iovec iov[1];
ssize_t ret;
+ size_t len_last;
#ifdef __linux__
struct cmsghdr *cmptr;
size_t sizeof_cred = sizeof(lttng_sock_cred);
#endif /* __linux__ */
do {
+ len_last = iov[0].iov_len;
ret = recvmsg(sock, &msg, 0);
- } while (ret < 0 && errno == EINTR);
+ if (ret > 0) {
+ iov[0].iov_base += ret;
+ iov[0].iov_len -= ret;
+ assert(ret <= len_last);
+ }
+ } while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR));
if (ret < 0) {
PERROR("recvmsg fds");
goto end;
+ } else if (ret > 0) {
+ ret = len;
}
+ /* Else ret = 0 meaning an orderly shutdown. */
#ifdef __linux__
if (msg.msg_flags & MSG_CTRUNC) {
#else
#error "Please implement credential support for your OS."
#endif /* __linux__ */
-
-/*
- * Set socket reciving timeout.
- */
-LTTNG_HIDDEN
-int lttcomm_setsockopt_rcv_timeout(int sock, unsigned int sec)
-{
- int ret;
- struct timeval tv;
-
- tv.tv_sec = sec;
- tv.tv_usec = 0;
-
- ret = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
- if (ret < 0) {
- PERROR("setsockopt SO_RCVTIMEO");
- ret = -errno;
- }
-
- return ret;
-}
-
-/*
- * Set socket sending timeout.
- */
-LTTNG_HIDDEN
-int lttcomm_setsockopt_snd_timeout(int sock, unsigned int sec)
-{
- int ret;
- struct timeval tv;
-
- tv.tv_sec = sec;
- tv.tv_usec = 0;
-
- ret = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
- if (ret < 0) {
- PERROR("setsockopt SO_SNDTIMEO");
- ret = -errno;
- }
-
- return ret;
-}
lttng_sock_cred *creds);
extern int lttcomm_setsockopt_creds_unix_sock(int sock);
-extern int lttcomm_setsockopt_rcv_timeout(int sock, unsigned int sec);
-extern int lttcomm_setsockopt_snd_timeout(int sock, unsigned int sec);
#endif /* _LTTCOMM_UNIX_H */
#include <urcu/list.h>
#include <signal.h>
+#include <bin/lttng-consumerd/health-consumerd.h>
#include <common/common.h>
#include <common/sessiond-comm/sessiond-comm.h>
#include <common/relayd/relayd.h>
#include <common/consumer-stream.h>
#include <common/consumer-timer.h>
#include <common/utils.h>
+#include <common/index/index.h>
#include "ust-consumer.h"
cds_list_for_each_entry_safe(stream, stmp, &channel->streams.head,
send_node) {
+
+ health_code_update();
+
cds_list_del(&stream->send_node);
ustctl_destroy_stream(stream->ustream);
free(stream);
const char *pathname, const char *name, uid_t uid, gid_t gid,
uint64_t relayd_id, uint64_t key, enum lttng_event_output output,
uint64_t tracefile_size, uint64_t tracefile_count,
- uint64_t session_id_per_pid, unsigned int monitor)
+ uint64_t session_id_per_pid, unsigned int monitor,
+ unsigned int live_timer_interval)
{
assert(pathname);
assert(name);
return consumer_allocate_channel(key, session_id, pathname, name, uid,
gid, relayd_id, output, tracefile_size,
- tracefile_count, session_id_per_pid, monitor);
+ tracefile_count, session_id_per_pid, monitor, live_timer_interval);
}
/*
/* Get the right pipe where the stream will be sent. */
if (stream->metadata_flag) {
+ ret = consumer_add_metadata_stream(stream);
+ if (ret) {
+ ERR("Consumer add metadata stream %" PRIu64 " failed.",
+ stream->key);
+ goto error;
+ }
stream_pipe = ctx->consumer_metadata_pipe;
} else {
+ ret = consumer_add_data_stream(stream);
+ if (ret) {
+ ERR("Consumer add stream %" PRIu64 " failed.",
+ stream->key);
+ goto error;
+ }
stream_pipe = ctx->consumer_data_pipe;
}
+ /*
+ * From this point on, the stream's ownership has been moved away from
+ * the channel and becomes globally visible.
+ */
+ stream->globally_visible = 1;
+
ret = lttng_pipe_write(stream_pipe, &stream, sizeof(stream));
if (ret < 0) {
ERR("Consumer write %s stream to pipe %d",
stream->metadata_flag ? "metadata" : "data",
lttng_pipe_get_writefd(stream_pipe));
+ if (stream->metadata_flag) {
+ consumer_del_stream_for_metadata(stream);
+ } else {
+ consumer_del_stream_for_data(stream);
+ }
}
-
+error:
return ret;
}
*/
while ((ustream = ustctl_create_stream(channel->uchan, cpu))) {
int wait_fd;
+ int ust_metadata_pipe[2];
+
+ health_code_update();
- wait_fd = ustctl_stream_get_wait_fd(ustream);
+ if (channel->type == CONSUMER_CHANNEL_TYPE_METADATA && channel->monitor) {
+ ret = utils_create_pipe_cloexec_nonblock(ust_metadata_pipe);
+ if (ret < 0) {
+ ERR("Create ust metadata poll pipe");
+ goto error;
+ }
+ wait_fd = ust_metadata_pipe[0];
+ } else {
+ wait_fd = ustctl_stream_get_wait_fd(ustream);
+ }
/* Allocate consumer stream object. */
stream = allocate_stream(cpu, wait_fd, channel, ctx, &ret);
/* Keep stream reference when creating metadata. */
if (channel->type == CONSUMER_CHANNEL_TYPE_METADATA) {
channel->metadata_stream = stream;
+ stream->ust_metadata_poll_pipe[0] = ust_metadata_pipe[0];
+ stream->ust_metadata_poll_pipe[1] = ust_metadata_pipe[1];
}
}
assert(stream);
assert(sock >= 0);
- DBG2("UST consumer sending stream %" PRIu64 " to sessiond", stream->key);
+ DBG("UST consumer sending stream %" PRIu64 " to sessiond", stream->key);
/* Send stream to session daemon. */
ret = ustctl_send_stream_to_sessiond(sock, stream->ustream);
if (channel->relayd_id != (uint64_t) -1ULL) {
cds_list_for_each_entry(stream, &channel->streams.head, send_node) {
+
+ health_code_update();
+
/* Try to send the stream to the relayd if one is available. */
ret = consumer_send_relayd_stream(stream, stream->chan->pathname);
if (ret < 0) {
/* The channel was sent successfully to the sessiond at this point. */
cds_list_for_each_entry(stream, &channel->streams.head, send_node) {
+
+ health_code_update();
+
/* Send stream to session daemon. */
ret = send_sessiond_stream(sock, stream);
if (ret < 0) {
/* Send streams to the corresponding thread. */
cds_list_for_each_entry_safe(stream, stmp, &channel->streams.head,
send_node) {
+
+ health_code_update();
+
/* Sending the stream to the thread. */
ret = send_stream_to_thread(stream, ctx);
if (ret < 0) {
* If we are unable to send the stream to the thread, there is
* a big problem so just stop everything.
*/
+ /* Remove node from the channel stream list. */
+ cds_list_del(&stream->send_node);
goto error;
}
/* Remove node from the channel stream list. */
cds_list_del(&stream->send_node);
- /*
- * From this point on, the stream's ownership has been moved away from
- * the channel and becomes globally visible.
- */
- stream->globally_visible = 1;
}
error:
return ret;
}
-/*
- * Write metadata to the given channel using ustctl to convert the string to
- * the ringbuffer.
- * Called only from consumer_metadata_cache_write.
- * The metadata cache lock MUST be acquired to write in the cache.
- *
- * Return 0 on success else a negative value.
- */
-int lttng_ustconsumer_push_metadata(struct lttng_consumer_channel *metadata,
- const char *metadata_str, uint64_t target_offset, uint64_t len)
-{
- int ret;
-
- assert(metadata);
- assert(metadata_str);
-
- DBG("UST consumer writing metadata to channel %s", metadata->name);
-
- if (!metadata->metadata_stream) {
- ret = 0;
- goto error;
- }
-
- assert(target_offset <= metadata->metadata_cache->max_offset);
- ret = ustctl_write_metadata_to_channel(metadata->uchan,
- metadata_str + target_offset, len);
- if (ret < 0) {
- ERR("ustctl write metadata fail with ret %d, len %" PRIu64, ret, len);
- goto error;
- }
-
- ustctl_flush_buffer(metadata->metadata_stream->ustream, 1);
-
-error:
- return ret;
-}
-
/*
* Flush channel's streams using the given key to retrieve the channel.
*
cds_lfht_for_each_entry_duplicate(ht->ht,
ht->hash_fct(&channel->key, lttng_ht_seed), ht->match_fct,
&channel->key, &iter.iter, stream, node_channel_id.node) {
- ustctl_flush_buffer(stream->ustream, 1);
+
+ health_code_update();
+
+ ustctl_flush_buffer(stream->ustream, 1);
}
error:
rcu_read_unlock();
return ret;
}
+/*
+ * Close metadata stream wakeup_fd using the given key to retrieve the channel.
+ * RCU read side lock MUST be acquired before calling this function.
+ *
+ * NOTE: This function does NOT take any channel nor stream lock.
+ *
+ * Return 0 on success else LTTng error code.
+ */
+static int _close_metadata(struct lttng_consumer_channel *channel)
+{
+ int ret = LTTNG_OK;
+
+ assert(channel);
+ assert(channel->type == CONSUMER_CHANNEL_TYPE_METADATA);
+
+ if (channel->switch_timer_enabled == 1) {
+ DBG("Deleting timer on metadata channel");
+ consumer_timer_switch_stop(channel);
+ }
+
+ if (channel->metadata_stream) {
+ ret = ustctl_stream_close_wakeup_fd(channel->metadata_stream->ustream);
+ if (ret < 0) {
+ ERR("UST consumer unable to close fd of metadata (ret: %d)", ret);
+ ret = LTTCOMM_CONSUMERD_ERROR_METADATA;
+ }
+
+ if (channel->monitor) {
+ /* Close the read-side in consumer_del_metadata_stream */
+ ret = close(channel->metadata_stream->ust_metadata_poll_pipe[1]);
+ if (ret < 0) {
+ PERROR("Close UST metadata write-side poll pipe");
+ ret = LTTCOMM_CONSUMERD_ERROR_METADATA;
+ }
+ }
+ }
+
+ return ret;
+}
/*
* Close metadata stream wakeup_fd using the given key to retrieve the channel.
}
pthread_mutex_lock(&consumer_data.lock);
+ pthread_mutex_lock(&channel->lock);
if (cds_lfht_is_node_deleted(&channel->node.node)) {
goto error_unlock;
}
- if (channel->switch_timer_enabled == 1) {
- DBG("Deleting timer on metadata channel");
- consumer_timer_switch_stop(channel);
- }
-
- if (channel->metadata_stream) {
- ret = ustctl_stream_close_wakeup_fd(channel->metadata_stream->ustream);
- if (ret < 0) {
- ERR("UST consumer unable to close fd of metadata (ret: %d)", ret);
- ret = LTTCOMM_CONSUMERD_ERROR_METADATA;
- goto error_unlock;
- }
- }
+ ret = _close_metadata(channel);
error_unlock:
+ pthread_mutex_unlock(&channel->lock);
pthread_mutex_unlock(&consumer_data.lock);
error:
return ret;
struct lttng_consumer_local_data *ctx)
{
int ret = 0;
- ssize_t write_len;
- uint64_t total_len = 0;
struct lttng_consumer_channel *metadata_channel;
struct lttng_consumer_stream *metadata_stream;
metadata_channel = consumer_find_channel(key);
if (!metadata_channel) {
- ERR("UST snapshot metadata channel not found for key %lu", key);
+ ERR("UST snapshot metadata channel not found for key %" PRIu64,
+ key);
ret = -1;
goto error;
}
assert(!metadata_channel->monitor);
+ health_code_update();
+
/*
* Ask the sessiond if we have new metadata waiting and update the
* consumer metadata cache.
*/
- ret = lttng_ustconsumer_request_metadata(ctx, metadata_channel);
+ ret = lttng_ustconsumer_request_metadata(ctx, metadata_channel, 0, 1);
if (ret < 0) {
goto error;
}
+ health_code_update();
+
/*
* The metadata stream is NOT created in no monitor mode when the channel
* is created on a sessiond ask channel command.
ret = utils_create_stream_file(path, metadata_stream->name,
metadata_stream->chan->tracefile_size,
metadata_stream->tracefile_count_current,
- metadata_stream->uid, metadata_stream->gid);
+ metadata_stream->uid, metadata_stream->gid, NULL);
if (ret < 0) {
goto error_stream;
}
metadata_stream->tracefile_size_current = 0;
}
- pthread_mutex_lock(&metadata_channel->metadata_cache->lock);
- while (total_len < metadata_channel->metadata_cache->total_bytes_written) {
- /*
- * Write at most one packet of metadata into the channel
- * to avoid blocking here.
- */
- write_len = ustctl_write_one_packet_to_channel(metadata_channel->uchan,
- metadata_channel->metadata_cache->data,
- metadata_channel->metadata_cache->total_bytes_written);
- if (write_len < 0) {
- ERR("UST consumer snapshot writing metadata packet");
- ret = -1;
- goto error_unlock;
- }
- total_len += write_len;
+ do {
+ health_code_update();
- DBG("Written %" PRIu64 " bytes to metadata (left: %" PRIu64 ")",
- write_len,
- metadata_channel->metadata_cache->total_bytes_written - write_len);
- ustctl_flush_buffer(metadata_stream->ustream, 1);
ret = lttng_consumer_read_subbuffer(metadata_stream, ctx);
if (ret < 0) {
- goto error_unlock;
+ goto error_stream;
}
- }
-
-error_unlock:
- pthread_mutex_unlock(&metadata_channel->metadata_cache->lock);
+ } while (ret > 0);
error_stream:
/*
* Returns 0 on success, < 0 on error
*/
static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id,
- struct lttng_consumer_local_data *ctx)
+ uint64_t max_stream_size, struct lttng_consumer_local_data *ctx)
{
int ret;
unsigned use_relayd = 0;
channel = consumer_find_channel(key);
if (!channel) {
- ERR("UST snapshot channel not found for key %lu", key);
+ ERR("UST snapshot channel not found for key %" PRIu64, key);
ret = -1;
goto error;
}
assert(!channel->monitor);
- DBG("UST consumer snapshot channel %lu", key);
+ DBG("UST consumer snapshot channel %" PRIu64, key);
cds_list_for_each_entry(stream, &channel->streams.head, send_node) {
+
+ health_code_update();
+
/* Lock stream because we are about to change its state. */
pthread_mutex_lock(&stream->lock);
stream->net_seq_idx = relayd_id;
ret = utils_create_stream_file(path, stream->name,
stream->chan->tracefile_size,
stream->tracefile_count_current,
- stream->uid, stream->gid);
+ stream->uid, stream->gid, NULL);
if (ret < 0) {
goto error_unlock;
}
goto error_unlock;
}
+ /*
+ * The original value is sent back if max stream size is larger than
+ * the possible size of the snapshot. Also, we asume that the session
+ * daemon should never send a maximum stream size that is lower than
+ * subbuffer size.
+ */
+ consumed_pos = consumer_get_consumed_maxsize(consumed_pos,
+ produced_pos, max_stream_size);
+
while (consumed_pos < produced_pos) {
ssize_t read_len;
unsigned long len, padded_len;
+ health_code_update();
+
DBG("UST consumer taking snapshot at pos %lu", consumed_pos);
ret = ustctl_get_subbuf(stream->ustream, &consumed_pos);
}
read_len = lttng_consumer_on_read_subbuffer_mmap(ctx, stream, len,
- padded_len - len);
+ padded_len - len, NULL);
if (use_relayd) {
if (read_len != len) {
- ret = -1;
+ ret = -EPERM;
goto error_put_subbuf;
}
} else {
if (read_len != padded_len) {
- ret = -1;
+ ret = -EPERM;
goto error_put_subbuf;
}
}
* Receive the metadata updates from the sessiond.
*/
int lttng_ustconsumer_recv_metadata(int sock, uint64_t key, uint64_t offset,
- uint64_t len, struct lttng_consumer_channel *channel)
+ uint64_t len, struct lttng_consumer_channel *channel,
+ int timer, int wait)
{
int ret, ret_code = LTTNG_OK;
char *metadata_str;
goto end;
}
+ health_code_update();
+
/* Receive metadata string. */
ret = lttcomm_recv_unix_sock(sock, metadata_str, len);
if (ret < 0) {
goto end_free;
}
- /*
- * XXX: The consumer data lock is acquired before calling metadata cache
- * write which calls push metadata that MUST be protected by the consumer
- * lock in order to be able to check the validity of the metadata stream of
- * the channel.
- *
- * Note that this will be subject to change to better fine grained locking
- * and ultimately try to get rid of this global consumer data lock.
- */
- pthread_mutex_lock(&consumer_data.lock);
+ health_code_update();
pthread_mutex_lock(&channel->metadata_cache->lock);
ret = consumer_metadata_cache_write(channel, offset, len, metadata_str);
* waiting for the metadata cache to be flushed.
*/
pthread_mutex_unlock(&channel->metadata_cache->lock);
- pthread_mutex_unlock(&consumer_data.lock);
goto end_free;
}
pthread_mutex_unlock(&channel->metadata_cache->lock);
- pthread_mutex_unlock(&consumer_data.lock);
- while (consumer_metadata_cache_flushed(channel, offset + len)) {
+ if (!wait) {
+ goto end_free;
+ }
+ while (consumer_metadata_cache_flushed(channel, offset + len, timer)) {
DBG("Waiting for metadata to be flushed");
+
+ health_code_update();
+
usleep(DEFAULT_METADATA_AVAILABILITY_WAIT_TIME);
}
struct lttcomm_consumer_msg msg;
struct lttng_consumer_channel *channel = NULL;
+ health_code_update();
+
ret = lttcomm_recv_unix_sock(sock, &msg, sizeof(msg));
if (ret != sizeof(msg)) {
DBG("Consumer received unexpected message size %zd (expects %zu)",
}
return ret;
}
+
+ health_code_update();
+
if (msg.cmd_type == LTTNG_CONSUMER_STOP) {
/*
* Notify the session daemon that the command is completed.
return -ENOENT;
}
+ health_code_update();
+
/* relayd needs RCU read-side lock */
rcu_read_lock();
/* Session daemon status message are handled in the following call. */
ret = consumer_add_relayd_socket(msg.u.relayd_sock.net_index,
msg.u.relayd_sock.type, ctx, sock, consumer_sockpoll,
- &msg.u.relayd_sock.sock, msg.u.relayd_sock.session_id);
+ &msg.u.relayd_sock.sock, msg.u.relayd_sock.session_id,
+ msg.u.relayd_sock.relayd_session_id);
goto end_nosignal;
}
case LTTNG_CONSUMER_DESTROY_RELAYD:
msg.u.ask_channel.tracefile_size,
msg.u.ask_channel.tracefile_count,
msg.u.ask_channel.session_id_per_pid,
- msg.u.ask_channel.monitor);
+ msg.u.ask_channel.monitor,
+ msg.u.ask_channel.live_timer_interval);
if (!channel) {
goto end_channel_error;
}
+ /*
+ * Assign UST application UID to the channel. This value is ignored for
+ * per PID buffers. This is specific to UST thus setting this after the
+ * allocation.
+ */
+ channel->ust_app_uid = msg.u.ask_channel.ust_app_uid;
+
/* Build channel attributes from received message. */
attr.subbuf_size = msg.u.ask_channel.subbuf_size;
attr.num_subbuf = msg.u.ask_channel.num_subbuf;
goto error_fatal;
};
+ health_code_update();
+
ret = ask_channel(ctx, sock, channel, &attr);
if (ret < 0) {
goto end_channel_error;
}
consumer_timer_switch_start(channel, attr.switch_timer_interval);
attr.switch_timer_interval = 0;
+ } else {
+ consumer_timer_live_start(channel,
+ msg.u.ask_channel.live_timer_interval);
}
+ health_code_update();
+
/*
* Add the channel to the internal state AFTER all streams were created
* and successfully sent to session daemon. This way, all streams must
}
consumer_metadata_cache_destroy(channel);
}
+ if (channel->live_timer_enabled == 1) {
+ consumer_timer_live_stop(channel);
+ }
goto end_channel_error;
}
+ health_code_update();
+
/*
* Channel and streams are now created. Inform the session daemon that
* everything went well and should wait to receive the channel and
goto end_msg_sessiond;
}
+ health_code_update();
+
/* Send everything to sessiond. */
ret = send_sessiond_channel(sock, channel, ctx, &relayd_err);
if (ret < 0) {
goto error_fatal;
}
+ health_code_update();
+
/*
* In no monitor mode, the streams ownership is kept inside the channel
* so don't send them to the data thread.
goto end_msg_sessiond;
}
+ health_code_update();
+
/* Tell session daemon we are ready to receive the metadata. */
ret = consumer_send_status_msg(sock, LTTNG_OK);
if (ret < 0) {
goto error_fatal;
}
+ health_code_update();
+
/* Wait for more data. */
- if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) {
+ health_poll_entry();
+ ret = lttng_consumer_poll_socket(consumer_sockpoll);
+ health_poll_exit();
+ if (ret < 0) {
goto error_fatal;
}
+ health_code_update();
+
ret = lttng_ustconsumer_recv_metadata(sock, key, offset,
- len, channel);
+ len, channel, 0, 1);
if (ret < 0) {
/* error receiving from sessiond */
goto error_fatal;
ret = snapshot_channel(msg.u.snapshot_channel.key,
msg.u.snapshot_channel.pathname,
msg.u.snapshot_channel.relayd_id,
+ msg.u.snapshot_channel.max_stream_size,
ctx);
if (ret < 0) {
ERR("Snapshot channel failed");
}
}
+ health_code_update();
ret = consumer_send_status_msg(sock, ret_code);
if (ret < 0) {
/* Somehow, the session daemon is not responding anymore. */
goto end_nosignal;
}
+ health_code_update();
break;
}
default:
end_nosignal:
rcu_read_unlock();
+ health_code_update();
+
/*
* Return 1 to indicate success since the 0 value can be a socket
* shutdown during the recv() or send() call.
goto error_fatal;
}
rcu_read_unlock();
+
+ health_code_update();
+
return 1;
end_channel_error:
if (channel) {
goto error_fatal;
}
rcu_read_unlock();
+
+ health_code_update();
+
return 1;
error_fatal:
rcu_read_unlock();
return ustctl_snapshot_get_consumed(stream->ustream, pos);
}
+void lttng_ustconsumer_flush_buffer(struct lttng_consumer_stream *stream,
+ int producer)
+{
+ assert(stream);
+ assert(stream->ustream);
+
+ ustctl_flush_buffer(stream->ustream, producer);
+}
+
+int lttng_ustconsumer_get_current_timestamp(
+ struct lttng_consumer_stream *stream, uint64_t *ts)
+{
+ assert(stream);
+ assert(stream->ustream);
+ assert(ts);
+
+ return ustctl_get_current_timestamp(stream->ustream, ts);
+}
+
/*
* Called when the stream signal the consumer that it has hang up.
*/
ustctl_destroy_stream(stream->ustream);
}
+/*
+ * Populate index values of a UST stream. Values are set in big endian order.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int get_index_values(struct lttng_packet_index *index,
+ struct ustctl_consumer_stream *ustream)
+{
+ int ret;
+
+ ret = ustctl_get_timestamp_begin(ustream, &index->timestamp_begin);
+ if (ret < 0) {
+ PERROR("ustctl_get_timestamp_begin");
+ goto error;
+ }
+ index->timestamp_begin = htobe64(index->timestamp_begin);
+
+ ret = ustctl_get_timestamp_end(ustream, &index->timestamp_end);
+ if (ret < 0) {
+ PERROR("ustctl_get_timestamp_end");
+ goto error;
+ }
+ index->timestamp_end = htobe64(index->timestamp_end);
+
+ ret = ustctl_get_events_discarded(ustream, &index->events_discarded);
+ if (ret < 0) {
+ PERROR("ustctl_get_events_discarded");
+ goto error;
+ }
+ index->events_discarded = htobe64(index->events_discarded);
+
+ ret = ustctl_get_content_size(ustream, &index->content_size);
+ if (ret < 0) {
+ PERROR("ustctl_get_content_size");
+ goto error;
+ }
+ index->content_size = htobe64(index->content_size);
+
+ ret = ustctl_get_packet_size(ustream, &index->packet_size);
+ if (ret < 0) {
+ PERROR("ustctl_get_packet_size");
+ goto error;
+ }
+ index->packet_size = htobe64(index->packet_size);
+
+ ret = ustctl_get_stream_id(ustream, &index->stream_id);
+ if (ret < 0) {
+ PERROR("ustctl_get_stream_id");
+ goto error;
+ }
+ index->stream_id = htobe64(index->stream_id);
+
+error:
+ return ret;
+}
+
+/*
+ * Write up to one packet from the metadata cache to the channel.
+ *
+ * Returns the number of bytes pushed in the cache, or a negative value
+ * on error.
+ */
+static
+int commit_one_metadata_packet(struct lttng_consumer_stream *stream)
+{
+ ssize_t write_len;
+ int ret;
+
+ pthread_mutex_lock(&stream->chan->metadata_cache->lock);
+ if (stream->chan->metadata_cache->contiguous
+ == stream->ust_metadata_pushed) {
+ ret = 0;
+ goto end;
+ }
+
+ write_len = ustctl_write_one_packet_to_channel(stream->chan->uchan,
+ &stream->chan->metadata_cache->data[stream->ust_metadata_pushed],
+ stream->chan->metadata_cache->contiguous
+ - stream->ust_metadata_pushed);
+ assert(write_len != 0);
+ if (write_len < 0) {
+ ERR("Writing one metadata packet");
+ ret = -1;
+ goto end;
+ }
+ stream->ust_metadata_pushed += write_len;
+
+ assert(stream->chan->metadata_cache->contiguous >=
+ stream->ust_metadata_pushed);
+ ret = write_len;
+
+end:
+ pthread_mutex_unlock(&stream->chan->metadata_cache->lock);
+ return ret;
+}
+
+
+/*
+ * Sync metadata meaning request them to the session daemon and snapshot to the
+ * metadata thread can consumer them.
+ *
+ * Metadata stream lock MUST be acquired.
+ *
+ * Return 0 if new metadatda is available, EAGAIN if the metadata stream
+ * is empty or a negative value on error.
+ */
+int lttng_ustconsumer_sync_metadata(struct lttng_consumer_local_data *ctx,
+ struct lttng_consumer_stream *metadata)
+{
+ int ret;
+ int retry = 0;
+
+ assert(ctx);
+ assert(metadata);
+
+ /*
+ * Request metadata from the sessiond, but don't wait for the flush
+ * because we locked the metadata thread.
+ */
+ ret = lttng_ustconsumer_request_metadata(ctx, metadata->chan, 0, 0);
+ if (ret < 0) {
+ goto end;
+ }
+
+ ret = commit_one_metadata_packet(metadata);
+ if (ret <= 0) {
+ goto end;
+ } else if (ret > 0) {
+ retry = 1;
+ }
+
+ ustctl_flush_buffer(metadata->ustream, 1);
+ ret = ustctl_snapshot(metadata->ustream);
+ if (ret < 0) {
+ if (errno != EAGAIN) {
+ ERR("Sync metadata, taking UST snapshot");
+ goto end;
+ }
+ DBG("No new metadata when syncing them.");
+ /* No new metadata, exit. */
+ ret = ENODATA;
+ goto end;
+ }
+
+ /*
+ * After this flush, we still need to extract metadata.
+ */
+ if (retry) {
+ ret = EAGAIN;
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * Read subbuffer from the given stream.
+ *
+ * Stream lock MUST be acquired.
+ *
+ * Return 0 on success else a negative value.
+ */
int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
struct lttng_consumer_local_data *ctx)
{
unsigned long len, subbuf_size, padding;
- int err;
+ int err, write_index = 1;
long ret = 0;
char dummy;
struct ustctl_consumer_stream *ustream;
+ struct lttng_packet_index index;
assert(stream);
assert(stream->ustream);
assert(ctx);
- DBG2("In UST read_subbuffer (wait_fd: %d, name: %s)", stream->wait_fd,
+ DBG("In UST read_subbuffer (wait_fd: %d, name: %s)", stream->wait_fd,
stream->name);
/* Ease our life for what's next. */
ustream = stream->ustream;
/* We can consume the 1 byte written into the wait_fd by UST */
- if (!stream->hangup_flush_done) {
+ if (stream->monitor && !stream->hangup_flush_done) {
ssize_t readlen;
do {
readlen = read(stream->wait_fd, &dummy, 1);
} while (readlen == -1 && errno == EINTR);
- if (readlen == -1) {
+ if (readlen == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
ret = readlen;
goto end;
}
}
+retry:
/* Get the next subbuffer */
err = ustctl_get_next_subbuf(ustream);
if (err != 0) {
+ /*
+ * Populate metadata info if the existing info has
+ * already been read.
+ */
+ if (stream->metadata_flag) {
+ ret = commit_one_metadata_packet(stream);
+ if (ret <= 0) {
+ goto end;
+ }
+ ustctl_flush_buffer(stream->ustream, 1);
+ goto retry;
+ }
+
ret = err; /* ustctl_get_next_subbuf returns negative, caller expect positive. */
/*
* This is a debug message even for single-threaded consumer,
goto end;
}
assert(stream->chan->output == CONSUMER_CHANNEL_MMAP);
+
+ if (!stream->metadata_flag) {
+ index.offset = htobe64(stream->out_fd_offset);
+ ret = get_index_values(&index, ustream);
+ if (ret < 0) {
+ goto end;
+ }
+ } else {
+ write_index = 0;
+ }
+
/* Get the full padded subbuffer size */
err = ustctl_get_padded_subbuf_size(ustream, &len);
assert(err == 0);
padding = len - subbuf_size;
/* write the subbuffer to the tracefile */
- ret = lttng_consumer_on_read_subbuffer_mmap(ctx, stream, subbuf_size, padding);
+ ret = lttng_consumer_on_read_subbuffer_mmap(ctx, stream, subbuf_size, padding, &index);
/*
* The mmap operation should write subbuf_size amount of data when network
* streaming or the full padding (len) size when we are _not_ streaming.
DBG("Error writing to tracefile "
"(ret: %ld != len: %lu != subbuf_size: %lu)",
ret, len, subbuf_size);
+ write_index = 0;
}
err = ustctl_put_next_subbuf(ustream);
assert(err == 0);
+ /* Write index if needed. */
+ if (!write_index) {
+ goto end;
+ }
+
+ if (stream->chan->live_timer_interval && !stream->metadata_flag) {
+ /*
+ * In live, block until all the metadata is sent.
+ */
+ err = consumer_stream_sync_metadata(ctx, stream->session_id);
+ if (err < 0) {
+ goto end;
+ }
+ }
+
+ assert(!stream->metadata_flag);
+ err = consumer_stream_write_index(stream, &index);
+ if (err < 0) {
+ goto end;
+ }
+
end:
return ret;
}
if (stream->net_seq_idx == (uint64_t) -1ULL && stream->chan->monitor) {
ret = utils_create_stream_file(stream->chan->pathname, stream->name,
stream->chan->tracefile_size, stream->tracefile_count_current,
- stream->uid, stream->gid);
+ stream->uid, stream->gid, NULL);
if (ret < 0) {
goto error;
}
stream->out_fd = ret;
stream->tracefile_size_current = 0;
+
+ if (!stream->metadata_flag) {
+ ret = index_create_file(stream->chan->pathname,
+ stream->name, stream->uid, stream->gid,
+ stream->chan->tracefile_size,
+ stream->tracefile_count_current);
+ if (ret < 0) {
+ goto error;
+ }
+ stream->index_fd = ret;
+ }
}
ret = 0;
DBG("UST consumer checking data pending");
- ret = ustctl_get_next_subbuf(stream->ustream);
- if (ret == 0) {
- /* There is still data so let's put back this subbuffer. */
- ret = ustctl_put_subbuf(stream->ustream);
- assert(ret == 0);
- ret = 1; /* Data is pending */
+ if (stream->endpoint_status != CONSUMER_ENDPOINT_ACTIVE) {
+ ret = 0;
goto end;
}
+ if (stream->chan->type == CONSUMER_CHANNEL_TYPE_METADATA) {
+ uint64_t contiguous, pushed;
+
+ /* Ease our life a bit. */
+ contiguous = stream->chan->metadata_cache->contiguous;
+ pushed = stream->ust_metadata_pushed;
+
+ /*
+ * We can simply check whether all contiguously available data
+ * has been pushed to the ring buffer, since the push operation
+ * is performed within get_next_subbuf(), and because both
+ * get_next_subbuf() and put_next_subbuf() are issued atomically
+ * thanks to the stream lock within
+ * lttng_ustconsumer_read_subbuffer(). This basically means that
+ * whetnever ust_metadata_pushed is incremented, the associated
+ * metadata has been consumed from the metadata stream.
+ */
+ DBG("UST consumer metadata pending check: contiguous %" PRIu64 " vs pushed %" PRIu64,
+ contiguous, pushed);
+ assert(((int64_t) contiguous - pushed) >= 0);
+ if ((contiguous != pushed) ||
+ (((int64_t) contiguous - pushed) > 0 || contiguous == 0)) {
+ ret = 1; /* Data is pending */
+ goto end;
+ }
+ } else {
+ ret = ustctl_get_next_subbuf(stream->ustream);
+ if (ret == 0) {
+ /*
+ * There is still data so let's put back this
+ * subbuffer.
+ */
+ ret = ustctl_put_subbuf(stream->ustream);
+ assert(ret == 0);
+ ret = 1; /* Data is pending */
+ goto end;
+ }
+ }
+
/* Data is NOT pending so ready to be read. */
ret = 0;
*/
void lttng_ustconsumer_close_metadata(struct lttng_ht *metadata_ht)
{
- int ret;
struct lttng_ht_iter iter;
struct lttng_consumer_stream *stream;
rcu_read_lock();
cds_lfht_for_each_entry(metadata_ht->ht, &iter.iter, stream,
node.node) {
- int fd = stream->wait_fd;
+ health_code_update();
+
+ pthread_mutex_lock(&stream->chan->lock);
/*
- * Whatever happens here we have to continue to try to close every
- * streams. Let's report at least the error on failure.
+ * Whatever returned value, we must continue to try to close everything
+ * so ignore it.
*/
- ret = ustctl_stream_close_wakeup_fd(stream->ustream);
- if (ret) {
- ERR("Unable to close metadata stream fd %d ret %d", fd, ret);
- }
- DBG("Metadata wait fd %d closed", fd);
+ (void) _close_metadata(stream->chan);
+ DBG("Metadata wait fd %d and poll pipe fd %d closed", stream->wait_fd,
+ stream->ust_metadata_poll_pipe[1]);
+ pthread_mutex_unlock(&stream->chan->lock);
+
}
rcu_read_unlock();
}
}
}
+/*
+ * Please refer to consumer-timer.c before adding any lock within this
+ * function or any of its callees. Timers have a very strict locking
+ * semantic with respect to teardown. Failure to respect this semantic
+ * introduces deadlocks.
+ */
int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
- struct lttng_consumer_channel *channel)
+ struct lttng_consumer_channel *channel, int timer, int wait)
{
struct lttcomm_metadata_request_msg request;
struct lttcomm_consumer_msg msg;
request.session_id = channel->session_id;
request.session_id_per_pid = channel->session_id_per_pid;
- request.uid = channel->uid;
+ /*
+ * Request the application UID here so the metadata of that application can
+ * be sent back. The channel UID corresponds to the user UID of the session
+ * used for the rights on the stream file(s).
+ */
+ request.uid = channel->ust_app_uid;
request.key = channel->key;
+
DBG("Sending metadata request to sessiond, session id %" PRIu64
- ", per-pid %" PRIu64,
- channel->session_id,
- channel->session_id_per_pid);
+ ", per-pid %" PRIu64 ", app UID %u and channek key %" PRIu64,
+ request.session_id, request.session_id_per_pid, request.uid,
+ request.key);
+
+ pthread_mutex_lock(&ctx->metadata_socket_lock);
+
+ health_code_update();
ret = lttcomm_send_unix_sock(ctx->consumer_metadata_socket, &request,
sizeof(request));
goto end;
}
+ health_code_update();
+
/* Receive the metadata from sessiond */
ret = lttcomm_recv_unix_sock(ctx->consumer_metadata_socket, &msg,
sizeof(msg));
goto end;
}
+ health_code_update();
+
if (msg.cmd_type == LTTNG_ERR_UND) {
/* No registry found */
(void) consumer_send_status_msg(ctx->consumer_metadata_socket,
DBG("No new metadata to receive for key %" PRIu64, key);
}
+ health_code_update();
+
/* Tell session daemon we are ready to receive the metadata. */
ret = consumer_send_status_msg(ctx->consumer_metadata_socket,
LTTNG_OK);
goto end;
}
+ health_code_update();
+
ret_code = lttng_ustconsumer_recv_metadata(ctx->consumer_metadata_socket,
- key, offset, len, channel);
+ key, offset, len, channel, timer, wait);
if (ret_code >= 0) {
/*
* Only send the status msg if the sessiond is alive meaning a positive
ret = 0;
end:
+ health_code_update();
+
+ pthread_mutex_unlock(&ctx->metadata_socket_lock);
return ret;
}
void lttng_ustconsumer_close_metadata(struct lttng_ht *ht);
void lttng_ustconsumer_close_stream_wakeup(struct lttng_consumer_stream *stream);
int lttng_ustconsumer_recv_metadata(int sock, uint64_t key, uint64_t offset,
- uint64_t len, struct lttng_consumer_channel *channel);
-int lttng_ustconsumer_push_metadata(struct lttng_consumer_channel *metadata,
- const char *metadata_str, uint64_t target_offset, uint64_t len);
+ uint64_t len, struct lttng_consumer_channel *channel,
+ int timer, int wait);
int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
- struct lttng_consumer_channel *channel);
+ struct lttng_consumer_channel *channel, int timer, int wait);
+int lttng_ustconsumer_sync_metadata(struct lttng_consumer_local_data *ctx,
+ struct lttng_consumer_stream *metadata);
+void lttng_ustconsumer_flush_buffer(struct lttng_consumer_stream *stream,
+ int producer);
+int lttng_ustconsumer_get_current_timestamp(
+ struct lttng_consumer_stream *stream, uint64_t *ts);
#else /* HAVE_LIBLTTNG_UST_CTL */
}
static inline
int lttng_ustconsumer_recv_metadata(int sock, uint64_t key, uint64_t offset,
- uint64_t len, struct lttng_consumer_channel *channel)
+ uint64_t len, struct lttng_consumer_channel *channel,
+ int timer)
{
return -ENOSYS;
}
static inline
-int lttng_ustconsumer_push_metadata(struct lttng_consumer_channel *metadata,
- const char *metadata_str, uint64_t target_offset, uint64_t len)
+int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
+ struct lttng_consumer_channel *channel, int timer, int wait)
{
return -ENOSYS;
}
static inline
-int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
- struct lttng_consumer_channel *channel)
+int lttng_ustconsumer_sync_metadata(struct lttng_consumer_local_data *ctx,
+ struct lttng_consumer_stream *metadata)
+{
+ return -ENOSYS;
+}
+static inline
+void lttng_ustconsumer_flush_buffer(struct lttng_consumer_stream *stream,
+ int producer)
+{
+}
+static inline
+int lttng_ustconsumer_get_current_timestamp(
+ struct lttng_consumer_stream *stream, uint64_t *ts)
{
return -ENOSYS;
}
#include <unistd.h>
#include <inttypes.h>
#include <regex.h>
+#include <grp.h>
#include <common/common.h>
#include <common/runas.h>
return ret;
}
+/*
+ * Create pipe and set fd flags to FD_CLOEXEC and O_NONBLOCK.
+ *
+ * Make sure the pipe opened by this function are closed at some point. Use
+ * utils_close_pipe(). Using pipe() and fcntl rather than pipe2() to
+ * support OSes other than Linux 2.6.23+.
+ */
+LTTNG_HIDDEN
+int utils_create_pipe_cloexec_nonblock(int *dst)
+{
+ int ret, i;
+
+ if (dst == NULL) {
+ return -1;
+ }
+
+ ret = utils_create_pipe(dst);
+ if (ret < 0) {
+ goto error;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
+ if (ret < 0) {
+ PERROR("fcntl pipe cloexec");
+ goto error;
+ }
+ /*
+ * Note: we override any flag that could have been
+ * previously set on the fd.
+ */
+ ret = fcntl(dst[i], F_SETFL, O_NONBLOCK);
+ if (ret < 0) {
+ PERROR("fcntl pipe nonblock");
+ goto error;
+ }
+ }
+
+error:
+ return ret;
+}
+
/*
* Close both read and write side of the pipe.
*/
*/
LTTNG_HIDDEN
int utils_create_stream_file(const char *path_name, char *file_name, uint64_t size,
- uint64_t count, int uid, int gid)
+ uint64_t count, int uid, int gid, char *suffix)
{
int ret, out_fd, flags, mode;
- char full_path[PATH_MAX], *path_name_id = NULL, *path;
+ char full_path[PATH_MAX], *path_name_suffix = NULL, *path;
+ char *extra = NULL;
assert(path_name);
assert(file_name);
goto error;
}
+ /* Setup extra string if suffix or/and a count is needed. */
+ if (size > 0 && suffix) {
+ ret = asprintf(&extra, "_%" PRIu64 "%s", count, suffix);
+ } else if (size > 0) {
+ ret = asprintf(&extra, "_%" PRIu64, count);
+ } else if (suffix) {
+ ret = asprintf(&extra, "%s", suffix);
+ }
+ if (ret < 0) {
+ PERROR("Allocating extra string to name");
+ goto error;
+ }
+
/*
* If we split the trace in multiple files, we have to add the count at the
* end of the tracefile name
*/
- if (size > 0) {
- ret = asprintf(&path_name_id, "%s_%" PRIu64, full_path, count);
+ if (extra) {
+ ret = asprintf(&path_name_suffix, "%s%s", full_path, extra);
if (ret < 0) {
- PERROR("Allocating path name ID");
- goto error;
+ PERROR("Allocating path name with extra string");
+ goto error_free_suffix;
}
- path = path_name_id;
+ path = path_name_suffix;
} else {
path = full_path;
}
ret = out_fd;
error_open:
- free(path_name_id);
+ free(path_name_suffix);
+error_free_suffix:
+ free(extra);
error:
return ret;
}
*/
LTTNG_HIDDEN
int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
- uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count)
+ uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count,
+ int *stream_fd)
{
int ret;
+ assert(new_count);
+ assert(stream_fd);
+
ret = close(out_fd);
if (ret < 0) {
PERROR("Closing tracefile");
(*new_count)++;
}
- return utils_create_stream_file(path_name, file_name, size, *new_count,
- uid, gid);
+ ret = utils_create_stream_file(path_name, file_name, size, *new_count,
+ uid, gid, 0);
+ if (ret < 0) {
+ goto error;
+ }
+ *stream_fd = ret;
+
+ /* Success. */
+ ret = 0;
+
error:
return ret;
}
return ret;
}
+
+/*
+ * Return the group ID matching name, else 0 if it cannot be found.
+ */
+LTTNG_HIDDEN
+gid_t utils_get_group_id(const char *name)
+{
+ struct group *grp;
+
+ grp = getgrnam(name);
+ if (!grp) {
+ static volatile int warn_once;
+
+ if (!warn_once) {
+ WARN("No tracing group detected");
+ warn_once = 1;
+ }
+ return 0;
+ }
+ return grp->gr_gid;
+}
char *utils_expand_path(const char *path);
int utils_create_pipe(int *dst);
int utils_create_pipe_cloexec(int *dst);
+int utils_create_pipe_cloexec_nonblock(int *dst);
void utils_close_pipe(int *src);
char *utils_strdupdelim(const char *begin, const char *end);
int utils_set_fd_cloexec(int fd);
int utils_create_pid_file(pid_t pid, const char *filepath);
int utils_mkdir_recursive(const char *path, mode_t mode);
int utils_create_stream_file(const char *path_name, char *file_name, uint64_t size,
- uint64_t count, int uid, int gid);
+ uint64_t count, int uid, int gid, char *suffix);
int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
- uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count);
+ uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count,
+ int *stream_fd);
int utils_parse_size_suffix(char *str, uint64_t *size);
int utils_get_count_order_u32(uint32_t x);
char *utils_get_home_dir(void);
size_t utils_get_current_time_str(const char *format, char *dst, size_t len);
+gid_t utils_get_group_id(const char *name);
#endif /* _COMMON_UTILS_H */
lib_LTLIBRARIES = liblttng-ctl.la
-liblttng_ctl_la_SOURCES = lttng-ctl.c snapshot.c
+liblttng_ctl_la_SOURCES = lttng-ctl.c snapshot.c lttng-ctl-helper.h \
+ lttng-ctl-health.c
liblttng_ctl_la_LIBADD = \
$(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \
// data is a pointer to a 'SParserParam' structure
//#define YYPARSE_PARAM parser_ctx
-// the argument for the 'yylex' function
-#define YYLEX_PARAM ((struct filter_parser_ctx *) parser_ctx)->scanner
-
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
int filter_parser_debug = 0;
LTTNG_HIDDEN
-int yyparse(struct filter_parser_ctx *parser_ctx);
+int yyparse(struct filter_parser_ctx *parser_ctx, yyscan_t scanner);
LTTNG_HIDDEN
-int yylex(union YYSTYPE *yyval, struct filter_parser_ctx *parser_ctx);
+int yylex(union YYSTYPE *yyval, yyscan_t scanner);
LTTNG_HIDDEN
int yylex_init_extra(struct filter_parser_ctx *parser_ctx, yyscan_t * ptr_yy_globals);
LTTNG_HIDDEN
}
LTTNG_HIDDEN
-void yyerror(struct filter_parser_ctx *parser_ctx, const char *str)
+void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str)
{
fprintf(stderr, "error %s\n", str);
}
#define parse_error(parser_ctx, str) \
do { \
- yyerror(parser_ctx, YY_("parse error: " str "\n")); \
+ yyerror(parser_ctx, parser_ctx->scanner, YY_("parse error: " str "\n")); \
YYERROR; \
} while (0)
LTTNG_HIDDEN
int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx)
{
- return yyparse(parser_ctx);
+ return yyparse(parser_ctx, parser_ctx->scanner);
}
LTTNG_HIDDEN
%define api.pure
/* %locations */
%parse-param {struct filter_parser_ctx *parser_ctx}
-%lex-param {struct filter_parser_ctx *parser_ctx}
+%parse-param {yyscan_t scanner}
+%lex-param {yyscan_t scanner}
%start translation_unit
%token CHARACTER_CONSTANT_START SQUOTE STRING_LITERAL_START DQUOTE
%token ESCSEQ CHAR_STRING_TOKEN
--- /dev/null
+/*
+ * lttng-ctl-health.c
+ *
+ * Linux Trace Toolkit Health Control Library
+ *
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 <unistd.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <limits.h>
+#include <errno.h>
+#include <lttng/health-internal.h>
+
+#include <bin/lttng-sessiond/health-sessiond.h>
+#include <bin/lttng-consumerd/health-consumerd.h>
+#include <bin/lttng-relayd/health-relayd.h>
+#include <common/defaults.h>
+#include <common/utils.h>
+
+#include "lttng-ctl-helper.h"
+
+enum health_component {
+ HEALTH_COMPONENT_SESSIOND,
+ HEALTH_COMPONENT_CONSUMERD,
+ HEALTH_COMPONENT_RELAYD,
+
+ NR_HEALTH_COMPONENT,
+};
+
+struct lttng_health_thread {
+ struct lttng_health *p;
+ int state;
+};
+
+struct lttng_health {
+ enum health_component component;
+ uint64_t state;
+ unsigned int nr_threads;
+ char health_sock_path[PATH_MAX];
+ /* For consumer health only */
+ enum lttng_health_consumerd consumerd_type;
+ struct lttng_health_thread thread[];
+};
+
+static
+const char *sessiond_thread_name[NR_HEALTH_SESSIOND_TYPES] = {
+ [ HEALTH_SESSIOND_TYPE_CMD ] = "Session daemon command",
+ [ HEALTH_SESSIOND_TYPE_APP_MANAGE ] = "Session daemon application manager",
+ [ HEALTH_SESSIOND_TYPE_APP_REG ] = "Session daemon application registration",
+ [ HEALTH_SESSIOND_TYPE_KERNEL ] = "Session daemon kernel",
+ [ HEALTH_SESSIOND_TYPE_CONSUMER ] = "Session daemon consumer manager",
+ [ HEALTH_SESSIOND_TYPE_HT_CLEANUP ] = "Session daemon hash table cleanup",
+ [ HEALTH_SESSIOND_TYPE_APP_MANAGE_NOTIFY ] = "Session daemon application notification manager",
+ [ HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH ] = "Session daemon application registration dispatcher",
+};
+
+static
+const char *consumerd_thread_name[NR_HEALTH_CONSUMERD_TYPES] = {
+ [ HEALTH_CONSUMERD_TYPE_CHANNEL ] = "Consumer daemon channel",
+ [ HEALTH_CONSUMERD_TYPE_METADATA ] = "Consumer daemon metadata",
+ [ HEALTH_CONSUMERD_TYPE_DATA ] = "Consumer daemon data",
+ [ HEALTH_CONSUMERD_TYPE_SESSIOND ] = "Consumer daemon session daemon command manager",
+ [ HEALTH_CONSUMERD_TYPE_METADATA_TIMER ] = "Consumer daemon metadata timer",
+};
+
+static
+const char *relayd_thread_name[NR_HEALTH_RELAYD_TYPES] = {
+ [ HEALTH_RELAYD_TYPE_DISPATCHER ] = "Relay daemon dispatcher",
+ [ HEALTH_RELAYD_TYPE_WORKER ] = "Relay daemon worker",
+ [ HEALTH_RELAYD_TYPE_LISTENER ] = "Relay daemon listener",
+ [ HEALTH_RELAYD_TYPE_LIVE_DISPATCHER ] = "Relay daemon live dispatcher",
+ [ HEALTH_RELAYD_TYPE_LIVE_WORKER ] = "Relay daemon live worker",
+ [ HEALTH_RELAYD_TYPE_LIVE_LISTENER ] = "Relay daemon live listener",
+};
+
+static
+const char **thread_name[NR_HEALTH_COMPONENT] = {
+ [ HEALTH_COMPONENT_SESSIOND ] = sessiond_thread_name,
+ [ HEALTH_COMPONENT_CONSUMERD] = consumerd_thread_name,
+ [ HEALTH_COMPONENT_RELAYD ] = relayd_thread_name,
+};
+
+/*
+ * Set health socket path.
+ *
+ * Returns 0 on success or -ENOMEM.
+ */
+static
+int set_health_socket_path(struct lttng_health *lh,
+ int tracing_group)
+{
+ uid_t uid;
+ const char *home;
+ int ret;
+ /* Global and home format strings */
+ const char *global_str, *home_str;
+
+ switch (lh->component) {
+ case HEALTH_COMPONENT_SESSIOND:
+ global_str = DEFAULT_GLOBAL_HEALTH_UNIX_SOCK;
+ home_str = DEFAULT_HOME_HEALTH_UNIX_SOCK;
+ break;
+ case HEALTH_COMPONENT_CONSUMERD:
+ switch (lh->consumerd_type) {
+ case LTTNG_HEALTH_CONSUMERD_UST_32:
+ global_str = DEFAULT_GLOBAL_USTCONSUMER32_HEALTH_UNIX_SOCK;
+ home_str = DEFAULT_HOME_USTCONSUMER32_HEALTH_UNIX_SOCK;
+ break;
+ case LTTNG_HEALTH_CONSUMERD_UST_64:
+ global_str = DEFAULT_GLOBAL_USTCONSUMER64_HEALTH_UNIX_SOCK;
+ home_str = DEFAULT_HOME_USTCONSUMER64_HEALTH_UNIX_SOCK;
+ break;
+ case LTTNG_HEALTH_CONSUMERD_KERNEL:
+ global_str = DEFAULT_GLOBAL_KCONSUMER_HEALTH_UNIX_SOCK;
+ home_str = DEFAULT_HOME_KCONSUMER_HEALTH_UNIX_SOCK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case HEALTH_COMPONENT_RELAYD:
+ if (lh->health_sock_path[0] == '\0') {
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+ break; /* Unreached */
+ default:
+ return -EINVAL;
+ }
+
+ uid = getuid();
+
+ if (uid == 0 || tracing_group) {
+ lttng_ctl_copy_string(lh->health_sock_path,
+ global_str,
+ sizeof(lh->health_sock_path));
+ return 0;
+ }
+
+ /*
+ * With GNU C < 2.1, snprintf returns -1 if the target buffer
+ * is too small; With GNU C >= 2.1, snprintf returns the
+ * required size (excluding closing null).
+ */
+ home = utils_get_home_dir();
+ if (home == NULL) {
+ /* Fallback in /tmp */
+ home = "/tmp";
+ }
+
+ ret = snprintf(lh->health_sock_path, sizeof(lh->health_sock_path),
+ home_str, home);
+ if ((ret < 0) || (ret >= sizeof(lh->health_sock_path))) {
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static
+struct lttng_health *lttng_health_create(enum health_component hc,
+ unsigned int nr_threads)
+{
+ struct lttng_health *lh;
+ int i;
+
+ lh = zmalloc(sizeof(*lh) + sizeof(lh->thread[0]) * nr_threads);
+ if (!lh) {
+ return NULL;
+ }
+
+ lh->component = hc;
+ lh->state = UINT64_MAX; /* All bits in error initially */
+ lh->nr_threads = nr_threads;
+ for (i = 0; i < nr_threads; i++) {
+ lh->thread[i].p = lh;
+ }
+ return lh;
+}
+
+struct lttng_health *lttng_health_create_sessiond(void)
+{
+ struct lttng_health *lh;
+
+ lh = lttng_health_create(HEALTH_COMPONENT_SESSIOND,
+ NR_HEALTH_SESSIOND_TYPES);
+ if (!lh) {
+ return NULL;
+ }
+ return lh;
+}
+
+struct lttng_health *
+ lttng_health_create_consumerd(enum lttng_health_consumerd consumerd)
+{
+ struct lttng_health *lh;
+
+ lh = lttng_health_create(HEALTH_COMPONENT_CONSUMERD,
+ NR_HEALTH_CONSUMERD_TYPES);
+ if (!lh) {
+ return NULL;
+ }
+ lh->consumerd_type = consumerd;
+ return lh;
+}
+
+struct lttng_health *lttng_health_create_relayd(const char *path)
+{
+ struct lttng_health *lh;
+
+ if (!path) {
+ return NULL;
+ }
+
+ lh = lttng_health_create(HEALTH_COMPONENT_RELAYD,
+ NR_HEALTH_RELAYD_TYPES);
+ if (!lh) {
+ return NULL;
+ }
+ lttng_ctl_copy_string(lh->health_sock_path, path,
+ sizeof(lh->health_sock_path));
+ return lh;
+}
+
+void lttng_health_destroy(struct lttng_health *lh)
+{
+ free(lh);
+}
+
+int lttng_health_query(struct lttng_health *health)
+{
+ int sock, ret, i, tracing_group;
+ struct health_comm_msg msg;
+ struct health_comm_reply reply;
+
+ if (!health) {
+ return -EINVAL;
+ }
+
+ tracing_group = lttng_check_tracing_group();
+retry:
+ ret = set_health_socket_path(health, tracing_group);
+ if (ret) {
+ goto error;
+ }
+ /* Connect to the sesssion daemon */
+ sock = lttcomm_connect_unix_sock(health->health_sock_path);
+ if (sock < 0) {
+ if (tracing_group) {
+ /* For tracing group, fallback to per-user */
+ tracing_group = 0;
+ goto retry;
+ }
+ ret = -1;
+ goto error;
+ }
+
+ msg.cmd = HEALTH_CMD_CHECK;
+
+ ret = lttcomm_send_unix_sock(sock, (void *)&msg, sizeof(msg));
+ if (ret < 0) {
+ ret = -1;
+ goto close_error;
+ }
+
+ ret = lttcomm_recv_unix_sock(sock, (void *)&reply, sizeof(reply));
+ if (ret < 0) {
+ ret = -1;
+ goto close_error;
+ }
+
+ health->state = reply.ret_code;
+ for (i = 0; i < health->nr_threads; i++) {
+ if (health->state & (1ULL << i)) {
+ health->thread[i].state = -1;
+ } else {
+ health->thread[i].state = 0;
+ }
+ }
+
+close_error:
+ {
+ int closeret;
+
+ closeret = close(sock);
+ assert(!closeret);
+ }
+
+error:
+ if (ret >= 0)
+ ret = 0;
+ return ret;
+}
+
+int lttng_health_state(const struct lttng_health *health)
+{
+ if (!health) {
+ return -EINVAL;
+ }
+
+ if (health->state == 0) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int lttng_health_get_nr_threads(const struct lttng_health *health)
+{
+ if (!health) {
+ return -EINVAL;
+ }
+ return health->nr_threads;
+}
+
+const struct lttng_health_thread *
+ lttng_health_get_thread(const struct lttng_health *health,
+ unsigned int nth_thread)
+{
+ if (!health || nth_thread >= health->nr_threads) {
+ return NULL;
+ }
+ return &health->thread[nth_thread];
+}
+
+int lttng_health_thread_state(const struct lttng_health_thread *thread)
+{
+ if (!thread) {
+ return -EINVAL;
+ }
+ return thread->state;
+}
+
+const char *lttng_health_thread_name(const struct lttng_health_thread *thread)
+{
+ unsigned int nr;
+
+ if (!thread) {
+ return NULL;
+ }
+ nr = thread - &thread->p->thread[0];
+ return thread_name[thread->p->component][nr];
+}
return lttng_ctl_ask_sessiond_varlen(lsm, NULL, 0, buf);
}
+int lttng_check_tracing_group(void);
+
#endif /* LTTNG_CTL_HELPER_H */
#include <common/uri.h>
#include <common/utils.h>
#include <lttng/lttng.h>
+#include <lttng/health-internal.h>
#include "filter/filter-ast.h"
#include "filter/filter-parser.h"
/* Socket to session daemon for communication */
static int sessiond_socket;
static char sessiond_sock_path[PATH_MAX];
-static char health_sock_path[PATH_MAX];
/* Variables */
static char *tracing_group;
switch (src->type) {
case LTTNG_DOMAIN_KERNEL:
case LTTNG_DOMAIN_UST:
+ case LTTNG_DOMAIN_JUL:
memcpy(dst, src, sizeof(struct lttng_domain));
break;
default:
*
* If yes return 1, else return -1.
*/
-static int check_tracing_group(const char *grp_name)
+LTTNG_HIDDEN
+int lttng_check_tracing_group(void)
{
struct group *grp_tracing; /* no free(). See getgrnam(3) */
gid_t *grp_list;
int grp_list_size, grp_id, i;
int ret = -1;
+ const char *grp_name = tracing_group;
/* Get GID of group 'tracing' */
grp_tracing = getgrnam(grp_name);
if (uid != 0) {
/* Are we in the tracing group ? */
- in_tgroup = check_tracing_group(tracing_group);
+ in_tgroup = lttng_check_tracing_group();
}
if ((uid == 0) || in_tgroup) {
}
_MSG("Waiting for data availability");
+ fflush(stdout);
/* Check for data availability */
do {
if (data_ret) {
usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME);
_MSG(".");
+ fflush(stdout);
}
} while (data_ret != 0);
lsm.cmd_type = LTTNG_ADD_CONTEXT;
- /* Copy channel name */
- lttng_ctl_copy_string(lsm.u.context.channel_name, channel_name,
- sizeof(lsm.u.context.channel_name));
+ /* If no channel name, send empty string. */
+ if (channel_name == NULL) {
+ lttng_ctl_copy_string(lsm.u.context.channel_name, "",
+ sizeof(lsm.u.context.channel_name));
+ } else {
+ lttng_ctl_copy_string(lsm.u.context.channel_name, channel_name,
+ sizeof(lsm.u.context.channel_name));
+ }
lttng_ctl_copy_lttng_domain(&lsm.domain, &handle->domain);
memset(&lsm, 0, sizeof(lsm));
- /* If no channel name, we put the default name */
+ /* If no channel name, send empty string. */
if (channel_name == NULL) {
- lttng_ctl_copy_string(lsm.u.enable.channel_name, DEFAULT_CHANNEL_NAME,
+ lttng_ctl_copy_string(lsm.u.enable.channel_name, "",
sizeof(lsm.u.enable.channel_name));
} else {
lttng_ctl_copy_string(lsm.u.enable.channel_name, channel_name,
lsm.cmd_type = LTTNG_ENABLE_EVENT_WITH_FILTER;
- /* Copy channel name */
- lttng_ctl_copy_string(lsm.u.enable.channel_name, channel_name,
- sizeof(lsm.u.enable.channel_name));
+ /* If no channel name, send empty string. */
+ if (channel_name == NULL) {
+ lttng_ctl_copy_string(lsm.u.enable.channel_name, "",
+ sizeof(lsm.u.enable.channel_name));
+ } else {
+ lttng_ctl_copy_string(lsm.u.enable.channel_name, channel_name,
+ sizeof(lsm.u.enable.channel_name));
+ }
+
/* Copy event name */
if (event) {
memcpy(&lsm.u.enable.event, event, sizeof(lsm.u.enable.event));
memset(&lsm, 0, sizeof(lsm));
- if (channel_name) {
- lttng_ctl_copy_string(lsm.u.disable.channel_name, channel_name,
+ /* If no channel name, send empty string. */
+ if (channel_name == NULL) {
+ lttng_ctl_copy_string(lsm.u.disable.channel_name, "",
sizeof(lsm.u.disable.channel_name));
} else {
- lttng_ctl_copy_string(lsm.u.disable.channel_name, DEFAULT_CHANNEL_NAME,
+ lttng_ctl_copy_string(lsm.u.disable.channel_name, channel_name,
sizeof(lsm.u.disable.channel_name));
}
return -ENOSYS;
}
-/*
- * Set health socket path by putting it in the global health_sock_path
- * variable.
- *
- * Returns 0 on success or assert(0) on ENOMEM.
- */
-static int set_health_socket_path(void)
-{
- int in_tgroup = 0; /* In tracing group */
- uid_t uid;
- const char *home;
-
- uid = getuid();
-
- if (uid != 0) {
- /* Are we in the tracing group ? */
- in_tgroup = check_tracing_group(tracing_group);
- }
-
- if ((uid == 0) || in_tgroup) {
- lttng_ctl_copy_string(health_sock_path,
- DEFAULT_GLOBAL_HEALTH_UNIX_SOCK, sizeof(health_sock_path));
- }
-
- if (uid != 0) {
- int ret;
-
- /*
- * With GNU C < 2.1, snprintf returns -1 if the target buffer is too small;
- * With GNU C >= 2.1, snprintf returns the required size (excluding closing null)
- */
- home = utils_get_home_dir();
- if (home == NULL) {
- /* Fallback in /tmp .. */
- home = "/tmp";
- }
-
- ret = snprintf(health_sock_path, sizeof(health_sock_path),
- DEFAULT_HOME_HEALTH_UNIX_SOCK, home);
- if ((ret < 0) || (ret >= sizeof(health_sock_path))) {
- /* ENOMEM at this point... just kill the control lib. */
- assert(0);
- }
- }
-
- return 0;
-}
-
-/*
- * Check session daemon health for a specific health component.
- *
- * Return 0 if health is OK or else 1 if BAD.
- *
- * Any other negative value is a lttng error code which can be translated with
- * lttng_strerror().
- */
-int lttng_health_check(enum lttng_health_component c)
-{
- int sock, ret;
- struct lttcomm_health_msg msg;
- struct lttcomm_health_data reply;
-
- /* Connect to the sesssion daemon */
- sock = lttcomm_connect_unix_sock(health_sock_path);
- if (sock < 0) {
- ret = -LTTNG_ERR_NO_SESSIOND;
- goto error;
- }
-
- msg.cmd = LTTNG_HEALTH_CHECK;
- msg.component = c;
-
- ret = lttcomm_send_unix_sock(sock, (void *)&msg, sizeof(msg));
- if (ret < 0) {
- ret = -LTTNG_ERR_FATAL;
- goto close_error;
- }
-
- ret = lttcomm_recv_unix_sock(sock, (void *)&reply, sizeof(reply));
- if (ret < 0) {
- ret = -LTTNG_ERR_FATAL;
- goto close_error;
- }
-
- ret = reply.ret_code;
-
-close_error:
- {
- int closeret;
-
- closeret = close(sock);
- assert(!closeret);
- }
-
-error:
- return ret;
-}
-
/*
* This is an extension of create session that is ONLY and SHOULD only be used
* by the lttng command line program. It exists to avoid using URI parsing in
return ret;
}
+/*
+ * Create a session exclusively used for snapshot.
+ *
+ * Returns LTTNG_OK on success or a negative error code.
+ */
+int lttng_create_session_snapshot(const char *name, const char *snapshot_url)
+{
+ int ret;
+ ssize_t size;
+ struct lttcomm_session_msg lsm;
+ struct lttng_uri *uris = NULL;
+
+ if (name == NULL) {
+ return -LTTNG_ERR_INVALID;
+ }
+
+ memset(&lsm, 0, sizeof(lsm));
+
+ lsm.cmd_type = LTTNG_CREATE_SESSION_SNAPSHOT;
+ lttng_ctl_copy_string(lsm.session.name, name, sizeof(lsm.session.name));
+
+ size = uri_parse_str_urls(snapshot_url, NULL, &uris);
+ if (size < 0) {
+ return -LTTNG_ERR_INVALID;
+ }
+
+ lsm.u.uri.size = size;
+
+ ret = lttng_ctl_ask_sessiond_varlen(&lsm, uris,
+ sizeof(struct lttng_uri) * size, NULL);
+
+ free(uris);
+ return ret;
+}
+
+/*
+ * Create a session exclusively used for live.
+ *
+ * Returns LTTNG_OK on success or a negative error code.
+ */
+int lttng_create_session_live(const char *name, const char *url,
+ unsigned int timer_interval)
+{
+ int ret;
+ ssize_t size;
+ struct lttcomm_session_msg lsm;
+ struct lttng_uri *uris = NULL;
+
+ if (name == NULL) {
+ return -LTTNG_ERR_INVALID;
+ }
+
+ memset(&lsm, 0, sizeof(lsm));
+
+ lsm.cmd_type = LTTNG_CREATE_SESSION_LIVE;
+ lttng_ctl_copy_string(lsm.session.name, name, sizeof(lsm.session.name));
+
+ size = uri_parse_str_urls(url, NULL, &uris);
+ if (size < 0) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ /* file:// is not accepted for live session. */
+ if (uris[0].dtype == LTTNG_DST_PATH) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ lsm.u.session_live.nb_uri = size;
+ lsm.u.session_live.timer_interval = timer_interval;
+
+ ret = lttng_ctl_ask_sessiond_varlen(&lsm, uris,
+ sizeof(struct lttng_uri) * size, NULL);
+
+end:
+ free(uris);
+ return ret;
+}
+
/*
* lib constructor
*/
{
/* Set default session group */
lttng_set_tracing_group(DEFAULT_TRACING_GROUP);
- /* Set socket for health check */
- (void) set_health_socket_path();
}
/*
*/
struct lttng_snapshot_output *lttng_snapshot_output_create(void)
{
- return zmalloc(sizeof(struct lttng_snapshot_output));
+ struct lttng_snapshot_output *output;
+
+ output = zmalloc(sizeof(struct lttng_snapshot_output));
+ if (!output) {
+ goto error;
+ }
+
+ output->max_size = (uint64_t) -1ULL;
+
+error:
+ return output;
}
/*
SUBDIRS = utils regression unit stress
-if USE_PYTHON
-check-am:
+installcheck-am:
./run.sh unit_tests
./run.sh fast_regression
+if USE_PYTHON
./run.sh with_bindings_regression
-else
+endif
+
check-am:
./run.sh unit_tests
./run.sh fast_regression
+if USE_PYTHON
+ ./run.sh with_bindings_regression
endif
dist_noinst_SCRIPTS = run.sh unit_tests fast_regression long_regression root_regression with_bindings_regression
EXTRA_DIST = run.sh unit_tests fast_regression long_regression root_regression with_bindings_regression
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
regression/tools/filtering/test_invalid_filter
regression/tools/filtering/test_unsupported_op
regression/tools/filtering/test_valid_filter
-regression/tools/health/test_thread_exit
-regression/tools/health/test_thread_stall
-regression/tools/health/test_tp_fail
regression/tools/streaming/test_ust
regression/tools/tracefile-limits/test_tracefile_count
regression/tools/tracefile-limits/test_tracefile_size
+regression/tools/snapshots/test_ust
+regression/tools/snapshots/test_ust_streaming
regression/ust/before-after/test_before_after
-regression/ust/buffers-uid/test_buffers_uid
-regression/ust/periodical-metadata-flush/test_periodical_metadata_flush
+regression/ust/buffers-pid/test_buffers_pid
regression/ust/multi-session/test_multi_session
regression/ust/nprocesses/test_nprocesses
regression/ust/overlap/test_overlap
regression/tools/tracefile-limits/test_tracefile_count
regression/tools/tracefile-limits/test_tracefile_size
regression/ust/before-after/test_before_after
-regression/ust/buffers-uid/test_buffers_uid
-regression/ust/periodical-metadata-flush/test_periodical_metadata_flush
+regression/ust/buffers-pid/test_buffers_pid
regression/ust/high-throughput/test_high_throughput
regression/ust/low-throughput/test_low_throughput
regression/ust/multi-session/test_multi_session
SUBDIRS += ust
endif # HAVE_LIBLTTNG_UST_CTL
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
EXTRA_DIST = test_event_basic test_all_events
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
CURDIR=$(dirname $0)/
TESTDIR=$CURDIR/../..
-NUM_TESTS=12
+NUM_TESTS=20
source $TESTDIR/utils/utils.sh
rm -rf $TRACE_PATH
}
+function test_enable_after_start()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="kernel_enable_after_start"
+
+ create_lttng_session $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_event $SESSION_NAME "sched_switch"
+
+ start_lttng_tracing
+ lttng_enable_kernel_event $SESSION_NAME "sched_process_exit"
+ stop_lttng_tracing
+
+ validate_trace "sched_switch" $TRACE_PATH
+ validate_trace "sched_process_exit" $TRACE_PATH
+
+ destroy_lttng_session $SESSION_NAME
+
+ rm -rf $TRACE_PATH
+}
+
# MUST set TESTDIR before calling those functions
plan_tests $NUM_TESTS
start_lttng_sessiond
test_event_basic
+ test_enable_after_start
stop_lttng_sessiond
}
noinst_SCRIPTS = test_unsupported_op test_invalid_filter test_valid_filter
EXTRA_DIST = test_unsupported_op test_invalid_filter test_valid_filter
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
#if !defined(_TRACEPOINT_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_TP_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
dist_noinst_SCRIPTS = test_thread_exit test_thread_stall test_tp_fail
EXTRA_DIST = test_thread_exit test_thread_stall test_tp_fail
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
*/
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#include "lttng/lttng.h"
+#include <lttng/health.h>
-#define HEALTH_CMD_FAIL (1 << 0)
-#define HEALTH_APP_MNG_FAIL (1 << 1)
-#define HEALTH_APP_REG_FAIL (1 << 2)
-#define HEALTH_KERNEL_FAIL (1 << 3)
-#define HEALTH_CSMR_FAIL (1 << 4)
+static const char *relayd_path;
-int main(int argc, char *argv[])
+static
+int check_component(struct lttng_health *lh, const char *component_name,
+ int ok_if_not_running)
{
- int health = -1;
- int status = 0;
+ const struct lttng_health_thread *thread;
+ int nr_threads, i, status;
- /* Command thread */
- health = lttng_health_check(LTTNG_HEALTH_CMD);
- printf("Health check cmd: %d\n", health);
+ if (lttng_health_query(lh)) {
+ if (ok_if_not_running) {
+ return 0;
+ }
+ fprintf(stderr, "Error querying %s health\n",
+ component_name);
+ return -1;
+ }
+ status = lttng_health_state(lh);
+ if (!status) {
+ return status;
+ }
- if (health) {
- status |= HEALTH_CMD_FAIL;
+ nr_threads = lttng_health_get_nr_threads(lh);
+ if (nr_threads < 0) {
+ fprintf(stderr, "Error getting number of threads\n");
+ return -1;
}
- /* App manage thread */
- health = lttng_health_check(LTTNG_HEALTH_APP_MANAGE);
- printf("Health check app. manage: %d\n", health);
+ printf("Component \"%s\" is in error.\n", component_name);
+ for (i = 0; i < nr_threads; i++) {
+ int thread_state;
+
+ thread = lttng_health_get_thread(lh, i);
+ if (!thread) {
+ fprintf(stderr, "Error getting thread %d\n", i);
+ return -1;
+ }
+ thread_state = lttng_health_thread_state(thread);
+ if (!thread_state) {
+ continue;
+ }
+ printf("Thread \"%s\" is not responding in component \"%s\".\n",
+ lttng_health_thread_name(thread),
+ component_name);
- if (health) {
- status |= HEALTH_APP_MNG_FAIL;
}
- /* App registration thread */
- health = lttng_health_check(LTTNG_HEALTH_APP_REG);
- printf("Health check app. registration: %d\n", health);
+ return status;
+}
+
+static
+int check_sessiond(void)
+{
+ struct lttng_health *lh;
+ int status;
- if (health) {
- status |= HEALTH_APP_REG_FAIL;
+ lh = lttng_health_create_sessiond();
+ if (!lh) {
+ perror("lttng_health_create_sessiond");
+ return -1;
}
- /* Kernel thread */
- health = lttng_health_check(LTTNG_HEALTH_KERNEL);
- printf("Health check kernel: %d\n", health);
+ status = check_component(lh, "sessiond", 0);
- if (health) {
- status |= HEALTH_KERNEL_FAIL;
+ lttng_health_destroy(lh);
+
+ return status;
+}
+
+static
+int check_consumerd(enum lttng_health_consumerd hc)
+{
+ struct lttng_health *lh;
+ int status;
+ static const char *cnames[NR_LTTNG_HEALTH_CONSUMERD] = {
+ "ust-consumerd-32",
+ "ust-consumerd-64",
+ "kernel-consumerd",
+ };
+
+ lh = lttng_health_create_consumerd(hc);
+ if (!lh) {
+ perror("lttng_health_create_consumerd");
+ return -1;
}
- /* Consumer thread */
- health = lttng_health_check(LTTNG_HEALTH_CONSUMER);
- printf("Health check consumer: %d\n", health);
+ status = check_component(lh, cnames[hc], 1);
+
+ lttng_health_destroy(lh);
+
+ return status;
+}
+
+static
+int check_relayd(const char *path)
+{
+ struct lttng_health *lh;
+ int status;
- if (health) {
- status |= HEALTH_CSMR_FAIL;
+ lh = lttng_health_create_relayd(path);
+ if (!lh) {
+ perror("lttng_health_create_relayd");
+ return -1;
}
+ status = check_component(lh, "relayd", 0);
+
+ lttng_health_destroy(lh);
+
return status;
}
+
+int main(int argc, char *argv[])
+{
+ int status = 0, i;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--relayd-path")) {
+ if (i >= argc - 1) {
+ fprintf(stderr, "Missing relayd path\n");
+ exit(EXIT_FAILURE);
+ }
+ relayd_path = argv[++i];
+ } else {
+ fprintf(stderr, "Unknown option \"%s\". Try --relayd-path PATH.\n", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ status |= check_sessiond();
+ for (i = 0; i < NR_LTTNG_HEALTH_CONSUMERD; i++) {
+ status |= check_consumerd(i);
+ }
+ if (relayd_path) {
+ status |= check_relayd(relayd_path);
+ }
+ if (!status) {
+ exit(EXIT_SUCCESS);
+ } else {
+ exit(EXIT_FAILURE);
+ }
+}
function test_thread_exit
{
test_thread_exit_name="$1"
- test_thread_exit_code="$2"
+ test_thread_error_string="$2"
diag "Test health failure with ${test_thread_exit_name}"
# Spawn sessiond with preload healthexit lib
export LD_PRELOAD="$CURDIR/$SESSIOND_PRELOAD"
+ # Set the socket timeout to 5 so the health check delta is set to 25.
+ export LTTNG_NETWORK_SOCKET_TIMEOUT=5
start_lttng_sessiond
# Cleanup some env. var.
unset ${test_thread_exit_name}_EXIT
# Check initial health status
- $CURDIR/$HEALTH_CHECK_BIN &> /dev/null
+ $CURDIR/$HEALTH_CHECK_BIN > /dev/null
# Wait
- sleep 25
+ sleep 30
- # Check health status, exit code should indicate failure
- $CURDIR/$HEALTH_CHECK_BIN &> /dev/null
+ # Check health status
+ $CURDIR/$HEALTH_CHECK_BIN > ${STDOUT_PATH} 2> ${STDERR_PATH}
- health_check_exit_code=$?
-
- if [ $health_check_exit_code -eq $test_thread_exit_code ]; then
- pass "Validate thread ${test_thread_exit_name} failure"
- stop_lttng_sessiond
- else
+ out=$(grep "${test_thread_error_string}" ${STDOUT_PATH} | wc -l)
+ if [ $out -eq 0 ]; then
fail "Validate thread ${test_thread_exit_name} failure"
-
- diag "Health returned: $health_check_exit_code\n"
+ diag "Health returned:"
+ diag "stdout:"
+ file=${STDOUT_PATH}
+ while read line ; do
+ diag "$line"
+ done < ${file}
+
+ diag "stderr:"
+ file=${STDERR_PATH}
+ while read line ; do
+ diag "$line"
+ done < ${file}
stop_lttng_sessiond
return 1
+ else
+ pass "Validate thread ${test_thread_exit_name} failure"
+ stop_lttng_sessiond
fi
}
"LTTNG_THREAD_MANAGE_APPS"
"LTTNG_THREAD_REG_APPS")
-# Exit code value to indicate specific thread failure
-EXIT_CODE=(1 2 4)
+ERROR_STRING=(
+ "Thread \"Session daemon command\" is not responding in component \"sessiond\"."
+ "Thread \"Session daemon application manager\" is not responding in component \"sessiond\"."
+ "Thread \"Session daemon application registration\" is not responding in component \"sessiond\"."
+)
+
+STDOUT_PATH=$(mktemp)
+STDERR_PATH=$(mktemp)
THREAD_COUNT=${#THREAD[@]}
i=0
while [ "$i" -lt "$THREAD_COUNT" ]; do
- test_thread_exit "${THREAD[$i]}" "${EXIT_CODE[$i]}"
+ test_thread_exit "${THREAD[$i]}" "${ERROR_STRING[$i]}"
if [ $? -eq 1 ]; then
exit 1
skip $isroot "Root access is needed. Skipping LTTNG_THREAD_MANAGE_KERNEL tests." "3" ||
{
- test_thread_exit "LTTNG_THREAD_MANAGE_KERNEL" "8"
+ test_thread_exit "LTTNG_THREAD_MANAGE_KERNEL" "Thread \"Session daemon kernel\" is not responding in component \"sessiond\"."
}
+rm -f ${STDOUT_PATH}
+rm -f ${STDERR_PATH}
# TODO: Special case manage consumer, need to spawn consumer via commands.
#"LTTNG_THREAD_MANAGE_CONSUMER"
# Spawn sessiond with preload healthexit lib
export LD_PRELOAD="$CURDIR/$SESSIOND_PRELOAD"
+ # Set the socket timeout to 5 so the health check delta is set to 25.
+ export LTTNG_NETWORK_SOCKET_TIMEOUT=5
start_lttng_sessiond
# Cleanup some env. var.
unset ${test_thread_stall_name}_STALL
# Check initial health status
- $CURDIR/$HEALTH_CHECK_BIN &> /dev/null
+ $CURDIR/$HEALTH_CHECK_BIN > /dev/null
# Wait
- sleep 25
-
- # Check health status, exit code should indicate failure
- $CURDIR/$HEALTH_CHECK_BIN &> /dev/null
-
- health_check_exit_code=$?
-
- if [ $health_check_exit_code -eq $test_thread_exit_code ]; then
- pass "Validate that ${test_thread_stall_name} is stalled"
- else
- fail "Validate that ${test_thread_stall_name} is stalled"
- diag "Health returned: $health_check_exit_code"
+ sleep 30
+
+ # Check health status, should indicate failure
+ $CURDIR/$HEALTH_CHECK_BIN > ${STDOUT_PATH} 2> ${STDERR_PATH}
+
+ out=$(grep "${test_thread_error_string}" ${STDOUT_PATH} | wc -l)
+ if [ $out -eq 0 ]; then
+ fail "Validate thread ${test_thread_stall_name} is stalled"
+ diag "Health returned:"
+ diag "stdout:"
+ file=${STDOUT_PATH}
+ while read line ; do
+ diag "$line"
+ done < ${file}
+
+ diag "stderr:"
+ file=${STDERR_PATH}
+ while read line ; do
+ diag "$line"
+ done < ${file}
stop_lttng_sessiond
return 1
+ else
+ pass "Validate thread ${test_thread_stall_name} is stalled"
fi
# Wait
sleep 40
- # Check health status, exit code should now pass
- $CURDIR/$HEALTH_CHECK_BIN &> /dev/null
-
- health_check_exit_code=$?
+ # Check health status, should now pass
+ $CURDIR/$HEALTH_CHECK_BIN > ${STDOUT_PATH} 2> ${STDERR_PATH}
+
+ out=$(grep "${test_thread_error_string}" ${STDOUT_PATH} | wc -l)
+ if [ $out -ne 0 ]; then
+ fail "Validate thread ${test_thread_stall_name} is not longer stalled"
+ diag "Health returned:"
+ diag "stdout:"
+ file=${STDOUT_PATH}
+ while read line ; do
+ diag "$line"
+ done < ${file}
+
+ diag "stderr:"
+ file=${STDERR_PATH}
+ while read line ; do
+ diag "$line"
+ done < ${file}
- if [ $health_check_exit_code -eq 0 ]; then
- pass "Validate that ${test_thread_stall_name} is no longer stalled"
stop_lttng_sessiond
+ return 1
else
- fail "Validate that ${test_thread_stall_name} is no longer stalled"
- diag "Health returned: $health_check_exit_code\n"
+ pass "Validate thread ${test_thread_stall_name} is not longer stalled"
stop_lttng_sessiond
- return 1
fi
-
-
}
plan_tests $NUM_TESTS
# "LTTNG_THREAD_REG_APPS"
)
-# Exit code value to indicate specific thread failure
-EXIT_CODE=(1
- 2
-# 4
+ERROR_STRING=(
+ "Thread \"Session daemon command\" is not responding in component \"sessiond\"."
+ "Thread \"Session daemon application manager\" is not responding in component \"sessiond\"."
+ "Thread \"Session daemon application registration\" is not responding in component \"sessiond\"."
)
+STDOUT_PATH=$(mktemp)
+STDERR_PATH=$(mktemp)
+
THREAD_COUNT=${#THREAD[@]}
i=0
while [ "$i" -lt "$THREAD_COUNT" ]; do
- test_thread_stall "${THREAD[$i]}" "${EXIT_CODE[$i]}"
+ test_thread_stall "${THREAD[$i]}" "${ERROR_STRING[$i]}"
if [ $? -eq 1 ]; then
exit 1
skip $isroot "Root access is needed. Skipping LTTNG_THREAD_MANAGE_KERNEL tests." "4" ||
{
- test_thread_stall "LTTNG_THREAD_MANAGE_KERNEL" "8"
+ test_thread_stall "LTTNG_THREAD_MANAGE_KERNEL" "Thread \"Session daemon kernel\" is not responding in component \"sessiond\"."
}
+
+rm -f ${STDOUT_PATH}
+rm -f ${STDERR_PATH}
function test_tp_fail
{
test_tp_fail_name="$1"
- test_tp_fail_code="$2"
+ test_tp_error_string="$2"
diag "Test health failure with ${test_tp_fail_name}"
unset LD_PRELOAD
unset ${test_tp_fail_name}_TP_FAIL
- # Check health status, exit code should indicate failure
- $CURDIR/$HEALTH_CHECK_BIN &> /dev/null
+ # Check health status
+ $CURDIR/$HEALTH_CHECK_BIN > ${STDOUT_PATH} 2> ${STDERR_PATH}
- health_check_exit_code=$?
+ out=$(grep "${test_tp_error_string}" ${STDOUT_PATH} | wc -l)
+ if [ $out -eq 0 ]; then
+ fail "Validate thread ${test_tp_fail_name} failure"
+ diag "Health returned:"
+
+ diag "stdout:"
+ file=${STDOUT_PATH}
+ while read line ; do
+ diag "$line"
+ done < ${file}
+
+ diag "stderr:"
+ file=${STDERR_PATH}
+ while read line ; do
+ diag "$line"
+ done < ${file}
- if [ $health_check_exit_code -eq $test_tp_fail_code ]; then
- pass "Validate thread ${test_tp_fail_name} failure"
stop_lttng_sessiond
+ return 1
else
- fail "Validate thread ${test_tp_fail_name} failure"
- diag "Health returned: $health_check_exit_code"
+ pass "Validate thread ${test_tp_fail_name} failure"
stop_lttng_sessiond
- return 1
fi
}
THREAD=("LTTNG_THREAD_MANAGE_CLIENTS"
"LTTNG_THREAD_MANAGE_APPS")
-# Exit code value to indicate specific thread failure
-EXIT_CODE=(1 2)
+ERROR_STRING=(
+ "Thread \"Session daemon command\" is not responding in component \"sessiond\"."
+ "Thread \"Session daemon application manager\" is not responding in component \"sessiond\"."
+)
+
+STDOUT_PATH=$(mktemp)
+STDERR_PATH=$(mktemp)
THREAD_COUNT=${#THREAD[@]}
i=0
while [ "$i" -lt "$THREAD_COUNT" ]; do
- test_tp_fail "${THREAD[$i]}" "${EXIT_CODE[$i]}"
+ test_tp_fail "${THREAD[$i]}" "${ERROR_STRING[$i]}"
if [ $? -eq 1 ]; then
exit 1
skip $isroot "Root access is needed. Skipping LTTNG_THREAD_MANAGE_KERNEL tests." "3" ||
{
- test_tp_fail "LTTNG_THREAD_MANAGE_KERNEL" "8"
+ test_tp_fail "LTTNG_THREAD_MANAGE_KERNEL" "Thread \"Session daemon kernel\" is not responding in component \"sessiond\"."
}
+rm -f ${STDOUT_PATH}
+rm -f ${STDERR_PATH}
# TODO: Special case manage consumer, need to spawn consumer via commands.
#"LTTNG_THREAD_MANAGE_CONSUMER"
noinst_SCRIPTS = test_kernel test_kernel_streaming test_ust test_ust_streaming
EXTRA_DIST = test_kernel test_kernel_streaming test_ust test_ust_streaming
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
CURDIR=$(dirname $0)/
TESTDIR=$CURDIR/../../..
EVENT_NAME="tp:tptest"
-BIN_NAME="gen-nevents"
PID_RELAYD=0
SESSION_NAME=""
CHANNEL_NAME="snapchan"
TRACE_PATH=$(mktemp -d)
-NUM_TESTS=2019
+NUM_TESTS=2075
source $TESTDIR/utils/utils.sh
BAIL_OUT "No UST events binary detected."
fi
+function snapshot_add_output ()
+{
+ local sess_name=$1
+ local trace_path=$2
+ local name=$3
+ local max_size=$4
+ local extra_opt=""
+
+ if [ ! -z $name ]; then
+ extra_opt+=" -n $name "
+ fi
+
+ if [ ! -z $max_size ]; then
+ extra_opt+=" -m $max_size "
+ fi
+
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN snapshot add-output \
+ -s $sess_name $extra_opt $trace_path > /dev/null 2>&1
+
+ ok $? "Added snapshot output $trace_path ($extra_opt)"
+}
+
+function snapshot_del_output ()
+{
+ local sess_name=$1
+ local name=$2
+
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN snapshot del-output \
+ -s $sess_name $name > /dev/null 2>&1
+
+ ok $? "Deleted snapshot output named $name"
+}
+
+function enable_mmap_overwrite_subbuf_ust_channel ()
+{
+ local sess_name=$1
+ local chan_name=$2
+ local subbuf_size=$3
+
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel -s $sess_name \
+ $chan_name -u --output mmap --overwrite \
+ --subbuf-size $subbuf_size > /dev/null 2>&1
+
+ ok $? "Enable channel $channel_name for session $sess_name with subbuf size $subbuf_size"
+}
+
+
+function test_ust_list_output ()
+{
+ output_names=("randomname" "somesnapshot")
+
+ diag "Test UST snapshot output listing"
+ create_lttng_session_no_output $SESSION_NAME
+ enable_lttng_mmap_overwrite_ust_channel $SESSION_NAME $CHANNEL_NAME
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME $CHANNEL_NAME
+
+ start_lttng_tracing $SESSION_NAME
+
+ snapshot_add_output $SESSION_NAME "file://$TRACE_PATH" ${output_names[0]}
+
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN snapshot list-output \
+ -s $SESSION_NAME 2>&1 | grep ${output_names[0]} > /dev/null
+ ok $? "Snapshot named ${output_names[0]} present in list-output listing"
+
+ snapshot_del_output $SESSION_NAME ${output_names[0]}
+
+ snapshot_add_output $SESSION_NAME "file://$TRACE_PATH" ${output_names[1]}
+
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN snapshot list-output \
+ -s $SESSION_NAME 2>&1 | grep ${output_names[1]} > /dev/null
+
+ ok $? "Snapshot named ${output_names[1]} present in list-output listing"
+
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+}
+
function test_ust_local_snapshot ()
{
diag "Test local UST snapshots"
kill $PID_APP >/dev/null 2>&1
}
+function test_ust_local_snapshot_max_size ()
+{
+ subbuf_size=8192
+ num_cpus=`nproc`
+
+ # The minimum size limit is min(subbuf_size) * nb_streams
+ max_size=$(($subbuf_size*$num_cpus))
+
+ diag "Test local UST snapshots with max size $max_size"
+ create_lttng_session_no_output $SESSION_NAME
+
+ enable_mmap_overwrite_subbuf_ust_channel $SESSION_NAME $CHANNEL_NAME $subbuf_size
+
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME $CHANNEL_NAME
+ start_lttng_tracing $SESSION_NAME
+
+ snapshot_add_output $SESSION_NAME "file://$TRACE_PATH" "" $max_size
+
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
+ ok $? "Start application to trace"
+
+ lttng_snapshot_record $SESSION_NAME
+
+ # Check file size
+ sum_size_tracefiles=$(find $TRACE_PATH -name "${CHANNEL_NAME}_*" \
+ -exec stat -c '%s' {} \; | awk '{s = s + $1}END{print s}')
+
+ if [ "$sum_size_tracefiles" -gt "$max_size" ]; then
+ fail "Tracefiles size sum validation"
+ diag "Tracefiles size sum: $sum_size_tracefiles Expected max: $max_size"
+ fi
+
+ pass "Tracefiles size sum validation"
+
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+
+ # Validate test
+ validate_trace $EVENT_NAME $TRACE_PATH/
+
+ if [ $? -eq 0 ]; then
+ # Only delete if successful
+ rm -rf $TRACE_PATH
+ fi
+
+ diag "Killing $TESTAPP_NAME"
+ PID_APP=`pidof $TESTAPP_NAME`
+ kill $PID_APP >/dev/null 2>&1
+}
+
+function test_ust_local_snapshot_large_metadata ()
+{
+ LM_EVENT="tp:tptest1,tp:tptest2,tp:tptest3,tp:tptest4,tp:tptest5"
+ LM_PATH="$TESTDIR/utils/testapp"
+ LM_NAME="gen-ust-nevents"
+ LM_BIN="$LM_PATH/$LM_NAME/$LM_NAME"
+
+ diag "Test local UST snapshots with > 4kB metadata"
+ create_lttng_session_no_output $SESSION_NAME
+ enable_lttng_mmap_overwrite_ust_channel $SESSION_NAME $CHANNEL_NAME
+ enable_ust_lttng_event $SESSION_NAME $LM_EVENT $CHANNEL_NAME
+ start_lttng_tracing $SESSION_NAME
+ lttng_snapshot_add_output $SESSION_NAME $TRACE_PATH
+ $LM_BIN 1 1
+ ok $? "Start application to trace"
+ lttng_snapshot_record $SESSION_NAME
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+
+ # Validate test
+ validate_trace $LM_EVENT $TRACE_PATH/
+ if [ $? -eq 0 ]; then
+ # Only delete if successful
+ rm -rf $TRACE_PATH
+ else
+ break
+ fi
+}
+
+function enable_channel_per_uid_mmap_overwrite()
+{
+ sess_name=$1
+ channel_name=$2
+
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-uid -u $channel_name -s $sess_name --output mmap --overwrite >/dev/null 2>&1
+ ok $? "Enable channel $channel_name per UID for session $sess_name"
+}
+
+function test_ust_per_uid_local_snapshot ()
+{
+ diag "Test local UST snapshots"
+ create_lttng_session_no_output $SESSION_NAME
+ enable_channel_per_uid_mmap_overwrite $SESSION_NAME $CHANNEL_NAME
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME $CHANNEL_NAME
+ start_lttng_tracing $SESSION_NAME
+ lttng_snapshot_add_output $SESSION_NAME $TRACE_PATH
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
+ ok $? "Start application to trace"
+ lttng_snapshot_record $SESSION_NAME
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+
+ # Validate test
+ validate_trace $EVENT_NAME $TRACE_PATH/
+ if [ $? -eq 0 ]; then
+ # Only delete if successful
+ rm -rf $TRACE_PATH
+ else
+ break
+ fi
+ diag "Killing $TESTAPP_NAME"
+ PID_APP=`pidof $TESTAPP_NAME`
+ kill $PID_APP >/dev/null 2>&1
+}
+
+function test_ust_per_uid_local_snapshot_post_mortem ()
+{
+ diag "Test local UST snapshots post-mortem"
+ create_lttng_session_no_output $SESSION_NAME
+ enable_channel_per_uid_mmap_overwrite $SESSION_NAME $CHANNEL_NAME
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME $CHANNEL_NAME
+ start_lttng_tracing $SESSION_NAME
+ lttng_snapshot_add_output $SESSION_NAME $TRACE_PATH
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
+ ok $? "Start application to trace"
+ diag "Killing $TESTAPP_NAME"
+ PID_APP=`pidof $TESTAPP_NAME`
+ kill $PID_APP >/dev/null 2>&1
+ lttng_snapshot_record $SESSION_NAME
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+
+ # Validate test
+ validate_trace $EVENT_NAME $TRACE_PATH/
+ if [ $? -eq 0 ]; then
+ # Only delete if successful
+ rm -rf $TRACE_PATH
+ else
+ break
+ fi
+}
+
function test_ust_1000_local_snapshots ()
{
NB_SNAP=1000
destroy_lttng_session $SESSION_NAME
diag "Killing $TESTAPP_NAME"
PID_APP=`pidof $TESTAPP_NAME`
-# kill $PID_APP >/dev/null 2>&1
+ kill $PID_APP >/dev/null 2>&1
}
plan_tests $NUM_TESTS
start_lttng_sessiond
-tests=( test_ust_local_snapshot test_ust_1000_local_snapshots )
+tests=( test_ust_list_output
+ test_ust_local_snapshot
+ test_ust_local_snapshot_max_size
+ test_ust_per_uid_local_snapshot
+ test_ust_per_uid_local_snapshot_post_mortem
+ test_ust_local_snapshot_large_metadata
+ test_ust_1000_local_snapshots )
for fct_test in ${tests[@]};
do
SESSION_NAME=$(randstring 16 0)
${fct_test}
-
done
stop_lttng_sessiond
TRACE_PATH=$(mktemp -d)
-NUM_TESTS=39
+NUM_TESTS=49
source $TESTDIR/utils/utils.sh
return $out
}
+function test_ust_default_name_custom_uri()
+{
+ diag "Test UST snapshot streaming with default name with custom URL"
+ create_lttng_session_no_output $SESSION_NAME
+ enable_lttng_mmap_overwrite_ust_channel $SESSION_NAME $CHANNEL_NAME
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME $CHANNEL_NAME
+ start_lttng_tracing $SESSION_NAME
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
+ ok $? "Start application to trace"
+ snapshot_add_output $SESSION_NAME "-C tcp://localhost:5342 -D tcp://localhost:5343"
+ lttng_snapshot_record $SESSION_NAME
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+ # Validate test
+ validate_trace $EVENT_NAME $TRACE_PATH/$HOSTNAME/snapshot-1*
+ out=$?
+
+ diag "Killing $TESTAPP_NAME"
+ PID_APP=`pidof $TESTAPP_NAME`
+ kill $PID_APP >/dev/null 2>&1
+
+ return $out
+}
+
# Test a snapshot using a custom name for the output destination.
function test_ust_custom_name()
{
start_lttng_relayd "-o $TRACE_PATH"
start_lttng_sessiond
-tests=( test_ust_default_name_with_del test_ust_default_name test_ust_custom_name )
+tests=( test_ust_default_name_with_del test_ust_default_name test_ust_custom_name test_ust_default_name_custom_uri )
for fct_test in ${tests[@]};
do
noinst_SCRIPTS = test_ust test_kernel test_high_throughput_limits
EXTRA_DIST = test_ust test_kernel test_high_throughput_limits
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
noinst_SCRIPTS = test_tracefile_count test_tracefile_size
EXTRA_DIST = test_tracefile_count test_tracefile_size
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
file_pattern="$2"
expected_max_count="$3"
- count=`find $path -name "$file_pattern" -type f | wc -l`
+ count=`find $path -name "$file_pattern" -type f \( ! -iname "*.idx" \) | wc -l`
if [ "$count" -gt "$expected_max_count" ]; then
fail "Validate file count: $file_pattern"
test_name+="$tracefile_size_limit bytes tracefile limit"
$TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel \
- -u $channel_name -s $sess_name \
+ -u $channel_name -s $sess_name --buffers-pid \
-C $tracefile_size_limit >/dev/null 2>&1
ok $? "$test_name"
if HAVE_LIBLTTNG_UST_CTL
SUBDIRS = nprocesses high-throughput low-throughput before-after multi-session \
- overlap buffers-uid linking daemon exit-fast fork libc-wrapper \
+ overlap buffers-pid linking daemon exit-fast fork libc-wrapper \
periodical-metadata-flush
EXTRA_DIST = test_event_basic test_event_wildcard
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
+
endif
noinst_SCRIPTS = test_before_after
EXTRA_DIST = test_before_after
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
--- /dev/null
+noinst_SCRIPTS = test_buffers_pid
+EXTRA_DIST = test_buffers_pid
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) - 2012 David Goulet <dgoulet@efficios.com>
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; version 2.1 of the License.
+#
+# 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
+TEST_DESC="UST tracer - Tracing with per PID buffers"
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../..
+NR_ITER=100
+NR_USEC_WAIT=100000
+SESSION_NAME="buffers-pid"
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+TESTAPP_NAME="gen-ust-events"
+TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
+EVENT_NAME="tp:tptest"
+NUM_TESTS=58
+
+source $TESTDIR/utils/utils.sh
+
+if [ ! -x "$TESTAPP_BIN" ]; then
+ BAIL_OUT "No UST events binary detected."
+fi
+
+# MUST set TESTDIR before calling those functions
+
+function enable_channel_per_pid()
+{
+ sess_name=$1
+ channel_name=$2
+
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-pid -u $channel_name -s $sess_name >/dev/null 2>&1
+ ok $? "Enable channel $channel_name per PID for session $sess_name"
+}
+
+function wait_apps
+{
+ diag "Waiting for applications to end..."
+ while [ -n "$(pidof $TESTAPP_NAME)" ]; do
+ sleep 1
+ done
+}
+
+test_after_multiple_apps() {
+ local out
+ local i
+
+ diag "Start multiple applications AFTER tracing is started"
+
+ # BEFORE application is spawned
+ create_lttng_session $SESSION_NAME $TRACE_PATH
+ enable_channel_per_pid $SESSION_NAME "channel0"
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
+ start_lttng_tracing $SESSION_NAME
+
+ for i in `seq 1 5`; do
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT & >/dev/null 2>&1
+ ok $? "Start application $i for tracing"
+ done
+ wait_apps
+
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+
+ trace_matches $EVENT_NAME $[NR_ITER * 5] $TRACE_PATH
+
+ return $?
+}
+
+test_before_multiple_apps() {
+ local out
+ local i
+
+ diag "Start multiple applications BEFORE tracing is started"
+
+ for i in `seq 1 5`; do
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT & >/dev/null 2>&1
+ ok $? "Start application $i for tracing"
+ done
+
+ # BEFORE application is spawned
+ create_lttng_session $SESSION_NAME $TRACE_PATH
+ enable_channel_per_pid $SESSION_NAME "channel0"
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
+ start_lttng_tracing $SESSION_NAME
+
+ # At least hit one event
+ sleep 2
+
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+
+ out=$(babeltrace $TRACE_PATH | grep $EVENT_NAME | wc -l)
+ if [ $out -eq 0 ]; then
+ fail "Trace validation"
+ diag "No event(s) found. We are supposed to have at least one."
+ out=1
+ else
+ pass "Trace validation"
+ diag "Found $out event(s). Coherent."
+ out=0
+ fi
+
+ wait_apps
+
+ return $out
+}
+
+test_after_app() {
+ local out
+
+ diag "Start application AFTER tracing is started"
+
+ # BEFORE application is spawned
+ create_lttng_session $SESSION_NAME $TRACE_PATH
+ enable_channel_per_pid $SESSION_NAME "channel0"
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
+ start_lttng_tracing $SESSION_NAME
+
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT
+ ok $? "Start application to trace"
+
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+
+ trace_matches $EVENT_NAME $NR_ITER $TRACE_PATH
+
+ return $?
+}
+
+test_before_app() {
+ local out
+
+ diag "Start application BEFORE tracing is started"
+
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
+ ok $? "Start application to trace"
+
+ # BEFORE application is spawned
+ create_lttng_session $SESSION_NAME $TRACE_PATH
+ enable_channel_per_pid $SESSION_NAME "channel0"
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
+ start_lttng_tracing $SESSION_NAME
+
+ # At least hit one event
+ sleep 2
+
+ stop_lttng_tracing $SESSION_NAME
+ destroy_lttng_session $SESSION_NAME
+
+ out=$(babeltrace $TRACE_PATH | grep $EVENT_NAME | wc -l)
+ if [ $out -eq 0 ]; then
+ fail "Trace validation"
+ diag "No event(s) found. We are supposed to have at least one."
+ out=1
+ else
+ pass "Trace validation"
+ diag "Found $out event(s). Coherent."
+ out=0
+ fi
+
+ wait_apps
+
+ return $out
+}
+
+test_multiple_channels() {
+ local out
+
+ diag "Start with multiple channels"
+
+ # BEFORE application is spawned
+ create_lttng_session $SESSION_NAME $TRACE_PATH
+ enable_channel_per_pid $SESSION_NAME "channel0"
+ enable_channel_per_pid $SESSION_NAME "channel1"
+ enable_channel_per_pid $SESSION_NAME "channel2"
+ enable_channel_per_pid $SESSION_NAME "channel3"
+ enable_channel_per_pid $SESSION_NAME "channel4"
+ # Enable event in all channels.
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel0 -s $SESSION_NAME -u >/dev/null 2>&1
+ ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel0"
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel1 -s $SESSION_NAME -u >/dev/null 2>&1
+ ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel1"
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel2 -s $SESSION_NAME -u >/dev/null 2>&1
+ ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel2"
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel3 -s $SESSION_NAME -u >/dev/null 2>&1
+ ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel3"
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel4 -s $SESSION_NAME -u >/dev/null 2>&1
+ ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel4"
+ start_lttng_tracing $SESSION_NAME
+
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT
+ ok $? "Start application to trace"
+
+ stop_lttng_tracing $SESSION_NAME
+ trace_matches $EVENT_NAME $[NR_ITER * 5] $TRACE_PATH
+ out=$?
+
+ destroy_lttng_session $SESSION_NAME
+
+ return $out
+}
+
+# MUST set TESTDIR before calling those functions
+plan_tests $NUM_TESTS
+
+print_test_banner "$TEST_DESC"
+
+TESTS=(
+ "test_before_app"
+ "test_after_app"
+ "test_after_multiple_apps"
+ "test_before_multiple_apps"
+ "test_multiple_channels"
+)
+
+TEST_COUNT=${#TESTS[@]}
+i=0
+
+start_lttng_sessiond
+
+while [ $i -lt $TEST_COUNT ]; do
+ TRACE_PATH=$(mktemp -d)
+ ${TESTS[$i]}
+ rm -rf $TRACE_PATH
+ let "i++"
+done
+
+stop_lttng_sessiond
+++ /dev/null
-noinst_SCRIPTS = test_buffers_uid
-EXTRA_DIST = test_buffers_uid
+++ /dev/null
-#!/bin/bash
-#
-# Copyright (C) - 2012 David Goulet <dgoulet@efficios.com>
-#
-# This library is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation; version 2.1 of the License.
-#
-# 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
-TEST_DESC="UST tracer - Tracing with per UID buffers"
-
-CURDIR=$(dirname $0)/
-TESTDIR=$CURDIR/../../..
-NR_ITER=100
-NR_USEC_WAIT=100000
-SESSION_NAME="buffers-uid"
-
-TESTAPP_PATH="$TESTDIR/utils/testapp"
-TESTAPP_NAME="gen-ust-events"
-TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
-EVENT_NAME="tp:tptest"
-NUM_TESTS=58
-
-source $TESTDIR/utils/utils.sh
-
-if [ ! -x "$TESTAPP_BIN" ]; then
- BAIL_OUT "No UST events binary detected."
-fi
-
-# MUST set TESTDIR before calling those functions
-
-function enable_channel_per_uid()
-{
- sess_name=$1
- channel_name=$2
-
- $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-uid -u $channel_name -s $sess_name >/dev/null 2>&1
- ok $? "Enable channel $channel_name per UID for session $sess_name"
-}
-
-function wait_apps
-{
- diag "Waiting for applications to end..."
- while [ -n "$(pidof $TESTAPP_NAME)" ]; do
- sleep 1
- done
-}
-
-test_after_multiple_apps() {
- local out
- local i
-
- diag "Start multiple applications AFTER tracing is started"
-
- # BEFORE application is spawned
- create_lttng_session $SESSION_NAME $TRACE_PATH
- enable_channel_per_uid $SESSION_NAME "channel0"
- enable_ust_lttng_event $SESSION_NAME $EVENT_NAME
- start_lttng_tracing $SESSION_NAME
-
- for i in `seq 1 5`; do
- $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT & >/dev/null 2>&1
- ok $? "Start application $i for tracing"
- done
- wait_apps
-
- stop_lttng_tracing $SESSION_NAME
- destroy_lttng_session $SESSION_NAME
-
- trace_matches $EVENT_NAME $[NR_ITER * 5] $TRACE_PATH
-
- return $?
-}
-
-test_before_multiple_apps() {
- local out
- local i
-
- diag "Start multiple applications BEFORE tracing is started"
-
- for i in `seq 1 5`; do
- $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT & >/dev/null 2>&1
- ok $? "Start application $i for tracing"
- done
-
- # BEFORE application is spawned
- create_lttng_session $SESSION_NAME $TRACE_PATH
- enable_channel_per_uid $SESSION_NAME "channel0"
- enable_ust_lttng_event $SESSION_NAME $EVENT_NAME
- start_lttng_tracing $SESSION_NAME
-
- # At least hit one event
- sleep 2
-
- stop_lttng_tracing $SESSION_NAME
- destroy_lttng_session $SESSION_NAME
-
- out=$(babeltrace $TRACE_PATH | grep $EVENT_NAME | wc -l)
- if [ $out -eq 0 ]; then
- fail "Trace validation"
- diag "No event(s) found. We are supposed to have at least one."
- out=1
- else
- pass "Trace validation"
- diag "Found $out event(s). Coherent."
- out=0
- fi
-
- wait_apps
-
- return $out
-}
-
-test_after_app() {
- local out
-
- diag "Start application AFTER tracing is started"
-
- # BEFORE application is spawned
- create_lttng_session $SESSION_NAME $TRACE_PATH
- enable_channel_per_uid $SESSION_NAME "channel0"
- enable_ust_lttng_event $SESSION_NAME $EVENT_NAME
- start_lttng_tracing $SESSION_NAME
-
- $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT
- ok $? "Start application to trace"
-
- stop_lttng_tracing $SESSION_NAME
- destroy_lttng_session $SESSION_NAME
-
- trace_matches $EVENT_NAME $NR_ITER $TRACE_PATH
-
- return $?
-}
-
-test_before_app() {
- local out
-
- diag "Start application BEFORE tracing is started"
-
- $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
- ok $? "Start application to trace"
-
- # BEFORE application is spawned
- create_lttng_session $SESSION_NAME $TRACE_PATH
- enable_channel_per_uid $SESSION_NAME "channel0"
- enable_ust_lttng_event $SESSION_NAME $EVENT_NAME
- start_lttng_tracing $SESSION_NAME
-
- # At least hit one event
- sleep 2
-
- stop_lttng_tracing $SESSION_NAME
- destroy_lttng_session $SESSION_NAME
-
- out=$(babeltrace $TRACE_PATH | grep $EVENT_NAME | wc -l)
- if [ $out -eq 0 ]; then
- fail "Trace validation"
- diag "No event(s) found. We are supposed to have at least one."
- out=1
- else
- pass "Trace validation"
- diag "Found $out event(s). Coherent."
- out=0
- fi
-
- wait_apps
-
- return $out
-}
-
-test_multiple_channels() {
- local out
-
- diag "Start with multiple channels"
-
- # BEFORE application is spawned
- create_lttng_session $SESSION_NAME $TRACE_PATH
- enable_channel_per_uid $SESSION_NAME "channel0"
- enable_channel_per_uid $SESSION_NAME "channel1"
- enable_channel_per_uid $SESSION_NAME "channel2"
- enable_channel_per_uid $SESSION_NAME "channel3"
- enable_channel_per_uid $SESSION_NAME "channel4"
- # Enable event in all channels.
- $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel0 -s $SESSION_NAME -u >/dev/null 2>&1
- ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel0"
- $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel1 -s $SESSION_NAME -u >/dev/null 2>&1
- ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel1"
- $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel2 -s $SESSION_NAME -u >/dev/null 2>&1
- ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel2"
- $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel3 -s $SESSION_NAME -u >/dev/null 2>&1
- ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel3"
- $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $EVENT_NAME -c channel4 -s $SESSION_NAME -u >/dev/null 2>&1
- ok $? "Enable event $EVENT_NAME for session $SESSION_NAME in channel4"
- start_lttng_tracing $SESSION_NAME
-
- $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT
- ok $? "Start application to trace"
-
- stop_lttng_tracing $SESSION_NAME
- trace_matches $EVENT_NAME $[NR_ITER * 5] $TRACE_PATH
- out=$?
-
- destroy_lttng_session $SESSION_NAME
-
- return $out
-}
-
-# MUST set TESTDIR before calling those functions
-plan_tests $NUM_TESTS
-
-print_test_banner "$TEST_DESC"
-
-TESTS=(
- "test_before_app"
- "test_after_app"
- "test_after_multiple_apps"
- "test_before_multiple_apps"
- "test_multiple_channels"
-)
-
-TEST_COUNT=${#TESTS[@]}
-i=0
-
-start_lttng_sessiond
-
-while [ $i -lt $TEST_COUNT ]; do
- TRACE_PATH=$(mktemp -d)
- ${TESTS[$i]}
- rm -rf $TRACE_PATH
- let "i++"
-done
-
-stop_lttng_sessiond
noinst_SCRIPTS = test_daemon test_daemon.py
EXTRA_DIST = test_daemon test_daemon.py
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
noinst_SCRIPTS = test_exit-fast test_exit-fast.py
EXTRA_DIST = test_exit-fast test_exit-fast.py
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
#undef TRACEPOINT_PROVIDER
#define TRACEPOINT_PROVIDER ust_tests_exitfast
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
#if !defined(_TRACEPOINT_UST_TESTS_EXITFAST_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_UST_TESTS_EXITFAST_H
#define TRACEPOINT_INCLUDE "./ust_tests_exitfast.h"
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
noinst_SCRIPTS = test_fork test_fork.py
EXTRA_DIST = test_fork test_fork.py
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
noinst_SCRIPTS = test_high_throughput
EXTRA_DIST = test_high_throughput
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
#if !defined(_TRACEPOINT_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_TP_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
noinst_SCRIPTS = test_libc-wrapper test_libc-wrapper.py
EXTRA_DIST = test_libc-wrapper test_libc-wrapper.py
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
noinst_SCRIPTS = test_linking test_linking.py demo_preload
EXTRA_DIST = test_linking test_linking.py demo_preload
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
#if !defined(_TRACEPOINT_UST_TESTS_DEMO_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_UST_TESTS_DEMO_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
#if !defined(_TRACEPOINT_UST_TESTS_DEMO2_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_UST_TESTS_DEMO2_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
#if !defined(_TRACEPOINT_UST_TESTS_DEMO3_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_UST_TESTS_DEMO3_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
noinst_SCRIPTS = test_low_throughput
EXTRA_DIST = test_low_throughput
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
#if !defined(_TRACEPOINT_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_TP_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
noinst_SCRIPTS = test_multi_session
EXTRA_DIST = test_multi_session
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
#if !defined(_TRACEPOINT_UST_GEN_NEVENTS_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_UST_GEN_NEVENTS_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
noinst_SCRIPTS = test_nprocesses
EXTRA_DIST = test_nprocesses
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
noinst_SCRIPTS = test_overlap
EXTRA_DIST = test_overlap
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
noinst_SCRIPTS = demo-trace
EXTRA_DIST = demo-trace
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
endif
#if !defined(_TRACEPOINT_UST_TESTS_DEMO_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_UST_TESTS_DEMO_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
#if !defined(_TRACEPOINT_UST_TESTS_DEMO2_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_UST_TESTS_DEMO2_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
#if !defined(_TRACEPOINT_UST_TESTS_DEMO3_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_UST_TESTS_DEMO3_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
noinst_SCRIPTS = test_periodical_metadata_flush
EXTRA_DIST = test_periodical_metadata_flush
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
local sess_name=$1
local channel_name=$2
- $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-uid -u $channel_name -s $sess_name --switch-timer 1000000 >/dev/null 2>&1
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-uid -u $channel_name -s $sess_name --switch-timer 100000 >/dev/null 2>&1
ok $? "Enable channel $channel_name per UID for session $sess_name"
}
local sess_name=$1
local channel_name=$2
- $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-pid -u $channel_name -s $sess_name --switch-timer 1000000 >/dev/null 2>&1
+ $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel --buffers-pid -u $channel_name -s $sess_name --switch-timer 100000 >/dev/null 2>&1
ok $? "Enable channel $channel_name per UID for session $sess_name"
}
diag "Start application AFTER tracing is started"
- # BEFORE application is spawned
create_lttng_session $SESSION_NAME $TRACE_PATH
enable_metadata_per_pid $SESSION_NAME
enable_channel_per_pid $SESSION_NAME "channel0"
- enable_ust_lttng_event $SESSION_NAME $EVENT_NAME
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
start_lttng_tracing $SESSION_NAME
- $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT
+ # Start application after tracing
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
ok $? "Start application to trace"
# At least hit one event
sleep 2
+
+ # Make sure the application does not generate any more data,
+ # thus ensuring that we are not flushing a packet concurrently
+ # with validate_trace.
+ killall -SIGSTOP -q $TESTAPP_NAME
+
+ # Give time to the consumer to write inflight data.
+ sleep 2
+
validate_trace
out=$?
stop_lttng_tracing $SESSION_NAME
destroy_lttng_session $SESSION_NAME
+ killall -SIGKILL -q $TESTAPP_NAME
+ wait_apps
+
return $out
}
$TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
ok $? "Start application to trace"
- # BEFORE application is spawned
+ # Start application before tracing
create_lttng_session $SESSION_NAME $TRACE_PATH
enable_metadata_per_pid $SESSION_NAME
enable_channel_per_pid $SESSION_NAME "channel0"
- enable_ust_lttng_event $SESSION_NAME $EVENT_NAME
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
start_lttng_tracing $SESSION_NAME
# At least hit one event
sleep 2
+
+ # Make sure the application does not generate any more data,
+ # thus ensuring that we are not flushing a packet concurrently
+ # with validate_trace.
+ killall -SIGSTOP -q $TESTAPP_NAME
+
+ # Give time to the consumer to write inflight data.
+ sleep 2
+
validate_trace
out=$?
stop_lttng_tracing $SESSION_NAME
destroy_lttng_session $SESSION_NAME
+ killall -SIGKILL -q $TESTAPP_NAME
wait_apps
return $out
diag "Start application AFTER tracing is started"
- # BEFORE application is spawned
create_lttng_session $SESSION_NAME $TRACE_PATH
enable_metadata_per_uid $SESSION_NAME
enable_channel_per_uid $SESSION_NAME "channel0"
- enable_ust_lttng_event $SESSION_NAME $EVENT_NAME
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
start_lttng_tracing $SESSION_NAME
- $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT
+ # Start application after tracing
+ $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
ok $? "Start application to trace"
# At least hit one event
sleep 2
+
+ # Make sure the application does not generate any more data,
+ # thus ensuring that we are not flushing a packet concurrently
+ # with validate_trace.
+ killall -SIGSTOP -q $TESTAPP_NAME
+
+ # Give time to the consumer to write inflight data.
+ sleep 2
+
validate_trace
out=$?
stop_lttng_tracing $SESSION_NAME
destroy_lttng_session $SESSION_NAME
+ killall -SIGKILL -q $TESTAPP_NAME
+ wait_apps
+
return $out
}
diag "Start application BEFORE tracing is started"
+ # Start application before tracing
$TESTAPP_BIN $NR_ITER $NR_USEC_WAIT &
ok $? "Start application to trace"
- # BEFORE application is spawned
create_lttng_session $SESSION_NAME $TRACE_PATH
enable_metadata_per_uid $SESSION_NAME
enable_channel_per_uid $SESSION_NAME "channel0"
- enable_ust_lttng_event $SESSION_NAME $EVENT_NAME
+ enable_ust_lttng_event $SESSION_NAME $EVENT_NAME "channel0"
start_lttng_tracing $SESSION_NAME
# At least hit one event
sleep 2
+
+ # Make sure the application does not generate any more data,
+ # thus ensuring that we are not flushing a packet concurrently
+ # with validate_trace.
+ killall -SIGSTOP -q $TESTAPP_NAME
+
+ # Give time to the consumer to write inflight data.
+ sleep 2
+
validate_trace
out=$?
stop_lttng_tracing $SESSION_NAME
destroy_lttng_session $SESSION_NAME
+ killall -SIGKILL -q $TESTAPP_NAME
wait_apps
return $out
while [ $i -lt $TEST_COUNT ]; do
TRACE_PATH=$(mktemp -d)
${TESTS[$i]}
- #rm -rf $TRACE_PATH
+ rm -rf $TRACE_PATH
let "i++"
done
test_multi_sessions_per_uid_5app_streaming
EXTRA_DIST = README launch_ust_app test_multi_sessions_per_uid_10app \
test_multi_sessions_per_uid_5app_streaming
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
LIBCOMMON=$(top_builddir)/src/common/libcommon.la
LIBSESSIOND_COMM=$(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la
LIBHASHTABLE=$(top_builddir)/src/common/hashtable/libhashtable.la
+LIBRELAYD=$(top_builddir)/src/common/relayd/librelayd.la
# Define test programs
noinst_PROGRAMS = test_uri test_session test_kernel_data test_utils_parse_size_suffix
SESSIONS=$(top_builddir)/src/bin/lttng-sessiond/session.o \
$(top_builddir)/src/bin/lttng-sessiond/consumer.o \
$(top_builddir)/src/bin/lttng-sessiond/utils.o \
- $(top_builddir)/src/bin/lttng-sessiond/health.o \
$(top_builddir)/src/bin/lttng-sessiond/snapshot.o \
$(top_builddir)/src/common/.libs/uri.o \
$(top_builddir)/src/common/.libs/utils.o \
- $(top_builddir)/src/common/.libs/error.o
+ $(top_builddir)/src/common/.libs/error.o \
+ $(top_builddir)/src/common/health/libhealth.la \
+ $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la
+
test_session_SOURCES = test_session.c
-test_session_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSESSIOND_COMM) $(LIBHASHTABLE) \
- -lrt
+test_session_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBRELAYD) $(LIBSESSIOND_COMM) \
+ $(LIBHASHTABLE) -lrt
test_session_LDADD += $(SESSIONS)
# UST data structures unit test
$(top_builddir)/src/bin/lttng-sessiond/ust-app.o \
$(top_builddir)/src/bin/lttng-sessiond/ust-consumer.o \
$(top_builddir)/src/bin/lttng-sessiond/fd-limit.o \
- $(top_builddir)/src/bin/lttng-sessiond/health.o \
$(top_builddir)/src/bin/lttng-sessiond/session.o \
$(top_builddir)/src/bin/lttng-sessiond/snapshot.o \
+ $(top_builddir)/src/bin/lttng-sessiond/jul.o \
$(top_builddir)/src/common/.libs/uri.o \
- $(top_builddir)/src/common/.libs/utils.o
+ $(top_builddir)/src/common/.libs/utils.o \
+ $(top_builddir)/src/common/health/libhealth.la \
+ $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la
test_ust_data_SOURCES = test_ust_data.c
-test_ust_data_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSESSIOND_COMM) $(LIBHASHTABLE) \
- -lrt -llttng-ust-ctl
+test_ust_data_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBRELAYD) $(LIBSESSIOND_COMM)\
+ $(LIBHASHTABLE) -lrt -llttng-ust-ctl
test_ust_data_LDADD += $(UST_DATA_TRACE)
endif
# Kernel data structures unit test
KERN_DATA_TRACE=$(top_builddir)/src/bin/lttng-sessiond/trace-kernel.o \
$(top_builddir)/src/bin/lttng-sessiond/consumer.o \
- $(top_builddir)/src/bin/lttng-sessiond/health.o \
$(top_builddir)/src/bin/lttng-sessiond/utils.o \
$(top_builddir)/src/common/.libs/uri.o \
- $(top_builddir)/src/common/.libs/utils.o
+ $(top_builddir)/src/common/.libs/utils.o \
+ $(top_builddir)/src/common/health/libhealth.la \
+ $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la
test_kernel_data_SOURCES = test_kernel_data.c
-test_kernel_data_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSESSIOND_COMM) $(LIBHASHTABLE) \
- -lrt
+test_kernel_data_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBRELAYD) $(LIBSESSIOND_COMM) \
+ $(LIBHASHTABLE) -lrt
test_kernel_data_LDADD += $(KERN_DATA_TRACE)
# parse_size_suffix unit test
kern->stream_count_global == 0 &&
kern->metadata == NULL,
"Validate kernel session");
-
- /* Init list in order to avoid sefaults from cds_list_del */
- trace_kernel_destroy_session(kern);
}
static void test_create_kernel_metadata(void)
EXTRA_DIST = utils.sh test_utils.py babelstats.pl
dist_noinst_SCRIPTS = utils.sh test_utils.py babelstats.pl
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
my $elapsed = '\((.*)\)';
my $hostname = '.*';
my $pname = '.*';
+ my $pinfo = '.*';
my $pid = '\d+';
my $tp_provider = '.*';
my $tp_name = '.*';
my $fields = '{(.*)}';
# Parse babeltrace text output format
- if (/$timestamp\s$elapsed\s($hostname):($pname):($pid)\s($tp_provider):($tp_name):\s$cpu_info,\s$fields/) {
+ if (/$timestamp\s$elapsed\s($pinfo)\s($tp_provider):($tp_name):\s$cpu_info,\s$fields/) {
my %event_hash;
-
$event_hash{'timestamp'} = $1;
$event_hash{'elapsed'} = $2;
- $event_hash{'hostname'} = $3;
- $event_hash{'pname'} = $4;
- $event_hash{'pid'} = $5;
- $event_hash{'tp_provider'} = $6;
- $event_hash{'tp_name'} = $7;
- $event_hash{'cpu_id'} = $8;
- $event_hash{'fields'} = parse_fields($9);
+ $event_hash{'pinfo'} = $3;
+
+# my @split_pinfo = split(':', $3);
+# $event_hash{'hostname'} = $split_pinfo[0];
+# $event_hash{'pname'} = defined($split_pinfo[1]) ? $split_pinfo[1] : undef;
+# $event_hash{'pid'} = defined($split_pinfo[2]) ? $split_pinfo[2] : undef;
+
+ $event_hash{'tp_provider'} = $4;
+ $event_hash{'tp_name'} = $5;
+ $event_hash{'cpu_id'} = $6;
+ $event_hash{'fields'} = parse_fields($7);
push @events, \%event_hash;
}
-lib_LTLIBRARIES = libtap.la
+noinst_LTLIBRARIES = libtap.la
libtap_la_SOURCES = tap.c tap.h
dist_noinst_SCRIPTS = tap.sh
EXTRA_DIST = tap.sh
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
expansions on it */
if(test_name != NULL) {
va_start(ap, test_name);
- vasprintf(&local_test_name, test_name, ap);
+ if (vasprintf(&local_test_name, test_name, ap) == -1) {
+ local_test_name = NULL;
+ }
va_end(ap);
/* Make sure the test name contains more than digits
skip(unsigned int n, char *fmt, ...)
{
va_list ap;
- char *skip_msg;
+ char *skip_msg = NULL;
LOCK;
va_start(ap, fmt);
- asprintf(&skip_msg, fmt, ap);
+ if (asprintf(&skip_msg, fmt, ap) == -1) {
+ skip_msg = NULL;
+ }
va_end(ap);
while(n-- > 0) {
LOCK;
va_start(ap, fmt);
- vasprintf(&todo_msg, fmt, ap);
+ if (vasprintf(&todo_msg, fmt, ap) == -1) {
+ todo_msg = NULL;
+ }
va_end(ap);
todo = 1;
-SUBDIRS = gen-ust-events
+SUBDIRS = gen-ust-events gen-ust-nevents
#if !defined(_TRACEPOINT_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _TRACEPOINT_TP_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
/* This part must be outside ifdef protection */
#include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
--- /dev/null
+AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(srcdir) -O2 -g
+AM_LDFLAGS =
+
+if LTTNG_TOOLS_BUILD_WITH_LIBDL
+AM_LDFLAGS += -ldl
+endif
+if LTTNG_TOOLS_BUILD_WITH_LIBC_DL
+AM_LDFLAGS += -lc
+endif
+
+if HAVE_LIBLTTNG_UST_CTL
+noinst_PROGRAMS = gen-ust-nevents
+gen_ust_nevents_SOURCES = gen-ust-nevents.c tp.c tp.h
+gen_ust_nevents_LDADD = -llttng-ust
+endif
--- /dev/null
+/*
+ * Copyright (C) - 2012 David Goulet <dgoulet@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; version 2.1 of the License.
+ *
+ * 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 <arpa/inet.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define TRACEPOINT_DEFINE
+#include "tp.h"
+
+int main(int argc, char **argv)
+{
+ int i, netint;
+ long values[] = { 1, 2, 3 };
+ char text[10] = "test";
+ double dbl = 2.0;
+ float flt = 2222.0;
+ unsigned int nr_iter = 100;
+ useconds_t nr_usec = 0;
+
+ if (argc >= 2) {
+ nr_iter = atoi(argv[1]);
+ }
+
+ if (argc == 3) {
+ /* By default, don't wait unless user specifies. */
+ nr_usec = atoi(argv[2]);
+ }
+
+ for (i = 0; i < nr_iter; i++) {
+ netint = htonl(i);
+ tracepoint(tp, tptest1, i, netint, values, text, strlen(text),
+ dbl, flt);
+ tracepoint(tp, tptest2, i, netint, values, text, strlen(text),
+ dbl, flt);
+ tracepoint(tp, tptest3, i, netint, values, text, strlen(text),
+ dbl, flt);
+ tracepoint(tp, tptest4, i, netint, values, text, strlen(text),
+ dbl, flt);
+ tracepoint(tp, tptest5, i, netint, values, text, strlen(text),
+ dbl, flt);
+ usleep(nr_usec);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) - 2012 David Goulet <dgoulet@efficios.com>
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED OR
+ * IMPLIED. ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program for any purpose,
+ * provided the above notices are retained on all copies. Permission to modify
+ * the code and to distribute modified code is granted, provided the above
+ * notices are retained, and a notice that the code was modified is included
+ * with the above copyright notice.
+ */
+
+#define TRACEPOINT_CREATE_PROBES
+#include "tp.h"
--- /dev/null
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER tp
+
+#if !defined(_TRACEPOINT_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_TP_H
+
+/*
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose, provided the above notices are retained on all copies.
+ * Permission to modify the code and to distribute modified code is granted,
+ * provided the above notices are retained, and a notice that the code was
+ * modified is included with the above copyright notice.
+ */
+
+#include <lttng/tracepoint.h>
+
+TRACEPOINT_EVENT(tp, tptest1,
+ TP_ARGS(int, anint, int, netint, long *, values,
+ char *, text, size_t, textlen,
+ double, doublearg, float, floatarg),
+ TP_FIELDS(
+ ctf_integer(int, intfield, anint)
+ ctf_integer_hex(int, intfield2, anint)
+ ctf_integer(long, longfield, anint)
+ ctf_integer_network(int, netintfield, netint)
+ ctf_integer_network_hex(int, netintfieldhex, netint)
+ ctf_array(long, arrfield1, values, 3)
+ ctf_array_text(char, arrfield2, text, 10)
+ ctf_sequence(char, seqfield1, text, size_t, textlen)
+ ctf_sequence_text(char, seqfield2, text, size_t, textlen)
+ ctf_string(stringfield, text)
+ ctf_float(float, floatfield, floatarg)
+ ctf_float(double, doublefield, doublearg)
+ )
+)
+TRACEPOINT_EVENT(tp, tptest2,
+ TP_ARGS(int, anint, int, netint, long *, values,
+ char *, text, size_t, textlen,
+ double, doublearg, float, floatarg),
+ TP_FIELDS(
+ ctf_integer(int, intfield, anint)
+ ctf_integer_hex(int, intfield2, anint)
+ ctf_integer(long, longfield, anint)
+ ctf_integer_network(int, netintfield, netint)
+ ctf_integer_network_hex(int, netintfieldhex, netint)
+ ctf_array(long, arrfield1, values, 3)
+ ctf_array_text(char, arrfield2, text, 10)
+ ctf_sequence(char, seqfield1, text, size_t, textlen)
+ ctf_sequence_text(char, seqfield2, text, size_t, textlen)
+ ctf_string(stringfield, text)
+ ctf_float(float, floatfield, floatarg)
+ ctf_float(double, doublefield, doublearg)
+ )
+)
+TRACEPOINT_EVENT(tp, tptest3,
+ TP_ARGS(int, anint, int, netint, long *, values,
+ char *, text, size_t, textlen,
+ double, doublearg, float, floatarg),
+ TP_FIELDS(
+ ctf_integer(int, intfield, anint)
+ ctf_integer_hex(int, intfield2, anint)
+ ctf_integer(long, longfield, anint)
+ ctf_integer_network(int, netintfield, netint)
+ ctf_integer_network_hex(int, netintfieldhex, netint)
+ ctf_array(long, arrfield1, values, 3)
+ ctf_array_text(char, arrfield2, text, 10)
+ ctf_sequence(char, seqfield1, text, size_t, textlen)
+ ctf_sequence_text(char, seqfield2, text, size_t, textlen)
+ ctf_string(stringfield, text)
+ ctf_float(float, floatfield, floatarg)
+ ctf_float(double, doublefield, doublearg)
+ )
+)
+TRACEPOINT_EVENT(tp, tptest4,
+ TP_ARGS(int, anint, int, netint, long *, values,
+ char *, text, size_t, textlen,
+ double, doublearg, float, floatarg),
+ TP_FIELDS(
+ ctf_integer(int, intfield, anint)
+ ctf_integer_hex(int, intfield2, anint)
+ ctf_integer(long, longfield, anint)
+ ctf_integer_network(int, netintfield, netint)
+ ctf_integer_network_hex(int, netintfieldhex, netint)
+ ctf_array(long, arrfield1, values, 3)
+ ctf_array_text(char, arrfield2, text, 10)
+ ctf_sequence(char, seqfield1, text, size_t, textlen)
+ ctf_sequence_text(char, seqfield2, text, size_t, textlen)
+ ctf_string(stringfield, text)
+ ctf_float(float, floatfield, floatarg)
+ ctf_float(double, doublefield, doublearg)
+ )
+)
+TRACEPOINT_EVENT(tp, tptest5,
+ TP_ARGS(int, anint, int, netint, long *, values,
+ char *, text, size_t, textlen,
+ double, doublearg, float, floatarg),
+ TP_FIELDS(
+ ctf_integer(int, intfield, anint)
+ ctf_integer_hex(int, intfield2, anint)
+ ctf_integer(long, longfield, anint)
+ ctf_integer_network(int, netintfield, netint)
+ ctf_integer_network_hex(int, netintfieldhex, netint)
+ ctf_array(long, arrfield1, values, 3)
+ ctf_array_text(char, arrfield2, text, 10)
+ ctf_sequence(char, seqfield1, text, size_t, textlen)
+ ctf_sequence_text(char, seqfield2, text, size_t, textlen)
+ ctf_string(stringfield, text)
+ ctf_float(float, floatfield, floatarg)
+ ctf_float(double, doublefield, doublearg)
+ )
+)
+
+#endif /* _TRACEPOINT_TP_H */
+
+#undef TRACEPOINT_INCLUDE_FILE
+#define TRACEPOINT_INCLUDE_FILE ./tp.h
+
+/* This part must be outside ifdef protection */
+#include <lttng/tracepoint-event.h>