Merge branch 'master' into memleak-finder memleak-finder
authorDavid Goulet <dgoulet@efficios.com>
Thu, 24 Oct 2013 14:23:34 +0000 (10:23 -0400)
committerDavid Goulet <dgoulet@efficios.com>
Thu, 24 Oct 2013 14:23:34 +0000 (10:23 -0400)
215 files changed:
.gitignore
ChangeLog
Makefile.am
README
configure.ac
doc/Makefile.am
doc/live-reading-howto.txt [new file with mode: 0644]
doc/live-reading-protocol.txt [new file with mode: 0644]
doc/man/Makefile.am
doc/man/lttng-health-check.3
doc/man/lttng-relayd.8
doc/man/lttng-sessiond.8
doc/man/lttng.1
doc/proposals/0006-lttng-snapshot.txt [new file with mode: 0644]
doc/snapshot-howto.txt [new file with mode: 0644]
extras/Makefile.am
extras/core-handler/Makefile.am [new file with mode: 0644]
extras/core-handler/README [new file with mode: 0644]
extras/core-handler/crash.c [new file with mode: 0644]
extras/core-handler/handler.sh [new file with mode: 0755]
extras/core-handler/install.sh [new file with mode: 0755]
extras/core-handler/test.sh [new file with mode: 0755]
extras/lttng-bash_completion
include/Makefile.am
include/lttng/health-internal.h [new file with mode: 0644]
include/lttng/health.h [new file with mode: 0644]
include/lttng/lttng-error.h
include/lttng/lttng.h
src/bin/lttng-consumerd/Makefile.am
src/bin/lttng-consumerd/health-consumerd.c [new file with mode: 0644]
src/bin/lttng-consumerd/health-consumerd.h [new file with mode: 0644]
src/bin/lttng-consumerd/lttng-consumerd.c
src/bin/lttng-consumerd/lttng-consumerd.h
src/bin/lttng-relayd/Makefile.am
src/bin/lttng-relayd/cmd-2-4.c [new file with mode: 0644]
src/bin/lttng-relayd/cmd-2-4.h [new file with mode: 0644]
src/bin/lttng-relayd/cmd.h
src/bin/lttng-relayd/ctf-trace.c [new file with mode: 0644]
src/bin/lttng-relayd/ctf-trace.h [new file with mode: 0644]
src/bin/lttng-relayd/health-relayd.c [new file with mode: 0644]
src/bin/lttng-relayd/health-relayd.h [new file with mode: 0644]
src/bin/lttng-relayd/index.c [new file with mode: 0644]
src/bin/lttng-relayd/index.h [new file with mode: 0644]
src/bin/lttng-relayd/live.c [new file with mode: 0644]
src/bin/lttng-relayd/live.h [new file with mode: 0644]
src/bin/lttng-relayd/lttng-relayd.h
src/bin/lttng-relayd/lttng-viewer.h [new file with mode: 0644]
src/bin/lttng-relayd/main.c
src/bin/lttng-sessiond/Makefile.am
src/bin/lttng-sessiond/buffer-registry.c
src/bin/lttng-sessiond/buffer-registry.h
src/bin/lttng-sessiond/channel.c
src/bin/lttng-sessiond/cmd.c
src/bin/lttng-sessiond/cmd.h
src/bin/lttng-sessiond/consumer.c
src/bin/lttng-sessiond/consumer.h
src/bin/lttng-sessiond/context.c
src/bin/lttng-sessiond/event.c
src/bin/lttng-sessiond/event.h
src/bin/lttng-sessiond/health-sessiond.h [new file with mode: 0644]
src/bin/lttng-sessiond/health.c [deleted file]
src/bin/lttng-sessiond/health.h [deleted file]
src/bin/lttng-sessiond/ht-cleanup.c
src/bin/lttng-sessiond/jul-thread.c [new file with mode: 0644]
src/bin/lttng-sessiond/jul-thread.h [new file with mode: 0644]
src/bin/lttng-sessiond/jul.c [new file with mode: 0644]
src/bin/lttng-sessiond/jul.h [new file with mode: 0644]
src/bin/lttng-sessiond/kernel-consumer.c
src/bin/lttng-sessiond/kernel.c
src/bin/lttng-sessiond/kernel.h
src/bin/lttng-sessiond/lttng-sessiond.h
src/bin/lttng-sessiond/lttng-ust-abi.h
src/bin/lttng-sessiond/main.c
src/bin/lttng-sessiond/modprobe.c
src/bin/lttng-sessiond/session.c
src/bin/lttng-sessiond/session.h
src/bin/lttng-sessiond/snapshot.c
src/bin/lttng-sessiond/snapshot.h
src/bin/lttng-sessiond/trace-kernel.c
src/bin/lttng-sessiond/trace-kernel.h
src/bin/lttng-sessiond/trace-ust.c
src/bin/lttng-sessiond/trace-ust.h
src/bin/lttng-sessiond/ust-app.c
src/bin/lttng-sessiond/ust-app.h
src/bin/lttng-sessiond/ust-consumer.c
src/bin/lttng-sessiond/ust-registry.c
src/bin/lttng-sessiond/ust-registry.h
src/bin/lttng-sessiond/ust-thread.c
src/bin/lttng/Makefile.am
src/bin/lttng/commands/add_context.c
src/bin/lttng/commands/calibrate.c
src/bin/lttng/commands/create.c
src/bin/lttng/commands/disable_channels.c
src/bin/lttng/commands/disable_events.c
src/bin/lttng/commands/enable_channels.c
src/bin/lttng/commands/enable_events.c
src/bin/lttng/commands/list.c
src/bin/lttng/commands/set_session.c
src/bin/lttng/commands/snapshot.c
src/bin/lttng/lttng.c
src/bin/lttng/utils.c
src/bin/lttng/utils.h
src/common/Makefile.am
src/common/consumer-metadata-cache.c
src/common/consumer-metadata-cache.h
src/common/consumer-stream.c
src/common/consumer-stream.h
src/common/consumer-timer.c
src/common/consumer-timer.h
src/common/consumer.c
src/common/consumer.h
src/common/defaults.h
src/common/error.c
src/common/hashtable/hashtable.c
src/common/hashtable/hashtable.h
src/common/hashtable/rculfhash.c
src/common/hashtable/utils.c
src/common/hashtable/utils.h
src/common/health/Makefile.am [new file with mode: 0644]
src/common/health/health.c [new file with mode: 0644]
src/common/index/Makefile.am [new file with mode: 0644]
src/common/index/index.c [new file with mode: 0644]
src/common/index/index.h [new file with mode: 0644]
src/common/index/lttng-index.h [new file with mode: 0644]
src/common/kernel-consumer/kernel-consumer.c
src/common/kernel-consumer/kernel-consumer.h
src/common/kernel-ctl/kernel-ctl.c
src/common/kernel-ctl/kernel-ctl.h
src/common/kernel-ctl/kernel-ioctl.h
src/common/relayd/relayd.c
src/common/relayd/relayd.h
src/common/runas.c
src/common/sessiond-comm/Makefile.am
src/common/sessiond-comm/inet.c
src/common/sessiond-comm/inet.h
src/common/sessiond-comm/inet6.c
src/common/sessiond-comm/jul.h [new file with mode: 0644]
src/common/sessiond-comm/relayd.h
src/common/sessiond-comm/sessiond-comm.c
src/common/sessiond-comm/sessiond-comm.h
src/common/sessiond-comm/unix.c
src/common/sessiond-comm/unix.h
src/common/ust-consumer/ust-consumer.c
src/common/ust-consumer/ust-consumer.h
src/common/utils.c
src/common/utils.h
src/lib/lttng-ctl/Makefile.am
src/lib/lttng-ctl/filter/filter-ast.h
src/lib/lttng-ctl/filter/filter-parser.y
src/lib/lttng-ctl/lttng-ctl-health.c [new file with mode: 0644]
src/lib/lttng-ctl/lttng-ctl-helper.h
src/lib/lttng-ctl/lttng-ctl.c
src/lib/lttng-ctl/snapshot.c
tests/Makefile.am
tests/fast_regression
tests/long_regression
tests/regression/Makefile.am
tests/regression/kernel/Makefile.am
tests/regression/kernel/test_event_basic
tests/regression/tools/filtering/Makefile.am
tests/regression/tools/filtering/tp.h
tests/regression/tools/health/Makefile.am
tests/regression/tools/health/health_check.c
tests/regression/tools/health/test_thread_exit
tests/regression/tools/health/test_thread_stall
tests/regression/tools/health/test_tp_fail
tests/regression/tools/snapshots/Makefile.am
tests/regression/tools/snapshots/test_ust
tests/regression/tools/snapshots/test_ust_streaming
tests/regression/tools/streaming/Makefile.am
tests/regression/tools/tracefile-limits/Makefile.am
tests/regression/tools/tracefile-limits/test_tracefile_count
tests/regression/tools/tracefile-limits/test_tracefile_size
tests/regression/ust/Makefile.am
tests/regression/ust/before-after/Makefile.am
tests/regression/ust/buffers-pid/Makefile.am [new file with mode: 0644]
tests/regression/ust/buffers-pid/test_buffers_pid [new file with mode: 0755]
tests/regression/ust/buffers-uid/Makefile.am [deleted file]
tests/regression/ust/buffers-uid/test_buffers_uid [deleted file]
tests/regression/ust/daemon/Makefile.am
tests/regression/ust/exit-fast/Makefile.am
tests/regression/ust/exit-fast/ust_tests_exitfast.h
tests/regression/ust/fork/Makefile.am
tests/regression/ust/high-throughput/Makefile.am
tests/regression/ust/high-throughput/tp.h
tests/regression/ust/libc-wrapper/Makefile.am
tests/regression/ust/linking/Makefile.am
tests/regression/ust/linking/ust_tests_demo.h
tests/regression/ust/linking/ust_tests_demo2.h
tests/regression/ust/linking/ust_tests_demo3.h
tests/regression/ust/low-throughput/Makefile.am
tests/regression/ust/low-throughput/tp.h
tests/regression/ust/multi-session/Makefile.am
tests/regression/ust/multi-session/ust_gen_nevents.h
tests/regression/ust/nprocesses/Makefile.am
tests/regression/ust/overlap/Makefile.am
tests/regression/ust/overlap/demo/Makefile.am
tests/regression/ust/overlap/demo/ust_tests_demo.h
tests/regression/ust/overlap/demo/ust_tests_demo2.h
tests/regression/ust/overlap/demo/ust_tests_demo3.h
tests/regression/ust/periodical-metadata-flush/Makefile.am
tests/regression/ust/periodical-metadata-flush/test_periodical_metadata_flush
tests/stress/Makefile.am
tests/unit/Makefile.am
tests/unit/test_kernel_data.c
tests/utils/Makefile.am
tests/utils/babelstats.pl
tests/utils/tap/Makefile.am
tests/utils/tap/tap.c
tests/utils/testapp/Makefile.am
tests/utils/testapp/gen-ust-events/tp.h
tests/utils/testapp/gen-ust-nevents/Makefile.am [new file with mode: 0644]
tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c [new file with mode: 0644]
tests/utils/testapp/gen-ust-nevents/tp.c [new file with mode: 0644]
tests/utils/testapp/gen-ust-nevents/tp.h [new file with mode: 0644]

index 96a1ab140c43aa9745d77e1800c2c763a10a6723..5d1e2ffa03204d652d980477e1eca9b4da55b274 100644 (file)
@@ -15,6 +15,7 @@ Makefile.in
 *.info
 *.bz2
 *.tar
+.dirstamp
 configure
 aclocal.m4
 autom4te.cache/
@@ -45,6 +46,7 @@ src/lib/lttng-ctl/filter/filter-parser.output
 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
 
@@ -75,5 +77,6 @@ tests/regression/ust/exit-fast/exit-fast
 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/
index af8d9c833660876ec018252570d18122d2459795..4868c2c23c2033af3068b890a1aa889f835b0e2d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,228 @@
+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
index b0537ce6e4f4ea55b049966c33ae64fecc9e35ab..584f59b2e63102d1fb687a988899eb32746f4753 100644 (file)
@@ -11,4 +11,4 @@ dist_doc_DATA = LICENSE \
 
 dist_noinst_DATA = CodingStyle
 
-EXTRA_DIST = extras/lttng-bash_completion
+EXTRA_DIST = extras/lttng-bash_completion gpl-2.0.txt lgpl-2.1.txt
diff --git a/README b/README
index 538996e9e8cd371044f2e82b3ad220b4642ef45b..e38602d53703a05fbe46343918ff92203c7e62c9 100644 (file)
--- a/README
+++ b/README
@@ -47,7 +47,7 @@ REQUIREMENTS:
     - python-dev (optional)
       Python headers
 
-      * Debian/Ubuntu package: python-dev
+      * Debian/Ubuntu package: python3-dev
 
     - For kernel tracing: modprobe
 
@@ -92,6 +92,11 @@ INSTALLATION INSTRUCTIONS:
   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:
 
index b43015249283690a7aea99b10c82d1a3b8e099c2..2b4a340f5e646dfdd4fc1633dd022aa05922ba51 100644 (file)
@@ -1,4 +1,4 @@
-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
@@ -6,8 +6,20 @@ AC_CONFIG_MACRO_DIR([config])
 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"], "")
@@ -98,6 +110,13 @@ AC_ARG_WITH([sessiond-bin],
        [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.])
@@ -105,6 +124,7 @@ AC_DEFINE_UNQUOTED([CONFIG_CONSUMERD64_LIBDIR], "$CONSUMERD64_LIBDIR", [Search f
 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], [],
@@ -287,7 +307,19 @@ LT_INIT
 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"
 
@@ -310,6 +342,7 @@ AC_CONFIG_FILES([
        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
@@ -320,6 +353,8 @@ AC_CONFIG_FILES([
        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
@@ -343,7 +378,7 @@ AC_CONFIG_FILES([
        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
@@ -359,6 +394,7 @@ AC_CONFIG_FILES([
        tests/utils/tap/Makefile
        tests/utils/testapp/Makefile
        tests/utils/testapp/gen-ust-events/Makefile
+       tests/utils/testapp/gen-ust-nevents/Makefile
 ])
 
 AC_OUTPUT
index 8edb6dc1e6e63817728960f6ee335a6dfe46a3f9..6f05d7a78de86977d8bcf845910060d6612e764c 100644 (file)
@@ -1,6 +1,8 @@
 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
diff --git a/doc/live-reading-howto.txt b/doc/live-reading-howto.txt
new file mode 100644 (file)
index 0000000..4a54f1c
--- /dev/null
@@ -0,0 +1,53 @@
+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)
diff --git a/doc/live-reading-protocol.txt b/doc/live-reading-protocol.txt
new file mode 100644 (file)
index 0000000..fcc75b3
--- /dev/null
@@ -0,0 +1,117 @@
+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.
index e20613a022fee1c3594dc9bc203e7fe37ef6b15a..d91a91a8822675664c865b8a5ab9a64225d530bd 100644 (file)
@@ -1,3 +1,2 @@
 dist_man1_MANS = lttng.1
-dist_man3_MANS = lttng-health-check.3
 dist_man8_MANS = lttng-sessiond.8 lttng-relayd.8
index 67aafd4c4668329b24d326a4e6e39f71b2a8f2da..9ab6635aecfafee7deb89407de52f2570444257a 100644 (file)
@@ -1,5 +1,7 @@
 .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
index 44631b1b2bbb4a171d14f9d620f893dd08c1db61..1d1802a4c291131b1c808423b710f7c50c9b5dc1 100644 (file)
@@ -61,6 +61,15 @@ Output base directory. Must use an absolute path (~/lttng-traces is the default)
 .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
index dfc94185ff6f2722ea8f865c26ca2e69e522063b..347bffcaa3ed980e8fdb920582654945bf45b3c9 100644 (file)
@@ -134,9 +134,14 @@ Debug-mode disabling use of clone/fork. Insecure, but required to allow
 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
index 72454f0d3e85ad0f1c6ffeb8e4221e8251a03a07..a16c7c3a7826306c3aa46270fd6f88239991c646 100644 (file)
@@ -1,4 +1,4 @@
-.TH "LTTNG" "1" "December 3rd, 2012" "" ""
+.TH "LTTNG" "1" "July 18th, 2013" "" ""
 
 .SH "NAME"
 lttng \(em LTTng 2.x tracer control command line tool
@@ -6,20 +6,18 @@ 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,
@@ -31,19 +29,18 @@ those traces is done using the babeltrace(1) text viewer.
 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).
@@ -82,9 +79,9 @@ Simple listing of lttng options.
 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,
@@ -96,7 +93,10 @@ For example, this command will add the context information 'prio' and two perf
 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.
@@ -107,30 +107,34 @@ Otherwise the context will be added only to the given channel (\-c).
 
 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
@@ -152,16 +156,21 @@ an empty function, gathering PMU counters LLC (Last Level Cache) misses
 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
@@ -172,10 +181,12 @@ staying on the same CPU must be considered.
 
 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.
@@ -183,29 +194,31 @@ We can conclude from this information that LLC load and store misses can be
 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.
 
@@ -219,87 +232,129 @@ $HOME/lttng-traces.
 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
@@ -308,205 +363,252 @@ contains it.
 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
@@ -514,28 +616,41 @@ NAME\fP again.
 
 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).
@@ -549,74 +664,157 @@ calls events).
 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
@@ -625,75 +823,75 @@ until the trace is readable for the session. Use \-\-no-wait to avoid this
 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
@@ -704,17 +902,22 @@ Note that all command line options override environment variables.
 .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
diff --git a/doc/proposals/0006-lttng-snapshot.txt b/doc/proposals/0006-lttng-snapshot.txt
new file mode 100644 (file)
index 0000000..399fcca
--- /dev/null
@@ -0,0 +1,164 @@
+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);
diff --git a/doc/snapshot-howto.txt b/doc/snapshot-howto.txt
new file mode 100644 (file)
index 0000000..957fb70
--- /dev/null
@@ -0,0 +1,88 @@
+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.
index 925dc2eb9baf47cb58c632a23acddbc42f0b9568..52de61821e9df0905f7f908e5667b77cb0e532a7 100644 (file)
@@ -1 +1 @@
-SUBDIRS = bindings
+SUBDIRS = bindings core-handler
diff --git a/extras/core-handler/Makefile.am b/extras/core-handler/Makefile.am
new file mode 100644 (file)
index 0000000..eaa6b16
--- /dev/null
@@ -0,0 +1,8 @@
+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
diff --git a/extras/core-handler/README b/extras/core-handler/README
new file mode 100644 (file)
index 0000000..039ce3b
--- /dev/null
@@ -0,0 +1,78 @@
+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
+[...]
diff --git a/extras/core-handler/crash.c b/extras/core-handler/crash.c
new file mode 100644 (file)
index 0000000..2b9cf4a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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;
+}
diff --git a/extras/core-handler/handler.sh b/extras/core-handler/handler.sh
new file mode 100755 (executable)
index 0000000..c2d2402
--- /dev/null
@@ -0,0 +1,62 @@
+#!/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
diff --git a/extras/core-handler/install.sh b/extras/core-handler/install.sh
new file mode 100755 (executable)
index 0000000..8116805
--- /dev/null
@@ -0,0 +1,36 @@
+#!/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
diff --git a/extras/core-handler/test.sh b/extras/core-handler/test.sh
new file mode 100755 (executable)
index 0000000..c36fdf7
--- /dev/null
@@ -0,0 +1,49 @@
+#!/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}."
index b515be08d484c9985e921556d9c313c0400d73e7..f5677e9bcef70f532867b490d6927bd805db6139 100644 (file)
 # 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)
@@ -32,25 +47,32 @@ _lttng_cmd_add_context() {
        --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)
@@ -61,29 +83,28 @@ _lttng_cmd_create() {
 
        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)
@@ -94,15 +115,13 @@ _lttng_cmd_enablechannel() {
 
        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)
@@ -112,44 +131,18 @@ _lttng_cmd_enableevent() {
        --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)
@@ -160,56 +153,41 @@ _lttng_cmd_disableconsumer() {
 
        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)
@@ -219,45 +197,75 @@ _lttng_cmd_list() {
 
        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
@@ -266,48 +274,26 @@ _lttng_cmd_stop() {
 }
 
 _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
@@ -325,11 +311,11 @@ _lttng_before_command() {
        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
 }
@@ -337,11 +323,13 @@ _lttng_before_command() {
 _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
@@ -352,14 +340,18 @@ _lttng_is_command() {
        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=""
@@ -367,18 +359,34 @@ _lttng() {
        # 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
index f3413e6dfa376bed65bdfea3db1a22eb0dabf574..e813575a7d6eed44fee8b018f69d593a3d112f2c 100644 (file)
@@ -1,3 +1,9 @@
-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
diff --git a/include/lttng/health-internal.h b/include/lttng/health-internal.h
new file mode 100644 (file)
index 0000000..2225e33
--- /dev/null
@@ -0,0 +1,130 @@
+#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 */
diff --git a/include/lttng/health.h b/include/lttng/health.h
new file mode 100644 (file)
index 0000000..9960928
--- /dev/null
@@ -0,0 +1,127 @@
+#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 */
index 8d2e1d0022bb101dc528e0b67a50019214fce6a5..7631dbcc2aaf95204f878272c784daccc3a80627 100644 (file)
@@ -81,8 +81,8 @@ enum lttng_error_code {
        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. */
@@ -92,7 +92,7 @@ enum lttng_error_code {
        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 */
@@ -113,8 +113,8 @@ enum lttng_error_code {
        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 */
index 83232454f865c5b38d5ed00dcd117c8b538c40e0..0307536d387e600c8d8127b923883af3a56d5385 100644 (file)
@@ -47,18 +47,9 @@ extern "C" {
  * 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. */
 };
 
 /*
@@ -126,6 +117,7 @@ enum lttng_event_context_type {
        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 {
@@ -280,7 +272,7 @@ struct lttng_event_field {
  *
  * 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 */
@@ -291,6 +283,8 @@ struct lttng_channel_attr {
        /* 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];
 };
@@ -325,12 +319,14 @@ struct lttng_calibrate {
  *
  * 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];
 };
@@ -389,6 +385,34 @@ extern void lttng_destroy_handle(struct lttng_handle *handle);
  */
 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.
  *
@@ -625,7 +649,8 @@ int lttng_disable_consumer(struct lttng_handle *handle);
  *
  * 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
index d340c68bab69420c26c373bcd6e3fdad34407791..1aa0f27f70cbeff0eeaa2ddc13dd3e884a8221f9 100644 (file)
@@ -2,12 +2,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
 
 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
diff --git a/src/bin/lttng-consumerd/health-consumerd.c b/src/bin/lttng-consumerd/health-consumerd.c
new file mode 100644 (file)
index 0000000..413df9a
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * 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;
+}
diff --git a/src/bin/lttng-consumerd/health-consumerd.h b/src/bin/lttng-consumerd/health-consumerd.h
new file mode 100644 (file)
index 0000000..210b3a3
--- /dev/null
@@ -0,0 +1,41 @@
+#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 */
index 8ddd5a372005490bb37f1ef8c01bc8045dabdbde..04adb973c76a198acd06fa7dc000ead230cca61c 100644 (file)
 #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;
@@ -72,6 +74,19 @@ static enum lttng_consumer_type opt_type = LTTNG_CONSUMER_KERNEL;
 /* 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
  */
@@ -137,9 +152,9 @@ static void usage(FILE *fp)
        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");
@@ -149,6 +164,8 @@ static void usage(FILE *fp)
                        "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                          "
@@ -172,6 +189,7 @@ static void parse_args(int argc, char **argv)
                { "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' },
@@ -185,7 +203,7 @@ static void parse_args(int argc, char **argv)
 
        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;
                }
@@ -206,6 +224,9 @@ static void parse_args(int argc, char **argv)
                case 'd':
                        opt_daemon = 1;
                        break;
+               case 'g':
+                       tracing_group_name = optarg;
+                       break;
                case 'h':
                        usage(stdout);
                        exit(EXIT_SUCCESS);
@@ -325,6 +346,11 @@ int main(int argc, char **argv)
                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);
@@ -367,25 +393,35 @@ int main(int argc, char **argv)
        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 */
@@ -412,25 +448,21 @@ int main(int argc, char **argv)
                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:
@@ -461,6 +493,17 @@ metadata_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);
@@ -476,6 +519,9 @@ error:
 end:
        lttng_consumer_destroy(ctx);
        lttng_consumer_cleanup();
+       if (health_consumerd) {
+               health_app_destroy(health_consumerd);
+       }
 
        return ret;
 }
index 9b34547c19f5718fc3a86c33361da2e897c1592e..5662429ad4c82e958ff47219d88cc8642f173e00 100644 (file)
@@ -19,4 +19,8 @@
 #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 */
index ed8214429a48310337f8289f066347b5bd2bdfa9..1e675454ccea25d9294f72c19f1a056d9a6dc16e 100644 (file)
@@ -7,9 +7,12 @@ AM_CFLAGS = -fno-strict-aliasing
 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 \
@@ -17,4 +20,6 @@ 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
diff --git a/src/bin/lttng-relayd/cmd-2-4.c b/src/bin/lttng-relayd/cmd-2-4.c
new file mode 100644 (file)
index 0000000..f199b83
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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;
+}
diff --git a/src/bin/lttng-relayd/cmd-2-4.h b/src/bin/lttng-relayd/cmd-2-4.h
new file mode 100644 (file)
index 0000000..fc61134
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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 */
index 14634954b1f3a83b863585861b1497dfc4b3023d..c8b37d5e55ad29d10b528326fbf5fce8ce4456c5 100644 (file)
@@ -22,5 +22,6 @@
 #include "cmd-generic.h"
 #include "cmd-2-1.h"
 #include "cmd-2-2.h"
+#include "cmd-2-4.h"
 
 #endif /* RELAYD_CMD_H */
diff --git a/src/bin/lttng-relayd/ctf-trace.c b/src/bin/lttng-relayd/ctf-trace.c
new file mode 100644 (file)
index 0000000..adfbac3
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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;
+}
+
diff --git a/src/bin/lttng-relayd/ctf-trace.h b/src/bin/lttng-relayd/ctf-trace.h
new file mode 100644 (file)
index 0000000..6e39af0
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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 */
diff --git a/src/bin/lttng-relayd/health-relayd.c b/src/bin/lttng-relayd/health-relayd.c
new file mode 100644 (file)
index 0000000..61e5036
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * 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;
+}
diff --git a/src/bin/lttng-relayd/health-relayd.h b/src/bin/lttng-relayd/health-relayd.h
new file mode 100644 (file)
index 0000000..6bfb73b
--- /dev/null
@@ -0,0 +1,41 @@
+#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 */
diff --git a/src/bin/lttng-relayd/index.c b/src/bin/lttng-relayd/index.c
new file mode 100644 (file)
index 0000000..8cacdd2
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * 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();
+}
diff --git a/src/bin/lttng-relayd/index.h b/src/bin/lttng-relayd/index.h
new file mode 100644 (file)
index 0000000..3ca7263
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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 */
diff --git a/src/bin/lttng-relayd/live.c b/src/bin/lttng-relayd/live.c
new file mode 100644 (file)
index 0000000..1bcab0a
--- /dev/null
@@ -0,0 +1,1888 @@
+/*
+ * 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;
+}
diff --git a/src/bin/lttng-relayd/live.h b/src/bin/lttng-relayd/live.h
new file mode 100644 (file)
index 0000000..5d8b77d
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 */
index 9bbafe1dd75745625a3518ac60ed13293220a7cd..6fdcca29189edb7e70583326fcebf43d7f1fa9df 100644 (file)
 #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
@@ -33,8 +38,10 @@ struct relay_cmd_queue {
 };
 
 enum connection_type {
-       RELAY_DATA,
-       RELAY_CONTROL,
+       RELAY_DATA                  = 1,
+       RELAY_CONTROL               = 2,
+       RELAY_VIEWER_COMMAND        = 3,
+       RELAY_VIEWER_NOTIFICATION   = 4,
 };
 
 /*
@@ -48,6 +55,23 @@ struct relay_session {
         */
        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;
 };
 
 /*
@@ -60,6 +84,10 @@ struct relay_stream {
        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;
@@ -69,11 +97,65 @@ struct relay_stream {
        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;
 };
 
 /*
@@ -91,8 +173,22 @@ struct relay_command {
        /* 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 */
diff --git a/src/bin/lttng-relayd/lttng-viewer.h b/src/bin/lttng-relayd/lttng-viewer.h
new file mode 100644 (file)
index 0000000..56fa563
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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 */
index 3b9f6708a3685e61e0a6918e94732046a9b9151f..f943488be5b94309efd332196bd52e876d2eea1a 100644 (file)
 #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
@@ -82,6 +88,7 @@ static int dispatch_thread_exit;
 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;
@@ -98,6 +105,22 @@ static struct relay_cmd_queue relay_cmd_queue;
 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
  */
@@ -111,6 +134,7 @@ void usage(void)
        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
@@ -124,6 +148,7 @@ int parse_args(int argc, char **argv)
                { "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', },
@@ -132,7 +157,7 @@ int parse_args(int argc, char **argv)
 
        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;
@@ -168,6 +193,9 @@ int parse_args(int argc, char **argv)
                case 'd':
                        opt_daemon = 1;
                        break;
+               case 'g':
+                       tracing_group_name = optarg;
+                       break;
                case 'h':
                        usage();
                        exit(EXIT_FAILURE);
@@ -221,6 +249,21 @@ int parse_args(int argc, char **argv)
                        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;
@@ -262,6 +305,18 @@ int notify_thread_pipe(int wpipe)
        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.
  */
@@ -277,6 +332,8 @@ void stop_threads(void)
                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);
@@ -482,6 +539,10 @@ void *relay_thread_listener(void *data)
 
        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;
@@ -513,10 +574,14 @@ void *relay_thread_listener(void *data)
        }
 
        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.
@@ -531,6 +596,8 @@ restart:
 
                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);
@@ -625,8 +692,10 @@ error_sock_relay:
        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;
@@ -638,17 +707,25 @@ error_sock_control:
 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) {
@@ -677,10 +754,20 @@ void *relay_thread_dispatcher(void *data)
                } 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;
@@ -690,15 +777,13 @@ error:
  * 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);
@@ -719,17 +804,79 @@ void deferred_free_stream(struct rcu_head *head)
 {
        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;
@@ -743,26 +890,45 @@ void relay_delete_session(struct relay_command *cmd, struct lttng_ht *streams_ht
        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;
 }
 
 /*
@@ -772,7 +938,8 @@ void relay_delete_session(struct relay_command *cmd, struct lttng_ht *streams_ht
  */
 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;
@@ -792,10 +959,24 @@ int relay_create_session(struct lttcomm_relayd_hdr *recv_hdr,
 
        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:
@@ -819,7 +1000,7 @@ 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;
@@ -856,6 +1037,10 @@ int relay_add_stream(struct lttcomm_relayd_hdr *recv_hdr,
        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) {
@@ -868,7 +1053,7 @@ int relay_add_stream(struct lttcomm_relayd_hdr *recv_hdr,
         * 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;
@@ -880,12 +1065,38 @@ int relay_add_stream(struct lttcomm_relayd_hdr *recv_hdr,
                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);
@@ -921,14 +1132,13 @@ err_free_stream:
  */
 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");
 
@@ -952,8 +1162,7 @@ int relay_close_stream(struct lttcomm_relayd_hdr *recv_hdr,
        }
 
        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;
@@ -963,18 +1172,7 @@ int relay_close_stream(struct lttcomm_relayd_hdr *recv_hdr,
        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:
@@ -1077,7 +1275,7 @@ end:
  */
 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;
@@ -1129,8 +1327,8 @@ int relay_recv_metadata(struct lttcomm_relayd_hdr *recv_hdr,
        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;
@@ -1151,6 +1349,8 @@ int relay_recv_metadata(struct lttcomm_relayd_hdr *recv_hdr,
        if (ret < 0) {
                goto end_unlock;
        }
+       metadata_stream->ctf_trace->metadata_received +=
+               payload_size + be32toh(metadata_struct->padding_size);
 
        DBG2("Relay metadata written");
 
@@ -1165,7 +1365,7 @@ end:
  */
 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;
@@ -1187,18 +1387,14 @@ int relay_send_version(struct lttcomm_relayd_hdr *recv_hdr,
                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;
        }
@@ -1231,7 +1427,7 @@ 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;
@@ -1265,7 +1461,7 @@ int relay_data_pending(struct lttcomm_relayd_hdr *recv_hdr,
        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;
@@ -1309,7 +1505,7 @@ end_no_session:
  */
 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;
@@ -1342,7 +1538,8 @@ int relay_quiescent_control(struct lttcomm_relayd_hdr *recv_hdr,
        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,
@@ -1371,7 +1568,7 @@ end_no_session:
  */
 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;
@@ -1382,7 +1579,6 @@ int relay_begin_data_pending(struct lttcomm_relayd_hdr *recv_hdr,
 
        assert(recv_hdr);
        assert(cmd);
-       assert(streams_ht);
 
        DBG("Init streams for data pending");
 
@@ -1413,7 +1609,8 @@ int relay_begin_data_pending(struct lttcomm_relayd_hdr *recv_hdr,
         * 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,
@@ -1445,7 +1642,7 @@ end_no_session:
  */
 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;
@@ -1457,7 +1654,6 @@ int relay_end_data_pending(struct lttcomm_relayd_hdr *recv_hdr,
 
        assert(recv_hdr);
        assert(cmd);
-       assert(streams_ht);
 
        DBG("End data pending command");
 
@@ -1484,7 +1680,8 @@ int relay_end_data_pending(struct lttcomm_relayd_hdr *recv_hdr,
 
        /* 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;
@@ -1508,44 +1705,178 @@ end_no_session:
 }
 
 /*
- * 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:
@@ -1559,13 +1890,95 @@ end:
        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;
@@ -1588,10 +2001,10 @@ int relay_process_data(struct relay_command *cmd, struct lttng_ht *streams_ht)
        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);
@@ -1603,7 +2016,7 @@ int relay_process_data(struct relay_command *cmd, struct lttng_ht *streams_ht)
                        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;
@@ -1621,32 +2034,45 @@ int relay_process_data(struct relay_command *cmd, struct lttng_ht *streams_ht)
                        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,
@@ -1654,29 +2080,18 @@ int relay_process_data(struct relay_command *cmd, struct lttng_ht *streams_ht)
 
        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;
@@ -1715,6 +2130,19 @@ int relay_add_connection(int fd, struct lttng_poll_event *events,
                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();
@@ -1743,19 +2171,20 @@ void deferred_free_connection(struct rcu_head *head)
 
 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);
 }
 
 /*
@@ -1771,23 +2200,28 @@ void *relay_thread_worker(void *data)
        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);
@@ -1804,9 +2238,13 @@ restart:
        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.
@@ -1829,6 +2267,8 @@ restart:
                        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) {
@@ -1867,8 +2307,7 @@ restart:
                                        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;
                                        }
@@ -1876,8 +2315,7 @@ restart:
                                        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;
                                        }
@@ -1891,8 +2329,7 @@ restart:
                                                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) {
@@ -1900,14 +2337,12 @@ restart:
                                                                                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;
@@ -1936,6 +2371,9 @@ restart:
                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;
@@ -1949,6 +2387,8 @@ restart:
                        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;
@@ -1973,13 +2413,12 @@ restart:
                                                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
@@ -1999,6 +2438,9 @@ restart:
                last_seen_data_fd = -1;
        }
 
+       /* Normal exit, no error */
+       ret = 0;
+
 exit:
 error:
        lttng_poll_clean(&events);
@@ -2006,19 +2448,20 @@ error:
        /* 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 */
@@ -2028,8 +2471,13 @@ relay_connections_ht_error:
        }
        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;
 }
 
@@ -2053,6 +2501,7 @@ int main(int argc, char **argv)
 {
        int ret = 0;
        void *status;
+       struct relay_local_data *relay_ctx;
 
        /* Create thread quit pipe */
        if ((ret = init_thread_quit_pipe()) < 0) {
@@ -2092,10 +2541,12 @@ int main(int argc, char **argv)
                }
        }
 
-       /* 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;
@@ -2114,6 +2565,53 @@ int main(int argc, char **argv)
        /* 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);
@@ -2124,7 +2622,7 @@ int main(int argc, char **argv)
 
        /* 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;
@@ -2138,27 +2636,63 @@ int main(int argc, char **argv)
                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) {
index 77cc1d21bd07acae7699d5a659175b8907797710..ec0750c68966ae92b878e076fff551c24b2b89d8 100644 (file)
@@ -22,16 +22,17 @@ lttng_sessiond_SOURCES = utils.c utils.h \
                        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
@@ -46,7 +47,8 @@ lttng_sessiond_LDADD = -lrt -lurcu-common -lurcu \
                $(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
index 93da2f12253270c54856bc9fe303ff392141d260..bab1f309f54560d12f56a8be8675d0532d8772bd 100644 (file)
@@ -377,6 +377,7 @@ void buffer_reg_stream_add(struct buffer_reg_stream *stream,
 
        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);
 }
 
@@ -504,6 +505,7 @@ void buffer_reg_channel_destroy(struct buffer_reg_channel *regp,
                /* Wipe stream */
                cds_list_for_each_entry_safe(sreg, stmp, &regp->streams, lnode) {
                        cds_list_del(&sreg->lnode);
+                       regp->stream_count--;
                        buffer_reg_stream_destroy(sreg, domain);
                }
 
index cb976dcdc3db25609bb32838a8abe50266908439..c2099b6883c7ff70174431635b33080eb3e4d4b2 100644 (file)
@@ -43,10 +43,14 @@ struct buffer_reg_channel {
        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;
index d826125c9b8c7acaf5a57f294aeb7d29f78803ea..817100ff449128e2f71705623d6ac4a39d95e0f4 100644 (file)
@@ -65,6 +65,7 @@ struct lttng_channel *channel_new_default_attr(int dom,
                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) {
@@ -76,6 +77,8 @@ struct lttng_channel *channel_new_default_attr(int dom,
                                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:
@@ -86,6 +89,8 @@ struct lttng_channel *channel_new_default_attr(int dom,
                                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;
@@ -184,6 +189,12 @@ int channel_kernel_create(struct ltt_kernel_session *ksession,
                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) {
@@ -263,6 +274,12 @@ int channel_ust_create(struct ltt_ust_session *usess,
                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
index a6a81c5dbd79989b474ffa1ee5b5d3c5edc83389..8dd3fcc60c914c9df4979ac46150aea1c518c147 100644 (file)
@@ -30,7 +30,7 @@
 #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"
@@ -182,6 +182,55 @@ static void list_lttng_channels(int domain, struct ltt_session *session,
        }
 }
 
+/*
+ * 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.
  */
@@ -462,9 +511,6 @@ static int init_kernel_tracing(struct ltt_kernel_session *session)
        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);
@@ -550,7 +596,8 @@ error:
  */
 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;
@@ -576,7 +623,8 @@ static int send_consumer_relayd_socket(int domain, unsigned int session_id,
 
        /* 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;
@@ -618,7 +666,8 @@ error:
  * 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;
 
@@ -628,7 +677,8 @@ static int send_consumer_relayd_sockets(int domain, unsigned int session_id,
        /* 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;
                }
@@ -637,7 +687,8 @@ static int send_consumer_relayd_sockets(int domain, unsigned int session_id,
        /* 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;
                }
@@ -674,12 +725,11 @@ int cmd_setup_relayd(struct ltt_session *session)
                /* 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;
@@ -693,12 +743,11 @@ int cmd_setup_relayd(struct ltt_session *session)
                        && 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;
@@ -857,6 +906,8 @@ int cmd_enable_channel(struct ltt_session *session,
 
        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.
@@ -866,7 +917,15 @@ int cmd_enable_channel(struct ltt_session *session,
                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:
@@ -876,7 +935,19 @@ int cmd_enable_channel(struct ltt_session *session,
                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);
                }
@@ -896,7 +967,19 @@ int cmd_enable_channel(struct ltt_session *session,
 
                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);
                }
@@ -931,6 +1014,16 @@ int cmd_disable_event(struct ltt_session *session, int domain,
 
                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;
@@ -952,6 +1045,16 @@ int cmd_disable_event(struct ltt_session *session, int domain,
 
                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) {
@@ -968,6 +1071,19 @@ int cmd_disable_event(struct ltt_session *session, int domain,
                                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:
@@ -1003,6 +1119,16 @@ int cmd_disable_event_all(struct ltt_session *session, int domain,
 
                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;
@@ -1024,6 +1150,16 @@ int cmd_disable_event_all(struct ltt_session *session, int domain,
 
                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) {
@@ -1038,6 +1174,19 @@ int cmd_disable_event_all(struct ltt_session *session, int domain,
 
                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
@@ -1077,7 +1226,6 @@ int cmd_add_context(struct ltt_session *session, int domain,
                        }
                        chan_kern_created = 1;
                }
-
                /* Add kernel context to kernel tracer */
                ret = context_kernel_add(session->kernel_session, ctx, channel_name);
                if (ret != LTTNG_OK) {
@@ -1087,10 +1235,11 @@ int cmd_add_context(struct ltt_session *session, int domain,
        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 */
@@ -1173,6 +1322,17 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain,
        {
                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) {
@@ -1222,6 +1382,16 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain,
 
                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);
@@ -1255,6 +1425,46 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain,
                }
                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:
@@ -1294,6 +1504,17 @@ int cmd_enable_event_all(struct ltt_session *session,
 
                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) {
@@ -1358,6 +1579,16 @@ int cmd_enable_event_all(struct ltt_session *session,
 
                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);
@@ -1404,6 +1635,41 @@ int cmd_enable_event_all(struct ltt_session *session,
                        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
@@ -1447,6 +1713,13 @@ ssize_t cmd_list_tracepoints(int domain, struct lttng_event **events)
                        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;
@@ -1676,7 +1949,7 @@ 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;
@@ -1714,6 +1987,7 @@ int cmd_create_session_uri(char *name, struct lttng_uri *uris,
        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) {
@@ -1743,6 +2017,72 @@ find_error:
        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.
  */
@@ -1864,13 +2204,15 @@ int cmd_register_consumer(struct ltt_session *session, int domain,
                        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;
                }
@@ -1926,6 +2268,10 @@ ssize_t cmd_list_domains(struct ltt_session *session,
        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));
@@ -1943,6 +2289,12 @@ ssize_t cmd_list_domains(struct ltt_session *session,
                (*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;
@@ -2033,6 +2385,12 @@ ssize_t cmd_list_events(int domain, struct ltt_session *session,
                }
                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;
@@ -2094,13 +2452,14 @@ void cmd_list_lttng_sessions(struct lttng_session *sessions, uid_t uid,
                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)
 {
@@ -2156,8 +2515,8 @@ int cmd_snapshot_add_output(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;
@@ -2188,27 +2547,6 @@ int cmd_snapshot_add_output(struct ltt_session *session,
                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) {
@@ -2233,26 +2571,31 @@ int cmd_snapshot_del_output(struct ltt_session *session,
                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;
@@ -2288,8 +2631,8 @@ ssize_t cmd_snapshot_list_outputs(struct ltt_session *session,
        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;
@@ -2350,12 +2693,12 @@ error:
  * 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;
 
@@ -2378,8 +2721,10 @@ static int set_relayd_for_snapshot(struct consumer_output *consumer,
        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;
                }
@@ -2393,10 +2738,11 @@ 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;
 
@@ -2408,28 +2754,35 @@ static int record_kernel_snapshot(struct ltt_kernel_session *ksess,
        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;
 }
@@ -2437,10 +2790,11 @@ error:
 /*
  * 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;
 
@@ -2452,32 +2806,68 @@ static int record_ust_snapshot(struct ltt_ust_session *usess,
        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.
  *
@@ -2490,15 +2880,17 @@ int cmd_snapshot_record(struct ltt_session *session,
                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;
@@ -2513,15 +2905,9 @@ int cmd_snapshot_record(struct ltt_session *session,
 
        /* 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;
@@ -2530,16 +2916,27 @@ int cmd_snapshot_record(struct ltt_session *session,
                        }
                        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;
@@ -2547,11 +2944,33 @@ int cmd_snapshot_record(struct ltt_session *session,
                        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();
                }
@@ -2560,11 +2979,13 @@ int cmd_snapshot_record(struct ltt_session *session,
        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;
@@ -2572,20 +2993,45 @@ int cmd_snapshot_record(struct ltt_session *session,
                        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;
 }
 
index 5be1688c9c97b502bd3292820741a618eb3afa61..6502a58a6ce5c09a78d037999c94c7d7b9816d5d 100644 (file)
@@ -29,6 +29,8 @@ void cmd_init(void);
 
 /* 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);
 
index d3c561c9ec6932531fc3c46a1a464835dc45e12f..55a2af4f794fa9681cddf90e91ba3ce3ecdd03b0 100644 (file)
 #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.
@@ -48,14 +137,8 @@ int consumer_recv_status_reply(struct consumer_socket *sock)
 
        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;
        }
 
@@ -89,14 +172,8 @@ int consumer_recv_status_channel(struct consumer_socket *sock,
        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;
        }
 
@@ -127,24 +204,15 @@ int consumer_send_destroy_relayd(struct consumer_socket *sock,
        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. */
@@ -152,9 +220,8 @@ int consumer_send_destroy_relayd(struct consumer_socket *sock,
 
        DBG2("Consumer send destroy relayd command done");
 
-error_send:
-       pthread_mutex_unlock(sock->lock);
 error:
+       pthread_mutex_unlock(sock->lock);
        return ret;
 }
 
@@ -213,7 +280,7 @@ int consumer_create_socket(struct consumer_data *data,
        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;
@@ -300,18 +367,20 @@ struct consumer_socket *consumer_find_socket(int key,
 /*
  * 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;
@@ -375,8 +444,8 @@ void consumer_destroy_socket(struct consumer_socket *sock)
         * 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);
@@ -408,6 +477,28 @@ error:
        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.
  *
@@ -419,17 +510,9 @@ void consumer_destroy_output(struct consumer_output *obj)
                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);
        }
@@ -493,13 +576,13 @@ int consumer_copy_sockets(struct consumer_output *dst,
        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;
@@ -634,10 +717,10 @@ int consumer_send_fds(struct consumer_socket *sock, int *fds, size_t nb_fd)
        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;
        }
 
@@ -657,13 +740,9 @@ int consumer_send_msg(struct consumer_socket *sock,
 
        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;
        }
 
@@ -683,18 +762,12 @@ int consumer_send_channel(struct consumer_socket *sock,
 
        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;
 }
@@ -709,6 +782,7 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg,
                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,
@@ -723,7 +797,8 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg,
                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);
 
@@ -736,6 +811,7 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *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;
@@ -748,6 +824,7 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg,
        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));
 
@@ -778,7 +855,8 @@ void consumer_init_channel_comm_msg(struct lttcomm_consumer_msg *msg,
                int type,
                uint64_t tracefile_size,
                uint64_t tracefile_count,
-               unsigned int monitor)
+               unsigned int monitor,
+               unsigned int live_timer_interval)
 {
        assert(msg);
 
@@ -798,6 +876,7 @@ void consumer_init_channel_comm_msg(struct lttcomm_consumer_msg *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));
@@ -840,16 +919,7 @@ int consumer_send_stream(struct consumer_socket *sock,
        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;
        }
@@ -870,7 +940,8 @@ 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;
@@ -886,6 +957,18 @@ int consumer_send_relayd_socket(struct consumer_socket *consumer_sock,
                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
@@ -897,15 +980,8 @@ int consumer_send_relayd_socket(struct consumer_socket *consumer_sock,
        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;
        }
@@ -998,15 +1074,9 @@ int consumer_is_data_pending(uint64_t session_id,
        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;
                }
@@ -1016,18 +1086,11 @@ int consumer_is_data_pending(uint64_t session_id,
                 * 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) {
@@ -1056,7 +1119,6 @@ int consumer_flush_channel(struct consumer_socket *socket, uint64_t key)
        struct lttcomm_consumer_msg msg;
 
        assert(socket);
-       assert(socket->fd >= 0);
 
        DBG2("Consumer flush channel key %" PRIu64, key);
 
@@ -1089,7 +1151,6 @@ int consumer_close_metadata(struct consumer_socket *socket,
        struct lttcomm_consumer_msg msg;
 
        assert(socket);
-       assert(socket->fd >= 0);
 
        DBG2("Consumer close metadata channel key %" PRIu64, metadata_key);
 
@@ -1122,7 +1183,6 @@ int consumer_setup_metadata(struct consumer_socket *socket,
        struct lttcomm_consumer_msg msg;
 
        assert(socket);
-       assert(socket->fd >= 0);
 
        DBG2("Consumer setup metadata channel key %" PRIu64, metadata_key);
 
@@ -1156,9 +1216,8 @@ int consumer_push_metadata(struct consumer_socket *socket,
        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;
@@ -1171,9 +1230,10 @@ int consumer_push_metadata(struct consumer_socket *socket,
                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;
        }
@@ -1196,13 +1256,12 @@ 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);
 
@@ -1211,15 +1270,16 @@ int consumer_snapshot_channel(struct consumer_socket *socket, uint64_t key,
        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;
@@ -1227,9 +1287,10 @@ int consumer_snapshot_channel(struct consumer_socket *socket, uint64_t key,
                }
        } 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;
index c3c8dfb7f1a15386c45dbd236bd34e2811df87f1..484d8f7bad90b149c5d72e6571bcc23ff60f6979 100644 (file)
@@ -33,8 +33,13 @@ enum consumer_dst_type {
 };
 
 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.
         */
@@ -87,13 +92,24 @@ struct consumer_data {
        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;
 };
 
@@ -147,6 +163,9 @@ struct consumer_output {
         */
        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;
@@ -157,7 +176,7 @@ struct consumer_socket *consumer_find_socket(int key,
                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,
@@ -165,6 +184,11 @@ 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);
@@ -181,7 +205,8 @@ int consumer_send_channel(struct consumer_socket *sock,
                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);
@@ -199,6 +224,7 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg,
                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,
@@ -213,7 +239,8 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg,
                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,
@@ -233,7 +260,8 @@ void consumer_init_channel_comm_msg(struct lttcomm_consumer_msg *msg,
                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,
@@ -248,6 +276,6 @@ int consumer_flush_channel(struct consumer_socket *socket, uint64_t key);
 /* 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 */
index b55254b4e398d0bfd55604c5fb8353dd3ba7f43a..6aacbad0ea1c96ad3375739ca908f765f1ba4370 100644 (file)
@@ -132,6 +132,7 @@ static int add_uctx_to_channel(struct ltt_ust_session *usess, int domain,
        /* 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);
 
index 9a8ed63d108696b223876ae0236097bddcd4ec2b..77818e587d4511f65eba47f38f71d5d292cc7f1e 100644 (file)
@@ -607,3 +607,180 @@ error:
        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;
+}
index 819111599c15da37b2d75bd0e11ce2f3fcc464bd..1b92a6a7f563e1524f344be40113f99303a27f87 100644 (file)
@@ -45,4 +45,10 @@ int event_ust_enable_all_tracepoints(struct ltt_ust_session *usess,
 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 */
diff --git a/src/bin/lttng-sessiond/health-sessiond.h b/src/bin/lttng-sessiond/health-sessiond.h
new file mode 100644 (file)
index 0000000..22ea1bb
--- /dev/null
@@ -0,0 +1,39 @@
+#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 */
diff --git a/src/bin/lttng-sessiond/health.c b/src/bin/lttng-sessiond/health.c
deleted file mode 100644 (file)
index dcf3b96..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * 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, &current_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, &current_time, sizeof(current_time));
-       } else {
-               if (time_diff_gt(&current_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, &current_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();
-}
diff --git a/src/bin/lttng-sessiond/health.h b/src/bin/lttng-sessiond/health.h
deleted file mode 100644 (file)
index d04d18b..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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 */
index 7a33840c992fa1e3bb6bb8c2db38195ffb331dcb..890c9a8215e26967c5ff6b42b5cf171295cdc306 100644 (file)
@@ -23,7 +23,7 @@
 #include <common/utils.h>
 
 #include "lttng-sessiond.h"
-#include "health.h"
+#include "health-sessiond.h"
 
 void *thread_ht_cleanup(void *data)
 {
@@ -36,7 +36,7 @@ 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();
 
@@ -132,7 +132,7 @@ error_poll_create:
                health_error();
                ERR("Health error occurred in %s", __func__);
        }
-       health_unregister();
+       health_unregister(health_sessiond);
        rcu_thread_offline();
        rcu_unregister_thread();
        return NULL;
diff --git a/src/bin/lttng-sessiond/jul-thread.c b/src/bin/lttng-sessiond/jul-thread.c
new file mode 100644 (file)
index 0000000..9721860
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * 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;
+}
diff --git a/src/bin/lttng-sessiond/jul-thread.h b/src/bin/lttng-sessiond/jul-thread.h
new file mode 100644 (file)
index 0000000..2f81d88
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 */
diff --git a/src/bin/lttng-sessiond/jul.c b/src/bin/lttng-sessiond/jul.c
new file mode 100644 (file)
index 0000000..a16e9b3
--- /dev/null
@@ -0,0 +1,850 @@
+/*
+ * 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();
+}
diff --git a/src/bin/lttng-sessiond/jul.h b/src/bin/lttng-sessiond/jul.h
new file mode 100644 (file)
index 0000000..1e32ea5
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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 */
index bf3559a21fa021a55e5cebeb7a582b72e80b87de..70759fb36d90149d8964967d3b20608c60bcc9ff 100644 (file)
@@ -26,7 +26,7 @@
 #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,
@@ -123,7 +123,8 @@ int kernel_consumer_add_channel(struct consumer_socket *sock,
                        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();
 
@@ -184,7 +185,7 @@ int kernel_consumer_add_metadata(struct consumer_socket *sock,
                        DEFAULT_KERNEL_CHANNEL_OUTPUT,
                        CONSUMER_CHANNEL_TYPE_METADATA,
                        0, 0,
-                       monitor);
+                       monitor, 0);
 
        health_code_update();
 
@@ -371,7 +372,6 @@ int kernel_consumer_destroy_channel(struct consumer_socket *socket,
 
        assert(channel);
        assert(socket);
-       assert(socket->fd >= 0);
 
        DBG("Sending kernel consumer destroy channel key %d", channel->fd);
 
@@ -400,7 +400,6 @@ int kernel_consumer_destroy_metadata(struct consumer_socket *socket,
 
        assert(metadata);
        assert(socket);
-       assert(socket->fd >= 0);
 
        DBG("Sending kernel consumer destroy channel key %d", metadata->fd);
 
index 6f2604c607d2c1bc5fe4c24a53a2cf0458a4053d..123be4be523d24194309cc93e5c9a52e9fba16c7 100644 (file)
@@ -136,11 +136,11 @@ int kernel_create_channel(struct ltt_kernel_session *session,
                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);
@@ -819,15 +819,16 @@ void kernel_destroy_channel(struct ltt_kernel_channel *kchan)
 /*
  * 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);
@@ -853,13 +854,15 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess,
                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
@@ -881,12 +884,27 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess,
 
                /* 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;
                        }
                }
@@ -894,7 +912,8 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess,
                /* 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;
@@ -910,8 +929,8 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess,
 
 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");
        }
 
index a87405a4dfcd7e0372f4461b90f512a83a987713..78d12aba3ddc0bd68bd0699181f9457eca898075 100644 (file)
@@ -55,7 +55,7 @@ int kernel_validate_version(int tracer_fd);
 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);
 
index aeb03037073f67dedab460903482891ccb5489e5..e21f6d0126c66bf3903683e4caadb8b1078ab557 100644 (file)
@@ -103,6 +103,11 @@ extern int ht_cleanup_pipe[2];
  */
 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);
 
index 03d2e65f6059ed4bd6359ef10f860fd7c79c95ac..98470e4d16a86d9d2324ce26c8408627c4bb6be3 100644 (file)
@@ -44,7 +44,7 @@
 #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 {
@@ -139,6 +139,7 @@ enum lttng_ust_context_type {
        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
index a117a1f1cfc8dd12cd02049455dbe82384825e04..05646279a63a48b3f81bbf64d67454b9aaf53a1c 100644 (file)
 #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;
@@ -90,7 +88,6 @@ static struct consumer_data kconsumer_data = {
        .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,
@@ -102,7 +99,6 @@ static struct consumer_data ustconsumer64_data = {
        .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,
@@ -114,7 +110,6 @@ static struct consumer_data ustconsumer32_data = {
        .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,
@@ -162,6 +157,7 @@ static pthread_t kernel_thread;
 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
@@ -236,6 +232,12 @@ static int app_socket_timeout;
 /* 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)
 {
@@ -325,25 +327,6 @@ int sessiond_check_thread_quit_pipe(int fd, uint32_t events)
        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.
  *
@@ -406,13 +389,13 @@ static void close_consumer_sockets(void)
        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) {
@@ -424,13 +407,13 @@ static void close_consumer_sockets(void)
        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");
                }
        }
 }
@@ -441,12 +424,15 @@ static void close_consumer_sockets(void)
 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);
 
        /*
@@ -460,18 +446,54 @@ static void cleanup(void)
                }
        }
 
-       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");
@@ -675,6 +697,8 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd)
                                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
@@ -688,9 +712,6 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd)
                                        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,
@@ -725,6 +746,12 @@ static void update_ust_app(int app_sock)
 {
        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);
@@ -750,7 +777,7 @@ static void *thread_manage_kernel(void *data)
 
        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
@@ -875,7 +902,7 @@ error_testpoint:
                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;
 }
@@ -916,7 +943,7 @@ static void *thread_manage_consumer(void *data)
 
        DBG("[thread] Manage consumer started");
 
-       health_register(HEALTH_TYPE_CONSUMER);
+       health_register(health_sessiond, HEALTH_SESSIOND_TYPE_CONSUMER);
 
        health_code_update();
 
@@ -1014,15 +1041,16 @@ restart:
                /* 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) {
@@ -1035,7 +1063,7 @@ restart:
                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));
@@ -1055,7 +1083,7 @@ restart:
        }
 
        /* 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;
@@ -1114,7 +1142,7 @@ restart_poll:
                                                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);
@@ -1133,6 +1161,13 @@ restart_poll:
 
 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);
@@ -1149,22 +1184,21 @@ 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);
@@ -1176,6 +1210,11 @@ error:
        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:
@@ -1183,7 +1222,7 @@ error_poll:
                health_error();
                ERR("Health error occurred in %s", __func__);
        }
-       health_unregister();
+       health_unregister(health_sessiond);
        DBG("consumer thread cleanup completed");
 
        return NULL;
@@ -1203,7 +1242,7 @@ static void *thread_manage_apps(void *data)
        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;
@@ -1291,12 +1330,6 @@ static void *thread_manage_apps(void *data)
                                                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();
@@ -1343,7 +1376,7 @@ error_testpoint:
                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();
@@ -1354,6 +1387,9 @@ error_testpoint:
  * 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().
  */
@@ -1361,9 +1397,14 @@ static int send_socket_to_thread(int fd, int sock)
 {
        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));
@@ -1481,7 +1522,7 @@ static void *thread_dispatch_ust_registration(void *data)
                .count = 0,
        };
 
-       health_register(HEALTH_TYPE_APP_REG_DISPATCH);
+       health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH);
 
        health_code_update();
 
@@ -1626,7 +1667,12 @@ static void *thread_dispatch_ust_registration(void *data)
                                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;
                                }
 
@@ -1651,7 +1697,12 @@ static void *thread_dispatch_ust_registration(void *data)
                                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;
                                }
 
@@ -1682,7 +1733,7 @@ error:
                health_error();
                ERR("Health error occurred in %s", __func__);
        }
-       health_unregister();
+       health_unregister(health_sessiond);
        return NULL;
 }
 
@@ -1702,7 +1753,7 @@ static void *thread_registration_apps(void *data)
 
        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;
@@ -1781,6 +1832,18 @@ static void *thread_registration_apps(void *data)
                                                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.
@@ -1882,7 +1945,7 @@ error_listen:
 error_create_poll:
 error_testpoint:
        DBG("UST Registration thread cleanup complete");
-       health_unregister();
+       health_unregister(health_sessiond);
 
        return NULL;
 }
@@ -2083,6 +2146,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                                "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:
@@ -2121,6 +2185,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                        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);
@@ -2166,6 +2231,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                        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);
@@ -2251,21 +2317,6 @@ error:
        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.
  */
@@ -2364,6 +2415,7 @@ static int copy_session_consumer(int domain, struct ltt_session *session)
                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) {
@@ -2407,6 +2459,7 @@ static int create_ust_session(struct ltt_session *session,
        assert(session->consumer);
 
        switch (domain->type) {
+       case LTTNG_DOMAIN_JUL:
        case LTTNG_DOMAIN_UST:
                break;
        default:
@@ -2426,6 +2479,8 @@ static int create_ust_session(struct ltt_session *session,
        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 */
@@ -2483,6 +2538,7 @@ static int create_kernel_session(struct ltt_session *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;
 
@@ -2538,6 +2594,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
 
        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:
@@ -2600,6 +2658,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
        /* 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:
@@ -2691,8 +2751,13 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
                }
 
                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;
@@ -2778,6 +2843,7 @@ skip_domain:
        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;
@@ -3040,7 +3106,7 @@ skip_domain:
                }
 
                ret = cmd_create_session_uri(cmd_ctx->lsm->session.name, uris, nb_uri,
-                       &cmd_ctx->creds);
+                       &cmd_ctx->creds, 0);
 
                free(uris);
 
@@ -3291,6 +3357,84 @@ skip_domain:
                                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;
@@ -3324,8 +3468,8 @@ static 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 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");
 
@@ -3342,6 +3486,27 @@ static void *thread_manage_health(void *data)
                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.
@@ -3432,58 +3597,18 @@ restart:
 
                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) {
@@ -3534,7 +3659,7 @@ static void *thread_manage_clients(void *data)
 
        rcu_register_thread();
 
-       health_register(HEALTH_TYPE_CMD);
+       health_register(health_sessiond, HEALTH_SESSIOND_TYPE_CMD);
 
        if (testpoint(thread_manage_clients)) {
                goto error_testpoint;
@@ -3758,7 +3883,7 @@ error_testpoint:
                ERR("Health error occurred in %s", __func__);
        }
 
-       health_unregister();
+       health_unregister(health_sessiond);
 
        DBG("Client thread dying");
 
@@ -3795,6 +3920,7 @@ static void usage(void)
        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");
 }
 
 /*
@@ -3827,12 +3953,13 @@ static int parse_args(int argc, char **argv)
                { "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;
@@ -3855,7 +3982,7 @@ static int parse_args(int argc, char **argv)
                        opt_daemon = 1;
                        break;
                case 'g':
-                       opt_tracing_group = optarg;
+                       tracing_group_name = optarg;
                        break;
                case 'h':
                        usage();
@@ -3912,6 +4039,24 @@ static int parse_args(int argc, char **argv)
                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 */
@@ -4016,14 +4161,7 @@ static int set_permissions(char *rundir)
        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);
@@ -4032,8 +4170,12 @@ static int set_permissions(char *rundir)
                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");
@@ -4047,21 +4189,21 @@ static int set_permissions(char *rundir)
        }
 
        /* 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");
@@ -4069,7 +4211,6 @@ static int set_permissions(char *rundir)
 
        DBG("All permissions are set");
 
-end:
        return ret;
 }
 
@@ -4106,7 +4247,7 @@ static int set_consumer_sockets(struct consumer_data *consumer_data,
        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;
@@ -4124,7 +4265,7 @@ static int set_consumer_sockets(struct consumer_data *consumer_data,
 
        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");
@@ -4133,6 +4274,14 @@ static int set_consumer_sockets(struct consumer_data *consumer_data,
                }
                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 =
@@ -4143,6 +4292,16 @@ static int set_consumer_sockets(struct consumer_data *consumer_data,
                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);
@@ -4467,6 +4626,12 @@ int main(int argc, char **argv)
         */
        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" */
 
        /*
@@ -4571,6 +4736,21 @@ int main(int argc, char **argv)
 
        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);
@@ -4624,7 +4804,15 @@ int main(int argc, char **argv)
                        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 */
@@ -4645,12 +4833,27 @@ int main(int argc, char **argv)
        }
 
 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) {
@@ -4704,6 +4907,8 @@ exit_health:
                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.
index 7e06dadb136f1b314683d07d15225e5e4cfbcfbc..a010e7cd76ab92b66d12ecef024efc5e98f07a98 100644 (file)
@@ -55,6 +55,8 @@ const struct kern_modules_param kern_modules_list[] = {
        { "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 },
index a777b041428443fe856b4e7613920a377e7b494f..07031a30a5c5bc6cf603b84847aac2a866919ede 100644 (file)
@@ -194,6 +194,16 @@ int session_create(char *name, uid_t uid, gid_t gid)
                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;
index 7b927efe1103aa8950c16530f86a3ec5582497e8..e052d6d337e10ee69490df9df9181962bd01b248 100644 (file)
@@ -18,6 +18,7 @@
 #ifndef _LTT_SESSION_H
 #define _LTT_SESSION_H
 
+#include <limits.h>
 #include <urcu/list.h>
 
 #include <common/hashtable/hashtable.h>
@@ -59,6 +60,7 @@ struct ltt_session_list {
  */
 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;
        /*
@@ -93,6 +95,16 @@ struct ltt_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 */
index b14a398c343ba58d2cab3a50b726f2d33475b515..6b1aa8db1085c5edf2c01e404e3ce9387561c17d 100644 (file)
@@ -34,25 +34,26 @@ static inline unsigned long get_next_output_id(struct snapshot *snapshot)
 }
 
 /*
- * 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);
        }
@@ -74,18 +75,12 @@ int snapshot_output_init(uint64_t max_size, const char *name,
                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) {
@@ -119,6 +114,49 @@ int snapshot_output_init(uint64_t max_size, const char *name,
 
 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;
 }
@@ -187,6 +225,32 @@ void snapshot_output_destroy(struct snapshot_output *obj)
        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.
index bf594b5fe3f79d7cb3bd99cdd67afec2b483a9a5..bdf0570366061c1691745a0a08c5ba4ceb5cc77e 100644 (file)
@@ -32,6 +32,8 @@ struct consumer_output;
 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;
@@ -50,6 +52,11 @@ struct snapshot_output {
 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;
 };
 
@@ -69,7 +76,13 @@ 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 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 */
index d889b6ab6582570587c9c4a66cdcf51fad30943a..a5c215fae98a068f359c0bf182e307a693dc9244 100644 (file)
@@ -38,6 +38,13 @@ struct ltt_kernel_channel *trace_kernel_get_channel_by_name(
        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) {
@@ -144,6 +151,16 @@ struct ltt_kernel_channel *trace_kernel_create_channel(
        }
        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;
@@ -191,7 +208,6 @@ struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev)
                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';
index 68301967a15f7cae616f85f24b42afc2c9f83fe7..934aaffe89f5174015a3fe4d9637eeeabbd818bc 100644 (file)
@@ -113,6 +113,8 @@ struct ltt_kernel_session {
        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;
 };
 
 /*
index 65fe84b7d37617a17d3d4a68130d2138158f1a8d..b64597599caaaa9e127306f0c1b0bee806c508f0 100644 (file)
@@ -128,6 +128,13 @@ struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht,
        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) {
@@ -184,6 +191,7 @@ error:
  */
 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 */
@@ -203,7 +211,7 @@ struct ltt_ust_session *trace_ust_create_session(uint64_t session_id)
         * 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. */
@@ -211,6 +219,10 @@ struct ltt_ust_session *trace_ust_create_session(uint64_t session_id)
 
        /* 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) {
@@ -231,6 +243,7 @@ struct ltt_ust_session *trace_ust_create_session(uint64_t session_id)
 
 error_consumer:
        ht_cleanup_push(lus->domain_global.channels);
+       jul_destroy_domain(&lus->domain_jul);
        free(lus);
 error:
        return NULL;
@@ -268,12 +281,22 @@ struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *chan)
                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);
@@ -432,6 +455,9 @@ struct ltt_ust_context *trace_ust_create_context(
        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;
@@ -445,6 +471,7 @@ struct ltt_ust_context *trace_ust_create_context(
 
        uctx->ctx.ctx = utype;
        lttng_ht_node_init_ulong(&uctx->node, (unsigned long) uctx->ctx.ctx);
+       CDS_INIT_LIST_HEAD(&uctx->list);
 
        return uctx;
 
@@ -473,11 +500,16 @@ static void destroy_contexts(struct lttng_ht *ht)
        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);
@@ -651,6 +683,7 @@ void trace_ust_destroy_session(struct ltt_ust_session *session)
 
        /* 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,
index 0dac36b213f5d76f7f82c4c1b8c677e5d3b79c0e..f25573a80d8015e5ca69280a1419d0a256d0acc7 100644 (file)
@@ -27,6 +27,7 @@
 #include <common/defaults.h>
 
 #include "consumer.h"
+#include "jul.h"
 #include "ust-ctl.h"
 
 struct ltt_ust_ht_key {
@@ -39,6 +40,7 @@ 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 */
@@ -56,6 +58,7 @@ struct ltt_ust_channel {
        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;
@@ -82,6 +85,7 @@ struct ltt_ust_session {
        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;
@@ -107,6 +111,9 @@ struct ltt_ust_session {
        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 */
 };
 
 /*
index c329650c9b4de709f3e810229d4d19b7ab23a905..11b588f77f675aef1a60d9a3c265cec76f2fb4bf 100644 (file)
@@ -34,7 +34,7 @@
 
 #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"
@@ -364,6 +364,7 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan,
 
        /* 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);
@@ -825,6 +826,7 @@ struct ust_app_channel *alloc_ust_app_channel(char *name,
        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) {
@@ -916,6 +918,8 @@ struct ust_app_ctx *alloc_ust_app_ctx(struct lttng_ust_context *uctx)
                goto error;
        }
 
+       CDS_INIT_LIST_HEAD(&ua_ctx->list);
+
        if (uctx) {
                memcpy(&ua_ctx->ctx, uctx, sizeof(ua_ctx->ctx));
        }
@@ -953,8 +957,7 @@ error:
  * 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;
@@ -1051,6 +1054,12 @@ int create_ust_channel_context(struct ust_app_channel *ua_chan,
                        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;
@@ -1089,6 +1098,12 @@ int set_ust_event_filter(struct ust_app_event *ua_event,
                        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;
@@ -1118,6 +1133,12 @@ static int disable_ust_event(struct ust_app *app,
                                        "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;
@@ -1148,6 +1169,12 @@ static int disable_ust_channel(struct ust_app *app,
                                        "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;
@@ -1178,6 +1205,12 @@ static int enable_ust_channel(struct ust_app *app,
                                        "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;
@@ -1210,6 +1243,12 @@ static int enable_ust_event(struct ust_app *app,
                                        "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;
@@ -1290,6 +1329,12 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess,
                        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;
@@ -1395,7 +1440,7 @@ static void shadow_copy_channel(struct ust_app_channel *ua_chan,
        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;
@@ -1403,6 +1448,7 @@ static void shadow_copy_channel(struct ust_app_channel *ua_chan,
                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 */
@@ -1457,6 +1503,7 @@ static void shadow_copy_session(struct ust_app_session *ua_sess,
        /* 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:
@@ -1727,6 +1774,13 @@ static int create_ust_app_session(struct ltt_ust_session *usess,
                                                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) {
@@ -1795,6 +1849,7 @@ int create_ust_app_channel_context(struct ust_app_session *ua_sess,
 
        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) {
@@ -2134,6 +2189,7 @@ static int create_buffer_reg_channel(struct buffer_reg_session *reg_sess,
        }
        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,
@@ -2993,6 +3049,12 @@ int ust_app_list_events(struct lttng_event **events)
                                                        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;
                        }
@@ -3087,6 +3149,12 @@ int ust_app_list_event_fields(struct lttng_event_field **fields)
                                                        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;
                        }
@@ -3702,6 +3770,13 @@ skip_setup:
                                        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;
        }
@@ -3777,6 +3852,12 @@ int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app)
                                        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;
        }
@@ -3800,6 +3881,7 @@ int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app)
                (void) push_metadata(registry, ua_sess->consumer);
        }
 
+end_unlock:
        pthread_mutex_unlock(&ua_sess->lock);
 end_no_session:
        rcu_read_unlock();
@@ -3854,8 +3936,11 @@ int ust_app_flush_trace(struct ltt_ust_session *usess, struct ust_app *app)
                        } 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;
@@ -3961,7 +4046,7 @@ int ust_app_stop_trace_all(struct ltt_ust_session *usess)
                }
        }
 
-       /* Flush buffers */
+       /* Flush buffers and push metadata (for UID buffers). */
        switch (usess->buffer_type) {
        case LTTNG_BUFFER_PER_UID:
        {
@@ -3969,6 +4054,7 @@ int ust_app_stop_trace_all(struct ltt_ust_session *usess)
 
                /* 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;
 
@@ -3989,7 +4075,14 @@ int ust_app_stop_trace_all(struct ltt_ust_session *usess)
                                 */
                                (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:
@@ -4043,7 +4136,7 @@ int ust_app_destroy_trace_all(struct ltt_ust_session *usess)
 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;
@@ -4058,7 +4151,7 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock)
 
        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
@@ -4115,8 +4208,11 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock)
                        }
                }
 
-               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;
@@ -4646,7 +4742,8 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name,
         */
        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
@@ -4871,58 +4968,215 @@ void ust_app_destroy(struct ust_app *app)
  * 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;
+}
index 9116a2192fdbaa2c4ac920c6bce3e7b95e1c2089..680ab6f2fe0339c180c9da2e86a4e2ea5787150c 100644 (file)
@@ -21,6 +21,8 @@
 #include <stdint.h>
 
 #include <common/compat/uuid.h>
+
+#include "jul.h"
 #include "trace-ust.h"
 #include "ust-registry.h"
 
@@ -102,6 +104,7 @@ struct ust_app_ctx {
        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 {
@@ -141,7 +144,14 @@ struct ust_app_channel {
        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;
@@ -199,6 +209,7 @@ struct ust_app_session {
        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 */
 };
 
 /*
@@ -250,6 +261,13 @@ struct ust_app {
         * 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
@@ -309,7 +327,15 @@ ssize_t ust_app_push_metadata(struct ust_registry_session *registry,
                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 */
 
@@ -509,11 +535,32 @@ void ust_app_destroy(struct ust_app *app)
 }
 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 */
index 9f3557cd0d02efa77f0e41be54bcc06d670b210e..ee56d6dea67b61406fd1d5ba946c03a23e6612a3 100644 (file)
@@ -28,7 +28,7 @@
 #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"
@@ -147,6 +147,7 @@ static int ask_channel_creation(struct ust_app_session *ua_sess,
                        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,
@@ -161,11 +162,12 @@ static int ask_channel_creation(struct ust_app_session *ua_sess,
                        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;
        }
@@ -206,7 +208,6 @@ int ust_consumer_ask_channel(struct ust_app_session *ua_sess,
        assert(ua_chan);
        assert(consumer);
        assert(socket);
-       assert(socket->fd >= 0);
        assert(registry);
 
        if (!consumer->enabled) {
@@ -241,7 +242,6 @@ int ust_consumer_get_channel(struct consumer_socket *socket,
 
        assert(ua_chan);
        assert(socket);
-       assert(socket->fd >= 0);
 
        msg.cmd_type = LTTNG_CONSUMER_GET_CHANNEL;
        msg.u.get_channel.key = ua_chan->key;
@@ -256,11 +256,11 @@ int ust_consumer_get_channel(struct consumer_socket *socket,
        }
 
        /* 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.");
                }
@@ -279,7 +279,7 @@ int ust_consumer_get_channel(struct consumer_socket *socket,
                }
 
                /* 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) {
@@ -289,7 +289,7 @@ int ust_consumer_get_channel(struct consumer_socket *socket,
                        }
                        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.");
                        }
@@ -336,7 +336,6 @@ int ust_consumer_destroy_channel(struct consumer_socket *socket,
 
        assert(ua_chan);
        assert(socket);
-       assert(socket->fd >= 0);
 
        msg.cmd_type = LTTNG_CONSUMER_DESTROY_CHANNEL;
        msg.u.destroy_channel.key = ua_chan->key;
@@ -444,10 +443,8 @@ int ust_consumer_metadata_request(struct consumer_socket *socket)
        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;
        }
 
index e22fc6fcfbfc93246172db98e398be6783b99285..65ba82c1b99b16bb51991161bc32f42dafcadcae 100644 (file)
@@ -23,6 +23,7 @@
 #include <lttng/lttng.h>
 
 #include "ust-registry.h"
+#include "ust-app.h"
 #include "utils.h"
 
 /*
@@ -71,16 +72,78 @@ static unsigned long ht_hash_event(void *_key, unsigned long seed)
        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");
@@ -185,7 +248,8 @@ end:
 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;
@@ -222,7 +286,7 @@ int ust_registry_create_event(struct ust_registry_session *session,
        }
 
        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;
index a82aeb208295bb1af8b55a8d00da879dfd1e9ed1..f195b7447b4122c8792ffa0302ffc3418349ab49 100644 (file)
@@ -225,7 +225,8 @@ void ust_registry_session_destroy(struct ust_registry_session *session);
 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,
index 67c2971d627ba36e8ec934bf6acf07bdd32a8e8b..85803e472217901a50eeec10e6ace02c3c426ca1 100644 (file)
@@ -23,7 +23,7 @@
 #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.
@@ -39,7 +39,8 @@ void *ust_thread_manage_notify(void *data)
        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();
 
@@ -176,7 +177,7 @@ error_poll_create:
                health_error();
                ERR("Health error occurred in %s", __func__);
        }
-       health_unregister();
+       health_unregister(health_sessiond);
        rcu_thread_offline();
        rcu_unregister_thread();
        return NULL;
index b7aa05d28bd967745c5873242250ed160f494258..c3a43fa2f2182b45d4d371dcf79d405a8e60fffe 100644 (file)
@@ -1,6 +1,8 @@
 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 \
index 4904d2772b5181bb835d928595671b456b38f562..3cd00f3d3411ac2e9b0801c3c8d421cc5feb8744 100644 (file)
@@ -67,6 +67,7 @@ enum context_type {
        CONTEXT_VPPID        = 9,
        CONTEXT_PTHREAD_ID   = 10,
        CONTEXT_HOSTNAME     = 11,
+       CONTEXT_IP           = 12,
 };
 
 /*
@@ -203,6 +204,7 @@ const struct ctx_opts {
        { "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),
@@ -366,7 +368,7 @@ static int add_context(char *session_name)
        } 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;
        }
@@ -478,7 +480,7 @@ int cmd_add_context(int argc, const char **argv)
                                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:
index 9c524bd12509286b91331153757c1313fe6c8d3b..092632cd78ffdea24da1261777d816d264e99c67 100644 (file)
@@ -73,8 +73,8 @@ static struct poptOption long_options[] = {
         * 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}
 };
@@ -128,7 +128,7 @@ static int calibrate_lttng(void)
        } 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;
        }
index 72702d5a54743a1d91a652ebb6707ff428f156bd..d234c06a4f3f12afd891fce9e6e67b579d9e2483 100644 (file)
@@ -17,6 +17,7 @@
 
 #define _GNU_SOURCE
 #include <assert.h>
+#include <ctype.h>
 #include <popt.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -33,6 +34,7 @@
 #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;
@@ -41,11 +43,14 @@ static char *opt_ctrl_url;
 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[] = {
@@ -59,6 +64,8 @@ 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}
 };
 
@@ -67,7 +74,7 @@ static struct poptOption long_options[] = {
  * 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
@@ -82,7 +89,17 @@ static void usage(FILE *ofp)
        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");
@@ -170,6 +187,46 @@ error:
        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.
@@ -276,7 +333,7 @@ static int create_session(void)
                        print_str_url = alloc_url + strlen("file://");
                }
        } else {
-               /* No output means --no-output. */
+               /* No output means --no-output or --snapshot mode. */
                url = NULL;
        }
 
@@ -286,7 +343,34 @@ static int create_session(void)
                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) {
@@ -300,8 +384,13 @@ static int create_session(void)
        }
 
        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);
@@ -310,8 +399,14 @@ static int create_session(void)
        }
 
        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 */
@@ -342,6 +437,7 @@ error:
 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);
@@ -355,6 +451,27 @@ int cmd_create(int argc, const char **argv)
                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;
index d861b37704a67aa52d01473cc04032eb2b67e96e..1aa3916aecb116a0122b50b2128b8111acc093ae 100644 (file)
@@ -65,7 +65,7 @@ static struct poptOption long_options[] = {
  */
 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");
@@ -93,7 +93,7 @@ static int disable_channels(char *session_name)
        } 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;
        }
@@ -116,7 +116,7 @@ static int disable_channels(char *session_name)
                        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 */
index c3a0f1da9abd7a91874b13ff74bdab0d2902b157..e71234342deb7f66bc751e5d33d73ebe26460354 100644 (file)
@@ -32,6 +32,7 @@ static char *opt_channel_name;
 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;
@@ -52,6 +53,7 @@ static struct poptOption long_options[] = {
        {"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 */
@@ -69,7 +71,7 @@ static struct poptOption long_options[] = {
  */
 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");
@@ -79,9 +81,22 @@ static void usage(FILE *ofp)
        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
  *
@@ -89,7 +104,7 @@ static void usage(FILE *ofp)
  */
 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;
 
@@ -100,22 +115,15 @@ static int disable_events(char *session_name)
                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) {
@@ -131,7 +139,7 @@ static int disable_events(char *session_name)
                }
 
                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;
        }
 
@@ -143,11 +151,16 @@ static int disable_events(char *session_name)
                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);
                }
 
@@ -162,9 +175,6 @@ error:
        if (warn) {
                ret = CMD_WARNING;
        }
-       if (opt_channel_name == NULL) {
-               free(channel_name);
-       }
        lttng_destroy_handle(handle);
 
        return ret;
index 49e16abf43f5184c35ad0274e9641096c3853192..18f8d020eef5089ef4309ec9608a3ebd5589fcd7 100644 (file)
@@ -89,7 +89,7 @@ static struct poptOption long_options[] = {
  */
 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");
@@ -130,8 +130,8 @@ static void usage(FILE *ofp)
                        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");
@@ -202,18 +202,18 @@ static int enable_channel(char *session_name)
                }
        } 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;
        }
@@ -269,6 +269,7 @@ static int enable_channel(char *session_name)
                        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;
@@ -280,8 +281,7 @@ static int enable_channel(char *session_name)
                        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 */
index 18793ae095a77cbe8365f09ac0576a06011ea407..a7b70c3f9dada482aa76cdd3113ccfa61535d4e0 100644 (file)
@@ -36,6 +36,7 @@ static int opt_loglevel_type;
 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;
@@ -72,6 +73,7 @@ static struct poptOption long_options[] = {
        {"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},
@@ -105,6 +107,7 @@ static void usage(FILE *ofp)
        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");
@@ -113,11 +116,11 @@ static void usage(FILE *ofp)
        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");
@@ -153,7 +156,7 @@ static void usage(FILE *ofp)
        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");
@@ -168,7 +171,7 @@ static void usage(FILE *ofp)
        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");
@@ -312,12 +315,24 @@ int loglevel_str_to_value(const char *inputstr)
        }
 }
 
+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;
@@ -340,22 +355,18 @@ static int enable_events(char *session_name)
        } 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) {
@@ -392,11 +403,15 @@ static int enable_events(char *session_name)
                                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;
@@ -406,28 +421,32 @@ static int enable_events(char *session_name)
                        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:
@@ -444,14 +463,17 @@ static int enable_events(char *session_name)
                        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;
@@ -473,7 +495,8 @@ static int enable_events(char *session_name)
                /* 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 */
@@ -528,7 +551,7 @@ static int enable_events(char *session_name)
 #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 */
@@ -560,8 +583,18 @@ static int enable_events(char *session_name)
                        } 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;
                }
@@ -573,17 +606,23 @@ static int enable_events(char *session_name)
                                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));
                        }
                }
 
@@ -595,14 +634,16 @@ static int enable_events(char *session_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;
@@ -620,9 +661,6 @@ error:
        if (warn) {
                ret = CMD_WARNING;
        }
-       if (opt_channel_name == NULL) {
-               free(channel_name);
-       }
        lttng_destroy_handle(handle);
 
        return ret;
index c96d27c2aad8bada8657687993412f8f9d25bfc8..bca06e75d49cecd7bee2b07a03aa415f7e468aba 100644 (file)
@@ -27,6 +27,7 @@
 
 static int opt_userspace;
 static int opt_kernel;
+static int opt_jul;
 static char *opt_channel;
 static int opt_domain;
 static int opt_fields;
@@ -52,6 +53,7 @@ static struct poptOption long_options[] = {
        /* 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},
@@ -71,7 +73,7 @@ static struct poptOption long_options[] = {
  */
 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");
@@ -82,6 +84,7 @@ static void usage(FILE *ofp)
        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");
@@ -128,13 +131,23 @@ static
 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)
 {
@@ -294,6 +307,60 @@ static void print_event_field(struct lttng_event_field *field)
                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.
  */
@@ -464,6 +531,44 @@ error:
        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.
  */
@@ -620,13 +725,16 @@ static int list_sessions(const char *session_name)
                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));
                }
        }
 
@@ -677,6 +785,9 @@ static int list_domains(const char *session_name)
                case LTTNG_DOMAIN_UST:
                        MSG("  - UST global");
                        break;
+               case LTTNG_DOMAIN_JUL:
+                       MSG("  - JUL (Java Util Logging)");
+                       break;
                default:
                        break;
                }
@@ -738,9 +849,12 @@ int cmd_list(int argc, const char **argv)
        } 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;
@@ -749,7 +863,7 @@ int cmd_list(int argc, const char **argv)
        }
 
        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;
@@ -773,6 +887,13 @@ int cmd_list(int argc, const char **argv)
                                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);
@@ -814,6 +935,9 @@ int cmd_list(int argc, const char **argv)
                                                        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;
@@ -830,6 +954,14 @@ int cmd_list(int argc, const char **argv)
                                        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;
index 1f79d6cf08e6f17c97f972a58269cb0b102362f9..4238016e40867c897299678d41097e2e497b33c6 100644 (file)
@@ -45,7 +45,7 @@ static struct poptOption long_options[] = {
  */
 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");
index 2a223d44509721fedbdbcbe6bc2f3c12da72828a..a357730d008444d1d1b1db4ffc36dc40637bd19e 100644 (file)
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <common/utils.h>
 #include <lttng/snapshot.h>
 
 #include "../command.h"
@@ -49,6 +50,7 @@ enum {
        OPT_HELP = 1,
        OPT_LIST_OPTIONS,
        OPT_MAX_SIZE,
+       OPT_LIST_COMMANDS,
 };
 
 static struct poptOption snapshot_opts[] = {
@@ -58,8 +60,9 @@ 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}
 };
 
@@ -76,13 +79,13 @@ static struct cmd_struct actions[] = {
  */
 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");
@@ -91,15 +94,16 @@ static void usage(FILE *ofp)
        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");
@@ -211,7 +215,7 @@ error:
 /*
  * 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;
@@ -222,7 +226,14 @@ static int del_output(uint32_t id)
                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;
@@ -233,8 +244,13 @@ static int del_output(uint32_t id)
                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);
@@ -297,6 +313,8 @@ end:
 static int cmd_del_output(int argc, const char **argv)
 {
        int ret = CMD_SUCCESS;
+       char *name;
+       long id;
 
        if (argc < 2) {
                usage(stderr);
@@ -304,7 +322,17 @@ static int cmd_del_output(int argc, const char **argv)
                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;
@@ -323,12 +351,10 @@ static int record(const char *url)
        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);
@@ -343,8 +369,6 @@ static int record(const char *url)
        } 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:
@@ -418,25 +442,20 @@ int cmd_snapshot(int argc, const char **argv)
                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;
index 5755db6379daa5466cd1542a6709bfb97318e724..dc9dd9241b9847eb12466fefdefa8120314321ad 100644 (file)
@@ -150,25 +150,6 @@ static void list_options(FILE *ofp)
        }
 }
 
-/*
- *  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
  */
@@ -466,7 +447,7 @@ static int parse_args(int argc, char **argv)
                        ret = 0;
                        goto end;
                case OPT_DUMP_COMMANDS:
-                       list_commands(stdout);
+                       list_commands(commands, stdout);
                        ret = 0;
                        goto end;
                default:
index 6041655ddda0f6eb42de5b829cb29688c11f1a24..556728da23773f19935d0ea8b484a1b2cf901664 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #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
@@ -56,6 +62,24 @@ error:
        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
@@ -230,3 +254,25 @@ int get_count_order_ulong(unsigned long x)
 
        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;
+}
index 57bec1250fe1ea237b0e6913b0160bd83b3c646c..9dacbb20f09513be2b604fd0eb1e00c5a161fd0c 100644 (file)
 
 #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);
 
 /*
@@ -41,4 +46,12 @@ int get_count_order_u64(uint64_t x);
  */
 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 */
index aedbc22bd87b507c4e8762729b4ff91d9fcd2bf4..0c02d533c021105cd73c6d1e105f77b251955fba 100644 (file)
@@ -1,7 +1,7 @@
 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
 
index 0c20e7fc7cbba6850a3337fc4ebb788d3e5d5f84..d597e64e37883c91182bcaf9c54327cd9cc0d54f 100644 (file)
@@ -105,15 +105,17 @@ int consumer_metadata_cache_write(struct lttng_consumer_channel *channel,
        }
 
        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:
@@ -177,11 +179,6 @@ void consumer_metadata_cache_destroy(struct lttng_consumer_channel *channel)
 
        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);
@@ -193,27 +190,37 @@ void consumer_metadata_cache_destroy(struct lttng_consumer_channel *channel)
  * 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. */
@@ -224,7 +231,10 @@ int consumer_metadata_cache_flushed(struct lttng_consumer_channel *channel,
        }
 
        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;
 }
index b1a4dc2b777fad31b91a8ecf040b439f180776c3..aaf9f24d2a5a978b4c0024d290be86910a1ae0df 100644 (file)
@@ -25,9 +25,9 @@ struct consumer_metadata_cache {
        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).
         */
@@ -54,6 +54,6 @@ int consumer_metadata_cache_write(struct lttng_consumer_channel *channel,
 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 */
index 723ec829f80095ff17bd3e5996540d01d754072a..808cae236ded5d142a6c5b962b27e760c41eac2a 100644 (file)
@@ -24,6 +24,8 @@
 #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>
 
@@ -135,6 +137,14 @@ void consumer_stream_close(struct lttng_consumer_stream *stream)
                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);
@@ -280,6 +290,7 @@ void consumer_stream_destroy(struct lttng_consumer_stream *stream,
                 */
                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);
@@ -293,6 +304,7 @@ void consumer_stream_destroy(struct lttng_consumer_stream *stream,
                        consumer_data.need_update = 1;
 
                        pthread_mutex_unlock(&stream->lock);
+                       pthread_mutex_unlock(&stream->chan->lock);
                        pthread_mutex_unlock(&consumer_data.lock);
                } else {
                        /*
@@ -312,3 +324,170 @@ void consumer_stream_destroy(struct lttng_consumer_stream *stream,
        /* 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;
+}
index 3096e1e7801d535084bfb3fd7ef076d0d11237a9..79efa721e80388a934343254e56a8ac3a602dd8b 100644 (file)
@@ -68,4 +68,13 @@ void consumer_stream_destroy(struct lttng_consumer_stream *stream,
  */
 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 */
index f9c41c0eb0f8f9f32def26028f743ce7df45d322..1571d86de78b95437d7f7ab833af79129a6b39e2 100644 (file)
 #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"
@@ -46,16 +50,24 @@ static void setmask(sigset_t *mask)
        }
        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)
@@ -74,7 +86,21 @@ static void metadata_switch_timer(struct lttng_consumer_local_data *ctx,
        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;
                }
@@ -86,6 +112,170 @@ static void metadata_switch_timer(struct lttng_consumer_local_data *ctx,
        }
 }
 
+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)
 {
@@ -194,6 +384,63 @@ void consumer_timer_switch_stop(struct lttng_consumer_channel *channel)
        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
@@ -213,23 +460,30 @@ void consumer_signal_init(void)
 }
 
 /*
- * 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");
@@ -242,10 +496,16 @@ void *consumer_timer_metadata_thread(void *data)
                        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;
 }
index 04743abf83a6d9625774953a08db931911df503e..f3fac5d25d8ce791b0767f55bea5fb5d2f6f9d13 100644 (file)
@@ -26,6 +26,7 @@
 
 #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
 
@@ -45,7 +46,10 @@ struct timer_signal_data {
 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 */
index c7bc1e7a05b75010135d18cda82bf7fa322b1150..187c0c30cdf003741f053417e1c9209c64b80713 100644 (file)
 #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"
@@ -91,6 +94,18 @@ static void notify_thread_lttng_pipe(struct lttng_pipe *pipe)
        (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,
@@ -291,6 +306,7 @@ void consumer_del_channel(struct lttng_consumer_channel *channel)
        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,
@@ -303,6 +319,10 @@ void consumer_del_channel(struct lttng_consumer_channel *channel)
                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;
@@ -324,6 +344,7 @@ void consumer_del_channel(struct lttng_consumer_channel *channel)
 
        call_rcu(&channel->node.head, free_channel_rcu);
 end:
+       pthread_mutex_unlock(&channel->lock);
        pthread_mutex_unlock(&consumer_data.lock);
 }
 
@@ -455,6 +476,19 @@ void consumer_del_stream(struct lttng_consumer_stream *stream,
        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,
@@ -483,12 +517,15 @@ struct lttng_consumer_stream *consumer_allocate_stream(uint64_t channel_key,
        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. */
@@ -496,6 +533,9 @@ struct lttng_consumer_stream *consumer_allocate_stream(uint64_t channel_key,
                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",
@@ -536,9 +576,9 @@ end:
 /*
  * 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);
@@ -547,6 +587,8 @@ static int add_stream(struct lttng_consumer_stream *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();
 
@@ -584,11 +626,18 @@ static int add_stream(struct lttng_consumer_stream *stream,
 
        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.
@@ -701,6 +750,7 @@ int consumer_send_relayd_stream(struct lttng_consumer_stream *stream,
                if (ret < 0) {
                        goto end;
                }
+
                uatomic_inc(&relayd->refcount);
                stream->sent_to_relayd = 1;
        } else {
@@ -811,7 +861,8 @@ struct lttng_consumer_channel *consumer_allocate_channel(uint64_t key,
                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;
 
@@ -832,6 +883,9 @@ struct lttng_consumer_channel *consumer_allocate_channel(uint64_t key,
        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
@@ -877,6 +931,8 @@ int consumer_add_channel(struct lttng_consumer_channel *channel,
        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);
@@ -893,6 +949,8 @@ int consumer_add_channel(struct lttng_consumer_channel *channel,
 
 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 &&
@@ -1128,7 +1186,7 @@ struct lttng_consumer_local_data *lttng_consumer_create(
                        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;
@@ -1145,6 +1203,7 @@ struct lttng_consumer_local_data *lttng_consumer_create(
 
        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;
@@ -1284,7 +1343,8 @@ end:
 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;
@@ -1302,6 +1362,7 @@ ssize_t lttng_consumer_on_read_subbuffer_mmap(
        if (stream->net_seq_idx != (uint64_t) -1ULL) {
                relayd = consumer_find_relayd(stream->net_seq_idx);
                if (relayd == NULL) {
+                       ret = -EPIPE;
                        goto end;
                }
        }
@@ -1311,28 +1372,31 @@ ssize_t lttng_consumer_on_read_subbuffer_mmap(
        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) {
@@ -1387,16 +1451,34 @@ ssize_t lttng_consumer_on_read_subbuffer_mmap(
                        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) {
@@ -1413,7 +1495,7 @@ ssize_t lttng_consumer_on_read_subbuffer_mmap(
                         */
                        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) {
@@ -1437,6 +1519,7 @@ ssize_t lttng_consumer_on_read_subbuffer_mmap(
                                        SYNC_FILE_RANGE_WRITE);
                        stream->out_fd_offset += ret;
                }
+               stream->output_written += ret;
                written += ret;
        }
        lttng_consumer_sync_trace_file(stream, orig_offset);
@@ -1470,7 +1553,8 @@ end:
 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;
@@ -1501,6 +1585,7 @@ ssize_t lttng_consumer_on_read_subbuffer_splice(
        if (stream->net_seq_idx != (uint64_t) -1ULL) {
                relayd = consumer_find_relayd(stream->net_seq_idx);
                if (relayd == NULL) {
+                       ret = -EPIPE;
                        goto end;
                }
        }
@@ -1569,16 +1654,32 @@ ssize_t lttng_consumer_on_read_subbuffer_splice(
                        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) {
@@ -1647,6 +1748,7 @@ ssize_t lttng_consumer_on_read_subbuffer_splice(
                                        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);
@@ -1852,6 +1954,7 @@ void consumer_del_metadata_stream(struct lttng_consumer_stream *stream,
        }
 
        pthread_mutex_lock(&consumer_data.lock);
+       pthread_mutex_lock(&stream->chan->lock);
        pthread_mutex_lock(&stream->lock);
 
        switch (consumer_data.type) {
@@ -1871,6 +1974,13 @@ void consumer_del_metadata_stream(struct lttng_consumer_stream *stream,
                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:
@@ -1939,12 +2049,13 @@ void consumer_del_metadata_stream(struct lttng_consumer_stream *stream,
 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
+        * 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) {
@@ -1959,9 +2070,9 @@ free_stream_rcu:
  * 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;
@@ -1972,6 +2083,8 @@ static int add_metadata_stream(struct lttng_consumer_stream *stream,
        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);
 
        /*
@@ -2017,6 +2130,8 @@ static int add_metadata_stream(struct lttng_consumer_stream *stream,
        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;
 }
@@ -2080,7 +2195,7 @@ static void validate_endpoint_status_metadata_stream(
  */
 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;
@@ -2091,6 +2206,10 @@ void *consumer_thread_metadata_poll(void *data)
 
        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. */
@@ -2116,14 +2235,19 @@ void *consumer_thread_metadata_poll(void *data)
        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) {
@@ -2137,14 +2261,11 @@ restart:
 
                /* 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");
@@ -2162,7 +2283,7 @@ restart:
                                        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.
                                                 */
@@ -2179,14 +2300,6 @@ restart:
                                        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);
@@ -2219,6 +2332,8 @@ restart:
 
                                        /* 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
@@ -2240,14 +2355,23 @@ restart:
                                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;
                                }
                        }
 
@@ -2256,6 +2380,8 @@ restart:
                }
        }
 
+       /* All is OK */
+       err = 0;
 error:
 end:
        DBG("Metadata poll thread exiting");
@@ -2264,6 +2390,11 @@ end:
 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;
 }
@@ -2274,7 +2405,7 @@ end_ht:
  */
 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;
@@ -2285,6 +2416,10 @@ void *consumer_thread_data_poll(void *data)
 
        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. */
@@ -2298,6 +2433,8 @@ void *consumer_thread_data_poll(void *data)
        }
 
        while (1) {
+               health_code_update();
+
                high_prio = 0;
                num_hup = 0;
 
@@ -2344,12 +2481,15 @@ void *consumer_thread_data_poll(void *data)
 
                /* 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) {
                        /*
@@ -2378,7 +2518,7 @@ void *consumer_thread_data_poll(void *data)
                        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;
                        }
@@ -2393,23 +2533,14 @@ void *consumer_thread_data_poll(void *data)
                                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;
                        }
@@ -2438,6 +2569,8 @@ void *consumer_thread_data_poll(void *data)
 
                /* Take care of low priority channels. */
                for (i = 0; i < nb_fd; i++) {
+                       health_code_update();
+
                        if (local_stream[i] == NULL) {
                                continue;
                        }
@@ -2458,6 +2591,8 @@ void *consumer_thread_data_poll(void *data)
 
                /* Handle hangup and errors */
                for (i = 0; i < nb_fd; i++) {
+                       health_code_update();
+
                        if (local_stream[i] == NULL) {
                                continue;
                        }
@@ -2503,6 +2638,8 @@ void *consumer_thread_data_poll(void *data)
                        }
                }
        }
+       /* All is OK */
+       err = 0;
 end:
        DBG("polling thread exiting");
        free(pollfd);
@@ -2520,6 +2657,12 @@ end:
 
        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;
 }
@@ -2601,7 +2744,7 @@ static void destroy_channel_ht(struct lttng_ht *ht)
  */
 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;
@@ -2612,6 +2755,10 @@ void *consumer_thread_channel_poll(void *data)
 
        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. */
@@ -2636,14 +2783,19 @@ void *consumer_thread_channel_poll(void *data)
        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) {
@@ -2657,6 +2809,8 @@ restart:
 
                /* 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);
 
@@ -2723,6 +2877,8 @@ restart:
                                                        /* 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);
@@ -2795,12 +2951,19 @@ restart:
                }
        }
 
+       /* 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;
 }
@@ -2838,7 +3001,7 @@ error:
  */
 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.
@@ -2848,6 +3011,10 @@ void *consumer_thread_sessiond_poll(void *data)
 
        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);
@@ -2908,7 +3075,12 @@ void *consumer_thread_sessiond_poll(void *data)
        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");
@@ -2923,14 +3095,19 @@ void *consumer_thread_sessiond_poll(void *data)
                         * 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");
 
@@ -2956,6 +3133,8 @@ end:
 
        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);
@@ -2970,6 +3149,12 @@ end:
                }
        }
 
+       if (err) {
+               health_error();
+               ERR("Health error occurred in %s", __func__);
+       }
+       health_unregister(health_consumerd);
+
        rcu_unregister_thread();
        return NULL;
 }
@@ -2980,6 +3165,9 @@ ssize_t lttng_consumer_read_subbuffer(struct lttng_consumer_stream *stream,
        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:
@@ -2996,6 +3184,10 @@ ssize_t lttng_consumer_read_subbuffer(struct lttng_consumer_stream *stream,
                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;
 }
@@ -3035,7 +3227,8 @@ void lttng_consumer_init(void)
 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;
@@ -3057,7 +3250,7 @@ int consumer_add_relayd_socket(uint64_t net_seq_idx, int sock_type,
                        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;
                }
 
@@ -3135,29 +3328,7 @@ int consumer_add_relayd_socket(uint64_t net_seq_idx, int sock_type,
                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:
@@ -3358,6 +3529,15 @@ int consumer_data_pending(uint64_t id)
                 */
                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) {
@@ -3457,3 +3637,23 @@ int consumer_send_status_channel(int sock,
 
        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;
+}
index 23f2c9d840fdcde302ca0a9b4024bd9533d499da..aef7f560e4dcae78e62343eae1492de6ea85434b 100644 (file)
@@ -32,6 +32,7 @@
 #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 {
@@ -115,7 +116,7 @@ struct lttng_consumer_channel {
        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. */
@@ -131,6 +132,7 @@ struct lttng_consumer_channel {
        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];
        /*
@@ -154,11 +156,16 @@ struct lttng_consumer_channel {
 
        /* 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;
@@ -168,6 +175,36 @@ struct lttng_consumer_channel {
         * 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;
 };
 
 /*
@@ -193,6 +230,8 @@ struct lttng_consumer_stream {
        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;
@@ -247,6 +286,8 @@ struct lttng_consumer_stream {
         *
         * 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;
@@ -279,6 +320,26 @@ struct lttng_consumer_stream {
         * 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;
 };
 
 /*
@@ -371,12 +432,18 @@ struct lttng_consumer_local_data {
         *   == 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 */
@@ -511,7 +578,8 @@ struct lttng_consumer_channel *consumer_allocate_channel(uint64_t key,
                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,
@@ -537,16 +605,18 @@ struct lttng_consumer_local_data *lttng_consumer_create(
                        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);
@@ -563,7 +633,7 @@ int lttng_consumer_on_recv_stream(struct lttng_consumer_stream *stream);
 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);
@@ -573,5 +643,12 @@ int consumer_send_status_channel(int sock,
 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 */
index 388b9f0d78348b54ed97889139b69a720d2ebb7e..821128eb4df197539d334fb7b5fe67988c022296 100644 (file)
@@ -19,6 +19,8 @@
 #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;
index 416365f08a77432bfc4e13233c09b6a957ea18cd..2733d0d11881cabaada536ac1d4a0d9401754f54 100644 (file)
@@ -82,10 +82,12 @@ static const char *error_string_array[] = {
        [ 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",
@@ -110,6 +112,8 @@ static const char *error_string_array[] = {
        [ 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"
index 8c26ebe15b63d3a71f2efd40398e2781e5ddbb59..b08a57e5cc0a07f53582a3181443d395c8b97765 100644 (file)
@@ -65,6 +65,17 @@ static int match_u64(struct cds_lfht_node *node, const void *key)
        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.
  */
@@ -103,6 +114,10 @@ struct lttng_ht *lttng_ht_new(unsigned long size, int type)
                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);
@@ -164,6 +179,19 @@ void lttng_ht_node_init_u64(struct lttng_ht_node_u64 *node,
        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.
  */
@@ -191,6 +219,15 @@ void lttng_ht_node_free_u64(struct lttng_ht_node_u64 *node)
        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.
  */
@@ -220,6 +257,20 @@ void lttng_ht_add_unique_str(struct lttng_ht *ht,
        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.
  */
@@ -281,6 +332,23 @@ void lttng_ht_add_unique_u64(struct lttng_ht *ht,
        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.
  */
@@ -425,6 +493,22 @@ struct lttng_ht_node_u64 *lttng_ht_iter_get_node_u64(
        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
  */
index 846c5ec47277596fdb231e766feb6f177c9e2f62..826e1207e00b3eb907715ba98299c896f9cbce6e 100644 (file)
@@ -33,6 +33,7 @@ enum lttng_ht_type {
        LTTNG_HT_TYPE_STRING,
        LTTNG_HT_TYPE_ULONG,
        LTTNG_HT_TYPE_U64,
+       LTTNG_HT_TYPE_TWO_U64,
 };
 
 struct lttng_ht {
@@ -63,6 +64,17 @@ struct lttng_ht_node_u64 {
        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);
@@ -73,9 +85,12 @@ extern void lttng_ht_node_init_ulong(struct lttng_ht_node_ulong *node,
                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);
@@ -87,10 +102,14 @@ extern void lttng_ht_add_unique_ulong(struct lttng_ht *ht,
                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,
@@ -110,5 +129,7 @@ extern struct lttng_ht_node_ulong *lttng_ht_iter_get_node_ulong(
                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 */
index b430a3dec76e681ebf561f33bb9fcc767c713037..0ddd21a3b6335ccf3fb2ddb5ea5f1a9dbba1564c 100644 (file)
@@ -570,6 +570,7 @@ void cds_lfht_resize_lazy_count(struct cds_lfht *ht, unsigned long size,
 
 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)
@@ -606,6 +607,8 @@ void alloc_split_items_count(struct cds_lfht *ht)
                        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);
@@ -721,14 +724,39 @@ void check_resize(struct cds_lfht *ht, unsigned long size, uint32_t chain_len)
         * 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
index 8d0e515aecafbb94a347c418f23bf341f719f71c..1ea699d6edb921364a30d3c92e31e9ee538571b4 100644 (file)
@@ -60,6 +60,7 @@
 #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
@@ -496,6 +497,17 @@ unsigned long hash_key_str(void *key, unsigned long seed)
        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.
  */
@@ -534,3 +546,20 @@ int hash_match_key_str(void *key1, void *key2)
 
        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;
+}
index 38d6121e4ce65cd891e7d1ce6d485515596985dd..9d53e3864f9d7ae54142e24c12d65216b4c327cf 100644 (file)
 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 */
diff --git a/src/common/health/Makefile.am b/src/common/health/Makefile.am
new file mode 100644 (file)
index 0000000..d82f9de
--- /dev/null
@@ -0,0 +1,5 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
+
+noinst_LTLIBRARIES = libhealth.la
+
+libhealth_la_SOURCES = health.c
diff --git a/src/common/health/health.c b/src/common/health/health.c
new file mode 100644 (file)
index 0000000..49f6dc0
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * 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, &current_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, &current_time, sizeof(current_time));
+       } else {
+               if (time_diff_gt(&current_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, &current_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);
+}
diff --git a/src/common/index/Makefile.am b/src/common/index/Makefile.am
new file mode 100644 (file)
index 0000000..104d99c
--- /dev/null
@@ -0,0 +1,5 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
+
+noinst_LTLIBRARIES = libindex.la
+
+libindex_la_SOURCES = index.c index.h lttng-index.h
diff --git a/src/common/index/index.c b/src/common/index/index.c
new file mode 100644 (file)
index 0000000..89b4fd7
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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;
+}
diff --git a/src/common/index/index.h b/src/common/index/index.h
new file mode 100644 (file)
index 0000000..58f2ac7
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 */
diff --git a/src/common/index/lttng-index.h b/src/common/index/lttng-index.h
new file mode 100644 (file)
index 0000000..4327cbd
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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 */
index 42af65ac3d8c715a8a0851cd44dfdb54f9278072..44d58d98317c24562a0442509418a10294986917 100644 (file)
@@ -29,6 +29,7 @@
 #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>
@@ -38,6 +39,8 @@
 #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"
 
@@ -57,8 +60,8 @@ int lttng_kconsumer_take_snapshot(struct lttng_consumer_stream *stream)
 
        ret = kernctl_snapshot(infd);
        if (ret != 0) {
-               errno = -ret;
                perror("Getting sub-buffer snapshot.");
+               ret = -errno;
        }
 
        return ret;
@@ -77,8 +80,8 @@ int lttng_kconsumer_get_produced_snapshot(struct lttng_consumer_stream *stream,
 
        ret = kernctl_snapshot_get_produced(infd, pos);
        if (ret != 0) {
-               errno = -ret;
                perror("kernctl_snapshot_get_produced");
+               ret = -errno;
        }
 
        return ret;
@@ -97,8 +100,8 @@ int lttng_kconsumer_get_consumed_snapshot(struct lttng_consumer_stream *stream,
 
        ret = kernctl_snapshot_get_consumed(infd, pos);
        if (ret != 0) {
-               errno = -ret;
                perror("kernctl_snapshot_get_consumed");
+               ret = -errno;
        }
 
        return ret;
@@ -110,20 +113,21 @@ int lttng_kconsumer_get_consumed_snapshot(struct lttng_consumer_stream *stream,
  * 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;
        }
@@ -136,6 +140,9 @@ int lttng_kconsumer_snapshot_channel(uint64_t key, char *path,
        }
 
        cds_list_for_each_entry(stream, &channel->streams.head, send_node) {
+
+               health_code_update();
+
                /*
                 * Lock stream because we are about to change its state.
                 */
@@ -157,7 +164,7 @@ int lttng_kconsumer_snapshot_channel(uint64_t key, char *path,
                        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;
@@ -166,13 +173,14 @@ int lttng_kconsumer_snapshot_channel(uint64_t key, char *path,
                        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;
                }
 
@@ -199,20 +207,33 @@ int lttng_kconsumer_snapshot_channel(uint64_t key, char *path,
                                        &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.");
@@ -223,17 +244,19 @@ int lttng_kconsumer_snapshot_channel(uint64_t key, char *path,
                        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
@@ -254,6 +277,7 @@ int lttng_kconsumer_snapshot_channel(uint64_t key, char *path,
                        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;
@@ -282,6 +306,7 @@ int lttng_kconsumer_snapshot_channel(uint64_t key, char *path,
 error_put_subbuf:
        ret = kernctl_put_subbuf(stream->wait_fd);
        if (ret < 0) {
+               ret = -errno;
                ERR("Snapshot kernctl_put_subbuf error path");
        }
 end_unlock:
@@ -335,7 +360,7 @@ int lttng_kconsumer_snapshot_metadata(uint64_t key, char *path,
                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;
                }
@@ -343,10 +368,12 @@ int lttng_kconsumer_snapshot_metadata(uint64_t key, char *path,
        }
 
        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;
                        }
@@ -394,6 +421,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
        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) {
@@ -402,6 +431,9 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                }
                return ret;
        }
+
+       health_code_update();
+
        if (msg.cmd_type == LTTNG_CONSUMER_STOP) {
                /*
                 * Notify the session daemon that the command is completed.
@@ -414,6 +446,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                return -ENOENT;
        }
 
+       health_code_update();
+
        /* relayd needs RCU read-side protection */
        rcu_read_lock();
 
@@ -423,7 +457,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                /* 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:
@@ -431,12 +466,17 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                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,
@@ -444,7 +484,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                                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;
@@ -473,6 +514,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        goto end_nosignal;
                };
 
+               health_code_update();
+
                if (ctx->on_recv_channel != NULL) {
                        ret_recv = ctx->on_recv_channel(new_channel);
                        if (ret_recv == 0) {
@@ -483,6 +526,12 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                } 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) {
@@ -517,23 +566,33 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        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)) {
@@ -542,6 +601,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        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
@@ -553,6 +614,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        goto end_nosignal;
                }
 
+               health_code_update();
+
                new_stream = consumer_allocate_stream(channel->key,
                                fd,
                                LTTNG_CONSUMER_ACTIVE_STREAM,
@@ -610,6 +673,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                 */
                new_stream->hangup_flush_done = 0;
 
+               health_code_update();
+
                if (ctx->on_recv_stream) {
                        ret = ctx->on_recv_stream(new_stream);
                        if (ret < 0) {
@@ -618,6 +683,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        }
                }
 
+               health_code_update();
+
                if (new_stream->metadata_flag) {
                        channel->metadata_stream = new_stream;
                }
@@ -643,21 +710,42 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
 
                /* 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);
@@ -696,6 +784,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        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. */
@@ -713,6 +803,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
 
                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) {
@@ -739,13 +831,17 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                } 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. */
@@ -764,12 +860,16 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        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.
@@ -797,6 +897,7 @@ end_nosignal:
         * 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:
@@ -805,6 +906,97 @@ 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.
  */
@@ -812,15 +1004,16 @@ ssize_t lttng_kconsumer_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;
        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,
@@ -829,18 +1022,27 @@ ssize_t lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
                 */
                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:
                /*
@@ -853,7 +1055,7 @@ ssize_t lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
 
                /* 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.
@@ -865,15 +1067,15 @@ ssize_t lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
                         */
                        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;
                }
 
@@ -884,7 +1086,7 @@ ssize_t lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
 
                /* 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_
@@ -899,24 +1101,43 @@ ssize_t lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
                        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;
        }
 
@@ -937,12 +1158,23 @@ int lttng_kconsumer_on_recv_stream(struct lttng_consumer_stream *stream)
        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) {
@@ -951,8 +1183,8 @@ int lttng_kconsumer_on_recv_stream(struct lttng_consumer_stream *stream)
 
                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;
@@ -995,6 +1227,11 @@ int lttng_kconsumer_data_pending(struct lttng_consumer_stream *stream)
 
        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. */
index 41482b13f4cea8d4763515102643d6dad756a611..1aad2733b28aa8def996b0a315e952eb29b43879 100644 (file)
 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 */
index d850f38c0337bdf53bc690f15240a82163ac66f8..5ea3e1ae12a122761750c291a7240708b4c36469 100644 (file)
@@ -390,3 +390,45 @@ int kernctl_put_subbuf(int fd)
 {
        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);
+}
index ea2aa581928084cdaa4eb9aea4d0ff6ddbfa2277..7c5f8bebefb55b2156b7214b9ef59476aef927a6 100644 (file)
@@ -67,4 +67,13 @@ int kernctl_put_subbuf(int fd);
 
 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 */
index 75d6da0ccdd9cffe1226c695261a513d3d0bee12..1db964e0023f3e232b354b2ef963e49752ce75b4 100644 (file)
 /* 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)
index 2283865cf0d86be6c905baa287fd5809f17af8f2..7c90b4d1a05a4e6fdc0d041288cd7fe21784b2d7 100644 (file)
@@ -26,6 +26,7 @@
 #include <common/common.h>
 #include <common/defaults.h>
 #include <common/sessiond-comm/relayd.h>
+#include <common/index/lttng-index.h>
 
 #include "relayd.h"
 
@@ -115,6 +116,50 @@ error:
        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.
@@ -122,7 +167,9 @@ error:
  * 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;
@@ -132,8 +179,19 @@ int relayd_create_session(struct lttcomm_relayd_sock *rsock, uint64_t *session_i
 
        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;
        }
@@ -671,3 +729,63 @@ int relayd_end_data_pending(struct lttcomm_relayd_sock *rsock, uint64_t id,
 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;
+}
index dd435e905dd1d2be3136c230c2697615369be7b5..d12d7a495d80fdb3e19e191d49b7f22e010125c1 100644 (file)
@@ -25,7 +25,9 @@
 
 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);
@@ -43,5 +45,8 @@ int relayd_quiescent_control(struct lttcomm_relayd_sock *sock,
 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 */
index bd51cd4ed2b7b6b8c00221b19209a63dba10c698..3ae2e4c38d35e5d943f8ca9f3e49213d14472ed4 100644 (file)
@@ -96,8 +96,15 @@ int _mkdir_recursive(void *_data)
 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
@@ -130,14 +137,16 @@ int child_run_as(void *_data)
                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;
                }
        }
        /*
@@ -145,6 +154,8 @@ int child_run_as(void *_data)
         */
        umask(0);
        sendret.i = (*data->cmd)(data->data);
+
+write_return:
        /* send back return value */
        writeleft = sizeof(sendret);
        index = 0;
index 24063f862b3e30b0119e328578c675b63c99f07b..c72c87fbcd4a20897afabe25745044f98c7fe435 100644 (file)
@@ -5,4 +5,5 @@ noinst_LTLIBRARIES = libsessiond-comm.la
 
 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
index b2bd65d9029672efb47a4469ef3632b30a649740..fa9218769b31c1d903e9cd33e6c12c0e3fbe0bd5 100644 (file)
 #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.
  */
@@ -43,6 +50,8 @@ static const struct lttcomm_proto_ops inet_ops = {
        .sendmsg = lttcomm_sendmsg_inet_sock,
 };
 
+unsigned long lttcomm_inet_tcp_timeout;
+
 /*
  * Creates an PF_INET socket.
  */
@@ -50,6 +59,7 @@ LTTNG_HIDDEN
 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) {
@@ -67,6 +77,17 @@ int lttcomm_create_inet_sock(struct lttcomm_sock *sock, int type, int proto)
                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;
 
@@ -91,6 +112,130 @@ int lttcomm_bind_inet_sock(struct lttcomm_sock *sock)
        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.
  */
@@ -99,8 +244,11 @@ int lttcomm_connect_inet_sock(struct lttcomm_sock *sock)
 {
        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;
@@ -302,3 +450,75 @@ int lttcomm_close_inet_sock(struct lttcomm_sock *sock)
 
        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);
+}
index 89716b8bafdb0b19cfaa1cca503e34af140711dc..83209bb719fff759e4719ed48f34cf69bb8fd7a2 100644 (file)
 
 #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;
 
@@ -41,4 +61,7 @@ extern ssize_t lttcomm_recvmsg_inet_sock(struct lttcomm_sock *sock, void *buf,
 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 */
index 8d883740478f0c0cfca365f2334daed6a7669ba5..5b137c4eb3a73430ad364b9e9acacce4f6126d3b 100644 (file)
 #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.
  */
@@ -50,6 +57,7 @@ LTTNG_HIDDEN
 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) {
@@ -67,6 +75,17 @@ int lttcomm_create_inet6_sock(struct lttcomm_sock *sock, int type, int proto)
                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;
 
@@ -91,6 +110,130 @@ int lttcomm_bind_inet6_sock(struct lttcomm_sock *sock)
        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.
  */
@@ -99,8 +242,11 @@ int lttcomm_connect_inet6_sock(struct lttcomm_sock *sock)
 {
        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;
diff --git a/src/common/sessiond-comm/jul.h b/src/common/sessiond-comm/jul.h
new file mode 100644 (file)
index 0000000..fa14dbf
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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 */
index aa99248fb45de4bf920f5571a522d4a357f71edd..24c4c6e84336584f986921bddbdbf212b9ad686b 100644 (file)
 
 #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.
@@ -148,4 +150,28 @@ struct lttcomm_relayd_quiescent_control {
        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 */
index 9a13f41488a9ef6deb61323cf403f7ed8688da1a..65952b273eaf49c52a17955ab6bda441bd65f554 100644 (file)
@@ -38,6 +38,8 @@
 /* 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 },
@@ -70,6 +72,8 @@ static const char *lttcomm_readable_code[] = {
        [ 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.
@@ -368,3 +372,70 @@ error_free:
 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;
+}
index b76135e2cf08f2b21a2356a3bbef33aca21ae5a1..229b91e5901cae11e83a4675f48c047e8e07c32f 100644 (file)
@@ -81,12 +81,14 @@ enum lttcomm_sessiond_command {
        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 {
@@ -101,6 +103,11 @@ 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,
 };
 
 /*
@@ -254,6 +261,10 @@ struct lttcomm_session_msg {
                        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;
 
@@ -289,15 +300,6 @@ struct lttcomm_lttng_output_id {
        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
@@ -323,6 +325,8 @@ struct lttcomm_consumer_msg {
                        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;
@@ -338,6 +342,8 @@ struct lttcomm_consumer_msg {
                        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;
@@ -351,6 +357,7 @@ struct lttcomm_consumer_msg {
                        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 */
@@ -367,6 +374,13 @@ struct lttcomm_consumer_msg {
                        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;
@@ -395,7 +409,7 @@ struct lttcomm_consumer_msg {
                        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;
@@ -473,4 +487,11 @@ extern void lttcomm_copy_sock(struct lttcomm_sock *dst,
 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 */
index 48fa1049ac99e6d597e8982bceb5e4cfcf9236fe..2c87b82c451f87de02fb5db074cb24a32c752696 100644 (file)
@@ -435,6 +435,7 @@ ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len,
        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);
@@ -461,12 +462,21 @@ ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len,
 #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) {
@@ -539,45 +549,3 @@ int lttcomm_setsockopt_creds_unix_sock(int sock)
 #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;
-}
index 34f156ffd46171298be768abafd1ebca899dcef0..19b91ce40e0dcb689529e886f6737ac9664b8fe8 100644 (file)
@@ -45,7 +45,5 @@ extern ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len,
                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 */
index 1bb07c0c73606139e38a7c8e8c0e932ab7709459..df0911c7130c4995fc617acc0994a4366efcf03e 100644 (file)
@@ -32,6 +32,7 @@
 #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>
@@ -40,6 +41,7 @@
 #include <common/consumer-stream.h>
 #include <common/consumer-timer.h>
 #include <common/utils.h>
+#include <common/index/index.h>
 
 #include "ust-consumer.h"
 
@@ -62,6 +64,9 @@ static void destroy_channel(struct lttng_consumer_channel *channel)
 
        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);
@@ -116,14 +121,15 @@ static struct lttng_consumer_channel *allocate_channel(uint64_t session_id,
                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);
 }
 
 /*
@@ -194,18 +200,41 @@ static int send_stream_to_thread(struct lttng_consumer_stream *stream,
 
        /* 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;
 }
 
@@ -230,8 +259,20 @@ static int create_ust_streams(struct lttng_consumer_channel *channel,
         */
        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);
@@ -285,6 +326,8 @@ static int create_ust_streams(struct lttng_consumer_channel *channel,
                /* 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];
                }
        }
 
@@ -343,7 +386,7 @@ static int send_sessiond_stream(int sock, struct lttng_consumer_stream *stream)
        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);
@@ -375,6 +418,9 @@ static int send_sessiond_channel(int sock,
 
        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) {
@@ -413,6 +459,9 @@ static int send_sessiond_channel(int sock,
 
        /* 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) {
@@ -514,6 +563,9 @@ static int send_streams_to_thread(struct lttng_consumer_channel *channel,
        /* 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) {
@@ -521,60 +573,20 @@ static int send_streams_to_thread(struct lttng_consumer_channel *channel,
                         * 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.
  *
@@ -604,12 +616,54 @@ static int flush_channel(uint64_t chan_key)
        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.
@@ -638,26 +692,16 @@ static int close_metadata(uint64_t chan_key)
        }
 
        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;
@@ -750,8 +794,6 @@ static int snapshot_metadata(uint64_t key, char *path, uint64_t relayd_id,
                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;
 
@@ -765,21 +807,26 @@ static int snapshot_metadata(uint64_t key, char *path, uint64_t relayd_id,
 
        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.
@@ -802,7 +849,7 @@ static int snapshot_metadata(uint64_t key, char *path, uint64_t relayd_id,
                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;
                }
@@ -810,34 +857,14 @@ static int snapshot_metadata(uint64_t key, char *path, uint64_t relayd_id,
                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:
        /*
@@ -859,7 +886,7 @@ error:
  * 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;
@@ -878,14 +905,17 @@ static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id,
 
        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;
@@ -899,7 +929,7 @@ static int snapshot_channel(uint64_t key, char *path, uint64_t 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;
                        }
@@ -930,10 +960,21 @@ static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id,
                        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);
@@ -960,15 +1001,15 @@ static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id,
                        }
 
                        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;
                                }
                        }
@@ -1006,7 +1047,8 @@ error:
  * 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;
@@ -1020,6 +1062,8 @@ int lttng_ustconsumer_recv_metadata(int sock, uint64_t key, uint64_t offset,
                goto end;
        }
 
+       health_code_update();
+
        /* Receive metadata string. */
        ret = lttcomm_recv_unix_sock(sock, metadata_str, len);
        if (ret < 0) {
@@ -1028,16 +1072,7 @@ int lttng_ustconsumer_recv_metadata(int sock, uint64_t key, uint64_t offset,
                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);
@@ -1050,14 +1085,18 @@ int lttng_ustconsumer_recv_metadata(int sock, uint64_t key, uint64_t offset,
                 * 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);
        }
 
@@ -1080,6 +1119,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
        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)",
@@ -1094,6 +1135,9 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                }
                return ret;
        }
+
+       health_code_update();
+
        if (msg.cmd_type == LTTNG_CONSUMER_STOP) {
                /*
                 * Notify the session daemon that the command is completed.
@@ -1106,6 +1150,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                return -ENOENT;
        }
 
+       health_code_update();
+
        /* relayd needs RCU read-side lock */
        rcu_read_lock();
 
@@ -1115,7 +1161,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                /* 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:
@@ -1190,11 +1237,19 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                                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;
@@ -1226,6 +1281,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        goto error_fatal;
                };
 
+               health_code_update();
+
                ret = ask_channel(ctx, sock, channel, &attr);
                if (ret < 0) {
                        goto end_channel_error;
@@ -1239,8 +1296,13 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        }
                        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
@@ -1256,9 +1318,14 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                                }
                                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
@@ -1287,6 +1354,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        goto end_msg_sessiond;
                }
 
+               health_code_update();
+
                /* Send everything to sessiond. */
                ret = send_sessiond_channel(sock, channel, ctx, &relayd_err);
                if (ret < 0) {
@@ -1306,6 +1375,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        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.
@@ -1378,6 +1449,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        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) {
@@ -1385,13 +1458,20 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        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;
@@ -1425,6 +1505,7 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        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");
@@ -1432,11 +1513,13 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        }
                }
 
+               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:
@@ -1446,6 +1529,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
 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.
@@ -1463,6 +1548,9 @@ end_msg_sessiond:
                goto error_fatal;
        }
        rcu_read_unlock();
+
+       health_code_update();
+
        return 1;
 end_channel_error:
        if (channel) {
@@ -1479,6 +1567,9 @@ end_channel_error:
                goto error_fatal;
        }
        rcu_read_unlock();
+
+       health_code_update();
+
        return 1;
 error_fatal:
        rcu_read_unlock();
@@ -1554,6 +1645,25 @@ int lttng_ustconsumer_get_consumed_snapshot(
        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.
  */
@@ -1589,41 +1699,218 @@ void lttng_ustconsumer_del_stream(struct lttng_consumer_stream *stream)
        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,
@@ -1636,6 +1923,17 @@ int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
                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);
@@ -1649,7 +1947,7 @@ int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
 
        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.
@@ -1667,10 +1965,32 @@ int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream,
                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;
 }
@@ -1690,12 +2010,23 @@ int lttng_ustconsumer_on_recv_stream(struct lttng_consumer_stream *stream)
        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;
 
@@ -1720,15 +2051,50 @@ int lttng_ustconsumer_data_pending(struct lttng_consumer_stream *stream)
 
        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;
 
@@ -1747,7 +2113,6 @@ end:
  */
 void lttng_ustconsumer_close_metadata(struct lttng_ht *metadata_ht)
 {
-       int ret;
        struct lttng_ht_iter iter;
        struct lttng_consumer_stream *stream;
 
@@ -1759,17 +2124,19 @@ void lttng_ustconsumer_close_metadata(struct lttng_ht *metadata_ht)
        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();
 }
@@ -1784,8 +2151,14 @@ void lttng_ustconsumer_close_stream_wakeup(struct lttng_consumer_stream *stream)
        }
 }
 
+/*
+ * 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;
@@ -1811,12 +2184,22 @@ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
 
        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));
@@ -1825,6 +2208,8 @@ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
                goto end;
        }
 
+       health_code_update();
+
        /* Receive the metadata from sessiond */
        ret = lttcomm_recv_unix_sock(ctx->consumer_metadata_socket, &msg,
                        sizeof(msg));
@@ -1839,6 +2224,8 @@ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
                goto end;
        }
 
+       health_code_update();
+
        if (msg.cmd_type == LTTNG_ERR_UND) {
                /* No registry found */
                (void) consumer_send_status_msg(ctx->consumer_metadata_socket,
@@ -1860,6 +2247,8 @@ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
                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);
@@ -1871,8 +2260,10 @@ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
                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
@@ -1883,5 +2274,8 @@ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx,
        ret = 0;
 
 end:
+       health_code_update();
+
+       pthread_mutex_unlock(&ctx->metadata_socket_lock);
        return ret;
 }
index 191cdabf8c2569211a665cd47e0a072ede574408..2dfd2e43c3dcf47c8ab73f3728e343bc8038de93 100644 (file)
@@ -54,11 +54,16 @@ int lttng_ustconsumer_data_pending(struct lttng_consumer_stream *stream);
 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 */
 
@@ -166,19 +171,31 @@ void lttng_ustconsumer_close_stream_wakeup(struct lttng_consumer_stream *stream)
 }
 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;
 }
index cfb6555a1d65fc061eb20c2c69326a91175f97ec..da4c036b9630a3ecddebd9aa74d419609705379d 100644 (file)
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <inttypes.h>
 #include <regex.h>
+#include <grp.h>
 
 #include <common/common.h>
 #include <common/runas.h>
@@ -142,6 +143,48 @@ error:
        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.
  */
@@ -309,10 +352,11 @@ error:
  */
 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);
@@ -324,17 +368,30 @@ int utils_create_stream_file(const char *path_name, char *file_name, uint64_t si
                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;
        }
@@ -355,7 +412,9 @@ int utils_create_stream_file(const char *path_name, char *file_name, uint64_t si
        ret = out_fd;
 
 error_open:
-       free(path_name_id);
+       free(path_name_suffix);
+error_free_suffix:
+       free(extra);
 error:
        return ret;
 }
@@ -371,10 +430,14 @@ error:
  */
 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");
@@ -387,8 +450,16 @@ int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
                (*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;
 }
@@ -624,3 +695,24 @@ size_t utils_get_current_time_str(const char *format, char *dst, size_t len)
 
        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;
+}
index 06aef4f1a33417b80e18672240ee500c648e5ce6..52f2798d116764cf01a783f64eb14d64751b5e8d 100644 (file)
 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 */
index 2178ed32f4728c61aedd98a131da6f73fdd9a12f..c588037d9ed50b6c39d548580b68057192e43b3f 100644 (file)
@@ -4,7 +4,8 @@ SUBDIRS = filter
 
 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 \
index 4fb60d0b264b7a175bf340ad7cb33ef3f2bd9f4c..405c6686a4f5fd576e3033a4cfafe7086d2326ce 100644 (file)
@@ -39,9 +39,6 @@
 // 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;
index 3f301340dbb4ccff7150d9ce79d4d86046d07563..29e2866a7b0990979703ad5bc0a7434786e20c91 100644 (file)
@@ -40,9 +40,9 @@ LTTNG_HIDDEN
 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
@@ -188,7 +188,7 @@ static struct filter_node *make_op_node(struct filter_parser_ctx *scanner,
 }
 
 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);
 }
@@ -201,7 +201,7 @@ int yywrap(void)
 
 #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)
 
@@ -238,7 +238,7 @@ static void filter_ast_free(struct filter_ast *ast)
 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
@@ -301,7 +301,8 @@ void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx)
 %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
diff --git a/src/lib/lttng-ctl/lttng-ctl-health.c b/src/lib/lttng-ctl/lttng-ctl-health.c
new file mode 100644 (file)
index 0000000..534c01c
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * 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];
+}
index ed9a24fbfcb5f02508b9d3f2ab7a8456e00b1a4d..58b3dba57fecb13c93a946fd8b759ace6b531ba1 100644 (file)
@@ -54,4 +54,6 @@ int lttng_ctl_ask_sessiond(struct lttcomm_session_msg *lsm, void **buf)
        return lttng_ctl_ask_sessiond_varlen(lsm, NULL, 0, buf);
 }
 
+int lttng_check_tracing_group(void);
+
 #endif /* LTTNG_CTL_HELPER_H */
index 3863aa1ceb9de16836d4edc6c55d908fd289e08f..83a46a45d1edfd12699597005007de9c4b6c47ff 100644 (file)
@@ -34,6 +34,7 @@
 #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"
@@ -59,7 +60,6 @@ do {                                                          \
 /* 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;
@@ -103,6 +103,7 @@ void lttng_ctl_copy_lttng_domain(struct lttng_domain *dst,
                switch (src->type) {
                case LTTNG_DOMAIN_KERNEL:
                case LTTNG_DOMAIN_UST:
+               case LTTNG_DOMAIN_JUL:
                        memcpy(dst, src, sizeof(struct lttng_domain));
                        break;
                default:
@@ -197,12 +198,14 @@ end:
  *
  *  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);
@@ -293,7 +296,7 @@ static int set_session_daemon_path(void)
 
        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) {
@@ -574,6 +577,7 @@ static int _lttng_stop_tracing(const char *session_name, int wait)
        }
 
        _MSG("Waiting for data availability");
+       fflush(stdout);
 
        /* Check for data availability */
        do {
@@ -591,6 +595,7 @@ static int _lttng_stop_tracing(const char *session_name, int wait)
                if (data_ret) {
                        usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME);
                        _MSG(".");
+                       fflush(stdout);
                }
        } while (data_ret != 0);
 
@@ -640,9 +645,14 @@ int lttng_add_context(struct lttng_handle *handle,
 
        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);
 
@@ -671,9 +681,9 @@ int lttng_enable_event(struct lttng_handle *handle,
 
        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,
@@ -800,9 +810,15 @@ int lttng_enable_event_with_filter(struct lttng_handle *handle,
 
        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));
@@ -855,11 +871,12 @@ int lttng_disable_event(struct lttng_handle *handle, const char *name,
 
        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));
        }
 
@@ -1347,104 +1364,6 @@ int lttng_disable_consumer(struct lttng_handle *handle)
        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
@@ -1547,6 +1466,86 @@ int lttng_data_pending(const char *session_name)
        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
  */
@@ -1554,8 +1553,6 @@ static void __attribute__((constructor)) init()
 {
        /* Set default session group */
        lttng_set_tracing_group(DEFAULT_TRACING_GROUP);
-       /* Set socket for health check */
-       (void) set_health_socket_path();
 }
 
 /*
index 9dc2c679b885da9de502e71d8fba9268c0d0a6de..6b7b8a9fc237e44487c0a3c1692bc1f5ad39a5ec 100644 (file)
@@ -221,7 +221,17 @@ int lttng_snapshot_record(const char *session_name,
  */
 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;
 }
 
 /*
index 6d5b00d9a6da78fc196a4a01f6abc461b8f5219a..c14e733a3207f97f1851ae60e874c82457e36971 100644 (file)
@@ -1,15 +1,32 @@
 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
index a7ba925e063a4e5d7c05ffc6d4aa7d1c7245e3dc..69bc06db6d286b9d1e6a4f688be193c9ef3bbd87 100644 (file)
@@ -1,15 +1,13 @@
 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
index 246259ab7dd854d932b04391736335c530bb362c..2c6afcdc11a2324242770dd7119728130f6e3ff6 100644 (file)
@@ -8,8 +8,7 @@ regression/tools/streaming/test_ust
 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
index ddb737213320c3e354a44dad83d6c1f5012c1f71..53e3e43c2a19b3ee84dbe323e6307bce7dfd2510 100644 (file)
@@ -6,3 +6,16 @@ if HAVE_LIBLTTNG_UST_CTL
 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
index 6b2b89141da5ba562521ccc0dff11a9985e8ee8d..a3afbf8ddbb4f67a6db6c458373542990e418de3 100644 (file)
@@ -1 +1,15 @@
 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
index 5c19744d3b4c0eeb5d74fd373b3931ae49622572..a182f9f21565327885b75c0570b2b024e1ca3bf6 100755 (executable)
@@ -19,7 +19,7 @@ TEST_DESC="Kernel tracer - Basic event"
 
 CURDIR=$(dirname $0)/
 TESTDIR=$CURDIR/../..
-NUM_TESTS=12
+NUM_TESTS=20
 
 source $TESTDIR/utils/utils.sh
 
@@ -46,6 +46,27 @@ function test_event_basic()
        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
 
@@ -62,6 +83,7 @@ skip $isroot "Root access is needed. Skipping all tests." $NUM_TESTS ||
        start_lttng_sessiond
 
        test_event_basic
+       test_enable_after_start
 
        stop_lttng_sessiond
 }
index ca60dbf632d7c90466d8000597231d0e7047e09e..7889c87919b213d2c325609b49ea96cef2b713bb 100644 (file)
@@ -16,3 +16,17 @@ endif
 
 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
index 15f81e5c05895ac199fc777a5fe5edf4b552c874..dde56afbb71f130c81f47f81d6ede2afd85e0feb 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -51,7 +47,3 @@ TRACEPOINT_EVENT(tp, tptest,
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index 098fbf421aee5ee9c119b0fc14ecf66cc7ecd6d0..c22d5ee390ab1592583dfe1a93987300a21d81af 100644 (file)
@@ -39,3 +39,17 @@ 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
index 3eef1104008d0deab00324740d9c8447a1dd07f1..fbf45c81329bba7161e7cc8bcd3dc83c135ee3dd 100644 (file)
  */
 
 #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);
+       }
+}
index aa2bd57c4bedb93f7a6b200e3bc6ff293da6c22d..ea0b4d4ea1d99cb1d01ee124d051241757e27659 100755 (executable)
@@ -35,7 +35,7 @@ fi
 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}"
 
@@ -47,6 +47,8 @@ function test_thread_exit
 
        # 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.
@@ -54,26 +56,35 @@ function test_thread_exit
        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
 }
 
@@ -85,13 +96,19 @@ THREAD=("LTTNG_THREAD_MANAGE_CLIENTS"
        "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
@@ -109,9 +126,11 @@ fi
 
 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"
index 0e8b51007f21d66a32a3262e215e8f297f8b4431..0c8244d5c98d738bf719f8fc608094d44c16eb4e 100755 (executable)
@@ -47,6 +47,8 @@ function test_thread_stall
 
        # 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.
@@ -54,45 +56,64 @@ function test_thread_stall
        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
@@ -106,16 +127,19 @@ THREAD=("LTTNG_THREAD_MANAGE_CLIENTS"
 #      "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
@@ -133,5 +157,8 @@ fi
 
 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}
index c7db62488f7bb42b00dd5e35c50ce7bc59e60dce..591d60835f5a677b5f0b262d25eadd067ddb1baf 100755 (executable)
@@ -35,7 +35,7 @@ fi
 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}"
 
@@ -53,19 +53,31 @@ function test_tp_fail
        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
 }
 
@@ -76,13 +88,18 @@ print_test_banner "$TEST_DESC"
 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
@@ -100,9 +117,11 @@ fi
 
 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"
index 40cb8e1102b4e6ae14ec1af172c6a2ab642ba78e..66e9ddd318039c50be580a7bc9f61e0e1defe0b8 100644 (file)
@@ -1,2 +1,16 @@
 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
index 1e24411aa22eb0ff070e8f6f1f847a8de35ad8ee..bd2521c48bd3089cd8b58e7426735b697a1038d6 100755 (executable)
@@ -19,7 +19,6 @@ TEST_DESC="Snapshots - UST tracing"
 CURDIR=$(dirname $0)/
 TESTDIR=$CURDIR/../../..
 EVENT_NAME="tp:tptest"
-BIN_NAME="gen-nevents"
 PID_RELAYD=0
 SESSION_NAME=""
 CHANNEL_NAME="snapchan"
@@ -31,7 +30,7 @@ NR_USEC_WAIT=100
 
 TRACE_PATH=$(mktemp -d)
 
-NUM_TESTS=2019
+NUM_TESTS=2075
 
 source $TESTDIR/utils/utils.sh
 
@@ -39,6 +38,83 @@ if [ ! -x "$TESTAPP_BIN" ]; then
        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"
@@ -66,6 +142,148 @@ function test_ust_local_snapshot ()
        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
@@ -94,7 +312,7 @@ function test_ust_1000_local_snapshots ()
        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
@@ -109,13 +327,18 @@ fi
 
 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
index 3aa38581eac519e56d368d568318d7c3a2760f6a..e64b10cdc6c2665f666c93c3bbd815b6a6fe3273 100755 (executable)
@@ -31,7 +31,7 @@ NR_USEC_WAIT=100
 
 TRACE_PATH=$(mktemp -d)
 
-NUM_TESTS=39
+NUM_TESTS=49
 
 source $TESTDIR/utils/utils.sh
 
@@ -119,6 +119,30 @@ function test_ust_default_name()
        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()
 {
@@ -167,7 +191,7 @@ fi
 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
index 050cdeacc0806821c7497b4fb2ba21dc8a17376d..6519670dec128815e561be2725c2c3f4d3913a1b 100644 (file)
@@ -1,2 +1,16 @@
 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
index f6ca236108d43f9a816e34ec06a58e83db1b11c4..b9b45c5cb2e84103ea8e0ba371f08473e72f4439 100644 (file)
@@ -1,2 +1,16 @@
 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
index 0c23d843b049e9a4e97fc0480cff0ecab15e8505..afe5435bd4e491388d7b42a5963b87389814a43a 100755 (executable)
@@ -94,7 +94,7 @@ function validate_file_count
        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"
index 99301b59d4e624b5c01e8dd84401509c35559bbd..ed58e70a2f0c4169bb4d392d895bffd8b6abd1a5 100755 (executable)
@@ -53,7 +53,7 @@ function enable_lttng_channel_size_limit ()
        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"
index dbc9a17c16e415c69431954eeff7765c53061750..e7b4cf71b8f27f0c8f8ead42074c13bcf42db409 100644 (file)
@@ -1,8 +1,22 @@
 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
index e37fe96d7cf3525631d89fe2064c126798015f13..9ff98747a98f55bb58fa4b1b3591dc8fd3f18077 100644 (file)
@@ -1,2 +1,16 @@
 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
diff --git a/tests/regression/ust/buffers-pid/Makefile.am b/tests/regression/ust/buffers-pid/Makefile.am
new file mode 100644 (file)
index 0000000..74aacba
--- /dev/null
@@ -0,0 +1,16 @@
+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
diff --git a/tests/regression/ust/buffers-pid/test_buffers_pid b/tests/regression/ust/buffers-pid/test_buffers_pid
new file mode 100755 (executable)
index 0000000..2d227bf
--- /dev/null
@@ -0,0 +1,241 @@
+#!/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
diff --git a/tests/regression/ust/buffers-uid/Makefile.am b/tests/regression/ust/buffers-uid/Makefile.am
deleted file mode 100644 (file)
index 4fefd80..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-noinst_SCRIPTS = test_buffers_uid
-EXTRA_DIST = test_buffers_uid
diff --git a/tests/regression/ust/buffers-uid/test_buffers_uid b/tests/regression/ust/buffers-uid/test_buffers_uid
deleted file mode 100755 (executable)
index 6af675c..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-#!/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
index d457c06430f06f9c37420b326801e71ae5806fe9..76a34ef07eb7cb41a4919dd0eb6ebfd1bd963e45 100644 (file)
@@ -13,3 +13,17 @@ endif
 
 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
index ff47cd9dee56b7b78edfd8982633fcef7e0c3a09..539803d7beb82fcd995345cd88867d03211847bd 100644 (file)
@@ -13,3 +13,17 @@ endif
 
 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
index 71d81f793ea74ac68fe01e79156f20b3ddaa5471..874dfc5a6fa53fb599f767c8dfe5d8921e3a8d1a 100644 (file)
 #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
 
@@ -46,7 +42,3 @@ TRACEPOINT_LOGLEVEL(ust_tests_exitfast, message, TRACE_INFO)
 #define TRACEPOINT_INCLUDE "./ust_tests_exitfast.h"
 
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
index 97f134f79ad293c9a4d02d3186a4903307342af1..89a2ec985e972848cf51ae80376340c8c8d6bfea 100644 (file)
@@ -18,3 +18,17 @@ endif
 
 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
index 544640469df393feb7ed069564767311a615fe9f..6e9c89e14d1e043bc99a2ca2e95fb473441b5676 100644 (file)
@@ -14,3 +14,17 @@ gen_events_LDADD = -llttng-ust
 
 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
index 12286b4dbf4edf5b648c195813726edd096e1eb7..b43cb4e0a4ac322de453f4799fec0ddfc726b2bd 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -55,7 +51,3 @@ TRACEPOINT_EVENT(tp, tptest_sighandler,
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index af12d31edbc2c8dd6e657c78614943ed1f48a3c5..d35ca81294a23f0487a2b9fb947e30245f33f5e2 100644 (file)
@@ -6,3 +6,17 @@ prog_LDADD = -llttng-ust -llttng-ust-libc-wrapper
 
 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
index d2c4ecabf5e599770a706057338d5cafc38b575c..9f7ebecb9e8d8d3dbe1de3ae93d29af653c6d99d 100644 (file)
@@ -83,3 +83,17 @@ endif
 
 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
index d2492b8753b7fc9f9e6b38c5b1078d46ce419ff0..e88c0e59263a6cd4d6566e43c12f915ccbaff9c5 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -55,7 +51,3 @@ TRACEPOINT_LOGLEVEL(ust_tests_demo, done, TRACE_CRIT)
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index 25d42ad18eea0cdf19c96a508a0c2a666b051fe4..eec0349e35c26365cee5a67663a9a719519e65e8 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -62,7 +58,3 @@ TRACEPOINT_LOGLEVEL(ust_tests_demo2, loop, TRACE_WARNING)
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index c7b63dd8b2750c7d995b9853ea611e482d855b49..232d577aaea9186409c8254bdafc1fafef6e4264 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -47,7 +43,3 @@ TRACEPOINT_LOGLEVEL(ust_tests_demo3, done, TRACE_WARNING)
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index e62706007a2b097af442fc6adbe676cfe58e8ee6..3eef3ac5edcde95f17c35bbd4a854378b1bec1ca 100644 (file)
@@ -14,3 +14,17 @@ gen_events_LDADD = -llttng-ust -lurcu
 
 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
index b45a3dd87fd686af845c1ace38daa260676e727c..f6c8634bfa7247bc052d01c25709fe8ab3e9f727 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -39,7 +35,3 @@ TRACEPOINT_EVENT(tp, slow,
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index 2d978e593030c82e972265ebaebeabf03756a3bd..6ff204a6628ee831c2cc147c664ff14b663cdda6 100644 (file)
@@ -14,3 +14,17 @@ gen_nevents_LDADD = -llttng-ust
 
 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
index 58e4c17e3c712e528fade959a462c656b5503ae6..7a1c72870e07ca32fd79610dc262938a56ef953e 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -62,7 +58,3 @@ TRACEPOINT_EVENT(ust_gen_nevents, tptest3,
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index 70a4eeabf3441f7ddd12b6492125c21e5c13f091..c8f505501a2a1b7ae2809500a2303f52fbd0ed6a 100644 (file)
@@ -1,2 +1,16 @@
 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
index 5e0a80467eced8aeb33f0bfe136edabf8c43d691..1adddb542210cbb048c20d951c69acc840336f6d 100644 (file)
@@ -2,3 +2,17 @@ SUBDIRS = demo
 
 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
index 68b6e087f84f506aeaf9137e43bc659ad9f5f0c9..c5e9f92d589334a93cfbc46a07ba4c6ad4248032 100644 (file)
@@ -35,4 +35,18 @@ endif
 
 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
index 3e024b44eabb11b9f236e8352fa38130e9b651bd..c213e3e4bdd42356c62077080408a935140e30b2 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -57,7 +53,3 @@ TRACEPOINT_MODEL_EMF_URI(ust_tests_demo, done,
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index cf4424b61e8426f15557cd79b7998c979f618a1c..262f651c31fc598b72ec59ca86fcb78466e3e20f 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -54,7 +50,3 @@ TRACEPOINT_LOGLEVEL(ust_tests_demo2, loop, TRACE_WARNING)
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index e58ee86f7200cee7cfb661dbe3caf1990d00f9c8..46a19ce90110cff5300af72d3ce2fb7c99378cc7 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -39,7 +35,3 @@ TRACEPOINT_LOGLEVEL(ust_tests_demo3, done, TRACE_WARNING)
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
index c020920057672eda74d4868dc4bbecbe54bf8b6e..540cfb504cdd86a71ce427904eb85f8a178a7b7d 100644 (file)
@@ -1,2 +1,16 @@
 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
index 03d83e31486ef430bdfd844eff44caf595905f9b..ad9d33d521ddbbdb022040df5599f9a539777a12 100755 (executable)
@@ -41,7 +41,7 @@ function enable_channel_per_uid()
        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"
 }
 
@@ -50,7 +50,7 @@ function enable_channel_per_pid()
        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"
 }
 
@@ -103,24 +103,36 @@ test_after_app_pid() {
 
        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
 }
 
@@ -132,21 +144,31 @@ test_before_app_pid() {
        $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
@@ -157,24 +179,36 @@ test_after_app_uid() {
 
        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
 }
 
@@ -183,24 +217,34 @@ test_before_app_uid() {
 
        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
@@ -226,7 +270,7 @@ start_lttng_sessiond
 while [ $i -lt $TEST_COUNT ]; do
        TRACE_PATH=$(mktemp -d)
        ${TESTS[$i]}
-       #rm -rf $TRACE_PATH
+       rm -rf $TRACE_PATH
        let "i++"
 done
 
index 2f510f0949fc5952248f6bb450f061edee3f12c1..433d6ad83e0b03c72196b6f671f6ed21cc6dc1b0 100644 (file)
@@ -2,3 +2,17 @@ noinst_SCRIPTS = README launch_ust_app test_multi_sessions_per_uid_10app \
                                 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
index f517fdcbbc28c6bcfcca07f44b62992801365317..a8dec2bd96e91aa25da8e841d590a3d2717fd676 100644 (file)
@@ -13,6 +13,7 @@ LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la
 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
@@ -29,15 +30,17 @@ test_uri_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBHASHTABLE)
 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
@@ -51,29 +54,32 @@ UST_DATA_TRACE=$(top_builddir)/src/bin/lttng-sessiond/trace-ust.o \
                   $(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
index 0b4bb08c948c4ac375ef9765b94705aef5ccfa24..16d4f7b3991de7d980e2689c3a14571e10a1ac63 100644 (file)
@@ -79,9 +79,6 @@ static void test_create_one_kernel_session(void)
           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)
index 931c865bc4efde14469e780d9038756d7f3199aa..b3d5929738f4d8c7d92b929763685ca3cda8c717 100644 (file)
@@ -2,3 +2,17 @@ SUBDIRS = tap testapp
 
 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
index d8d4dd08e04cafb92276549b9b279713858b61f9..64a243fc8a69f8b1428d8bf7c5a2231cc8756afd 100755 (executable)
@@ -146,6 +146,7 @@ while (<>)
        my $elapsed     = '\((.*)\)';
        my $hostname    = '.*';
        my $pname       = '.*';
+       my $pinfo       = '.*';
        my $pid         = '\d+';
        my $tp_provider = '.*';
        my $tp_name     = '.*';
@@ -153,18 +154,21 @@ while (<>)
        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;
        }
index 14cd2349776819b6889855f584c784a9079b4c42..0e78398feea29b5d359a910048648cb5d38d1074 100644 (file)
@@ -1,4 +1,18 @@
-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
index a430951193328aca551e34dfa1f3622a9b23ef28..8bf72f6fcde57dac97fd9673cb0df5efdf9b6b0c 100644 (file)
@@ -83,7 +83,9 @@ _gen_result(int ok, const char *func, char *file, unsigned int line,
           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
@@ -294,12 +296,14 @@ int
 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) {
@@ -324,7 +328,9 @@ todo_start(char *fmt, ...)
        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;
index 8b631eebcf3b358320737190d10f5c0344181984..be2758de3ecc4f5032e1fa5f639eb0624c032668 100644 (file)
@@ -1,2 +1,2 @@
-SUBDIRS = gen-ust-events
+SUBDIRS = gen-ust-events gen-ust-nevents
 
index 6ffbc32abfb423d83d0236436b5091705108925a..ab6469d89b377369dd2f02f05b4551466e5e18cc 100644 (file)
@@ -4,10 +4,6 @@
 #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>
  *
@@ -50,7 +46,3 @@ TRACEPOINT_EVENT(tp, tptest,
 
 /* This part must be outside ifdef protection */
 #include <lttng/tracepoint-event.h>
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/tests/utils/testapp/gen-ust-nevents/Makefile.am b/tests/utils/testapp/gen-ust-nevents/Makefile.am
new file mode 100644 (file)
index 0000000..e11b5f3
--- /dev/null
@@ -0,0 +1,15 @@
+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
diff --git a/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c b/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c
new file mode 100644 (file)
index 0000000..c7a5e5f
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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;
+}
diff --git a/tests/utils/testapp/gen-ust-nevents/tp.c b/tests/utils/testapp/gen-ust-nevents/tp.c
new file mode 100644 (file)
index 0000000..a09561d
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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"
diff --git a/tests/utils/testapp/gen-ust-nevents/tp.h b/tests/utils/testapp/gen-ust-nevents/tp.h
new file mode 100644 (file)
index 0000000..085ed6b
--- /dev/null
@@ -0,0 +1,124 @@
+#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>
This page took 0.406689 seconds and 4 git commands to generate.