Move all public libraries under 'src/lib/'.
This is part of an effort to standardize our autotools setup across
projects to simplify maintenance.
Change-Id: I98ed0e9da28c111e62b189f26b0a225b5aa500e1
Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
/tests/unit/ust-utils/test_ust_utils_cxx
# Java agent library
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-all/*.jar
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-common/classnoinst.stamp
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-common/context-jni-header.stamp
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-common/*.jar
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-jul/classnoinst.stamp
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-jul/jul-jni-header.stamp
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-jul/*.jar
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/classnoinst.stamp
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/log4j-jni-header.stamp
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/*.jar
-/src/liblttng-ust-java-agent/jni/common/org_lttng_ust_agent_context_LttngContextApi.h
-/src/liblttng-ust-java-agent/jni/jul/org_lttng_ust_agent_jul_LttngJulApi.h
-/src/liblttng-ust-java-agent/jni/log4j/org_lttng_ust_agent_log4j_LttngLog4jApi.h
-/src/liblttng-ust-java/classnoinst.stamp
-/src/liblttng-ust-java/jni-header.stamp
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-all/*.jar
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common/classnoinst.stamp
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common/context-jni-header.stamp
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common/*.jar
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-jul/classnoinst.stamp
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-jul/jul-jni-header.stamp
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-jul/*.jar
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j/classnoinst.stamp
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j/log4j-jni-header.stamp
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j/*.jar
+/src/lib/lttng-ust-java-agent/jni/common/org_lttng_ust_agent_context_LttngContextApi.h
+/src/lib/lttng-ust-java-agent/jni/jul/org_lttng_ust_agent_jul_LttngJulApi.h
+/src/lib/lttng-ust-java-agent/jni/log4j/org_lttng_ust_agent_log4j_LttngLog4jApi.h
+/src/lib/lttng-ust-java/classnoinst.stamp
+/src/lib/lttng-ust-java/jni-header.stamp
# Python agent
/src/python-lttngust/lttngust/version.py
/src/lib/lttng-ust-dl/Makefile
/src/lib/lttng-ust-fd/Makefile
/src/lib/lttng-ust-fork/Makefile
-/src/liblttng-ust-java-agent/Makefile
-/src/liblttng-ust-java-agent/java/Makefile
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-all/Makefile
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile
-/src/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile
-/src/liblttng-ust-java-agent/jni/Makefile
-/src/liblttng-ust-java-agent/jni/common/Makefile
-/src/liblttng-ust-java-agent/jni/jul/Makefile
-/src/liblttng-ust-java-agent/jni/log4j/Makefile
-/src/liblttng-ust-java/Makefile
+/src/lib/lttng-ust-java-agent/Makefile
+/src/lib/lttng-ust-java-agent/java/Makefile
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-all/Makefile
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common/Makefile
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile
+/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile
+/src/lib/lttng-ust-java-agent/jni/Makefile
+/src/lib/lttng-ust-java-agent/jni/common/Makefile
+/src/lib/lttng-ust-java-agent/jni/jul/Makefile
+/src/lib/lttng-ust-java-agent/jni/log4j/Makefile
+/src/lib/lttng-ust-java/Makefile
/src/lib/lttng-ust-libc-wrapper/Makefile
/src/lib/lttng-ust-python-agent/Makefile
/src/lib/Makefile
src/lib/lttng-ust-dl/Makefile
src/lib/lttng-ust-fd/Makefile
src/lib/lttng-ust-fork/Makefile
- src/liblttng-ust-java-agent/java/lttng-ust-agent-all/Makefile
- src/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile
- src/liblttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile
- src/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile
- src/liblttng-ust-java-agent/java/Makefile
- src/liblttng-ust-java-agent/jni/common/Makefile
- src/liblttng-ust-java-agent/jni/jul/Makefile
- src/liblttng-ust-java-agent/jni/log4j/Makefile
- src/liblttng-ust-java-agent/jni/Makefile
- src/liblttng-ust-java-agent/Makefile
- src/liblttng-ust-java/Makefile
+ src/lib/lttng-ust-java-agent/java/lttng-ust-agent-all/Makefile
+ src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common/Makefile
+ src/lib/lttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile
+ src/lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile
+ src/lib/lttng-ust-java-agent/java/Makefile
+ src/lib/lttng-ust-java-agent/jni/common/Makefile
+ src/lib/lttng-ust-java-agent/jni/jul/Makefile
+ src/lib/lttng-ust-java-agent/jni/log4j/Makefile
+ src/lib/lttng-ust-java-agent/jni/Makefile
+ src/lib/lttng-ust-java-agent/Makefile
+ src/lib/lttng-ust-java/Makefile
src/lib/lttng-ust-libc-wrapper/Makefile
src/liblttng-ust/Makefile
src/lib/lttng-ust-python-agent/Makefile
cd $$subdir && \
$(MAKE) all \
CLASSPATH="$(CLASSPATH)" \
- JAVA_CLASSPATH_OVERRIDE_JUL="../../../src/liblttng-ust-java-agent/java/lttng-ust-agent-jul" \
- JAVA_CLASSPATH_OVERRIDE_COMMON="../../../src/liblttng-ust-java-agent/java/lttng-ust-agent-common" \
+ JAVA_CLASSPATH_OVERRIDE_JUL="../../../src/lib/lttng-ust-java-agent/java/lttng-ust-agent-jul" \
+ JAVA_CLASSPATH_OVERRIDE_COMMON="../../../src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common" \
$(AM_MAKEFLAGS) \
) || exit 1; \
done; \
cd $$subdir && \
$(MAKE) all \
CLASSPATH="$(CLASSPATH)" \
- JAVA_CLASSPATH_OVERRIDE_LOG4J="../../../src/liblttng-ust-java-agent/java/lttng-ust-agent-log4j" \
- JAVA_CLASSPATH_OVERRIDE_COMMON="../../../src/liblttng-ust-java-agent/java/lttng-ust-agent-common" \
+ JAVA_CLASSPATH_OVERRIDE_LOG4J="../../../src/lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j" \
+ JAVA_CLASSPATH_OVERRIDE_COMMON="../../../src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common" \
$(AM_MAKEFLAGS) \
) || exit 1; \
done; \
cd $DIR
if [ -f "$DIR/.intree" ]; then
- CLASSPATH="../../../liblttng-ust-java-agent/java/lttng-ust-agent-common/$JARFILE_COMMON"
- CLASSPATH="$CLASSPATH:../../../liblttng-ust-java-agent/java/lttng-ust-agent-jul/$JARFILE_JUL"
- LIBPATH="../../../liblttng-ust-java-agent/jni/jul/.libs"
+ CLASSPATH="../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-common/$JARFILE_COMMON"
+ CLASSPATH="$CLASSPATH:../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-jul/$JARFILE_JUL"
+ LIBPATH="../../../lib/lttng-ust-java-agent/jni/jul/.libs"
else
CLASSPATH="/usr/local/share/java/$JARFILE_COMMON:/usr/share/java/$JARFILE_COMMON"
CLASSPATH="$CLASSPATH:/usr/local/share/java/$JARFILE_JUL:/usr/share/java/$JARFILE_JUL"
cd $DIR
if [ -f "$DIR/.intree" ]; then
- CLASSPATH="$CLASSPATH:../../../liblttng-ust-java-agent/java/lttng-ust-agent-common/$JARFILE_COMMON"
- CLASSPATH="$CLASSPATH:../../../liblttng-ust-java-agent/java/lttng-ust-agent-log4j/$JARFILE_LOG4J"
- LIBPATH="../../../liblttng-ust-java-agent/jni/log4j/.libs"
+ CLASSPATH="$CLASSPATH:../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-common/$JARFILE_COMMON"
+ CLASSPATH="$CLASSPATH:../../../lib/lttng-ust-java-agent/java/lttng-ust-agent-log4j/$JARFILE_LOG4J"
+ LIBPATH="../../../lib/lttng-ust-java-agent/jni/log4j/.libs"
else
CLASSPATH="$CLASSPATH:/usr/local/share/java/$JARFILE_COMMON:/usr/share/java/$JARFILE_COMMON"
CLASSPATH="$CLASSPATH:/usr/local/share/java/$JARFILE_LOG4J:/usr/share/java/$JARFILE_LOG4J"
liblttng-ust-ctl \
lib
-if ENABLE_JNI_INTERFACE
-SUBDIRS += liblttng-ust-java
-endif
-
-if ENABLE_JAVA_AGENT
-SUBDIRS += liblttng-ust-java-agent
-endif
-
if ENABLE_PYTHON_AGENT
SUBDIRS += python-lttngust
endif
SUBDIRS += lttng-ust-dl
endif
+if ENABLE_JNI_INTERFACE
+SUBDIRS += lttng-ust-java
+endif
+
+if ENABLE_JAVA_AGENT
+SUBDIRS += lttng-ust-java-agent
+endif
+
if ENABLE_PYTHON_AGENT
SUBDIRS += lttng-ust-python-agent
endif
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+SUBDIRS = java jni
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+SUBDIRS = lttng-ust-agent-common lttng-ust-agent-all
+
+if ENABLE_JAVA_AGENT_WITH_JUL
+SUBDIRS += lttng-ust-agent-jul
+endif
+
+if ENABLE_JAVA_AGENT_WITH_LOG4J
+SUBDIRS += lttng-ust-agent-log4j
+endif
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+JAVAROOT = .
+
+jarfile_version = 1.0.0
+jarfile_manifest = $(srcdir)/Manifest.txt
+jarfile_symlink = lttng-ust-agent-all.jar
+jarfile = lttng-ust-agent-all-$(jarfile_version).jar
+
+# Compatibility symlink provided for applications expecting the agent
+# jar file installed by UST 2.7.
+jarfile_compat_symlink = liblttng-ust-agent.jar
+
+jardir = $(datadir)/java
+
+dist_noinst_DATA = $(jarfile_manifest)
+
+jar_DATA = $(jarfile)
+
+$(jarfile):
+ $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) \
+ && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink) \
+ && rm -f $(jarfile_compat_symlink) && $(LN_S) $(jarfile_symlink) $(jarfile_compat_symlink)
+
+install-data-hook:
+ cd $(DESTDIR)/$(jardir) \
+ && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink) \
+ && rm -f $(jarfile_compat_symlink) && $(LN_S) $(jarfile_symlink) $(jarfile_compat_symlink)
+
+uninstall-hook:
+ cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && rm -f $(jarfile_compat_symlink)
+
+CLEANFILES = *.jar
--- /dev/null
+Name: org/lttng/ust/agent/all/
+Specification-Title: LTTng UST All Java Agents
+Specification-Version: 1.0.0
+Specification-Vendor: LTTng Project
+Implementation-Title: org.lttng.ust.agent.all
+Implementation-Version: 1.0.0
+Implementation-Vendor: LTTng Project
+Class-Path: lttng-ust-agent-common.jar lttng-ust-agent-jul.jar lttng-ust-agent-log4j.jar
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+JAVAROOT = .
+
+pkgpath = org/lttng/ust/agent
+
+jarfile_version = 1.0.0
+jarfile_manifest = $(srcdir)/Manifest.txt
+jarfile_symlink = lttng-ust-agent-common.jar
+jarfile = lttng-ust-agent-common-$(jarfile_version).jar
+
+jardir = $(datadir)/java
+jnioutdir = ../../jni/common
+
+dist_noinst_JAVA = $(pkgpath)/AbstractLttngAgent.java \
+ $(pkgpath)/EventNamePattern.java \
+ $(pkgpath)/ILttngAgent.java \
+ $(pkgpath)/ILttngHandler.java \
+ $(pkgpath)/LTTngAgent.java \
+ $(pkgpath)/client/ILttngTcpClientListener.java \
+ $(pkgpath)/client/SessiondCommand.java \
+ $(pkgpath)/client/LttngAgentResponse.java \
+ $(pkgpath)/client/LttngTcpSessiondClient.java \
+ $(pkgpath)/client/SessiondCommandHeader.java \
+ $(pkgpath)/client/SessiondDisableAppContextCommand.java \
+ $(pkgpath)/client/SessiondDisableEventCommand.java \
+ $(pkgpath)/client/SessiondEnableAppContextCommand.java \
+ $(pkgpath)/client/SessiondEnableEventCommand.java \
+ $(pkgpath)/client/SessiondListLoggersCommand.java \
+ $(pkgpath)/context/LttngContextApi.java \
+ $(pkgpath)/context/ContextInfoManager.java \
+ $(pkgpath)/context/ContextInfoSerializer.java \
+ $(pkgpath)/context/IContextInfoRetriever.java \
+ $(pkgpath)/filter/FilterChangeNotifier.java \
+ $(pkgpath)/filter/IFilterChangeListener.java \
+ $(pkgpath)/session/EventRule.java \
+ $(pkgpath)/session/LogLevelSelector.java \
+ $(pkgpath)/utils/LttngUstAgentLogger.java
+
+
+dist_noinst_DATA = $(jarfile_manifest)
+
+jar_DATA = $(jarfile)
+
+classes = $(pkgpath)/*.class \
+ $(pkgpath)/client/*.class \
+ $(pkgpath)/context/*.class \
+ $(pkgpath)/filter/*.class \
+ $(pkgpath)/session/*.class \
+ $(pkgpath)/utils/*.class
+
+$(jarfile): classnoinst.stamp
+ $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink)
+
+if !HAVE_JAVAH
+# If we don't have javah, assume we are running openjdk >= 10 and use javac
+# to generate the jni header file.
+AM_JAVACFLAGS = -h $(jnioutdir)
+else
+context-jni-header.stamp: $(dist_noinst_JAVA)
+ $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(jnioutdir) $(JAVAHFLAGS) org.lttng.ust.agent.context.LttngContextApi && \
+ echo "Context API JNI header generated" > context-jni-header.stamp
+
+all-local: context-jni-header.stamp
+endif
+
+install-data-hook:
+ cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink)
+
+uninstall-hook:
+ cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink)
+
+CLEANFILES = *.jar \
+ $(pkgpath)/*.class \
+ $(pkgpath)/client/*.class \
+ $(pkgpath)/context/*.class \
+ $(pkgpath)/filter/*.class \
+ $(pkgpath)/session/*.class \
+ $(pkgpath)/utils/*.class \
+ context-jni-header.stamp \
+ $(jnioutdir)/org_lttng_ust_agent_context_LttngContextApi.h
--- /dev/null
+Name: org/lttng/ust/agent/
+Specification-Title: LTTng UST Java Agent
+Specification-Version: 1.0.0
+Specification-Vendor: LTTng Project
+Implementation-Title: org.lttng.ust.agent
+Implementation-Version: 1.0.0
+Implementation-Vendor: LTTng Project
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.regex.Matcher;
+
+import org.lttng.ust.agent.client.ILttngTcpClientListener;
+import org.lttng.ust.agent.client.LttngTcpSessiondClient;
+import org.lttng.ust.agent.filter.FilterChangeNotifier;
+import org.lttng.ust.agent.session.EventRule;
+import org.lttng.ust.agent.utils.LttngUstAgentLogger;
+
+/**
+ * Base implementation of a {@link ILttngAgent}.
+ *
+ * @author Alexandre Montplaisir
+ * @param <T>
+ * The type of logging handler that should register to this agent
+ */
+public abstract class AbstractLttngAgent<T extends ILttngHandler>
+ implements ILttngAgent<T>, ILttngTcpClientListener {
+
+ private static final int INIT_TIMEOUT = 3; /* Seconds */
+
+ /** The handlers registered to this agent */
+ private final Set<T> registeredHandlers = new HashSet<T>();
+
+ /**
+ * The trace events currently enabled in the sessions.
+ *
+ * The key is the {@link EventNamePattern} that comes from the event name.
+ * The value is the ref count (how many different sessions currently have
+ * this event enabled). Once the ref count falls to 0, this means we can
+ * avoid sending log events through JNI because nobody wants them.
+ *
+ * Its accesses should be protected by the {@link #enabledEventNamesLock}
+ * below.
+ */
+ private final Map<EventNamePattern, Integer> enabledPatterns = new HashMap<EventNamePattern, Integer>();
+
+ /**
+ * Cache of already-checked event names. As long as enabled/disabled events
+ * don't change in the session, we can avoid re-checking events that were
+ * previously checked against all known enabled patterns.
+ *
+ * Its accesses should be protected by the {@link #enabledEventNamesLock}
+ * below, with the exception of concurrent get operations.
+ */
+ private final Map<String, Boolean> enabledEventNamesCache = new ConcurrentHashMap<String, Boolean>();
+
+ /**
+ * Lock protecting accesses to the {@link #enabledPatterns} and
+ * {@link #enabledEventNamesCache} maps.
+ */
+ private final Lock enabledEventNamesLock = new ReentrantLock();
+
+ /**
+ * The application contexts currently enabled in the tracing sessions.
+ *
+ * It is first indexed by context retriever, then by context name. This
+ * allows to efficiently query all the contexts for a given retriever.
+ *
+ * Works similarly as {@link #enabledEvents}, but for app contexts (and with
+ * an extra degree of indexing).
+ *
+ * TODO Could be changed to a Guava Table once/if we start using it.
+ */
+ private final Map<String, Map<String, Integer>> enabledAppContexts = new ConcurrentHashMap<String, Map<String, Integer>>();
+
+ /** Tracing domain. Defined by the sub-classes via the constructor. */
+ private final Domain domain;
+
+ /* Lazy-loaded sessiond clients and their thread objects */
+ private LttngTcpSessiondClient rootSessiondClient = null;
+ private LttngTcpSessiondClient userSessiondClient = null;
+ private Thread rootSessiondClientThread = null;
+ private Thread userSessiondClientThread = null;
+
+ /** Indicates if this agent has been initialized. */
+ private boolean initialized = false;
+
+ /**
+ * Constructor. Should only be called by sub-classes via super(...);
+ *
+ * @param domain
+ * The tracing domain of this agent.
+ */
+ protected AbstractLttngAgent(Domain domain) {
+ this.domain = domain;
+ }
+
+ @Override
+ public Domain getDomain() {
+ return domain;
+ }
+
+ @Override
+ public void registerHandler(T handler) {
+ synchronized (registeredHandlers) {
+ if (registeredHandlers.isEmpty()) {
+ /*
+ * This is the first handler that registers, we will initialize
+ * the agent.
+ */
+ init();
+ }
+ registeredHandlers.add(handler);
+ }
+ }
+
+ @Override
+ public void unregisterHandler(T handler) {
+ synchronized (registeredHandlers) {
+ registeredHandlers.remove(handler);
+ if (registeredHandlers.isEmpty()) {
+ /* There are no more registered handlers, close the connection. */
+ dispose();
+ }
+ }
+ }
+
+ private void init() {
+ /*
+ * Only called from a synchronized (registeredHandlers) block, should
+ * not need additional synchronization.
+ */
+ if (initialized) {
+ return;
+ }
+
+ LttngUstAgentLogger.log(AbstractLttngAgent.class, "Initializing Agent for domain: " + domain.name());
+
+ String rootClientThreadName = "Root sessiond client started by agent: " + this.getClass().getSimpleName();
+
+ rootSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), true);
+ rootSessiondClientThread = new Thread(rootSessiondClient, rootClientThreadName);
+ rootSessiondClientThread.setDaemon(true);
+ rootSessiondClientThread.start();
+
+ String userClientThreadName = "User sessiond client started by agent: " + this.getClass().getSimpleName();
+
+ userSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), false);
+ userSessiondClientThread = new Thread(userSessiondClient, userClientThreadName);
+ userSessiondClientThread.setDaemon(true);
+ userSessiondClientThread.start();
+
+ /* Give the threads' registration a chance to end. */
+ if (!rootSessiondClient.waitForConnection(INIT_TIMEOUT)) {
+ userSessiondClient.waitForConnection(INIT_TIMEOUT);
+ }
+
+ initialized = true;
+ }
+
+ /**
+ * Dispose the agent
+ */
+ private void dispose() {
+ LttngUstAgentLogger.log(AbstractLttngAgent.class, "Disposing Agent for domain: " + domain.name());
+
+ /*
+ * Only called from a synchronized (registeredHandlers) block, should
+ * not need additional synchronization.
+ */
+ rootSessiondClient.close();
+ userSessiondClient.close();
+
+ try {
+ rootSessiondClientThread.join();
+ userSessiondClientThread.join();
+
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ rootSessiondClient = null;
+ rootSessiondClientThread = null;
+ userSessiondClient = null;
+ userSessiondClientThread = null;
+
+ /*
+ * Send filter change notifications for all event rules currently
+ * active, then clear them.
+ */
+ FilterChangeNotifier fcn = FilterChangeNotifier.getInstance();
+
+ enabledEventNamesLock.lock();
+ try {
+ for (Map.Entry<EventNamePattern, Integer> entry : enabledPatterns.entrySet()) {
+ String eventName = entry.getKey().getEventName();
+ Integer nb = entry.getValue();
+ for (int i = 0; i < nb.intValue(); i++) {
+ fcn.removeEventRules(eventName);
+ }
+ }
+ enabledPatterns.clear();
+ enabledEventNamesCache.clear();
+ } finally {
+ enabledEventNamesLock.unlock();
+ }
+
+ /*
+ * Also clear tracked app contexts (no filter notifications sent for
+ * those currently).
+ */
+ enabledAppContexts.clear();
+
+ initialized = false;
+ }
+
+ @Override
+ public boolean eventEnabled(EventRule eventRule) {
+ /* Notify the filter change manager of the command */
+ FilterChangeNotifier.getInstance().addEventRule(eventRule);
+
+ String eventName = eventRule.getEventName();
+ EventNamePattern pattern = new EventNamePattern(eventName);
+
+ enabledEventNamesLock.lock();
+ try {
+ boolean ret = incrementRefCount(pattern, enabledPatterns);
+ enabledEventNamesCache.clear();
+ return ret;
+ } finally {
+ enabledEventNamesLock.unlock();
+ }
+ }
+
+ @Override
+ public boolean eventDisabled(String eventName) {
+ /* Notify the filter change manager of the command */
+ FilterChangeNotifier.getInstance().removeEventRules(eventName);
+
+ EventNamePattern pattern = new EventNamePattern(eventName);
+
+ enabledEventNamesLock.lock();
+ try {
+ boolean ret = decrementRefCount(pattern, enabledPatterns);
+ enabledEventNamesCache.clear();
+ return ret;
+ } finally {
+ enabledEventNamesLock.unlock();
+ }
+ }
+
+ @Override
+ public boolean appContextEnabled(String contextRetrieverName, String contextName) {
+ synchronized (enabledAppContexts) {
+ Map<String, Integer> retrieverMap = enabledAppContexts.get(contextRetrieverName);
+ if (retrieverMap == null) {
+ /* There is no submap for this retriever, let's create one. */
+ retrieverMap = new ConcurrentHashMap<String, Integer>();
+ enabledAppContexts.put(contextRetrieverName, retrieverMap);
+ }
+
+ return incrementRefCount(contextName, retrieverMap);
+ }
+ }
+
+ @Override
+ public boolean appContextDisabled(String contextRetrieverName, String contextName) {
+ synchronized (enabledAppContexts) {
+ Map<String, Integer> retrieverMap = enabledAppContexts.get(contextRetrieverName);
+ if (retrieverMap == null) {
+ /* There was no submap for this retriever, invalid command? */
+ return false;
+ }
+
+ boolean ret = decrementRefCount(contextName, retrieverMap);
+
+ /* If the submap is now empty we can remove it from the main map. */
+ if (retrieverMap.isEmpty()) {
+ enabledAppContexts.remove(contextRetrieverName);
+ }
+
+ return ret;
+ }
+ }
+
+ /*
+ * Implementation of this method is domain-specific.
+ */
+ @Override
+ public abstract Collection<String> listAvailableEvents();
+
+ @Override
+ public boolean isEventEnabled(String eventName) {
+ Boolean cachedEnabled = enabledEventNamesCache.get(eventName);
+ if (cachedEnabled != null) {
+ /* We have seen this event previously */
+ /*
+ * Careful! enabled == null could also mean that the null value is
+ * associated with the key. But we should have never inserted null
+ * values in the map.
+ */
+ return cachedEnabled.booleanValue();
+ }
+
+ /*
+ * We have not previously checked this event. Run it against all known
+ * enabled event patterns to determine if it should pass or not.
+ */
+ enabledEventNamesLock.lock();
+ try {
+ boolean enabled = false;
+ for (EventNamePattern enabledPattern : enabledPatterns.keySet()) {
+ Matcher matcher = enabledPattern.getPattern().matcher(eventName);
+ if (matcher.matches()) {
+ enabled = true;
+ break;
+ }
+ }
+
+ /* Add the result to the cache */
+ enabledEventNamesCache.put(eventName, Boolean.valueOf(enabled));
+ return enabled;
+
+ } finally {
+ enabledEventNamesLock.unlock();
+ }
+ }
+
+ @Override
+ public Collection<Map.Entry<String, Map<String, Integer>>> getEnabledAppContexts() {
+ return enabledAppContexts.entrySet();
+ }
+
+ private static <T> boolean incrementRefCount(T key, Map<T, Integer> refCountMap) {
+ synchronized (refCountMap) {
+ Integer count = refCountMap.get(key);
+ if (count == null) {
+ /* This is the first instance of this event being enabled */
+ refCountMap.put(key, Integer.valueOf(1));
+ return true;
+ }
+ if (count.intValue() <= 0) {
+ /* It should not have been in the map in the first place! */
+ throw new IllegalStateException();
+ }
+ /* The event was already enabled, increment its refcount */
+ refCountMap.put(key, Integer.valueOf(count.intValue() + 1));
+ return true;
+ }
+ }
+
+ private static <T> boolean decrementRefCount(T key, Map<T, Integer> refCountMap) {
+ synchronized (refCountMap) {
+ Integer count = refCountMap.get(key);
+ if (count == null || count.intValue() <= 0) {
+ /*
+ * The sessiond asked us to disable an event that was not
+ * enabled previously. Command error?
+ */
+ return false;
+ }
+ if (count.intValue() == 1) {
+ /*
+ * This is the last instance of this event being disabled,
+ * remove it from the map so that we stop sending it.
+ */
+ refCountMap.remove(key);
+ return true;
+ }
+ /*
+ * Other sessions are still looking for this event, simply decrement
+ * its refcount.
+ */
+ refCountMap.put(key, Integer.valueOf(count.intValue() - 1));
+ return true;
+ }
+ }
+}
+
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2017 EfficiOS Inc.
+ * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+package org.lttng.ust.agent;
+
+import java.util.regex.Pattern;
+
+/**
+ * Class encapsulating an event name from the session daemon, and its
+ * corresponding {@link Pattern}. This allows referring back to the original
+ * event name, for example when we receive a disable command.
+ *
+ * @author Philippe Proulx
+ * @author Alexandre Montplaisir
+ */
+class EventNamePattern {
+
+ private final String originalEventName;
+
+ /*
+ * Note that two Patterns coming from the exact same String will not be
+ * equals()! As such, it would be confusing to make the pattern part of this
+ * class's equals/hashCode
+ */
+ private final transient Pattern pattern;
+
+ public EventNamePattern(String eventName) {
+ if (eventName == null) {
+ throw new IllegalArgumentException();
+ }
+
+ originalEventName = eventName;
+ pattern = patternFromEventName(eventName);
+ }
+
+ public String getEventName() {
+ return originalEventName;
+ }
+
+ public Pattern getPattern() {
+ return pattern;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + originalEventName.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ EventNamePattern other = (EventNamePattern) obj;
+ if (!originalEventName.equals(other.originalEventName)) {
+ return false;
+ }
+ return true;
+ }
+
+ private static Pattern patternFromEventName(String eventName) {
+ /*
+ * The situation here is that `\*` means a literal `*` in the event
+ * name, and `*` is a wildcard star. We check the event name one
+ * character at a time and create a list of tokens to be converter to
+ * partial patterns.
+ */
+ StringBuilder bigBuilder = new StringBuilder("^");
+ StringBuilder smallBuilder = new StringBuilder();
+
+ for (int i = 0; i < eventName.length(); i++) {
+ char c = eventName.charAt(i);
+
+ switch (c) {
+ case '*':
+ /* Add current quoted builder's string if not empty. */
+ if (smallBuilder.length() > 0) {
+ bigBuilder.append(Pattern.quote(smallBuilder.toString()));
+ smallBuilder.setLength(0);
+ }
+
+ /* Append the equivalent regex which is `.*`. */
+ bigBuilder.append(".*");
+ continue;
+
+ case '\\':
+ /* We only escape `*` and `\` here. */
+ if (i < (eventName.length() - 1)) {
+ char nextChar = eventName.charAt(i + 1);
+
+ if (nextChar == '*' || nextChar == '\\') {
+ smallBuilder.append(nextChar);
+ } else {
+ smallBuilder.append(c);
+ smallBuilder.append(nextChar);
+ }
+
+ i++;
+ continue;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ smallBuilder.append(c);
+ }
+
+ /* Add current quoted builder's string if not empty. */
+ if (smallBuilder.length() > 0) {
+ bigBuilder.append(Pattern.quote(smallBuilder.toString()));
+ }
+
+ bigBuilder.append("$");
+
+ return Pattern.compile(bigBuilder.toString());
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Interface to define LTTng Java agents.
+ *
+ * An "agent" is a representative of an LTTng session daemon in the Java world.
+ * It tracks the settings of a tracing session as they defined in the session
+ * daemon.
+ *
+ * It also track the current logging handlers that are sending events to UST.
+ *
+ * @author Alexandre Montplaisir
+ *
+ * @param <T>
+ * The type of logging handler that should register to this agent
+ */
+public interface ILttngAgent<T extends ILttngHandler> {
+
+ // ------------------------------------------------------------------------
+ // Agent configuration elements
+ // ------------------------------------------------------------------------
+
+ /**
+ * Tracing domains. Corresponds to domains defined by LTTng Tools.
+ */
+ enum Domain {
+ JUL(3), LOG4J(4);
+ private int value;
+
+ private Domain(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return value;
+ }
+ }
+
+ /**
+ * The tracing domain of this agent.
+ *
+ * @return The tracing domain.
+ */
+ Domain getDomain();
+
+ // ------------------------------------------------------------------------
+ // Log handler registering
+ // ------------------------------------------------------------------------
+
+ /**
+ * Register a handler to this agent.
+ *
+ * @param handler
+ * The handler to register
+ */
+ void registerHandler(T handler);
+
+ /**
+ * Deregister a handler from this agent.
+ *
+ * @param handler
+ * The handler to deregister.
+ */
+ void unregisterHandler(T handler);
+
+ // ------------------------------------------------------------------------
+ // Tracing session parameters
+ // ------------------------------------------------------------------------
+
+ /**
+ * Query if a given event is currently enabled in a current tracing session,
+ * meaning it should be sent to UST.
+ *
+ * @param eventName
+ * The name of the event to check.
+ * @return True if the event is currently enabled, false if it is not.
+ */
+ boolean isEventEnabled(String eventName);
+
+ /**
+ * Return the list of application contexts enabled in the tracing sessions.
+ *
+ * @return The application contexts, first indexed by retriever name, then
+ * by context name
+ */
+ Collection<Map.Entry<String, Map<String, Integer>>> getEnabledAppContexts();
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent;
+
+/**
+ * Simple interface to organize all LTTng log handlers under one type.
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface ILttngHandler {
+
+ /**
+ * Get the number of events logged by this handler since its inception.
+ *
+ * @return The number of logged events
+ */
+ long getEventCount();
+
+ /**
+ * Close the log handler. Should be called once the application is done
+ * logging through it.
+ */
+ void close();
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.logging.Handler;
+import java.util.logging.Logger;
+
+/**
+ * The central agent managing the JUL and Log4j handlers.
+ *
+ * @author David Goulet
+ * @deprecated Applications are now expected to manage their Logger and Handler
+ * objects.
+ */
+@Deprecated
+public class LTTngAgent {
+
+ private static LTTngAgent instance = null;
+
+ /**
+ * Public getter to acquire a reference to this singleton object.
+ *
+ * @return The agent instance
+ */
+ public static synchronized LTTngAgent getLTTngAgent() {
+ if (instance == null) {
+ instance = new LTTngAgent();
+ }
+ return instance;
+ }
+
+ /**
+ * Dispose the agent. Applications should call this once they are done
+ * logging. This dispose function is non-static for backwards
+ * compatibility purposes.
+ */
+ @SuppressWarnings("static-method")
+ public void dispose() {
+ synchronized (LTTngAgent.class) {
+ if (instance != null) {
+ instance.disposeInstance();
+ instance = null;
+ }
+ }
+ return;
+ }
+
+ private ILttngHandler julHandler = null;
+ private ILttngHandler log4jAppender = null;
+
+ /**
+ * Private constructor. This is a singleton and a reference should be
+ * acquired using {@link #getLTTngAgent()}.
+ */
+ private LTTngAgent() {
+ initJulHandler();
+ initLog4jAppender();
+ }
+
+ /**
+ * "Destructor" method.
+ */
+ private void disposeInstance() {
+ disposeJulHandler();
+ disposeLog4jAppender();
+ }
+
+ /**
+ * Create a LTTng-JUL handler, and attach it to the JUL root logger.
+ */
+ private void initJulHandler() {
+ try {
+ Class<?> julHandlerClass = Class.forName("org.lttng.ust.agent.jul.LttngLogHandler");
+ /*
+ * It is safer to use Constructor.newInstance() rather than
+ * Class.newInstance(), because it will catch the exceptions thrown
+ * by the constructor below (which happens if the Java library is
+ * present, but the matching JNI one is not).
+ */
+ Constructor<?> julHandlerCtor = julHandlerClass.getConstructor();
+ julHandler = (ILttngHandler) julHandlerCtor.newInstance();
+
+ /* Attach the handler to the root JUL logger */
+ Logger.getLogger("").addHandler((Handler) julHandler);
+
+ /*
+ * If any of the following exceptions happen, it means we could not
+ * find or initialize LTTng JUL classes. We will not setup LTTng JUL
+ * tracing in this case.
+ */
+ } catch (SecurityException e) {
+ } catch (IllegalAccessException e) {
+ } catch (IllegalArgumentException e) {
+ } catch (ClassNotFoundException e) {
+ } catch (NoSuchMethodException e) {
+ } catch (InstantiationException e) {
+ } catch (InvocationTargetException e) {
+ }
+ }
+
+ /**
+ * Create a LTTng-logj4 appender, and attach it to the log4j root logger.
+ */
+ private void initLog4jAppender() {
+ /*
+ * Since Log4j is a 3rd party library, we first need to check if we can
+ * load any of its classes.
+ */
+ if (!testLog4jClasses()) {
+ return;
+ }
+
+ try {
+ Class<?> log4jAppenderClass = Class.forName("org.lttng.ust.agent.log4j.LttngLogAppender");
+ Constructor<?> log4jAppendCtor = log4jAppenderClass.getConstructor();
+ log4jAppender = (ILttngHandler) log4jAppendCtor.newInstance();
+
+ /*
+ * If any of the following exceptions happen, it means we could not
+ * find or initialize LTTng log4j classes. We will not setup LTTng
+ * log4j tracing in this case.
+ */
+ } catch (SecurityException e) {
+ return;
+ } catch (ClassNotFoundException e) {
+ return;
+ } catch (NoSuchMethodException e) {
+ return;
+ } catch (IllegalArgumentException e) {
+ return;
+ } catch (InstantiationException e) {
+ return;
+ } catch (IllegalAccessException e) {
+ return;
+ } catch (InvocationTargetException e) {
+ return;
+ }
+
+ /*
+ * Attach the appender to the root Log4j logger. Slightly more tricky
+ * here, as log4j.Logger is not in the base Java library, and we do not
+ * want the "common" package to depend on log4j. So we have to obtain it
+ * through reflection too.
+ */
+ try {
+ Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
+ Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
+
+ Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
+ Method addAppenderMethod = loggerClass.getMethod("addAppender", appenderClass);
+
+ Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
+ addAppenderMethod.invoke(rootLogger, log4jAppender);
+
+ /*
+ * We have checked for the log4j library version previously, none of
+ * the following exceptions should happen.
+ */
+ } catch (SecurityException e) {
+ throw new IllegalStateException(e);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException(e);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalStateException(e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ } catch (InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Check if log4j >= 1.2.15 library is present.
+ */
+ private static boolean testLog4jClasses() {
+ Class<?> loggingEventClass;
+
+ try {
+ loggingEventClass = Class.forName("org.apache.log4j.spi.LoggingEvent");
+ } catch (ClassNotFoundException e) {
+ /*
+ * Log4j classes not found, no need to create the relevant objects
+ */
+ return false;
+ }
+
+ /*
+ * Detect capabilities of the log4j library. We only support log4j >=
+ * 1.2.15. The getTimeStamp() method was introduced in log4j 1.2.15, so
+ * verify that it is available.
+ *
+ * We can't rely on the getPackage().getImplementationVersion() call
+ * that would retrieves information from the manifest file found in the
+ * JAR since the manifest file shipped from upstream is known to be
+ * broken in several versions of the library.
+ *
+ * More info: https://issues.apache.org/bugzilla/show_bug.cgi?id=44370
+ */
+ try {
+ loggingEventClass.getDeclaredMethod("getTimeStamp");
+ } catch (NoSuchMethodException e) {
+ System.err.println(
+ "Warning: The loaded log4j library is too old. Log4j tracing with LTTng will be disabled.");
+ return false;
+ } catch (SecurityException e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Detach the JUL handler from its logger and close it.
+ */
+ private void disposeJulHandler() {
+ if (julHandler == null) {
+ /* The JUL handler was not activated, we have nothing to do */
+ return;
+ }
+ Logger.getLogger("").removeHandler((Handler) julHandler);
+ julHandler.close();
+ julHandler = null;
+ }
+
+ /**
+ * Detach the log4j appender from its logger and close it.
+ */
+ private void disposeLog4jAppender() {
+ if (log4jAppender == null) {
+ /* The log4j appender was not active, we have nothing to do */
+ return;
+ }
+
+ /*
+ * Detach the appender from the log4j root logger. Again, we have to do
+ * this via reflection.
+ */
+ try {
+ Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
+ Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
+
+ Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
+ Method removeAppenderMethod = loggerClass.getMethod("removeAppender", appenderClass);
+
+ Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
+ removeAppenderMethod.invoke(rootLogger, log4jAppender);
+
+ /*
+ * We were able to attach the appender previously, we should not
+ * have problems here either!
+ */
+ } catch (SecurityException e) {
+ throw new IllegalStateException(e);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException(e);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalStateException(e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ } catch (InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ }
+
+ /* Close the appender */
+ log4jAppender.close();
+ log4jAppender = null;
+ }
+
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.util.Collection;
+
+import org.lttng.ust.agent.session.EventRule;
+
+/**
+ * TCP client listener interface.
+ *
+ * This interface contains callbacks that are called when the TCP client
+ * receives commands from the session daemon. These callbacks will define what
+ * do to with each command.
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface ILttngTcpClientListener {
+
+ /**
+ * Callback for the TCP client to notify the listener agent that a request
+ * for enabling an event rule was sent from the session daemon.
+ *
+ * @param eventRule
+ * The event rule that was requested to be enabled
+ * @return Since we do not track individual sessions, right now this command
+ * cannot fail. It will always return true.
+ */
+ boolean eventEnabled(EventRule eventRule);
+
+ /**
+ * Callback for the TCP client to notify the listener agent that a request
+ * for disabling an event was sent from the session daemon.
+ *
+ * @param eventName
+ * The name of the event that was requested to be disabled.
+ * @return True if the command completed successfully, false if we should
+ * report an error (event was not enabled, etc.)
+ */
+ boolean eventDisabled(String eventName);
+
+ /**
+ * Callback for the TCP client to notify the listener agent that a request
+ * for enabling an application-specific context was sent from the session
+ * daemon.
+ *
+ * @param contextRetrieverName
+ * The name of the retriever in which the context is present.
+ * This is used to namespace the contexts.
+ * @param contextName
+ * The name of the context that was requested to be enabled
+ * @return Since we do not track individual sessions, right now this command
+ * cannot fail. It will always return true.
+ */
+ boolean appContextEnabled(String contextRetrieverName, String contextName);
+
+ /**
+ * Callback for the TCP client to notify the listener agent that a request
+ * for disabling an application-specific context was sent from the session
+ * daemon.
+ *
+ * @param contextRetrieverName
+ * The name of the retriever in which the context is present.
+ * This is used to namespace the contexts.
+ * @param contextName
+ * The name of the context that was requested to be disabled.
+ * @return True if the command completed successfully, false if we should
+ * report an error (context was not previously enabled for example)
+ */
+ boolean appContextDisabled(String contextRetrieverName, String contextName);
+
+ /**
+ * List the events that are available in the agent's tracing domain.
+ *
+ * In Java terms, this means loggers that have at least one LTTng log
+ * handler of their corresponding domain attached.
+ *
+ * @return The list of available events
+ */
+ Collection<String> listAvailableEvents();
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Interface for all response messages sent from the Java agent to the sessiond
+ * daemon. Normally sent after a command coming from the session daemon was
+ * executed.
+ *
+ * @author Alexandre Montplaisir
+ */
+abstract class LttngAgentResponse {
+
+ private static final int INT_SIZE = 4;
+
+ public static final LttngAgentResponse SUCESS_RESPONSE = new LttngAgentResponse() {
+ @Override
+ public ReturnCode getReturnCode() {
+ return ReturnCode.CODE_SUCCESS_CMD;
+ }
+ };
+
+ public static final LttngAgentResponse FAILURE_RESPONSE = new LttngAgentResponse() {
+ @Override
+ public ReturnCode getReturnCode() {
+ return ReturnCode.CODE_INVALID_CMD;
+ }
+ };
+
+ /**
+ * Return codes used in agent responses, to indicate success or different
+ * types of failures of the commands.
+ */
+ protected enum ReturnCode {
+
+ CODE_SUCCESS_CMD(1, "sucess"),
+ CODE_INVALID_CMD(2, "invalid"),
+ CODE_UNKNOWN_LOGGER_NAME(3, "unknown logger name");
+
+ private final int code;
+ private final String toString;
+
+ private ReturnCode(int c, String str) {
+ code = c;
+ toString = str;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * Mainly used for debugging. The strings are not sent through the
+ * socket.
+ */
+ @Override
+ public String toString() {
+ return toString;
+ }
+ }
+
+ /**
+ * Get the {@link ReturnCode} that goes with this response. It is expected
+ * by the session daemon, but some commands may require more than this
+ * in their response.
+ *
+ * @return The return code
+ */
+ public abstract ReturnCode getReturnCode();
+
+ /**
+ * Gets a byte array of the response so that it may be streamed.
+ *
+ * @return The byte array of the response
+ */
+ public byte[] getBytes() {
+ byte data[] = new byte[INT_SIZE];
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ buf.putInt(getReturnCode().getCode());
+ return data;
+ }
+
+ @Override
+ public String toString() {
+ return "LttngAgentResponse["
+ + "code=" + getReturnCode().getCode()
+ + ", " + getReturnCode().toString()
+ + "]";
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015-2016 EfficiOS Inc.
+ * Copyright (C) 2015-2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.lttng.ust.agent.utils.LttngUstAgentLogger;
+
+/**
+ * Client for agents to connect to a local session daemon, using a TCP socket.
+ *
+ * @author David Goulet
+ */
+public class LttngTcpSessiondClient implements Runnable {
+
+ private static final String SESSION_HOST = "127.0.0.1";
+ private static final String ROOT_PORT_FILE = "/var/run/lttng/agent.port";
+ private static final String USER_PORT_FILE = "/.lttng/agent.port";
+ private static final Charset PORT_FILE_ENCODING = Charset.forName("UTF-8");
+
+ private static final int PROTOCOL_MAJOR_VERSION = 2;
+ private static final int PROTOCOL_MINOR_VERSION = 0;
+
+ /** Command header from the session deamon. */
+ private final CountDownLatch registrationLatch = new CountDownLatch(1);
+
+ private Socket sessiondSock;
+ private volatile boolean quit = false;
+
+ private DataInputStream inFromSessiond;
+ private DataOutputStream outToSessiond;
+
+ private final ILttngTcpClientListener logAgent;
+ private final int domainValue;
+ private final boolean isRoot;
+
+ /**
+ * Constructor
+ *
+ * @param logAgent
+ * The listener this client will operate on, typically an LTTng
+ * agent.
+ * @param domainValue
+ * The integer to send to the session daemon representing the
+ * tracing domain to handle.
+ * @param isRoot
+ * True if this client should connect to the root session daemon,
+ * false if it should connect to the user one.
+ */
+ public LttngTcpSessiondClient(ILttngTcpClientListener logAgent, int domainValue, boolean isRoot) {
+ this.logAgent = logAgent;
+ this.domainValue = domainValue;
+ this.isRoot = isRoot;
+ }
+
+ /**
+ * Wait until this client has successfully established a connection to its
+ * target session daemon.
+ *
+ * @param seconds
+ * A timeout in seconds after which this method will return
+ * anyway.
+ * @return True if the the client actually established the connection, false
+ * if we returned because the timeout has elapsed or the thread was
+ * interrupted.
+ */
+ public boolean waitForConnection(int seconds) {
+ try {
+ return registrationLatch.await(seconds, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (;;) {
+ if (this.quit) {
+ break;
+ }
+
+ try {
+
+ /*
+ * Connect to the session daemon before anything else.
+ */
+ log("Connecting to sessiond");
+ connectToSessiond();
+
+ /*
+ * Register to the session daemon as the Java component of the
+ * UST application.
+ */
+ log("Registering to sessiond");
+ registerToSessiond();
+
+ /*
+ * Block on socket receive and wait for command from the
+ * session daemon. This will return if and only if there is a
+ * fatal error or the socket closes.
+ */
+ log("Waiting on sessiond commands...");
+ handleSessiondCmd();
+ } catch (UnknownHostException uhe) {
+ uhe.printStackTrace();
+ /*
+ * Terminate agent thread.
+ */
+ close();
+ } catch (IOException ioe) {
+ /*
+ * I/O exception may have been triggered by a session daemon
+ * closing the socket. Close our own socket and
+ * retry connecting after a delay.
+ */
+ try {
+ if (this.sessiondSock != null) {
+ this.sessiondSock.close();
+ }
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ /*
+ * Retry immediately if sleep is interrupted.
+ */
+ } catch (IOException closeioe) {
+ closeioe.printStackTrace();
+ /*
+ * Terminate agent thread.
+ */
+ close();
+ }
+ }
+ }
+ }
+
+ /**
+ * Dispose this client and close any socket connection it may hold.
+ */
+ public void close() {
+ log("Closing client");
+ this.quit = true;
+
+ try {
+ if (this.sessiondSock != null) {
+ this.sessiondSock.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void connectToSessiond() throws IOException {
+ int rootPort = getPortFromFile(ROOT_PORT_FILE);
+ int userPort = getPortFromFile(getHomePath() + USER_PORT_FILE);
+
+ /*
+ * Check for the edge case of both files existing but pointing to the
+ * same port. In this case, let the root client handle it.
+ */
+ if ((rootPort != 0) && (rootPort == userPort) && (!isRoot)) {
+ log("User and root config files both point to port " + rootPort +
+ ". Letting the root client handle it.");
+ throw new IOException();
+ }
+
+ int portToUse = (isRoot ? rootPort : userPort);
+
+ if (portToUse == 0) {
+ /* No session daemon available. Stop and retry later. */
+ throw new IOException();
+ }
+
+ this.sessiondSock = new Socket(SESSION_HOST, portToUse);
+ this.inFromSessiond = new DataInputStream(sessiondSock.getInputStream());
+ this.outToSessiond = new DataOutputStream(sessiondSock.getOutputStream());
+ }
+
+ private static String getHomePath() {
+ /*
+ * The environment variable LTTNG_HOME overrides HOME if
+ * defined.
+ */
+ String homePath = System.getenv("LTTNG_HOME");
+
+ if (homePath == null) {
+ homePath = System.getProperty("user.home");
+ }
+ return homePath;
+ }
+
+ /**
+ * Read port number from file created by the session daemon.
+ *
+ * @return port value if found else 0.
+ */
+ private static int getPortFromFile(String path) throws IOException {
+ BufferedReader br = null;
+
+ try {
+ br = new BufferedReader(new InputStreamReader(new FileInputStream(path), PORT_FILE_ENCODING));
+ String line = br.readLine();
+ if (line == null) {
+ /* File exists but is empty. */
+ return 0;
+ }
+
+ int port = Integer.parseInt(line, 10);
+ if (port < 0 || port > 65535) {
+ /* Invalid value. Ignore. */
+ port = 0;
+ }
+ return port;
+
+ } catch (NumberFormatException e) {
+ /* File contained something that was not a number. */
+ return 0;
+ } catch (FileNotFoundException e) {
+ /* No port available. */
+ return 0;
+ } finally {
+ if (br != null) {
+ br.close();
+ }
+ }
+ }
+
+ private void registerToSessiond() throws IOException {
+ byte data[] = new byte[16];
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
+
+ buf.putInt(domainValue);
+ buf.putInt(Integer.parseInt(pid));
+ buf.putInt(PROTOCOL_MAJOR_VERSION);
+ buf.putInt(PROTOCOL_MINOR_VERSION);
+ this.outToSessiond.write(data, 0, data.length);
+ this.outToSessiond.flush();
+ }
+
+ /**
+ * Handle session command from the session daemon.
+ */
+ private void handleSessiondCmd() throws IOException {
+ /* Data read from the socket */
+ byte inputData[] = null;
+ /* Reply data written to the socket, sent to the sessiond */
+ LttngAgentResponse response;
+
+ while (true) {
+ /* Get header from session daemon. */
+ SessiondCommandHeader cmdHeader = recvHeader();
+
+ if (cmdHeader.getDataSize() > 0) {
+ inputData = recvPayload(cmdHeader);
+ }
+
+ switch (cmdHeader.getCommandType()) {
+ case CMD_REG_DONE:
+ {
+ /*
+ * Countdown the registration latch, meaning registration is
+ * done and we can proceed to continue tracing.
+ */
+ registrationLatch.countDown();
+ /*
+ * We don't send any reply to the registration done command.
+ * This just marks the end of the initial session setup.
+ */
+ log("Registration done");
+ continue;
+ }
+ case CMD_LIST:
+ {
+ SessiondCommand listLoggerCmd = new SessiondListLoggersCommand();
+ response = listLoggerCmd.execute(logAgent);
+ log("Received list loggers command");
+ break;
+ }
+ case CMD_EVENT_ENABLE:
+ {
+ if (inputData == null) {
+ /* Invalid command */
+ response = LttngAgentResponse.FAILURE_RESPONSE;
+ break;
+ }
+ SessiondCommand enableEventCmd = new SessiondEnableEventCommand(inputData);
+ response = enableEventCmd.execute(logAgent);
+ log("Received enable event command: " + enableEventCmd.toString());
+ break;
+ }
+ case CMD_EVENT_DISABLE:
+ {
+ if (inputData == null) {
+ /* Invalid command */
+ response = LttngAgentResponse.FAILURE_RESPONSE;
+ break;
+ }
+ SessiondCommand disableEventCmd = new SessiondDisableEventCommand(inputData);
+ response = disableEventCmd.execute(logAgent);
+ log("Received disable event command: " + disableEventCmd.toString());
+ break;
+ }
+ case CMD_APP_CTX_ENABLE:
+ {
+ if (inputData == null) {
+ /* This commands expects a payload, invalid command */
+ response = LttngAgentResponse.FAILURE_RESPONSE;
+ break;
+ }
+ SessiondCommand enableAppCtxCmd = new SessiondEnableAppContextCommand(inputData);
+ response = enableAppCtxCmd.execute(logAgent);
+ log("Received enable app-context command");
+ break;
+ }
+ case CMD_APP_CTX_DISABLE:
+ {
+ if (inputData == null) {
+ /* This commands expects a payload, invalid command */
+ response = LttngAgentResponse.FAILURE_RESPONSE;
+ break;
+ }
+ SessiondCommand disableAppCtxCmd = new SessiondDisableAppContextCommand(inputData);
+ response = disableAppCtxCmd.execute(logAgent);
+ log("Received disable app-context command");
+ break;
+ }
+ default:
+ {
+ /* Unknown command, send empty reply */
+ response = null;
+ log("Received unknown command, ignoring");
+ break;
+ }
+ }
+
+ /* Send response to the session daemon. */
+ byte[] responseData;
+ if (response == null) {
+ responseData = new byte[4];
+ ByteBuffer buf = ByteBuffer.wrap(responseData);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ } else {
+ log("Sending response: " + response.toString());
+ responseData = response.getBytes();
+ }
+ this.outToSessiond.write(responseData, 0, responseData.length);
+ this.outToSessiond.flush();
+ }
+ }
+
+ /**
+ * Receive header data from the session daemon using the LTTng command
+ * static buffer of the right size.
+ */
+ private SessiondCommandHeader recvHeader() throws IOException {
+ byte data[] = new byte[SessiondCommandHeader.HEADER_SIZE];
+ int bytesLeft = data.length;
+ int bytesOffset = 0;
+
+ while (bytesLeft > 0) {
+ int bytesRead = this.inFromSessiond.read(data, bytesOffset, bytesLeft);
+
+ if (bytesRead < 0) {
+ throw new IOException();
+ }
+ bytesLeft -= bytesRead;
+ bytesOffset += bytesRead;
+ }
+ return new SessiondCommandHeader(data);
+ }
+
+ /**
+ * Receive payload from the session daemon. This MUST be done after a
+ * recvHeader() so the header value of a command are known.
+ *
+ * The caller SHOULD use isPayload() before which returns true if a payload
+ * is expected after the header.
+ */
+ private byte[] recvPayload(SessiondCommandHeader headerCmd) throws IOException {
+ byte payload[] = new byte[(int) headerCmd.getDataSize()];
+ int bytesLeft = payload.length;
+ int bytesOffset = 0;
+
+ /* Failsafe check so we don't waste our time reading 0 bytes. */
+ if (bytesLeft == 0) {
+ return null;
+ }
+
+ while (bytesLeft > 0) {
+ int bytesRead = inFromSessiond.read(payload, bytesOffset, bytesLeft);
+
+ if (bytesRead < 0) {
+ throw new IOException();
+ }
+ bytesLeft -= bytesRead;
+ bytesOffset += bytesRead;
+ }
+ return payload;
+ }
+
+ /**
+ * Wrapper for this class's logging, adds the connection's characteristics
+ * to help differentiate between multiple TCP clients.
+ */
+ private void log(String message) {
+ LttngUstAgentLogger.log(getClass(),
+ "(root=" + isRoot + ", domain=" + domainValue + ") " + message);
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015-2016 EfficiOS Inc.
+ * Copyright (C) 2015-2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+/**
+ * Base class to represent all commands sent from the session daemon to the Java
+ * agent. The agent is then expected to execute the command and provide a
+ * response.
+ *
+ * @author Alexandre Montplaisir
+ */
+abstract class SessiondCommand {
+
+ /**
+ * Encoding that should be used for the strings in the sessiond agent
+ * protocol on the socket.
+ */
+ protected static final Charset SESSIOND_PROTOCOL_CHARSET = Charset.forName("UTF-8");
+
+ enum CommandType {
+ /** List logger(s). */
+ CMD_LIST(1),
+ /** Enable logger by name. */
+ CMD_EVENT_ENABLE(2),
+ /** Disable logger by name. */
+ CMD_EVENT_DISABLE(3),
+ /** Registration done */
+ CMD_REG_DONE(4),
+ /** Enable application context */
+ CMD_APP_CTX_ENABLE(5),
+ /** Disable application context */
+ CMD_APP_CTX_DISABLE(6);
+
+ private int code;
+
+ private CommandType(int c) {
+ code = c;
+ }
+
+ public int getCommandType() {
+ return code;
+ }
+ }
+
+ /**
+ * Execute the command handler's action on the specified tracing agent.
+ *
+ * @param agent
+ * The agent on which to execute the command
+ * @return If the command completed successfully or not
+ */
+ public abstract LttngAgentResponse execute(ILttngTcpClientListener agent);
+
+ /**
+ * Utility method to read agent-protocol strings passed on the socket. The
+ * buffer will contain a 32-bit integer representing the length, immediately
+ * followed by the string itself.
+ *
+ * @param buffer
+ * The ByteBuffer from which to read. It should already be setup
+ * and positioned where the read should begin.
+ * @return The string that was read, or <code>null</code> if it was badly
+ * formatted.
+ */
+ protected static String readNextString(ByteBuffer buffer) {
+ int nbBytes = buffer.getInt();
+ if (nbBytes < 0) {
+ /* The string length should be positive */
+ return null;
+ }
+ if (nbBytes == 0) {
+ /* The string is explicitly an empty string */
+ return "";
+ }
+
+ byte[] stringBytes = new byte[nbBytes];
+ buffer.get(stringBytes);
+ return new String(stringBytes, SESSIOND_PROTOCOL_CHARSET).trim();
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.lttng.ust.agent.client.SessiondCommand.CommandType;
+
+/**
+ * Header of session daemon commands.
+ *
+ * @author Alexandre Montplaisir
+ * @author David Goulet
+ */
+class SessiondCommandHeader {
+
+ /** ABI size of command header. */
+ public static final int HEADER_SIZE = 16;
+
+ /** Payload size in bytes following this header. */
+ private final long dataSize;
+
+ /** Command type. */
+ private final CommandType cmd;
+
+ public SessiondCommandHeader(byte[] data) {
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ buf.order(ByteOrder.BIG_ENDIAN);
+
+ dataSize = buf.getLong();
+ cmd = CommandType.values()[buf.getInt() - 1];
+ buf.getInt(); // command version, currently unused
+ }
+
+ public long getDataSize() {
+ return dataSize;
+ }
+
+ public CommandType getCommandType() {
+ return cmd;
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Session daemon command indicating to the Java agent that an
+ * application-specific context was disabled in the tracing session.
+ *
+ * @author Alexandre Montplaisir
+ */
+class SessiondDisableAppContextCommand extends SessiondCommand {
+
+ private final String retrieverName;
+ private final String contextName;
+
+ private final boolean commandIsValid;
+
+ public SessiondDisableAppContextCommand(byte[] data) {
+ if (data == null) {
+ throw new IllegalArgumentException();
+ }
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ buf.order(ByteOrder.BIG_ENDIAN);
+
+ /*
+ * The buffer contains the retriever name first, followed by the
+ * context's name.
+ */
+ retrieverName = readNextString(buf);
+ contextName = readNextString(buf);
+
+ /* If any of these strings were null then the command was invalid */
+ commandIsValid = ((retrieverName != null) && (contextName != null));
+ }
+
+ @Override
+ public LttngAgentResponse execute(ILttngTcpClientListener agent) {
+ if (!commandIsValid) {
+ return LttngAgentResponse.FAILURE_RESPONSE;
+ }
+
+ boolean success = agent.appContextDisabled(retrieverName, contextName);
+ return (success ? LttngAgentResponse.SUCESS_RESPONSE : DISABLE_APP_CONTEXT_FAILURE_RESPONSE);
+ }
+
+ /**
+ * Response sent when the disable-context command asks to disable an
+ * unknown context name.
+ */
+ private static final LttngAgentResponse DISABLE_APP_CONTEXT_FAILURE_RESPONSE = new LttngAgentResponse() {
+ @Override
+ public ReturnCode getReturnCode() {
+ /* Same return code used for unknown event/logger names */
+ return ReturnCode.CODE_UNKNOWN_LOGGER_NAME;
+ }
+ };
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Session daemon command indicating to the Java agent that some events were
+ * disabled in the tracing session.
+ *
+ * @author Alexandre Montplaisir
+ * @author David Goulet
+ */
+class SessiondDisableEventCommand extends SessiondCommand {
+
+ /**
+ * Response sent when the disable-event command asks to disable an
+ * unknown event.
+ */
+ private static final LttngAgentResponse DISABLE_EVENT_FAILURE_RESPONSE = new LttngAgentResponse() {
+ @Override
+ public ReturnCode getReturnCode() {
+ return ReturnCode.CODE_UNKNOWN_LOGGER_NAME;
+ }
+ };
+
+ /** Event name to disable from the tracing session */
+ private final String eventName;
+
+ public SessiondDisableEventCommand(byte[] data) {
+ if (data == null) {
+ throw new IllegalArgumentException();
+ }
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ eventName = new String(data, SESSIOND_PROTOCOL_CHARSET).trim();
+ }
+
+ @Override
+ public LttngAgentResponse execute(ILttngTcpClientListener agent) {
+ boolean success = agent.eventDisabled(this.eventName);
+ return (success ? LttngAgentResponse.SUCESS_RESPONSE : DISABLE_EVENT_FAILURE_RESPONSE);
+ }
+
+ @Override
+ public String toString() {
+ return "SessiondDisableEventCommand["
+ + "eventName=" + eventName
+ +"]";
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Session daemon command indicating to the Java agent that an
+ * application-specific context was enabled in the tracing session.
+ *
+ * @author Alexandre Montplaisir
+ */
+class SessiondEnableAppContextCommand extends SessiondCommand {
+
+ private final String retrieverName;
+ private final String contextName;
+
+ private final boolean commandIsValid;
+
+ public SessiondEnableAppContextCommand(byte[] data) {
+ if (data == null) {
+ throw new IllegalArgumentException();
+ }
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ buf.order(ByteOrder.BIG_ENDIAN);
+
+ /*
+ * The buffer contains the retriever name first, followed by the
+ * context's name.
+ */
+ retrieverName = readNextString(buf);
+ contextName = readNextString(buf);
+
+ /* If any of these strings were null then the command was invalid */
+ commandIsValid = ((retrieverName != null) && (contextName != null));
+ }
+
+ @Override
+ public LttngAgentResponse execute(ILttngTcpClientListener agent) {
+ if (!commandIsValid) {
+ return LttngAgentResponse.FAILURE_RESPONSE;
+ }
+
+ boolean success = agent.appContextEnabled(retrieverName, contextName);
+ return (success ? LttngAgentResponse.SUCESS_RESPONSE : LttngAgentResponse.FAILURE_RESPONSE);
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.lttng.ust.agent.session.EventRule;
+import org.lttng.ust.agent.session.LogLevelSelector;
+
+/**
+ * Session daemon command indicating to the Java agent that some events were
+ * enabled in the tracing session.
+ *
+ * @author Alexandre Montplaisir
+ * @author David Goulet
+ */
+class SessiondEnableEventCommand extends SessiondCommand {
+
+ /** Fixed event name length. Value defined by the lttng agent protocol. */
+ private static final int EVENT_NAME_LENGTH = 256;
+
+ private final boolean commandIsValid;
+
+ /* Parameters of the event rule being enabled */
+ private final String eventName;
+ private final LogLevelSelector logLevelFilter;
+ private final String filterString;
+
+ public SessiondEnableEventCommand(byte[] data) {
+ if (data == null) {
+ throw new IllegalArgumentException();
+ }
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ int logLevel = buf.getInt();
+ int logLevelType = buf.getInt();
+ logLevelFilter = new LogLevelSelector(logLevel, logLevelType);
+
+ /* Read the event name */
+ byte[] eventNameBytes = new byte[EVENT_NAME_LENGTH];
+ buf.get(eventNameBytes);
+ eventName = new String(eventNameBytes, SESSIOND_PROTOCOL_CHARSET).trim();
+
+ /* Read the filter string */
+ filterString = readNextString(buf);
+
+ /* The command was invalid if the string could not be read correctly */
+ commandIsValid = (filterString != null);
+ }
+
+ @Override
+ public LttngAgentResponse execute(ILttngTcpClientListener agent) {
+ if (!commandIsValid) {
+ return LttngAgentResponse.FAILURE_RESPONSE;
+ }
+
+ EventRule rule = new EventRule(eventName, logLevelFilter, filterString);
+ boolean success = agent.eventEnabled(rule);
+ return (success ? LttngAgentResponse.SUCESS_RESPONSE : LttngAgentResponse.FAILURE_RESPONSE);
+ }
+
+ @Override
+ public String toString() {
+ return "SessiondEnableEventCommand["
+ + "eventName=" + eventName
+ + ", logLevel=" + logLevelFilter.toString()
+ + ", filterString=" + filterString
+ +"]";
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent.client;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Collection;
+
+/**
+ * Session daemon command asking the Java agent to list its registered loggers,
+ * which corresponds to event names in the tracing session.
+ *
+ * @author Alexandre Montplaisir
+ * @author David Goulet
+ */
+class SessiondListLoggersCommand extends SessiondCommand {
+
+ @Override
+ public LttngAgentResponse execute(ILttngTcpClientListener agent) {
+ final Collection<String> loggerList = agent.listAvailableEvents();
+ return new SessiondListLoggersResponse(loggerList);
+ }
+
+ private static class SessiondListLoggersResponse extends LttngAgentResponse {
+
+ private final static int SIZE = 12;
+
+ private final Collection<String> loggers;
+
+ public SessiondListLoggersResponse(Collection<String> loggers) {
+ this.loggers = loggers;
+ }
+
+ @Override
+ public ReturnCode getReturnCode() {
+ /* This command can't really fail */
+ return ReturnCode.CODE_SUCCESS_CMD;
+ }
+
+ @Override
+ public byte[] getBytes() {
+ /*
+ * Compute the data size, which is the number of bytes of each
+ * encoded string, +1 per string for the \0
+ */
+ int dataSize = 0;
+ for (String logger : loggers) {
+ dataSize += logger.getBytes(SESSIOND_PROTOCOL_CHARSET).length + 1;
+ }
+
+ /* Prepare the buffer */
+ byte data[] = new byte[SIZE + dataSize];
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ buf.order(ByteOrder.BIG_ENDIAN);
+
+ /* Write the header section of the response */
+ buf.putInt(getReturnCode().getCode());
+ buf.putInt(dataSize);
+ buf.putInt(loggers.size());
+
+ /* Write the payload */
+ for (String logger : loggers) {
+ buf.put(logger.getBytes(SESSIOND_PROTOCOL_CHARSET));
+ /* NULL terminated byte after the logger name. */
+ buf.put((byte) 0x0);
+ }
+ return data;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.context;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The singleton manager of {@link IContextInfoRetriever} objects.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class ContextInfoManager {
+
+ private static final String SHARED_LIBRARY_NAME = "lttng-ust-context-jni";
+
+ private static final Pattern VALID_CONTEXT_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_\\.]+$");
+
+ private static ContextInfoManager instance;
+
+ private final Map<String, IContextInfoRetriever> contextInfoRetrievers = new ConcurrentHashMap<String, IContextInfoRetriever>();
+ private final Map<String, Long> contextInforRetrieverRefs = new HashMap<String, Long>();
+
+ /**
+ * Lock used to keep the two maps above in sync when retrievers are
+ * registered or unregistered.
+ */
+ private final Object retrieverLock = new Object();
+
+ /** Singleton class, constructor should not be accessed directly */
+ private ContextInfoManager() {
+ }
+
+ /**
+ * Get the singleton instance.
+ *
+ * <p>
+ * Usage of this class requires the "liblttng-ust-context-jni.so" native
+ * library to be present on the system and available (passing
+ * -Djava.library.path=path to the JVM may be needed).
+ * </p>
+ *
+ * @return The singleton instance
+ * @throws IOException
+ * If the shared library cannot be found.
+ * @throws SecurityException
+ * We will forward any SecurityExcepion that may be thrown when
+ * trying to load the JNI library.
+ */
+ public static synchronized ContextInfoManager getInstance() throws IOException, SecurityException {
+ if (instance == null) {
+ try {
+ System.loadLibrary(SHARED_LIBRARY_NAME);
+ } catch (UnsatisfiedLinkError e) {
+ throw new IOException(e);
+ }
+ instance = new ContextInfoManager();
+ }
+ return instance;
+ }
+
+ /**
+ * Register a new context info retriever.
+ *
+ * <p>
+ * Each context info retriever is registered with a given "retriever name",
+ * which specifies the namespace of the context elements. This name is
+ * specified separately from the retriever objects, which would allow
+ * register the same retriever under different namespaces for example.
+ * </p>
+ *
+ * <p>
+ * If the method returns false (indicating registration failure), then the
+ * retriever object will *not* be used for context information.
+ * </p>
+ *
+ * @param retrieverName
+ * The name to register to the context retriever object with.
+ * @param contextInfoRetriever
+ * The context info retriever to register
+ * @return True if the retriever was successfully registered, false if there
+ * was an error, for example if a retriever is already registered
+ * with that name.
+ */
+ public boolean registerContextInfoRetriever(String retrieverName, IContextInfoRetriever contextInfoRetriever) {
+ synchronized (retrieverLock) {
+ if (!validateRetrieverName(retrieverName)) {
+ return false;
+ }
+
+ if (contextInfoRetrievers.containsKey(retrieverName)) {
+ /*
+ * There is already a retriever registered with that name,
+ * refuse the new registration.
+ */
+ return false;
+ }
+ /*
+ * Inform LTTng-UST of the new retriever. The names have to start
+ * with "$app." on the UST side!
+ */
+ long ref = LttngContextApi.registerProvider("$app." + retrieverName);
+ if (ref == 0) {
+ return false;
+ }
+
+ contextInfoRetrievers.put(retrieverName, contextInfoRetriever);
+ contextInforRetrieverRefs.put(retrieverName, Long.valueOf(ref));
+
+ return true;
+ }
+ }
+
+ /**
+ * Unregister a previously added context info retriever.
+ *
+ * This method has no effect if the retriever was not already registered.
+ *
+ * @param retrieverName
+ * The context info retriever to unregister
+ * @return True if unregistration was successful, false if there was an
+ * error
+ */
+ public boolean unregisterContextInfoRetriever(String retrieverName) {
+ synchronized (retrieverLock) {
+ if (!contextInfoRetrievers.containsKey(retrieverName)) {
+ /*
+ * There was no retriever registered with that name.
+ */
+ return false;
+ }
+ contextInfoRetrievers.remove(retrieverName);
+ long ref = contextInforRetrieverRefs.remove(retrieverName).longValue();
+
+ /* Unregister the retriever on the UST side too */
+ LttngContextApi.unregisterProvider(ref);
+
+ return true;
+ }
+ }
+
+ /**
+ * Return the context info retriever object registered with the given name.
+ *
+ * @param retrieverName
+ * The retriever name to look for
+ * @return The corresponding retriever object, or <code>null</code> if there
+ * was none
+ */
+ public IContextInfoRetriever getContextInfoRetriever(String retrieverName) {
+ /*
+ * Note that this method does not take the retrieverLock, it lets
+ * concurrent threads access the ConcurrentHashMap directly.
+ *
+ * It's fine for a get() to happen during a registration or
+ * unregistration, it's first-come-first-serve.
+ */
+ return contextInfoRetrievers.get(retrieverName);
+ }
+
+ /**
+ * Validate that the given retriever name contains only the allowed
+ * characters, which are alphanumerical characters, period "." and
+ * underscore "_". The name must also not start with a number.
+ */
+ private static boolean validateRetrieverName(String contextName) {
+ if (contextName.isEmpty()) {
+ return false;
+ }
+
+ /* First character must not be a number */
+ if (Character.isDigit(contextName.charAt(0))) {
+ return false;
+ }
+
+ /* Validate the other characters of the string */
+ Matcher matcher = VALID_CONTEXT_NAME_PATTERN.matcher(contextName);
+ return matcher.matches();
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.context;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Map;
+
+import org.lttng.ust.agent.utils.LttngUstAgentLogger;
+
+/**
+ * This class is used to serialize the list of "context info" objects to pass
+ * through JNI.
+ *
+ * The protocol expects two byte array parameters, which are contained here in
+ * the {@link SerializedContexts} inner class.
+ *
+ * The first byte array is called the "entries array", and contains fixed-size
+ * entries, one per context element.
+ *
+ * The second one is the "strings array", it is of variable length and used to
+ * hold the variable-length strings. Each one of these strings is formatted as a
+ * UTF-8 C-string, meaning in will end with a "\0" byte to indicate its end.
+ * Entries in the first array may refer to offsets in the second array to point
+ * to relevant strings.
+ *
+ * The fixed-size entries in the entries array contain the following elements
+ * (size in bytes in parentheses):
+ *
+ * <ul>
+ * <li>The offset in the strings array pointing to the full context name, like
+ * "$app.myprovider:mycontext" (4)</li>
+ * <li>The context value type (1)</li>
+ * <li>The context value itself (8)</li>
+ * </ul>
+ *
+ * The context value type will indicate how many bytes are used for the value.
+ * If the it is of String type, then we use 4 bytes to represent the offset in
+ * the strings array.
+ *
+ * So the total size of each entry is 13 bytes. All unused bytes (for context
+ * values shorter than 8 bytes for example) will be zero'ed.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class ContextInfoSerializer {
+
+ private enum DataType {
+ NULL(0),
+ INTEGER(1),
+ LONG(2),
+ DOUBLE(3),
+ FLOAT(4),
+ BYTE(5),
+ SHORT(6),
+ BOOLEAN(7),
+ STRING(8);
+
+ private final byte value;
+
+ private DataType(int value) {
+ this.value = (byte) value;
+ }
+
+ public byte getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * Class used to wrap the two byte arrays returned by
+ * {@link #queryAndSerializeRequestedContexts}.
+ */
+ public static class SerializedContexts {
+
+ private final byte[] contextEntries;
+ private final byte[] contextStrings;
+
+ /**
+ * Constructor
+ *
+ * @param entries
+ * Arrays for the fixed-size context entries.
+ * @param strings
+ * Arrays for variable-length strings
+ */
+ public SerializedContexts(byte[] entries, byte[] strings) {
+ contextEntries = entries;
+ contextStrings = strings;
+ }
+
+ /**
+ * @return The entries array
+ */
+ public byte[] getEntriesArray() {
+ return contextEntries;
+ }
+
+ /**
+ * @return The strings array
+ */
+ public byte[] getStringsArray() {
+ return contextStrings;
+ }
+ }
+
+ private static final String UST_APP_CTX_PREFIX = "$app.";
+ private static final int ENTRY_LENGTH = 13;
+ private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder();
+ private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+ private static final SerializedContexts EMPTY_CONTEXTS = new SerializedContexts(new byte[0], new byte[0]);
+
+ /**
+ * From the list of requested contexts in the tracing session, look them up
+ * in the {@link ContextInfoManager}, retrieve the available ones, and
+ * serialize them into a byte array.
+ *
+ * @param enabledContexts
+ * The contexts that are enabled in the tracing session (indexed
+ * first by retriever name, then by index names). Should come
+ * from the LTTng Agent.
+ * @return The byte array representing the intersection of the requested and
+ * available contexts.
+ */
+ public static SerializedContexts queryAndSerializeRequestedContexts(Collection<Map.Entry<String, Map<String, Integer>>> enabledContexts) {
+ if (enabledContexts.isEmpty()) {
+ /* Early return if there is no requested context information */
+ return EMPTY_CONTEXTS;
+ }
+
+ ContextInfoManager contextManager;
+ try {
+ contextManager = ContextInfoManager.getInstance();
+ } catch (IOException e) {
+ /*
+ * The JNI library is not available, do not send any context
+ * information. No retriever could have been defined anyways.
+ */
+ return EMPTY_CONTEXTS;
+ }
+
+ /* Compute the total number of contexts (flatten the map) */
+ int totalArraySize = 0;
+ for (Map.Entry<String, Map<String, Integer>> contexts : enabledContexts) {
+ totalArraySize += contexts.getValue().size() * ENTRY_LENGTH;
+ }
+
+ /* Prepare the ByteBuffer that will generate the "entries" array */
+ ByteBuffer entriesBuffer = ByteBuffer.allocate(totalArraySize);
+ entriesBuffer.order(NATIVE_ORDER);
+ entriesBuffer.clear();
+
+ /* Prepare the streams that will generate the "strings" array */
+ ByteArrayOutputStream stringsBaos = new ByteArrayOutputStream();
+ DataOutputStream stringsDos = new DataOutputStream(stringsBaos);
+
+ try {
+ for (Map.Entry<String, Map<String, Integer>> entry : enabledContexts) {
+ String requestedRetrieverName = entry.getKey();
+ Map<String, Integer> requestedContexts = entry.getValue();
+
+ IContextInfoRetriever retriever = contextManager.getContextInfoRetriever(requestedRetrieverName);
+
+ for (String requestedContext : requestedContexts.keySet()) {
+ Object contextInfo;
+ if (retriever == null) {
+ contextInfo = null;
+ } else {
+ contextInfo = retriever.retrieveContextInfo(requestedContext);
+ /*
+ * 'contextInfo' can still be null here, which would
+ * indicate the retriever does not supply this context.
+ * We will still write this information so that the
+ * tracer can know about it.
+ */
+ }
+
+ /* Serialize the result to the buffers */
+ // FIXME Eventually pass the retriever name only once?
+ String fullContextName = (UST_APP_CTX_PREFIX + requestedRetrieverName + ':' + requestedContext);
+ byte[] strArray = fullContextName.getBytes(UTF8_CHARSET);
+
+ entriesBuffer.putInt(stringsDos.size());
+ stringsDos.write(strArray);
+ stringsDos.writeChar('\0');
+
+ LttngUstAgentLogger.log(ContextInfoSerializer.class,
+ "ContextInfoSerializer: Context to be sent through JNI: " + fullContextName + '=' +
+ (contextInfo == null ? "null" : contextInfo.toString()));
+
+ serializeContextInfo(entriesBuffer, stringsDos, contextInfo);
+ }
+ }
+
+ stringsDos.flush();
+ stringsBaos.flush();
+
+ } catch (IOException e) {
+ /*
+ * Should not happen because we are wrapping a
+ * ByteArrayOutputStream, which writes to memory
+ */
+ e.printStackTrace();
+ }
+
+ byte[] entriesArray = entriesBuffer.array();
+ byte[] stringsArray = stringsBaos.toByteArray();
+ return new SerializedContexts(entriesArray, stringsArray);
+ }
+
+ private static final int CONTEXT_VALUE_LENGTH = 8;
+
+ private static void serializeContextInfo(ByteBuffer entriesBuffer, DataOutputStream stringsDos, Object contextInfo) throws IOException {
+ int remainingBytes;
+ if (contextInfo == null) {
+ entriesBuffer.put(DataType.NULL.getValue());
+ remainingBytes = CONTEXT_VALUE_LENGTH;
+
+ } else if (contextInfo instanceof Integer) {
+ entriesBuffer.put(DataType.INTEGER.getValue());
+ entriesBuffer.putInt(((Integer) contextInfo).intValue());
+ remainingBytes = CONTEXT_VALUE_LENGTH - 4;
+
+ } else if (contextInfo instanceof Long) {
+ entriesBuffer.put(DataType.LONG.getValue());
+ entriesBuffer.putLong(((Long) contextInfo).longValue());
+ remainingBytes = CONTEXT_VALUE_LENGTH - 8;
+
+ } else if (contextInfo instanceof Double) {
+ entriesBuffer.put(DataType.DOUBLE.getValue());
+ entriesBuffer.putDouble(((Double) contextInfo).doubleValue());
+ remainingBytes = CONTEXT_VALUE_LENGTH - 8;
+
+ } else if (contextInfo instanceof Float) {
+ entriesBuffer.put(DataType.FLOAT.getValue());
+ entriesBuffer.putFloat(((Float) contextInfo).floatValue());
+ remainingBytes = CONTEXT_VALUE_LENGTH - 4;
+
+ } else if (contextInfo instanceof Byte) {
+ entriesBuffer.put(DataType.BYTE.getValue());
+ entriesBuffer.put(((Byte) contextInfo).byteValue());
+ remainingBytes = CONTEXT_VALUE_LENGTH - 1;
+
+ } else if (contextInfo instanceof Short) {
+ entriesBuffer.put(DataType.SHORT.getValue());
+ entriesBuffer.putShort(((Short) contextInfo).shortValue());
+ remainingBytes = CONTEXT_VALUE_LENGTH - 2;
+
+ } else if (contextInfo instanceof Boolean) {
+ entriesBuffer.put(DataType.BOOLEAN.getValue());
+ boolean b = ((Boolean) contextInfo).booleanValue();
+ /* Converted to one byte, write 1 for true, 0 for false */
+ entriesBuffer.put((byte) (b ? 1 : 0));
+ remainingBytes = CONTEXT_VALUE_LENGTH - 1;
+
+ } else {
+ /* Also includes the case of Character. */
+ /*
+ * We'll write the object as a string, into the strings array. We
+ * will write the corresponding offset to the entries array.
+ */
+ String str = contextInfo.toString();
+ byte[] strArray = str.getBytes(UTF8_CHARSET);
+
+ entriesBuffer.put(DataType.STRING.getValue());
+
+ entriesBuffer.putInt(stringsDos.size());
+ stringsDos.write(strArray);
+ stringsDos.writeChar('\0');
+
+ remainingBytes = CONTEXT_VALUE_LENGTH - 4;
+ }
+ entriesBuffer.position(entriesBuffer.position() + remainingBytes);
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.context;
+
+/**
+ * Context-retrieving object specified by the application to extract
+ * application-specific context information, which can then be passed on to the
+ * Java agents and saved to a trace.
+ *
+ * Retriever objects should be registered to the {@link ContextInfoManager} to
+ * make them available to the LTTng agents.
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface IContextInfoRetriever {
+
+ /**
+ * Retrieve a piece of context information from the application, identified
+ * by a key.
+ *
+ * @param key
+ * The key identifying the context information
+ * @return The context information.
+ */
+ Object retrieveContextInfo(String key);
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.context;
+
+/**
+ * Virtual class containing the Java side of the LTTng-UST context provider
+ * registering/unregistering methods.
+ *
+ * @author Alexandre Montplaisir
+ */
+final class LttngContextApi {
+
+ private LttngContextApi() {}
+
+ /**
+ * Register a context provider to UST.
+ *
+ * The callbacks are the same for all providers, and are defined in the .c
+ * file. The only needed information is the retriever (which is called
+ * "provider" from UST'S point of view) name.
+ *
+ * @param provider_name
+ * The name of the provider
+ * @return The pointer to the created provider object. It's useless in the
+ * Java space, but will be needed for
+ * {@link #unregisterProvider(long)}.
+ */
+ static native long registerProvider(String provider_name);
+
+ /**
+ * Unregister a previously-registered context provider from UST.
+ *
+ * @param provider_ref
+ * The pointer to the provider object, obtained from
+ * {@link #registerProvider}
+ */
+ static native void unregisterProvider(long provider_ref);
+}
+
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.filter;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.lttng.ust.agent.session.EventRule;
+
+/**
+ * Singleton class managing the filter notifications.
+ *
+ * Applications can register a {@link IFilterChangeListener} to be notified when
+ * event filtering rules change in the tracing sessions.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class FilterChangeNotifier {
+
+ /** Lazy-loaded singleton instance object */
+ private static FilterChangeNotifier instance = null;
+
+ private final Map<EventRule, Integer> enabledEventRules = new HashMap<EventRule, Integer>();
+ private final Collection<IFilterChangeListener> registeredListeners = new LinkedList<IFilterChangeListener>();
+
+
+ /**
+ * Private constructor, singleton class should not be instantiated directly.
+ */
+ private FilterChangeNotifier() {
+ }
+
+ /**
+ * Get the singleton instance, initializing it if needed.
+ *
+ * @return The singleton instance
+ */
+ public static synchronized FilterChangeNotifier getInstance() {
+ if (instance == null) {
+ instance = new FilterChangeNotifier();
+ }
+ return instance;
+ }
+
+ /**
+ * Notify the filter manager that a new rule was enabled in a tracing
+ * session ("lttng enable-event ...")
+ *
+ * This is meant to be called by the LTTng Agent only. External Java
+ * applications should not call this.
+ *
+ * @param rule
+ * The rule that was added
+ */
+ public synchronized void addEventRule(EventRule rule) {
+ Integer count = enabledEventRules.get(rule);
+ if (count == null) {
+ /*
+ * This is the first instance of this rule being enabled. Add it to
+ * the map and send notifications to the registered notifiers.
+ */
+ enabledEventRules.put(rule, Integer.valueOf(1));
+ notifyForAddedRule(rule);
+ return;
+ }
+ if (count.intValue() <= 0) {
+ /* It should not have been in the map! */
+ throw new IllegalStateException();
+ }
+ /*
+ * This exact event rule was already enabled, just increment its
+ * refcount without sending notifications
+ */
+ enabledEventRules.put(rule, Integer.valueOf(count.intValue() + 1));
+ }
+
+ /**
+ * Notify the filter manager that an event name was disabled in the tracing
+ * sessions ("lttng disable-event ...").
+ *
+ * The "disable-event" only specifies an event name. This means all the
+ * rules containing this event name are to be disabled.
+ *
+ * This is meant to be called by the LTTng Agent only. External Java
+ * applications should not call this.
+ *
+ * @param eventName
+ * The event name to disable
+ */
+ public synchronized void removeEventRules(String eventName) {
+ List<EventRule> rulesToRemove = new LinkedList<EventRule>();
+
+ for (EventRule eventRule : enabledEventRules.keySet()) {
+ if (eventRule.getEventName().equals(eventName)) {
+ rulesToRemove.add(eventRule);
+ }
+ }
+ /*
+ * We cannot modify the map while iterating on it. We have to do the
+ * removal separately from the iteration above.
+ */
+ for (EventRule rule : rulesToRemove) {
+ removeEventRule(rule);
+ }
+ }
+
+ private synchronized void removeEventRule(EventRule eventRule) {
+ Integer count = enabledEventRules.get(eventRule);
+ if (count == null || count.intValue() <= 0) {
+ /*
+ * We were asked us to disable an event rule that was not enabled
+ * previously. Command error?
+ */
+ throw new IllegalStateException();
+ }
+ if (count.intValue() == 1) {
+ /*
+ * This is the last instance of this event rule being disabled,
+ * remove it from the map and send notifications of this rule being
+ * gone.
+ */
+ enabledEventRules.remove(eventRule);
+ notifyForRemovedRule(eventRule);
+ return;
+ }
+ /*
+ * Other sessions/daemons are still looking for this event rule, simply
+ * decrement its refcount, and do not send notifications.
+ */
+ enabledEventRules.put(eventRule, Integer.valueOf(count.intValue() - 1));
+
+ }
+
+ /**
+ * Register a new listener to the manager.
+ *
+ * @param listener
+ * The listener to add
+ */
+ public synchronized void registerListener(IFilterChangeListener listener) {
+ registeredListeners.add(listener);
+
+ /* Send the current rules to the new listener ("statedump") */
+ for (EventRule rule : enabledEventRules.keySet()) {
+ listener.eventRuleAdded(rule);
+ }
+ }
+
+ /**
+ * Unregister a listener from the manager.
+ *
+ * @param listener
+ * The listener to remove
+ */
+ public synchronized void unregisterListener(IFilterChangeListener listener) {
+ registeredListeners.remove(listener);
+ }
+
+ private void notifyForAddedRule(final EventRule rule) {
+ for (IFilterChangeListener notifier : registeredListeners) {
+ notifier.eventRuleAdded(rule);
+ }
+ }
+
+ private void notifyForRemovedRule(final EventRule rule) {
+ for (IFilterChangeListener notifier : registeredListeners) {
+ notifier.eventRuleRemoved(rule);
+ }
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.filter;
+
+import org.lttng.ust.agent.session.EventRule;
+
+/**
+ * Filter notification listener interface.
+ * <p>
+ * Applications wanting to be notified of event filtering rule changes should
+ * implement this interface, then register their listener using
+ * {@link FilterChangeNotifier#registerListener}.
+ * </p>
+ * <p>
+ * The callbacks defined in this interface will be called whenever an event rule
+ * is added or removed. The manager will take care of the reference-counting in
+ * case multiple tracing sessions enable the exact same rules. For example, the
+ * {@link #eventRuleRemoved} callback is only called when there are no more
+ * session interested into it.
+ * </p>
+ * <p>
+ * Do not forget to unregister the listener after use, using
+ * {@link FilterChangeNotifier#unregisterListener}. If you do not, or if
+ * you use an anonymous listener for example, these will remain attached until
+ * the complete shutdown of the application.
+ * </p>
+ * <p>
+ * Only one thread is used to dispatch notifications, sequentially. This means
+ * that if a callback hangs it will prevent other listeners from receiving
+ * notifications. Please take care of not blocking inside the listener
+ * callbacks, and use separate threads for potentially long or blocking
+ * operations.
+ * </p>
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface IFilterChangeListener {
+
+ /**
+ * Notification that a new event rule is now enabled in the tracing
+ * sessions.
+ *
+ * @param rule
+ * The event rule that was enabled
+ */
+ void eventRuleAdded(EventRule rule);
+
+ /**
+ * Notification that an existing event rule is now disabled in the tracing
+ * sessions.
+ *
+ * @param rule
+ * The event rule that was disabled
+ */
+ void eventRuleRemoved(EventRule rule);
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.session;
+
+/**
+ * Event filtering rule present in a tracing session.
+ *
+ * It typically comes from a "lttng enable-event" command, and contains a
+ * domain, event name, log level and filter string.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class EventRule {
+
+ private final String eventName;
+ private final LogLevelSelector logLevelSelector;
+ private final String filterString;
+
+ /**
+ * Constructor.
+ *
+ * @param eventName
+ * The name of the tracepoint
+ * @param logLevelSelector
+ * The log level of the event rule
+ * @param filterString
+ * The filtering string. May be null if there is no extra filter.
+ */
+ public EventRule(String eventName, LogLevelSelector logLevelSelector, String filterString) {
+ this.eventName = eventName;
+ this.logLevelSelector = logLevelSelector;
+ this.filterString = filterString;
+ }
+
+ /**
+ * Get the event name of this rule.
+ *
+ * @return The event name
+ */
+ public String getEventName() {
+ return eventName;
+ }
+
+ /**
+ * Get the log level filter configuration of the rule.
+ *
+ * @return The log level selector
+ */
+ public LogLevelSelector getLogLevelSelector() {
+ return logLevelSelector;
+ }
+
+ /**
+ * Get the filter string associated with this rule.
+ *
+ * @return The filter string, may be null for no filter string.
+ */
+ public String getFilterString() {
+ return filterString;
+ }
+
+ // ------------------------------------------------------------------------
+ // Methods from Object
+ // ------------------------------------------------------------------------
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((eventName == null) ? 0 : eventName.hashCode());
+ result = prime * result + ((filterString == null) ? 0 : filterString.hashCode());
+ result = prime * result + ((logLevelSelector == null) ? 0 : logLevelSelector.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ EventRule other = (EventRule) obj;
+
+ if (eventName == null) {
+ if (other.eventName != null) {
+ return false;
+ }
+ } else if (!eventName.equals(other.eventName)) {
+ return false;
+ }
+ /* else, continue */
+
+ if (filterString == null) {
+ if (other.filterString != null) {
+ return false;
+ }
+ } else if (!filterString.equals(other.filterString)) {
+ return false;
+ }
+ /* else, continue */
+
+ if (logLevelSelector == null) {
+ if (other.logLevelSelector != null) {
+ return false;
+ }
+ } else if (!logLevelSelector.equals(other.logLevelSelector)) {
+ return false;
+ }
+ /* else, continue */
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Event name = " + getEventName() +
+ ", Log level selector = (" + getLogLevelSelector().toString() + ")" +
+ ", Filter string = " + getFilterString();
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.session;
+
+/**
+ * Log level filtering element, which is part of an {@link EventRule}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LogLevelSelector {
+
+ /**
+ * The type of log level filter that is enabled.
+ *
+ * Defined from lttng-tools' include/lttng/event.h.
+ */
+ public enum LogLevelType {
+ /**
+ * All log levels are enabled. This overrides the value of
+ * {@link LogLevelSelector#getLogLevel}.
+ */
+ LTTNG_EVENT_LOGLEVEL_ALL(0),
+
+ /** This log level along with all log levels of higher severity are enabled. */
+ LTTNG_EVENT_LOGLEVEL_RANGE(1),
+
+ /** Only this exact log level is enabled. */
+ LTTNG_EVENT_LOGLEVEL_SINGLE(2);
+
+ private final int value;
+
+ private LogLevelType(int value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the numerical (int) value representing this log level type in the
+ * communication protocol.
+ *
+ * @return The int value
+ */
+ public int getValue() {
+ return value;
+ }
+
+ static LogLevelType fromValue(int val) {
+ switch (val) {
+ case 0:
+ return LTTNG_EVENT_LOGLEVEL_ALL;
+ case 1:
+ return LTTNG_EVENT_LOGLEVEL_RANGE;
+ case 2:
+ return LTTNG_EVENT_LOGLEVEL_SINGLE;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ private final int logLevel;
+ private final LogLevelType logLevelType;
+
+ /**
+ * Constructor using numerical values straight from the communication
+ * protocol.
+ *
+ * @param logLevel
+ * The numerical value of the log level. The exact value depends
+ * on the tracing domain, see include/lttng/event.h in the
+ * lttng-tools tree for the complete enumeration.
+ * @param logLevelType
+ * The numerical value of the log level type. It will be
+ * converted to a {@link LogLevelType} by this constructor.
+ * @throws IllegalArgumentException
+ * If the 'logLevelType' does not correspond to a valid value.
+ */
+ public LogLevelSelector(int logLevel, int logLevelType) {
+ this.logLevel = logLevel;
+ this.logLevelType = LogLevelType.fromValue(logLevelType);
+ }
+
+ /**
+ * "Manual" constructor, specifying the {@link LogLevelType} directly.
+ *
+ * @param logLevel
+ * The numerical value of the log level. The exact value depends
+ * on the tracing domain, see include/lttng/event.h in the
+ * lttng-tools tree for the complete enumeration.
+ * @param type
+ * The log level filter type.
+ */
+ public LogLevelSelector(int logLevel, LogLevelType type) {
+ this.logLevel = logLevel;
+ this.logLevelType = type;
+ }
+
+ /**
+ * Get the numerical value of the log level element. Does not apply if
+ * {@link #getLogLevelType} returns
+ * {@link LogLevelType#LTTNG_EVENT_LOGLEVEL_ALL}.
+ *
+ * @return The numerical value of the log level
+ */
+ public int getLogLevel() {
+ return logLevel;
+ }
+
+ /**
+ * Get the log level filter type.
+ *
+ * @return The log level filter type
+ */
+ public LogLevelType getLogLevelType() {
+ return logLevelType;
+ }
+
+ /**
+ * Helper method to determine if an event with the given log level should be
+ * traced when considering this filter.
+ *
+ * For example, if this filter object represents "higher severity than 5",
+ * and the log level passed in parameter is "8", it will return that it
+ * matches (higher value means higher severity).
+ *
+ * @param targetLogLevel
+ * The log level value of the event to check for
+ * @return Should this event be traced, or not
+ */
+ public boolean matches(int targetLogLevel) {
+ switch (logLevelType) {
+ case LTTNG_EVENT_LOGLEVEL_ALL:
+ return true;
+ case LTTNG_EVENT_LOGLEVEL_RANGE:
+ return (targetLogLevel >= logLevel);
+ case LTTNG_EVENT_LOGLEVEL_SINGLE:
+ return (targetLogLevel == logLevel);
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Methods from Object
+ // ------------------------------------------------------------------------
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + logLevel;
+ result = prime * result + ((logLevelType == null) ? 0 : logLevelType.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ LogLevelSelector other = (LogLevelSelector) obj;
+
+ if (logLevel != other.logLevel) {
+ return false;
+ }
+ if (logLevelType != other.logLevelType) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (getLogLevelType() == LogLevelType.LTTNG_EVENT_LOGLEVEL_ALL) {
+ return LogLevelType.LTTNG_EVENT_LOGLEVEL_ALL.toString();
+ }
+ return String.valueOf(getLogLevel()) + ", " + getLogLevelType().toString();
+ }
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.utils;
+
+/**
+ * Logging infrastructure for the lttng-ust Java agent. It prints log messages
+ * to stderr but only when the environment variable LTTNG_UST_DEBUG is defined.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LttngUstAgentLogger {
+
+ private static final String ENV_VAR_NAME = "LTTNG_UST_DEBUG";
+ private static final boolean LOGGING_ENABLED = (System.getenv(ENV_VAR_NAME) == null ? false : true);
+
+ /**
+ * Log event. Will be printed to stderr if the environment variable
+ * "LTTNG_UST_DEBUG" is defined.
+ *
+ * @param c
+ * The class logging the message (should normally be called with
+ * {@link #getClass()}).
+ * @param message
+ * The message to print
+ */
+ public static void log(Class<?> c, String message) {
+ if (LOGGING_ENABLED) {
+ System.err.println(c.getSimpleName() + ": " + message);
+ }
+ }
+}
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+JAVAROOT = .
+AM_JAVACFLAGS = -classpath $(CLASSPATH):$(builddir)/../lttng-ust-agent-common/lttng-ust-agent-common.jar
+
+pkgpath = org/lttng/ust/agent/jul
+
+jarfile_version = 1.0.0
+jarfile_manifest = $(srcdir)/Manifest.txt
+jarfile_symlink = lttng-ust-agent-jul.jar
+jarfile = lttng-ust-agent-jul-$(jarfile_version).jar
+
+jardir = $(datadir)/java
+
+juljniout = ../../jni/jul
+
+dist_noinst_JAVA = $(pkgpath)/LttngJulAgent.java \
+ $(pkgpath)/LttngJulApi.java \
+ $(pkgpath)/LttngLogHandler.java
+
+dist_noinst_DATA = $(jarfile_manifest)
+
+jar_DATA = $(jarfile)
+
+stamp = jul-jni-header.stamp
+classes = $(pkgpath)/*.class
+
+$(jarfile): classnoinst.stamp
+ $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink)
+
+if !HAVE_JAVAH
+# If we don't have javah, assume we are running openjdk >= 10 and use javac
+# to generate the jni header file.
+AM_JAVACFLAGS += -h $(juljniout)
+else
+jul-jni-header.stamp: $(dist_noinst_JAVA)
+ $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(juljniout) $(JAVAHFLAGS) org.lttng.ust.agent.jul.LttngJulApi && \
+ echo "JUL JNI header generated" > jul-jni-header.stamp
+
+all-local: $(stamp)
+endif
+
+install-data-hook:
+ cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink)
+
+uninstall-hook:
+ cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink)
+
+CLEANFILES = *.jar \
+ $(pkgpath)/*.class \
+ jul-jni-header.stamp \
+ $(juljniout)/org_lttng_ust_agent_jul_LttngJulApi.h
--- /dev/null
+Name: org/lttng/ust/agent/jul/
+Specification-Title: LTTng UST Java Agent JUL Integration
+Specification-Version: 1.0.0
+Specification-Vendor: LTTng Project
+Implementation-Title: org.lttng.ust.agent.jul
+Implementation-Version: 1.0.0
+Implementation-Vendor: LTTng Project
+Class-Path: lttng-ust-agent-common.jar
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.jul;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+import org.lttng.ust.agent.AbstractLttngAgent;
+
+/**
+ * Agent implementation for tracing from JUL loggers.
+ *
+ * @author Alexandre Montplaisir
+ */
+class LttngJulAgent extends AbstractLttngAgent<LttngLogHandler> {
+
+ private static LttngJulAgent instance = null;
+
+ private LttngJulAgent() {
+ super(Domain.JUL);
+ }
+
+ public static synchronized LttngJulAgent getInstance() {
+ if (instance == null) {
+ instance = new LttngJulAgent();
+ }
+ return instance;
+ }
+
+ @Override
+ public Collection<String> listAvailableEvents() {
+ Set<String> ret = new TreeSet<String>();
+
+ List<String> loggersNames = Collections.list(LogManager.getLogManager().getLoggerNames());
+ for (String name : loggersNames) {
+ /*
+ * Skip the root logger. An empty string is not a valid event name
+ * in LTTng.
+ */
+ if (name.equals("") || name.equals("global")) {
+ continue;
+ }
+
+ /*
+ * Check if that logger has at least one LTTng JUL handler attached.
+ */
+ Logger logger = Logger.getLogger(name);
+ if (hasLttngHandlerAttached(logger)) {
+ ret.add(name);
+ }
+ }
+
+ return ret;
+ }
+
+ private static boolean hasLttngHandlerAttached(Logger logger) {
+ for (Handler handler : logger.getHandlers()) {
+ if (handler instanceof LttngLogHandler) {
+ return true;
+ }
+ }
+
+ /*
+ * A parent logger, if any, may be connected to an LTTng handler. In
+ * this case, we will want to include this child logger in the output,
+ * since it will be accessible by LTTng.
+ */
+ Logger parent = logger.getParent();
+ if (parent != null) {
+ return hasLttngHandlerAttached(parent);
+ }
+
+ /*
+ * We have reached the root logger and have not found any LTTng handler,
+ * this event will not be accessible.
+ */
+ return false;
+ }
+
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.jul;
+
+/**
+ * Virtual class containing the Java side of the LTTng-JUL JNI API methods.
+ *
+ * @author Alexandre Montplaisir
+ */
+final class LttngJulApi {
+
+ private LttngJulApi() {}
+
+ static native void tracepoint(String msg,
+ String logger_name,
+ String class_name,
+ String method_name,
+ long millis,
+ int log_level,
+ int thread_id);
+
+ static native void tracepointWithContext(String msg,
+ String logger_name,
+ String class_name,
+ String method_name,
+ long millis,
+ int log_level,
+ int thread_id,
+ byte[] contextEntries,
+ byte[] contextStrings);
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+package org.lttng.ust.agent.jul;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Formatter;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+
+import org.lttng.ust.agent.ILttngAgent;
+import org.lttng.ust.agent.ILttngHandler;
+import org.lttng.ust.agent.context.ContextInfoSerializer;
+
+/**
+ * LTTng-UST JUL log handler.
+ *
+ * Applications can attach this handler to their
+ * {@link java.util.logging.Logger} to have it generate UST events from logging
+ * events received through the logger.
+ *
+ * It sends its events to UST via the JNI library "liblttng-ust-jul-jni.so".
+ * Make sure this library is available before using this handler.
+ *
+ * @author Alexandre Montplaisir
+ * @author David Goulet
+ */
+public class LttngLogHandler extends Handler implements ILttngHandler {
+
+ private static final String SHARED_OBJECT_NAME = "lttng-ust-jul-jni";
+
+ /**
+ * Dummy Formatter object, so we can use its
+ * {@link Formatter#formatMessage(LogRecord)} method.
+ */
+ private static final Formatter FORMATTER = new Formatter() {
+ @Override
+ public String format(LogRecord record) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ private final ILttngAgent<LttngLogHandler> agent;
+
+ /** Number of events logged (really sent through JNI) by this handler */
+ private final AtomicLong eventCount = new AtomicLong(0);
+
+ /**
+ * Constructor
+ *
+ * @throws IOException
+ * This handler requires the lttng-ust-jul-jni.so native
+ * library, through which it will send the trace events. This
+ * exception is throw is this library cannot be found.
+ * @throws SecurityException
+ * We will forward any SecurityExcepion that may be thrown when
+ * trying to load the JNI library.
+ */
+ public LttngLogHandler() throws IOException, SecurityException {
+ super();
+ /* Initialize LTTng UST tracer. */
+ try {
+ System.loadLibrary(SHARED_OBJECT_NAME); //$NON-NLS-1$
+ } catch (UnsatisfiedLinkError e) {
+ throw new IOException(e);
+ }
+
+ /** Register to the relevant agent */
+ agent = LttngJulAgent.getInstance();
+ agent.registerHandler(this);
+ }
+
+ @Override
+ public synchronized void close() {
+ agent.unregisterHandler(this);
+ }
+
+ /**
+ * Get the number of events logged by this handler so far. This means the
+ * number of events actually sent through JNI to UST.
+ *
+ * @return The number of events logged so far
+ */
+ @Override
+ public long getEventCount() {
+ return eventCount.get();
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ /*
+ * Check if the current message should be logged, according to the UST
+ * session settings.
+ */
+ if (!agent.isEventEnabled(record.getLoggerName())) {
+ return;
+ }
+
+ String formattedMessage = FORMATTER.formatMessage(record);
+
+ /* Retrieve all the requested context information we can find */
+ Collection<Entry<String, Map<String, Integer>>> enabledContexts = agent.getEnabledAppContexts();
+ ContextInfoSerializer.SerializedContexts contextInfo = ContextInfoSerializer.queryAndSerializeRequestedContexts(enabledContexts);
+
+ eventCount.incrementAndGet();
+
+ /*
+ * Specific tracepoint designed for JUL events. The source class of the
+ * caller is used for the event name, the raw message is taken, the
+ * loglevel of the record and the thread ID.
+ */
+ LttngJulApi.tracepointWithContext(formattedMessage,
+ record.getLoggerName(),
+ record.getSourceClassName(),
+ record.getSourceMethodName(),
+ record.getMillis(),
+ record.getLevel().intValue(),
+ record.getThreadID(),
+ contextInfo.getEntriesArray(),
+ contextInfo.getStringsArray());
+ }
+
+}
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+JAVAROOT = .
+AM_JAVACFLAGS = -classpath $(CLASSPATH):$(builddir)/../lttng-ust-agent-common/lttng-ust-agent-common.jar
+
+pkgpath = org/lttng/ust/agent/log4j
+
+jarfile_version = 1.0.0
+jarfile_manifest = $(srcdir)/Manifest.txt
+jarfile_symlink = lttng-ust-agent-log4j.jar
+jarfile = lttng-ust-agent-log4j-$(jarfile_version).jar
+
+jardir = $(datadir)/java
+
+log4jjniout = ../../jni/log4j
+
+dist_noinst_JAVA = $(pkgpath)/LttngLog4jAgent.java \
+ $(pkgpath)/LttngLog4jApi.java \
+ $(pkgpath)/LttngLogAppender.java
+
+dist_noinst_DATA = $(jarfile_manifest)
+
+jar_DATA = $(jarfile)
+
+stamp = log4j-jni-header.stamp
+classes = $(pkgpath)/*.class
+
+$(jarfile): classnoinst.stamp
+ $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink)
+
+if !HAVE_JAVAH
+# If we don't have javah, assume we are running openjdk >= 10 and use javac
+# to generate the jni header file.
+AM_JAVACFLAGS += -h $(log4jjniout)
+else
+log4j-jni-header.stamp: $(dist_noinst_JAVA)
+ $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(log4jjniout) $(JAVAHFLAGS) org.lttng.ust.agent.log4j.LttngLog4jApi && \
+ echo "Log4j JNI header generated" > log4j-jni-header.stamp
+
+all-local: $(stamp)
+endif
+
+install-data-hook:
+ cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink)
+
+uninstall-hook:
+ cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink)
+
+CLEANFILES = *.jar \
+ $(pkgpath)/*.class \
+ log4j-jni-header.stamp \
+ $(log4jjniout)/org_lttng_ust_agent_log4j_LttngLog4jApi.h
--- /dev/null
+Name: org/lttng/ust/agent/log4j/
+Specification-Title: LTTng UST Java Agent Log4J 1.x Integration
+Specification-Version: 1.0.0
+Specification-Vendor: LTTng Project
+Implementation-Title: org.lttng.ust.agent.log4j
+Implementation-Version: 1.0.0
+Implementation-Vendor: LTTng Project
+Class-Path: lttng-ust-agent-common.jar
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.log4j;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.lttng.ust.agent.AbstractLttngAgent;
+
+/**
+ * Agent implementation for using the Log4j logger, connecting to a root session
+ * daemon.
+ *
+ * @author Alexandre Montplaisir
+ */
+class LttngLog4jAgent extends AbstractLttngAgent<LttngLogAppender> {
+
+ private static LttngLog4jAgent instance = null;
+
+ private LttngLog4jAgent() {
+ super(Domain.LOG4J);
+ }
+
+ public static synchronized LttngLog4jAgent getInstance() {
+ if (instance == null) {
+ instance = new LttngLog4jAgent();
+ }
+ return instance;
+ }
+
+ @Override
+ public Collection<String> listAvailableEvents() {
+ Set<String> ret = new TreeSet<String>();
+
+ @SuppressWarnings("unchecked")
+ List<Logger> loggers = Collections.list(LogManager.getCurrentLoggers());
+ for (Logger logger : loggers) {
+ if (logger == null) {
+ continue;
+ }
+
+ /*
+ * Check if that logger has at least one LTTng log4j appender
+ * attached.
+ */
+ if (hasLttngAppenderAttached(logger)) {
+ ret.add(logger.getName());
+ }
+ }
+
+ return ret;
+ }
+
+ private static boolean hasLttngAppenderAttached(Category logger) {
+ @SuppressWarnings("unchecked")
+ Enumeration<Appender> appenders = logger.getAllAppenders();
+ if (appenders != null) {
+ for (Appender appender : Collections.list(appenders)) {
+ if (appender instanceof LttngLogAppender) {
+ return true;
+ }
+ }
+ }
+
+ /*
+ * A parent logger, if any, may be connected to an LTTng handler. In
+ * this case, we will want to include this child logger in the output,
+ * since it will be accessible by LTTng.
+ */
+ Category parent = logger.getParent();
+ if (parent != null) {
+ return hasLttngAppenderAttached(parent);
+ }
+
+ /*
+ * We have reached the root logger and have not found any LTTng handler,
+ * this event will not be accessible.
+ */
+ return false;
+ }
+
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+package org.lttng.ust.agent.log4j;
+
+/**
+ * Virtual class containing the Java side of the LTTng-log4j JNI API methods.
+ *
+ * @author Alexandre Montplaisir
+ */
+final class LttngLog4jApi {
+
+ private LttngLog4jApi() {}
+
+ static native void tracepoint(String msg,
+ String logger_name,
+ String class_name,
+ String method_name,
+ String file_name,
+ int line_number,
+ long timestamp,
+ int loglevel,
+ String thread_name);
+
+ static native void tracepointWithContext(String msg,
+ String logger_name,
+ String class_name,
+ String method_name,
+ String file_name,
+ int line_number,
+ long timestamp,
+ int loglevel,
+ String thread_name,
+ byte[] contextEntries,
+ byte[] contextStrings);
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2014 Christian Babeux <christian.babeux@efficios.com>
+ */
+
+package org.lttng.ust.agent.log4j;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.spi.LoggingEvent;
+import org.lttng.ust.agent.ILttngAgent;
+import org.lttng.ust.agent.ILttngHandler;
+import org.lttng.ust.agent.context.ContextInfoSerializer;
+
+/**
+ * LTTng-UST Log4j 1.x log handler.
+ *
+ * Applications can attach this appender to their
+ * {@link org.apache.log4j.Logger} to have it generate UST events from logging
+ * events received through the logger.
+ *
+ * It sends its events to UST via the JNI library "liblttng-ust-log4j-jni.so".
+ * Make sure this library is available before using this appender.
+ *
+ * @author Alexandre Montplaisir
+ * @author Christian Babeux
+ */
+public class LttngLogAppender extends AppenderSkeleton implements ILttngHandler {
+
+ private static final String SHARED_OBJECT_NAME = "lttng-ust-log4j-jni";
+
+ private final AtomicLong eventCount = new AtomicLong(0);
+
+ private final ILttngAgent<LttngLogAppender> agent;
+
+
+ /**
+ * Constructor
+ *
+ * @throws IOException
+ * This handler requires the lttng-ust-log4j-jni.so native
+ * library, through which it will send the trace events. This
+ * exception is throw is this library cannot be found.
+ * @throws SecurityException
+ * We will forward any SecurityExcepion that may be thrown when
+ * trying to load the JNI library.
+ */
+ public LttngLogAppender() throws IOException, SecurityException {
+ super();
+ /* Initialize LTTng UST tracer. */
+ try {
+ System.loadLibrary(SHARED_OBJECT_NAME); // $NON-NLS-1$
+ } catch (UnsatisfiedLinkError e) {
+ throw new IOException(e);
+ }
+
+ /** Register to the relevant agent */
+ agent = LttngLog4jAgent.getInstance();
+ agent.registerHandler(this);
+ }
+
+ @Override
+ public synchronized void close() {
+ agent.unregisterHandler(this);
+ }
+
+ /**
+ * Get the number of events logged by this handler so far. This means the
+ * number of events actually sent through JNI to UST.
+ *
+ * @return The number of events logged so far
+ */
+ @Override
+ public long getEventCount() {
+ return eventCount.get();
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ @Override
+ protected void append(LoggingEvent event) {
+ /*
+ * Check if the current message should be logged, according to the UST
+ * session settings.
+ */
+ if (!agent.isEventEnabled(event.getLoggerName())) {
+ return;
+ }
+
+ /*
+ * The line number returned from LocationInformation is a string. At
+ * least try to convert to a proper int.
+ */
+ int line;
+ try {
+ String lineString = event.getLocationInformation().getLineNumber();
+ line = Integer.parseInt(lineString);
+ } catch (NumberFormatException n) {
+ line = -1;
+ }
+
+ /* Retrieve all the requested context information we can find */
+ Collection<Entry<String, Map<String, Integer>>> enabledContexts = agent.getEnabledAppContexts();
+ ContextInfoSerializer.SerializedContexts contextInfo = ContextInfoSerializer.queryAndSerializeRequestedContexts(enabledContexts);
+
+ eventCount.incrementAndGet();
+
+ LttngLog4jApi.tracepointWithContext(event.getRenderedMessage(),
+ event.getLoggerName(),
+ event.getLocationInformation().getClassName(),
+ event.getLocationInformation().getMethodName(),
+ event.getLocationInformation().getFileName(),
+ line,
+ event.getTimeStamp(),
+ event.getLevel().toInt(),
+ event.getThreadName(),
+ contextInfo.getEntriesArray(),
+ contextInfo.getStringsArray());
+ }
+
+}
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+SUBDIRS = common
+
+if ENABLE_JAVA_AGENT_WITH_JUL
+SUBDIRS += jul
+endif
+
+if ENABLE_JAVA_AGENT_WITH_LOG4J
+SUBDIRS += log4j
+endif
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+AM_CPPFLAGS += -I$(builddir) -I$(srcdir) $(JNI_CPPFLAGS)
+
+lib_LTLIBRARIES = liblttng-ust-context-jni.la
+liblttng_ust_context_jni_la_SOURCES = lttng_ust_context.c lttng_ust_context.h
+
+nodist_liblttng_ust_context_jni_la_SOURCES = org_lttng_ust_agent_context_LttngContextApi.h
+
+liblttng_ust_context_jni_la_LIBADD = -lc \
+ $(top_builddir)/src/liblttng-ust/liblttng-ust.la
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#include "org_lttng_ust_agent_context_LttngContextApi.h"
+
+#include <string.h>
+#include <inttypes.h>
+#include <lttng/ust-events.h>
+#include <lttng/ringbuffer-context.h>
+#include <common/ust-context-provider.h>
+
+#include "common/macros.h"
+#include "lttng_ust_context.h"
+
+enum lttng_ust_jni_type {
+ JNI_TYPE_NULL = 0,
+ JNI_TYPE_INTEGER = 1,
+ JNI_TYPE_LONG = 2,
+ JNI_TYPE_DOUBLE = 3,
+ JNI_TYPE_FLOAT = 4,
+ JNI_TYPE_BYTE = 5,
+ JNI_TYPE_SHORT = 6,
+ JNI_TYPE_BOOLEAN = 7,
+ JNI_TYPE_STRING = 8,
+};
+
+struct lttng_ust_jni_ctx_entry {
+ int32_t context_name_offset;
+ char type; /* enum lttng_ust_jni_type */
+ union {
+ int32_t _integer;
+ int64_t _long;
+ double _double;
+ float _float;
+ signed char _byte;
+ int16_t _short;
+ signed char _boolean;
+ int32_t _string_offset;
+ } value;
+} __attribute__((packed));
+
+struct lttng_ust_jni_provider {
+ struct lttng_ust_registered_context_provider *reg_provider;
+ char *name;
+ struct lttng_ust_context_provider provider;
+};
+
+/* TLS passing context info from JNI to callbacks. */
+__thread struct lttng_ust_jni_tls lttng_ust_context_info_tls;
+
+static const char *get_ctx_string_at_offset(int32_t offset)
+{
+ signed char *ctx_strings_array = lttng_ust_context_info_tls.ctx_strings;
+
+ if (offset < 0 || offset >= lttng_ust_context_info_tls.ctx_strings_len) {
+ return NULL;
+ }
+ return (const char *) (ctx_strings_array + offset);
+}
+
+static struct lttng_ust_jni_ctx_entry *lookup_ctx_by_name(const char *ctx_name)
+{
+ struct lttng_ust_jni_ctx_entry *ctx_entries_array = lttng_ust_context_info_tls.ctx_entries;
+ int i, len = lttng_ust_context_info_tls.ctx_entries_len / sizeof(struct lttng_ust_jni_ctx_entry);
+
+ for (i = 0; i < len; i++) {
+ int32_t offset = ctx_entries_array[i].context_name_offset;
+ const char *string = get_ctx_string_at_offset(offset);
+
+ if (string && strcmp(string, ctx_name) == 0) {
+ return &ctx_entries_array[i];
+ }
+ }
+ return NULL;
+}
+
+static size_t get_size_cb(void *priv, size_t offset)
+{
+ struct lttng_ust_jni_ctx_entry *jctx;
+ size_t size = 0;
+ struct lttng_ust_jni_provider *jni_provider = (struct lttng_ust_jni_provider *) priv;
+ const char *ctx_name = jni_provider->name;
+ enum lttng_ust_jni_type jni_type;
+
+ size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(char));
+ size += sizeof(char); /* tag */
+ jctx = lookup_ctx_by_name(ctx_name);
+ if (!jctx) {
+ jni_type = JNI_TYPE_NULL;
+ } else {
+ jni_type = jctx->type;
+ }
+ switch (jni_type) {
+ case JNI_TYPE_NULL:
+ break;
+ case JNI_TYPE_INTEGER:
+ size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(int32_t));
+ size += sizeof(int32_t); /* variant */
+ break;
+ case JNI_TYPE_LONG:
+ size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(int64_t));
+ size += sizeof(int64_t); /* variant */
+ break;
+ case JNI_TYPE_DOUBLE:
+ size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(double));
+ size += sizeof(double); /* variant */
+ break;
+ case JNI_TYPE_FLOAT:
+ size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(float));
+ size += sizeof(float); /* variant */
+ break;
+ case JNI_TYPE_SHORT:
+ size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(int16_t));
+ size += sizeof(int16_t); /* variant */
+ break;
+ case JNI_TYPE_BYTE: /* Fall-through. */
+ case JNI_TYPE_BOOLEAN:
+ size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(char));
+ size += sizeof(char); /* variant */
+ break;
+ case JNI_TYPE_STRING:
+ {
+ /* The value is an offset, the string is in the "strings" array */
+ int32_t string_offset = jctx->value._string_offset;
+ const char *string = get_ctx_string_at_offset(string_offset);
+
+ if (string) {
+ size += strlen(string) + 1;
+ }
+ break;
+ }
+ default:
+ abort();
+ }
+ return size;
+
+}
+
+static void record_cb(void *priv,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ struct lttng_ust_channel_buffer *lttng_chan_buf)
+{
+ struct lttng_ust_jni_ctx_entry *jctx;
+ struct lttng_ust_jni_provider *jni_provider = (struct lttng_ust_jni_provider *) priv;
+ const char *ctx_name = jni_provider->name;
+ enum lttng_ust_jni_type jni_type;
+ char sel_char;
+
+ jctx = lookup_ctx_by_name(ctx_name);
+ if (!jctx) {
+ jni_type = JNI_TYPE_NULL;
+ } else {
+ jni_type = jctx->type;
+ }
+
+ switch (jni_type) {
+ case JNI_TYPE_NULL:
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_NONE;
+ lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
+ break;
+ case JNI_TYPE_INTEGER:
+ {
+ int32_t v = jctx->value._integer;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S32;
+ lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
+ lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
+ break;
+ }
+ case JNI_TYPE_LONG:
+ {
+ int64_t v = jctx->value._long;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S64;
+ lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
+ lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
+ break;
+ }
+ case JNI_TYPE_DOUBLE:
+ {
+ double v = jctx->value._double;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_DOUBLE;
+ lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
+ lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
+ break;
+ }
+ case JNI_TYPE_FLOAT:
+ {
+ float v = jctx->value._float;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_FLOAT;
+ lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
+ lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
+ break;
+ }
+ case JNI_TYPE_SHORT:
+ {
+ int16_t v = jctx->value._short;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S16;
+ lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
+ lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
+ break;
+ }
+ case JNI_TYPE_BYTE:
+ {
+ char v = jctx->value._byte;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S8;
+ lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
+ lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
+ break;
+ }
+ case JNI_TYPE_BOOLEAN:
+ {
+ char v = jctx->value._boolean;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S8;
+ lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
+ lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
+ break;
+ }
+ case JNI_TYPE_STRING:
+ {
+ int32_t offset = jctx->value._string_offset;
+ const char *str = get_ctx_string_at_offset(offset);
+
+ if (str) {
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_STRING;
+ } else {
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_NONE;
+ }
+ lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
+ if (str) {
+ lttng_chan_buf->ops->event_write(ctx, str, strlen(str) + 1, 1);
+ }
+ break;
+ }
+ default:
+ abort();
+ }
+}
+
+static void get_value_cb(void *priv, struct lttng_ust_ctx_value *value)
+{
+ struct lttng_ust_jni_provider *jni_provider = (struct lttng_ust_jni_provider *) priv;
+ struct lttng_ust_jni_ctx_entry *jctx;
+ const char *ctx_name = jni_provider->name;
+ enum lttng_ust_jni_type jni_type;
+
+ jctx = lookup_ctx_by_name(ctx_name);
+ if (!jctx) {
+ jni_type = JNI_TYPE_NULL;
+ } else {
+ jni_type = jctx->type;
+ }
+
+ switch (jni_type) {
+ case JNI_TYPE_NULL:
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_NONE;
+ break;
+ case JNI_TYPE_INTEGER:
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
+ value->u.s64 = (int64_t) jctx->value._integer;
+ break;
+ case JNI_TYPE_LONG:
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
+ value->u.s64 = jctx->value._long;
+ break;
+ case JNI_TYPE_DOUBLE:
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_DOUBLE;
+ value->u.d = jctx->value._double;
+ break;
+ case JNI_TYPE_FLOAT:
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_DOUBLE;
+ value->u.d = (double) jctx->value._float;
+ break;
+ case JNI_TYPE_SHORT:
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
+ value->u.s64 = (int64_t) jctx->value._short;
+ break;
+ case JNI_TYPE_BYTE:
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
+ value->u.s64 = (int64_t) jctx->value._byte;
+ break;
+ case JNI_TYPE_BOOLEAN:
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
+ value->u.s64 = (int64_t) jctx->value._boolean;
+ break;
+ case JNI_TYPE_STRING:
+ {
+ int32_t offset = jctx->value._string_offset;
+ const char *str = get_ctx_string_at_offset(offset);
+
+ if (str) {
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_STRING;
+ value->u.str = str;
+ } else {
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_NONE;
+ }
+ break;
+ }
+ default:
+ abort();
+ }
+}
+
+/*
+ * Register a context provider to UST.
+ *
+ * Called from the Java side when an application registers a context retriever,
+ * so we create and register a corresponding provider on the C side.
+ */
+JNIEXPORT jlong JNICALL Java_org_lttng_ust_agent_context_LttngContextApi_registerProvider(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring provider_name)
+{
+ jboolean iscopy;
+ const char *provider_name_jstr;
+ char *provider_name_cstr;
+ struct lttng_ust_context_provider *provider;
+ struct lttng_ust_jni_provider *jni_provider;
+ /*
+ * Note: a "jlong" is 8 bytes on all architectures, whereas a
+ * C "long" varies.
+ */
+ jlong provider_ref;
+
+ provider_name_jstr = (*env)->GetStringUTFChars(env, provider_name, &iscopy);
+ if (!provider_name_jstr) {
+ goto error_jstr;
+ }
+ /* Keep our own copy of the string so UST can use it. */
+ provider_name_cstr = strdup(provider_name_jstr);
+ (*env)->ReleaseStringUTFChars(env, provider_name, provider_name_jstr);
+ if (!provider_name_cstr) {
+ goto error_strdup;
+ }
+ jni_provider = zmalloc(sizeof(*jni_provider));
+ if (!jni_provider) {
+ goto error_provider;
+ }
+ provider = &jni_provider->provider;
+ provider->struct_size = sizeof(*provider);
+ jni_provider->name = provider_name_cstr;
+ provider->name = jni_provider->name;
+ provider->get_size = get_size_cb;
+ provider->record = record_cb;
+ provider->get_value = get_value_cb;
+ provider->priv = jni_provider;
+
+ jni_provider->reg_provider = lttng_ust_context_provider_register(provider);
+ if (!jni_provider->reg_provider) {
+ goto error_register;
+ }
+
+ provider_ref = (jlong) (long) jni_provider;
+ return provider_ref;
+
+ /* Error handling. */
+error_register:
+ free(jni_provider);
+error_provider:
+ free(provider_name_cstr);
+error_strdup:
+error_jstr:
+ return 0;
+}
+
+/*
+ * Unregister a previously-registered context provider.
+ *
+ * Called from the Java side when an application unregisters a context retriever,
+ * so we unregister and delete the corresponding provider on the C side.
+ */
+JNIEXPORT void JNICALL Java_org_lttng_ust_agent_context_LttngContextApi_unregisterProvider(JNIEnv *env __attribute__((unused)),
+ jobject jobj __attribute__((unused)),
+ jlong provider_ref)
+{
+ struct lttng_ust_jni_provider *jni_provider =
+ (struct lttng_ust_jni_provider *) (unsigned long) provider_ref;
+
+ if (!jni_provider) {
+ return;
+ }
+
+ lttng_ust_context_provider_unregister(jni_provider->reg_provider);
+
+ free(jni_provider->name);
+ free(jni_provider);
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ */
+
+#ifndef LIBLTTNG_UST_JAVA_AGENT_JNI_COMMON_LTTNG_UST_CONTEXT_H_
+#define LIBLTTNG_UST_JAVA_AGENT_JNI_COMMON_LTTNG_UST_CONTEXT_H_
+
+struct lttng_ust_jni_ctx_entry;
+
+struct lttng_ust_jni_tls {
+ struct lttng_ust_jni_ctx_entry *ctx_entries;
+ int32_t ctx_entries_len;
+ signed char *ctx_strings;
+ int32_t ctx_strings_len;
+};
+
+extern __thread struct lttng_ust_jni_tls lttng_ust_context_info_tls;
+
+#endif /* LIBLTTNG_UST_JAVA_AGENT_JNI_COMMON_LTTNG_UST_CONTEXT_H_ */
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+AM_CPPFLAGS += -I$(builddir) -I$(srcdir) $(JNI_CPPFLAGS)
+
+lib_LTLIBRARIES = liblttng-ust-jul-jni.la
+liblttng_ust_jul_jni_la_SOURCES = lttng_ust_jul.c \
+ lttng_ust_jul.h
+
+nodist_liblttng_ust_jul_jni_la_SOURCES = org_lttng_ust_agent_jul_LttngJulApi.h
+
+liblttng_ust_jul_jni_la_LIBADD = -lc \
+ $(top_builddir)/src/liblttng-ust/liblttng-ust.la \
+ $(top_builddir)/src/lib/lttng-ust-java-agent/jni/common/liblttng-ust-context-jni.la
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#define _LGPL_SOURCE
+#include "org_lttng_ust_agent_jul_LttngJulApi.h"
+
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_CREATE_PROBES
+#include "lttng_ust_jul.h"
+#include "../common/lttng_ust_context.h"
+
+/*
+ * Deprecated function from before the context information was passed.
+ */
+JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LttngJulApi_tracepoint(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring msg,
+ jstring logger_name,
+ jstring class_name,
+ jstring method_name,
+ jlong millis,
+ jint log_level,
+ jint thread_id)
+{
+ jboolean iscopy;
+ const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
+ const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy);
+ const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy);
+ const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy);
+
+ tracepoint(lttng_jul, event, msg_cstr, logger_name_cstr,
+ class_name_cstr, method_name_cstr, millis, log_level, thread_id);
+
+ (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
+ (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
+}
+
+/*
+ * Tracepoint used by Java applications using the JUL handler.
+ */
+JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LttngJulApi_tracepointWithContext(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring msg,
+ jstring logger_name,
+ jstring class_name,
+ jstring method_name,
+ jlong millis,
+ jint log_level,
+ jint thread_id,
+ jbyteArray context_info_entries,
+ jbyteArray context_info_strings)
+{
+ jboolean iscopy;
+ const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
+ const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy);
+ const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy);
+ const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy);
+ signed char *context_info_entries_array;
+ signed char *context_info_strings_array;
+
+ /*
+ * Write these to the TLS variables, so that the UST callbacks in
+ * lttng_ust_context.c can access them.
+ */
+ context_info_entries_array = (*env)->GetByteArrayElements(env, context_info_entries, &iscopy);
+ lttng_ust_context_info_tls.ctx_entries = (struct lttng_ust_jni_ctx_entry *) context_info_entries_array;
+ lttng_ust_context_info_tls.ctx_entries_len = (*env)->GetArrayLength(env, context_info_entries);
+ context_info_strings_array = (*env)->GetByteArrayElements(env, context_info_strings, &iscopy);
+ lttng_ust_context_info_tls.ctx_strings = context_info_strings_array;
+ lttng_ust_context_info_tls.ctx_strings_len = (*env)->GetArrayLength(env, context_info_strings);
+
+ tracepoint(lttng_jul, event, msg_cstr, logger_name_cstr,
+ class_name_cstr, method_name_cstr, millis, log_level, thread_id);
+
+ lttng_ust_context_info_tls.ctx_entries = NULL;
+ lttng_ust_context_info_tls.ctx_entries_len = 0;
+ lttng_ust_context_info_tls.ctx_strings = NULL;
+ lttng_ust_context_info_tls.ctx_strings_len = 0;
+ (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
+ (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
+ (*env)->ReleaseByteArrayElements(env, context_info_entries, context_info_entries_array, 0);
+ (*env)->ReleaseByteArrayElements(env, context_info_strings, context_info_strings_array, 0);
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER lttng_jul
+
+#if !defined(_TRACEPOINT_LTTNG_UST_JUL_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_LTTNG_UST_JUL_H
+
+#include <lttng/tracepoint.h>
+
+/*
+ * Tracepoint used by Java applications using the JUL handler.
+ */
+TRACEPOINT_EVENT(lttng_jul, event,
+ TP_ARGS(
+ const char *, msg,
+ const char *, logger_name,
+ const char *, class_name,
+ const char *, method_name,
+ long, millis,
+ int, log_level,
+ int, thread_id),
+ TP_FIELDS(
+ ctf_string(msg, msg)
+ ctf_string(logger_name, logger_name)
+ ctf_string(class_name, class_name)
+ ctf_string(method_name, method_name)
+ ctf_integer(long, long_millis, millis)
+ ctf_integer(int, int_loglevel, log_level)
+ ctf_integer(int, int_threadid, thread_id)
+ )
+)
+
+#endif /* _TRACEPOINT_LTTNG_UST_JUL_H */
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./lttng_ust_jul.h"
+
+/* This part must be outside protection */
+#include <lttng/tracepoint-event.h>
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+AM_CPPFLAGS += -I$(builddir) -I$(srcdir) $(JNI_CPPFLAGS)
+
+lib_LTLIBRARIES = liblttng-ust-log4j-jni.la
+liblttng_ust_log4j_jni_la_SOURCES = lttng_ust_log4j.c \
+ lttng_ust_log4j.h
+
+nodist_liblttng_ust_log4j_jni_la_SOURCES = org_lttng_ust_agent_log4j_LttngLog4jApi.h
+
+liblttng_ust_log4j_jni_la_LIBADD = -lc \
+ $(top_builddir)/src/liblttng-ust/liblttng-ust.la \
+ $(top_builddir)/src/lib/lttng-ust-java-agent/jni/common/liblttng-ust-context-jni.la
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2016 EfficiOS Inc.
+ * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
+ * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#define _LGPL_SOURCE
+#include "org_lttng_ust_agent_log4j_LttngLog4jApi.h"
+
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_CREATE_PROBES
+#include "lttng_ust_log4j.h"
+#include "../common/lttng_ust_context.h"
+
+/*
+ * Deprecated function from before the context information was passed.
+ */
+JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LttngLog4jApi_tracepoint(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring msg,
+ jstring logger_name,
+ jstring class_name,
+ jstring method_name,
+ jstring file_name,
+ jint line_number,
+ jlong timestamp,
+ jint loglevel,
+ jstring thread_name)
+{
+ jboolean iscopy;
+ const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
+ const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy);
+ const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy);
+ const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy);
+ const char *file_name_cstr = (*env)->GetStringUTFChars(env, file_name, &iscopy);
+ const char *thread_name_cstr = (*env)->GetStringUTFChars(env, thread_name, &iscopy);
+
+ tracepoint(lttng_log4j, event, msg_cstr, logger_name_cstr,
+ class_name_cstr, method_name_cstr, file_name_cstr,
+ line_number, timestamp, loglevel, thread_name_cstr);
+
+ (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
+ (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, file_name, file_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, thread_name, thread_name_cstr);
+}
+
+/*
+ * Tracepoint used by Java applications using the log4j handler.
+ */
+JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LttngLog4jApi_tracepointWithContext(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring msg,
+ jstring logger_name,
+ jstring class_name,
+ jstring method_name,
+ jstring file_name,
+ jint line_number,
+ jlong timestamp,
+ jint loglevel,
+ jstring thread_name,
+ jbyteArray context_info_entries,
+ jbyteArray context_info_strings)
+{
+ jboolean iscopy;
+ const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
+ const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy);
+ const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy);
+ const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy);
+ const char *file_name_cstr = (*env)->GetStringUTFChars(env, file_name, &iscopy);
+ const char *thread_name_cstr = (*env)->GetStringUTFChars(env, thread_name, &iscopy);
+ signed char *context_info_entries_array;
+ signed char *context_info_strings_array;
+
+ /*
+ * Write these to the TLS variables, so that the UST callbacks in
+ * lttng_ust_context.c can access them.
+ */
+ context_info_entries_array = (*env)->GetByteArrayElements(env, context_info_entries, &iscopy);
+ lttng_ust_context_info_tls.ctx_entries = (struct lttng_ust_jni_ctx_entry *) context_info_entries_array;
+ lttng_ust_context_info_tls.ctx_entries_len = (*env)->GetArrayLength(env, context_info_entries);
+ context_info_strings_array = (*env)->GetByteArrayElements(env, context_info_strings, &iscopy);
+ lttng_ust_context_info_tls.ctx_strings = context_info_strings_array;
+ lttng_ust_context_info_tls.ctx_strings_len = (*env)->GetArrayLength(env, context_info_strings);
+
+ tracepoint(lttng_log4j, event, msg_cstr, logger_name_cstr,
+ class_name_cstr, method_name_cstr, file_name_cstr,
+ line_number, timestamp, loglevel, thread_name_cstr);
+
+ lttng_ust_context_info_tls.ctx_entries = NULL;
+ lttng_ust_context_info_tls.ctx_entries_len = 0;
+ lttng_ust_context_info_tls.ctx_strings = NULL;
+ lttng_ust_context_info_tls.ctx_strings_len = 0;
+ (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
+ (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, file_name, file_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, thread_name, thread_name_cstr);
+ (*env)->ReleaseByteArrayElements(env, context_info_entries, context_info_entries_array, 0);
+ (*env)->ReleaseByteArrayElements(env, context_info_strings, context_info_strings_array, 0);
+}
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER lttng_log4j
+
+#if !defined(_TRACEPOINT_LTTNG_UST_LOG4J_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_LTTNG_UST_LOG4J_H
+
+#include <lttng/tracepoint.h>
+
+/*
+ * Tracepoint used by Java applications using the log4j log appender.
+ */
+TRACEPOINT_EVENT(lttng_log4j, event,
+ TP_ARGS(
+ const char *, msg,
+ const char *, logger_name,
+ const char *, class_name,
+ const char *, method_name,
+ const char *, file_name,
+ int, line_number,
+ long, timestamp,
+ int, log_level,
+ const char *, thread_name),
+ TP_FIELDS(
+ ctf_string(msg, msg)
+ ctf_string(logger_name, logger_name)
+ ctf_string(class_name, class_name)
+ ctf_string(method_name, method_name)
+ ctf_string(filename, file_name)
+ ctf_integer(int, line_number, line_number)
+ ctf_integer(long, timestamp, timestamp)
+ ctf_integer(int, int_loglevel, log_level)
+ ctf_string(thread_name, thread_name)
+ )
+)
+
+#endif /* _TRACEPOINT_LTTNG_UST_LOG4J_H */
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./lttng_ust_log4j.h"
+
+/* This part must be outside protection */
+#include <lttng/tracepoint-event.h>
--- /dev/null
+org_lttng_ust_LTTngUst.h
+org/
+liblttng-ust-java.jar
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#define _LGPL_SOURCE
+#include "org_lttng_ust_LTTngUst.h"
+
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_CREATE_PROBES
+#include "lttng_ust_java.h"
+
+JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointInt(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring ev_name,
+ jint payload)
+{
+ jboolean iscopy;
+ const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
+
+ tracepoint(lttng_ust_java, int_event, ev_name_cstr, payload);
+
+ (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
+}
+
+JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointIntInt(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring ev_name,
+ jint payload1,
+ jint payload2)
+{
+ jboolean iscopy;
+ const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
+
+ tracepoint(lttng_ust_java, int_int_event, ev_name_cstr, payload1, payload2);
+
+ (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
+}
+
+JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointLong(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring ev_name,
+ jlong payload)
+{
+ jboolean iscopy;
+ const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
+
+ tracepoint(lttng_ust_java, long_event, ev_name_cstr, payload);
+
+ (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
+}
+
+JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointLongLong(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring ev_name,
+ jlong payload1,
+ jlong payload2)
+{
+ jboolean iscopy;
+ const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
+
+ tracepoint(lttng_ust_java, long_long_event, ev_name_cstr, payload1, payload2);
+
+ (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
+}
+
+JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointString(JNIEnv *env,
+ jobject jobj __attribute__((unused)),
+ jstring ev_name,
+ jstring payload)
+{
+ jboolean iscopy;
+ const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
+ const char *payload_cstr = (*env)->GetStringUTFChars(env, payload, &iscopy);
+
+ tracepoint(lttng_ust_java, string_event, ev_name_cstr, payload_cstr);
+
+ (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
+ (*env)->ReleaseStringUTFChars(env, payload, payload_cstr);
+}
+
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-only
+
+JAVAROOT = .
+jarfile = liblttng-ust-java.jar
+jardir = $(datadir)/java
+pkgpath = org/lttng/ust
+
+dist_noinst_JAVA = $(pkgpath)/LTTngUst.java
+jar_DATA = $(jarfile)
+BUILT_SOURCES = org_lttng_ust_LTTngUst.h
+
+AM_CPPFLAGS += -I$(builddir) -I$(srcdir) $(JNI_CPPFLAGS)
+lib_LTLIBRARIES = liblttng-ust-java.la
+liblttng_ust_java_la_SOURCES = LTTngUst.c lttng_ust_java.h
+nodist_liblttng_ust_java_la_SOURCES = org_lttng_ust_LTTngUst.h
+
+liblttng_ust_java_la_LIBADD = -lc \
+ $(top_builddir)/src/liblttng-ust/liblttng-ust.la
+
+$(jarfile): classnoinst.stamp
+ $(JAR) cf $(JARFLAGS) $@ $(pkgpath)/*.class
+
+if !HAVE_JAVAH
+# If we don't have javah, assume we are running openjdk >= 10 and use javac
+# to generate the jni header file.
+AM_JAVACFLAGS = -h .
+
+org_lttng_ust_LTTngUst.h: $(jarfile)
+else
+org_lttng_ust_LTTngUst.h: jni-header.stamp
+
+jni-header.stamp: $(dist_noinst_JAVA)
+ $(JAVAH) -classpath $(srcdir) $(JAVAHFLAGS) org.lttng.ust.LTTngUst && \
+ echo "JNI header generated" > jni-header.stamp
+endif
+
+all-local: org_lttng_ust_LTTngUst.h
+
+EXTRA_DIST = README
+
+CLEANFILES = $(jarfile) $(pkgpath)/*.class jni-header.stamp org_lttng_ust_LTTngUst.h
--- /dev/null
+This directory contains a simple API for instrumenting java applications.
+
+Configuration examples to build this library:
+
+dependency: openjdk-7-jdk
+./configure --enable-jni-interface
+
+Note that the OpenJDK 7 is used for development and continuous integration thus
+we directly support that version for this library. However, it has been tested
+with OpenJDK 6 also. Please let us know if other Java version (commercial or
+not) work with this library.
+
+After building, you can use the liblttng-ust-java.jar file in a Java project.
+It requires the liblttng-ust-java.so* files (which get installed when doing
+`make install') so make sure those are in the linker's library path.
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER lttng_ust_java
+
+#if !defined(_TRACEPOINT_LTTNG_UST_JAVA_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_LTTNG_UST_JAVA_H
+
+#include <lttng/tracepoint.h>
+
+TRACEPOINT_EVENT(lttng_ust_java, int_event,
+ TP_ARGS(const char *, name, int, payload),
+ TP_FIELDS(
+ ctf_string(name, name)
+ ctf_integer(int, int_payload, payload)
+ )
+)
+
+TRACEPOINT_EVENT(lttng_ust_java, int_int_event,
+ TP_ARGS(const char *, name, int, payload1, int, payload2),
+ TP_FIELDS(
+ ctf_string(name, name)
+ ctf_integer(int, int_payload1, payload1)
+ ctf_integer(int, int_payload2, payload2)
+ )
+)
+
+TRACEPOINT_EVENT(lttng_ust_java, long_event,
+ TP_ARGS(const char *, name, long, payload),
+ TP_FIELDS(
+ ctf_string(name, name)
+ ctf_integer(long, long_payload, payload)
+ )
+)
+
+TRACEPOINT_EVENT(lttng_ust_java, long_long_event,
+ TP_ARGS(const char *, name, long, payload1, long, payload2),
+ TP_FIELDS(
+ ctf_string(name, name)
+ ctf_integer(long, long_payload1, payload1)
+ ctf_integer(long, long_payload2, payload2)
+ )
+)
+
+TRACEPOINT_EVENT(lttng_ust_java, string_event,
+ TP_ARGS(const char *, name, const char *, payload),
+ TP_FIELDS(
+ ctf_string(name, name)
+ ctf_string(string_payload, payload)
+ )
+)
+
+#endif /* _TRACEPOINT_LTTNG_UST_JAVA_H */
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./lttng_ust_java.h"
+
+/* This part must be outside protection */
+#include <lttng/tracepoint-event.h>
--- /dev/null
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2012 Alexandre Montplaisir <alexandre.montplaisir@polymtl.ca>
+ */
+
+package org.lttng.ust;
+
+/**
+ * This class implements the the Java side of the LTTng-UST Java interface.
+ *
+ * First, make sure you have installed "liblttng-ust-java.so" where the linker
+ * can find it. You can then call LTTngUst.init() from your Java program to
+ * connect the methods exposed here to the native library.
+ *
+ * Because of limitations in the probe declaration, all trace events generated
+ * by this library will have "lttng_ust_java" for domain, and "<type>_event" for
+ * event name in the CTF trace files. The "name" parameter will instead appear
+ * as the first element of the event's payload.
+ *
+ * @author Mathieu Desnoyers
+ * @author Alexandre Montplaisir
+ *
+ */
+public abstract class LTTngUst {
+
+ /**
+ * Initialize the UST tracer. This should always be called first, before any
+ * tracepoint* method.
+ */
+ public static void init() {
+ System.loadLibrary("lttng-ust-java"); //$NON-NLS-1$
+ }
+
+ /**
+ * Insert a tracepoint with a payload of type Integer.
+ *
+ * @param name
+ * The name assigned to this event. For best performance, this
+ * should be a statically-defined String, or a literal.
+ * @param payload
+ * The int payload
+ */
+ public static native void tracepointInt(String name, int payload);
+
+ /**
+ * Insert a tracepoint with a payload consisting of two integers.
+ *
+ * @param name
+ * The name assigned to this event. For best performance, this
+ * should be a statically-defined String, or a literal.
+ * @param payload1
+ * The first int payload
+ * @param payload2
+ * The second int payload
+ */
+ public static native void
+ tracepointIntInt(String name, int payload1, int payload2);
+
+ /**
+ * Insert a tracepoint with a payload of type Long
+ *
+ * @param name
+ * The name assigned to this event. For best performance, this
+ * should be a statically-defined String, or a literal.
+ * @param payload
+ * The long payload
+ */
+ public static native void tracepointLong(String name, long payload);
+
+ /**
+ * Insert a tracepoint with a payload consisting of two longs.
+ *
+ * @param name
+ * The name assigned to this event. For best performance, this
+ * should be a statically-defined String, or a literal.
+ * @param payload1
+ * The first long payload
+ * @param payload2
+ * The second long payload
+ */
+ public static native void
+ tracepointLongLong(String name, long payload1, long payload2);
+
+ /**
+ * Insert a tracepoint with a String payload.
+ *
+ * @param name
+ * The name assigned to this event. For best performance, this
+ * should be a statically-defined String, or a literal.
+ * @param payload
+ * The String payload
+ */
+ public static native void tracepointString(String name, String payload);
+
+}
+
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-SUBDIRS = java jni
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-SUBDIRS = lttng-ust-agent-common lttng-ust-agent-all
-
-if ENABLE_JAVA_AGENT_WITH_JUL
-SUBDIRS += lttng-ust-agent-jul
-endif
-
-if ENABLE_JAVA_AGENT_WITH_LOG4J
-SUBDIRS += lttng-ust-agent-log4j
-endif
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-JAVAROOT = .
-
-jarfile_version = 1.0.0
-jarfile_manifest = $(srcdir)/Manifest.txt
-jarfile_symlink = lttng-ust-agent-all.jar
-jarfile = lttng-ust-agent-all-$(jarfile_version).jar
-
-# Compatibility symlink provided for applications expecting the agent
-# jar file installed by UST 2.7.
-jarfile_compat_symlink = liblttng-ust-agent.jar
-
-jardir = $(datadir)/java
-
-dist_noinst_DATA = $(jarfile_manifest)
-
-jar_DATA = $(jarfile)
-
-$(jarfile):
- $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) \
- && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink) \
- && rm -f $(jarfile_compat_symlink) && $(LN_S) $(jarfile_symlink) $(jarfile_compat_symlink)
-
-install-data-hook:
- cd $(DESTDIR)/$(jardir) \
- && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink) \
- && rm -f $(jarfile_compat_symlink) && $(LN_S) $(jarfile_symlink) $(jarfile_compat_symlink)
-
-uninstall-hook:
- cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && rm -f $(jarfile_compat_symlink)
-
-CLEANFILES = *.jar
+++ /dev/null
-Name: org/lttng/ust/agent/all/
-Specification-Title: LTTng UST All Java Agents
-Specification-Version: 1.0.0
-Specification-Vendor: LTTng Project
-Implementation-Title: org.lttng.ust.agent.all
-Implementation-Version: 1.0.0
-Implementation-Vendor: LTTng Project
-Class-Path: lttng-ust-agent-common.jar lttng-ust-agent-jul.jar lttng-ust-agent-log4j.jar
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-JAVAROOT = .
-
-pkgpath = org/lttng/ust/agent
-
-jarfile_version = 1.0.0
-jarfile_manifest = $(srcdir)/Manifest.txt
-jarfile_symlink = lttng-ust-agent-common.jar
-jarfile = lttng-ust-agent-common-$(jarfile_version).jar
-
-jardir = $(datadir)/java
-jnioutdir = ../../jni/common
-
-dist_noinst_JAVA = $(pkgpath)/AbstractLttngAgent.java \
- $(pkgpath)/EventNamePattern.java \
- $(pkgpath)/ILttngAgent.java \
- $(pkgpath)/ILttngHandler.java \
- $(pkgpath)/LTTngAgent.java \
- $(pkgpath)/client/ILttngTcpClientListener.java \
- $(pkgpath)/client/SessiondCommand.java \
- $(pkgpath)/client/LttngAgentResponse.java \
- $(pkgpath)/client/LttngTcpSessiondClient.java \
- $(pkgpath)/client/SessiondCommandHeader.java \
- $(pkgpath)/client/SessiondDisableAppContextCommand.java \
- $(pkgpath)/client/SessiondDisableEventCommand.java \
- $(pkgpath)/client/SessiondEnableAppContextCommand.java \
- $(pkgpath)/client/SessiondEnableEventCommand.java \
- $(pkgpath)/client/SessiondListLoggersCommand.java \
- $(pkgpath)/context/LttngContextApi.java \
- $(pkgpath)/context/ContextInfoManager.java \
- $(pkgpath)/context/ContextInfoSerializer.java \
- $(pkgpath)/context/IContextInfoRetriever.java \
- $(pkgpath)/filter/FilterChangeNotifier.java \
- $(pkgpath)/filter/IFilterChangeListener.java \
- $(pkgpath)/session/EventRule.java \
- $(pkgpath)/session/LogLevelSelector.java \
- $(pkgpath)/utils/LttngUstAgentLogger.java
-
-
-dist_noinst_DATA = $(jarfile_manifest)
-
-jar_DATA = $(jarfile)
-
-classes = $(pkgpath)/*.class \
- $(pkgpath)/client/*.class \
- $(pkgpath)/context/*.class \
- $(pkgpath)/filter/*.class \
- $(pkgpath)/session/*.class \
- $(pkgpath)/utils/*.class
-
-$(jarfile): classnoinst.stamp
- $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink)
-
-if !HAVE_JAVAH
-# If we don't have javah, assume we are running openjdk >= 10 and use javac
-# to generate the jni header file.
-AM_JAVACFLAGS = -h $(jnioutdir)
-else
-context-jni-header.stamp: $(dist_noinst_JAVA)
- $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(jnioutdir) $(JAVAHFLAGS) org.lttng.ust.agent.context.LttngContextApi && \
- echo "Context API JNI header generated" > context-jni-header.stamp
-
-all-local: context-jni-header.stamp
-endif
-
-install-data-hook:
- cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink)
-
-uninstall-hook:
- cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink)
-
-CLEANFILES = *.jar \
- $(pkgpath)/*.class \
- $(pkgpath)/client/*.class \
- $(pkgpath)/context/*.class \
- $(pkgpath)/filter/*.class \
- $(pkgpath)/session/*.class \
- $(pkgpath)/utils/*.class \
- context-jni-header.stamp \
- $(jnioutdir)/org_lttng_ust_agent_context_LttngContextApi.h
+++ /dev/null
-Name: org/lttng/ust/agent/
-Specification-Title: LTTng UST Java Agent
-Specification-Version: 1.0.0
-Specification-Vendor: LTTng Project
-Implementation-Title: org.lttng.ust.agent
-Implementation-Version: 1.0.0
-Implementation-Vendor: LTTng Project
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.regex.Matcher;
-
-import org.lttng.ust.agent.client.ILttngTcpClientListener;
-import org.lttng.ust.agent.client.LttngTcpSessiondClient;
-import org.lttng.ust.agent.filter.FilterChangeNotifier;
-import org.lttng.ust.agent.session.EventRule;
-import org.lttng.ust.agent.utils.LttngUstAgentLogger;
-
-/**
- * Base implementation of a {@link ILttngAgent}.
- *
- * @author Alexandre Montplaisir
- * @param <T>
- * The type of logging handler that should register to this agent
- */
-public abstract class AbstractLttngAgent<T extends ILttngHandler>
- implements ILttngAgent<T>, ILttngTcpClientListener {
-
- private static final int INIT_TIMEOUT = 3; /* Seconds */
-
- /** The handlers registered to this agent */
- private final Set<T> registeredHandlers = new HashSet<T>();
-
- /**
- * The trace events currently enabled in the sessions.
- *
- * The key is the {@link EventNamePattern} that comes from the event name.
- * The value is the ref count (how many different sessions currently have
- * this event enabled). Once the ref count falls to 0, this means we can
- * avoid sending log events through JNI because nobody wants them.
- *
- * Its accesses should be protected by the {@link #enabledEventNamesLock}
- * below.
- */
- private final Map<EventNamePattern, Integer> enabledPatterns = new HashMap<EventNamePattern, Integer>();
-
- /**
- * Cache of already-checked event names. As long as enabled/disabled events
- * don't change in the session, we can avoid re-checking events that were
- * previously checked against all known enabled patterns.
- *
- * Its accesses should be protected by the {@link #enabledEventNamesLock}
- * below, with the exception of concurrent get operations.
- */
- private final Map<String, Boolean> enabledEventNamesCache = new ConcurrentHashMap<String, Boolean>();
-
- /**
- * Lock protecting accesses to the {@link #enabledPatterns} and
- * {@link #enabledEventNamesCache} maps.
- */
- private final Lock enabledEventNamesLock = new ReentrantLock();
-
- /**
- * The application contexts currently enabled in the tracing sessions.
- *
- * It is first indexed by context retriever, then by context name. This
- * allows to efficiently query all the contexts for a given retriever.
- *
- * Works similarly as {@link #enabledEvents}, but for app contexts (and with
- * an extra degree of indexing).
- *
- * TODO Could be changed to a Guava Table once/if we start using it.
- */
- private final Map<String, Map<String, Integer>> enabledAppContexts = new ConcurrentHashMap<String, Map<String, Integer>>();
-
- /** Tracing domain. Defined by the sub-classes via the constructor. */
- private final Domain domain;
-
- /* Lazy-loaded sessiond clients and their thread objects */
- private LttngTcpSessiondClient rootSessiondClient = null;
- private LttngTcpSessiondClient userSessiondClient = null;
- private Thread rootSessiondClientThread = null;
- private Thread userSessiondClientThread = null;
-
- /** Indicates if this agent has been initialized. */
- private boolean initialized = false;
-
- /**
- * Constructor. Should only be called by sub-classes via super(...);
- *
- * @param domain
- * The tracing domain of this agent.
- */
- protected AbstractLttngAgent(Domain domain) {
- this.domain = domain;
- }
-
- @Override
- public Domain getDomain() {
- return domain;
- }
-
- @Override
- public void registerHandler(T handler) {
- synchronized (registeredHandlers) {
- if (registeredHandlers.isEmpty()) {
- /*
- * This is the first handler that registers, we will initialize
- * the agent.
- */
- init();
- }
- registeredHandlers.add(handler);
- }
- }
-
- @Override
- public void unregisterHandler(T handler) {
- synchronized (registeredHandlers) {
- registeredHandlers.remove(handler);
- if (registeredHandlers.isEmpty()) {
- /* There are no more registered handlers, close the connection. */
- dispose();
- }
- }
- }
-
- private void init() {
- /*
- * Only called from a synchronized (registeredHandlers) block, should
- * not need additional synchronization.
- */
- if (initialized) {
- return;
- }
-
- LttngUstAgentLogger.log(AbstractLttngAgent.class, "Initializing Agent for domain: " + domain.name());
-
- String rootClientThreadName = "Root sessiond client started by agent: " + this.getClass().getSimpleName();
-
- rootSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), true);
- rootSessiondClientThread = new Thread(rootSessiondClient, rootClientThreadName);
- rootSessiondClientThread.setDaemon(true);
- rootSessiondClientThread.start();
-
- String userClientThreadName = "User sessiond client started by agent: " + this.getClass().getSimpleName();
-
- userSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), false);
- userSessiondClientThread = new Thread(userSessiondClient, userClientThreadName);
- userSessiondClientThread.setDaemon(true);
- userSessiondClientThread.start();
-
- /* Give the threads' registration a chance to end. */
- if (!rootSessiondClient.waitForConnection(INIT_TIMEOUT)) {
- userSessiondClient.waitForConnection(INIT_TIMEOUT);
- }
-
- initialized = true;
- }
-
- /**
- * Dispose the agent
- */
- private void dispose() {
- LttngUstAgentLogger.log(AbstractLttngAgent.class, "Disposing Agent for domain: " + domain.name());
-
- /*
- * Only called from a synchronized (registeredHandlers) block, should
- * not need additional synchronization.
- */
- rootSessiondClient.close();
- userSessiondClient.close();
-
- try {
- rootSessiondClientThread.join();
- userSessiondClientThread.join();
-
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- rootSessiondClient = null;
- rootSessiondClientThread = null;
- userSessiondClient = null;
- userSessiondClientThread = null;
-
- /*
- * Send filter change notifications for all event rules currently
- * active, then clear them.
- */
- FilterChangeNotifier fcn = FilterChangeNotifier.getInstance();
-
- enabledEventNamesLock.lock();
- try {
- for (Map.Entry<EventNamePattern, Integer> entry : enabledPatterns.entrySet()) {
- String eventName = entry.getKey().getEventName();
- Integer nb = entry.getValue();
- for (int i = 0; i < nb.intValue(); i++) {
- fcn.removeEventRules(eventName);
- }
- }
- enabledPatterns.clear();
- enabledEventNamesCache.clear();
- } finally {
- enabledEventNamesLock.unlock();
- }
-
- /*
- * Also clear tracked app contexts (no filter notifications sent for
- * those currently).
- */
- enabledAppContexts.clear();
-
- initialized = false;
- }
-
- @Override
- public boolean eventEnabled(EventRule eventRule) {
- /* Notify the filter change manager of the command */
- FilterChangeNotifier.getInstance().addEventRule(eventRule);
-
- String eventName = eventRule.getEventName();
- EventNamePattern pattern = new EventNamePattern(eventName);
-
- enabledEventNamesLock.lock();
- try {
- boolean ret = incrementRefCount(pattern, enabledPatterns);
- enabledEventNamesCache.clear();
- return ret;
- } finally {
- enabledEventNamesLock.unlock();
- }
- }
-
- @Override
- public boolean eventDisabled(String eventName) {
- /* Notify the filter change manager of the command */
- FilterChangeNotifier.getInstance().removeEventRules(eventName);
-
- EventNamePattern pattern = new EventNamePattern(eventName);
-
- enabledEventNamesLock.lock();
- try {
- boolean ret = decrementRefCount(pattern, enabledPatterns);
- enabledEventNamesCache.clear();
- return ret;
- } finally {
- enabledEventNamesLock.unlock();
- }
- }
-
- @Override
- public boolean appContextEnabled(String contextRetrieverName, String contextName) {
- synchronized (enabledAppContexts) {
- Map<String, Integer> retrieverMap = enabledAppContexts.get(contextRetrieverName);
- if (retrieverMap == null) {
- /* There is no submap for this retriever, let's create one. */
- retrieverMap = new ConcurrentHashMap<String, Integer>();
- enabledAppContexts.put(contextRetrieverName, retrieverMap);
- }
-
- return incrementRefCount(contextName, retrieverMap);
- }
- }
-
- @Override
- public boolean appContextDisabled(String contextRetrieverName, String contextName) {
- synchronized (enabledAppContexts) {
- Map<String, Integer> retrieverMap = enabledAppContexts.get(contextRetrieverName);
- if (retrieverMap == null) {
- /* There was no submap for this retriever, invalid command? */
- return false;
- }
-
- boolean ret = decrementRefCount(contextName, retrieverMap);
-
- /* If the submap is now empty we can remove it from the main map. */
- if (retrieverMap.isEmpty()) {
- enabledAppContexts.remove(contextRetrieverName);
- }
-
- return ret;
- }
- }
-
- /*
- * Implementation of this method is domain-specific.
- */
- @Override
- public abstract Collection<String> listAvailableEvents();
-
- @Override
- public boolean isEventEnabled(String eventName) {
- Boolean cachedEnabled = enabledEventNamesCache.get(eventName);
- if (cachedEnabled != null) {
- /* We have seen this event previously */
- /*
- * Careful! enabled == null could also mean that the null value is
- * associated with the key. But we should have never inserted null
- * values in the map.
- */
- return cachedEnabled.booleanValue();
- }
-
- /*
- * We have not previously checked this event. Run it against all known
- * enabled event patterns to determine if it should pass or not.
- */
- enabledEventNamesLock.lock();
- try {
- boolean enabled = false;
- for (EventNamePattern enabledPattern : enabledPatterns.keySet()) {
- Matcher matcher = enabledPattern.getPattern().matcher(eventName);
- if (matcher.matches()) {
- enabled = true;
- break;
- }
- }
-
- /* Add the result to the cache */
- enabledEventNamesCache.put(eventName, Boolean.valueOf(enabled));
- return enabled;
-
- } finally {
- enabledEventNamesLock.unlock();
- }
- }
-
- @Override
- public Collection<Map.Entry<String, Map<String, Integer>>> getEnabledAppContexts() {
- return enabledAppContexts.entrySet();
- }
-
- private static <T> boolean incrementRefCount(T key, Map<T, Integer> refCountMap) {
- synchronized (refCountMap) {
- Integer count = refCountMap.get(key);
- if (count == null) {
- /* This is the first instance of this event being enabled */
- refCountMap.put(key, Integer.valueOf(1));
- return true;
- }
- if (count.intValue() <= 0) {
- /* It should not have been in the map in the first place! */
- throw new IllegalStateException();
- }
- /* The event was already enabled, increment its refcount */
- refCountMap.put(key, Integer.valueOf(count.intValue() + 1));
- return true;
- }
- }
-
- private static <T> boolean decrementRefCount(T key, Map<T, Integer> refCountMap) {
- synchronized (refCountMap) {
- Integer count = refCountMap.get(key);
- if (count == null || count.intValue() <= 0) {
- /*
- * The sessiond asked us to disable an event that was not
- * enabled previously. Command error?
- */
- return false;
- }
- if (count.intValue() == 1) {
- /*
- * This is the last instance of this event being disabled,
- * remove it from the map so that we stop sending it.
- */
- refCountMap.remove(key);
- return true;
- }
- /*
- * Other sessions are still looking for this event, simply decrement
- * its refcount.
- */
- refCountMap.put(key, Integer.valueOf(count.intValue() - 1));
- return true;
- }
- }
-}
-
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2017 EfficiOS Inc.
- * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-package org.lttng.ust.agent;
-
-import java.util.regex.Pattern;
-
-/**
- * Class encapsulating an event name from the session daemon, and its
- * corresponding {@link Pattern}. This allows referring back to the original
- * event name, for example when we receive a disable command.
- *
- * @author Philippe Proulx
- * @author Alexandre Montplaisir
- */
-class EventNamePattern {
-
- private final String originalEventName;
-
- /*
- * Note that two Patterns coming from the exact same String will not be
- * equals()! As such, it would be confusing to make the pattern part of this
- * class's equals/hashCode
- */
- private final transient Pattern pattern;
-
- public EventNamePattern(String eventName) {
- if (eventName == null) {
- throw new IllegalArgumentException();
- }
-
- originalEventName = eventName;
- pattern = patternFromEventName(eventName);
- }
-
- public String getEventName() {
- return originalEventName;
- }
-
- public Pattern getPattern() {
- return pattern;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + originalEventName.hashCode();
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- EventNamePattern other = (EventNamePattern) obj;
- if (!originalEventName.equals(other.originalEventName)) {
- return false;
- }
- return true;
- }
-
- private static Pattern patternFromEventName(String eventName) {
- /*
- * The situation here is that `\*` means a literal `*` in the event
- * name, and `*` is a wildcard star. We check the event name one
- * character at a time and create a list of tokens to be converter to
- * partial patterns.
- */
- StringBuilder bigBuilder = new StringBuilder("^");
- StringBuilder smallBuilder = new StringBuilder();
-
- for (int i = 0; i < eventName.length(); i++) {
- char c = eventName.charAt(i);
-
- switch (c) {
- case '*':
- /* Add current quoted builder's string if not empty. */
- if (smallBuilder.length() > 0) {
- bigBuilder.append(Pattern.quote(smallBuilder.toString()));
- smallBuilder.setLength(0);
- }
-
- /* Append the equivalent regex which is `.*`. */
- bigBuilder.append(".*");
- continue;
-
- case '\\':
- /* We only escape `*` and `\` here. */
- if (i < (eventName.length() - 1)) {
- char nextChar = eventName.charAt(i + 1);
-
- if (nextChar == '*' || nextChar == '\\') {
- smallBuilder.append(nextChar);
- } else {
- smallBuilder.append(c);
- smallBuilder.append(nextChar);
- }
-
- i++;
- continue;
- }
- break;
-
- default:
- break;
- }
-
- smallBuilder.append(c);
- }
-
- /* Add current quoted builder's string if not empty. */
- if (smallBuilder.length() > 0) {
- bigBuilder.append(Pattern.quote(smallBuilder.toString()));
- }
-
- bigBuilder.append("$");
-
- return Pattern.compile(bigBuilder.toString());
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Interface to define LTTng Java agents.
- *
- * An "agent" is a representative of an LTTng session daemon in the Java world.
- * It tracks the settings of a tracing session as they defined in the session
- * daemon.
- *
- * It also track the current logging handlers that are sending events to UST.
- *
- * @author Alexandre Montplaisir
- *
- * @param <T>
- * The type of logging handler that should register to this agent
- */
-public interface ILttngAgent<T extends ILttngHandler> {
-
- // ------------------------------------------------------------------------
- // Agent configuration elements
- // ------------------------------------------------------------------------
-
- /**
- * Tracing domains. Corresponds to domains defined by LTTng Tools.
- */
- enum Domain {
- JUL(3), LOG4J(4);
- private int value;
-
- private Domain(int value) {
- this.value = value;
- }
-
- public int value() {
- return value;
- }
- }
-
- /**
- * The tracing domain of this agent.
- *
- * @return The tracing domain.
- */
- Domain getDomain();
-
- // ------------------------------------------------------------------------
- // Log handler registering
- // ------------------------------------------------------------------------
-
- /**
- * Register a handler to this agent.
- *
- * @param handler
- * The handler to register
- */
- void registerHandler(T handler);
-
- /**
- * Deregister a handler from this agent.
- *
- * @param handler
- * The handler to deregister.
- */
- void unregisterHandler(T handler);
-
- // ------------------------------------------------------------------------
- // Tracing session parameters
- // ------------------------------------------------------------------------
-
- /**
- * Query if a given event is currently enabled in a current tracing session,
- * meaning it should be sent to UST.
- *
- * @param eventName
- * The name of the event to check.
- * @return True if the event is currently enabled, false if it is not.
- */
- boolean isEventEnabled(String eventName);
-
- /**
- * Return the list of application contexts enabled in the tracing sessions.
- *
- * @return The application contexts, first indexed by retriever name, then
- * by context name
- */
- Collection<Map.Entry<String, Map<String, Integer>>> getEnabledAppContexts();
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent;
-
-/**
- * Simple interface to organize all LTTng log handlers under one type.
- *
- * @author Alexandre Montplaisir
- */
-public interface ILttngHandler {
-
- /**
- * Get the number of events logged by this handler since its inception.
- *
- * @return The number of logged events
- */
- long getEventCount();
-
- /**
- * Close the log handler. Should be called once the application is done
- * logging through it.
- */
- void close();
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.logging.Handler;
-import java.util.logging.Logger;
-
-/**
- * The central agent managing the JUL and Log4j handlers.
- *
- * @author David Goulet
- * @deprecated Applications are now expected to manage their Logger and Handler
- * objects.
- */
-@Deprecated
-public class LTTngAgent {
-
- private static LTTngAgent instance = null;
-
- /**
- * Public getter to acquire a reference to this singleton object.
- *
- * @return The agent instance
- */
- public static synchronized LTTngAgent getLTTngAgent() {
- if (instance == null) {
- instance = new LTTngAgent();
- }
- return instance;
- }
-
- /**
- * Dispose the agent. Applications should call this once they are done
- * logging. This dispose function is non-static for backwards
- * compatibility purposes.
- */
- @SuppressWarnings("static-method")
- public void dispose() {
- synchronized (LTTngAgent.class) {
- if (instance != null) {
- instance.disposeInstance();
- instance = null;
- }
- }
- return;
- }
-
- private ILttngHandler julHandler = null;
- private ILttngHandler log4jAppender = null;
-
- /**
- * Private constructor. This is a singleton and a reference should be
- * acquired using {@link #getLTTngAgent()}.
- */
- private LTTngAgent() {
- initJulHandler();
- initLog4jAppender();
- }
-
- /**
- * "Destructor" method.
- */
- private void disposeInstance() {
- disposeJulHandler();
- disposeLog4jAppender();
- }
-
- /**
- * Create a LTTng-JUL handler, and attach it to the JUL root logger.
- */
- private void initJulHandler() {
- try {
- Class<?> julHandlerClass = Class.forName("org.lttng.ust.agent.jul.LttngLogHandler");
- /*
- * It is safer to use Constructor.newInstance() rather than
- * Class.newInstance(), because it will catch the exceptions thrown
- * by the constructor below (which happens if the Java library is
- * present, but the matching JNI one is not).
- */
- Constructor<?> julHandlerCtor = julHandlerClass.getConstructor();
- julHandler = (ILttngHandler) julHandlerCtor.newInstance();
-
- /* Attach the handler to the root JUL logger */
- Logger.getLogger("").addHandler((Handler) julHandler);
-
- /*
- * If any of the following exceptions happen, it means we could not
- * find or initialize LTTng JUL classes. We will not setup LTTng JUL
- * tracing in this case.
- */
- } catch (SecurityException e) {
- } catch (IllegalAccessException e) {
- } catch (IllegalArgumentException e) {
- } catch (ClassNotFoundException e) {
- } catch (NoSuchMethodException e) {
- } catch (InstantiationException e) {
- } catch (InvocationTargetException e) {
- }
- }
-
- /**
- * Create a LTTng-logj4 appender, and attach it to the log4j root logger.
- */
- private void initLog4jAppender() {
- /*
- * Since Log4j is a 3rd party library, we first need to check if we can
- * load any of its classes.
- */
- if (!testLog4jClasses()) {
- return;
- }
-
- try {
- Class<?> log4jAppenderClass = Class.forName("org.lttng.ust.agent.log4j.LttngLogAppender");
- Constructor<?> log4jAppendCtor = log4jAppenderClass.getConstructor();
- log4jAppender = (ILttngHandler) log4jAppendCtor.newInstance();
-
- /*
- * If any of the following exceptions happen, it means we could not
- * find or initialize LTTng log4j classes. We will not setup LTTng
- * log4j tracing in this case.
- */
- } catch (SecurityException e) {
- return;
- } catch (ClassNotFoundException e) {
- return;
- } catch (NoSuchMethodException e) {
- return;
- } catch (IllegalArgumentException e) {
- return;
- } catch (InstantiationException e) {
- return;
- } catch (IllegalAccessException e) {
- return;
- } catch (InvocationTargetException e) {
- return;
- }
-
- /*
- * Attach the appender to the root Log4j logger. Slightly more tricky
- * here, as log4j.Logger is not in the base Java library, and we do not
- * want the "common" package to depend on log4j. So we have to obtain it
- * through reflection too.
- */
- try {
- Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
- Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
-
- Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
- Method addAppenderMethod = loggerClass.getMethod("addAppender", appenderClass);
-
- Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
- addAppenderMethod.invoke(rootLogger, log4jAppender);
-
- /*
- * We have checked for the log4j library version previously, none of
- * the following exceptions should happen.
- */
- } catch (SecurityException e) {
- throw new IllegalStateException(e);
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException(e);
- } catch (NoSuchMethodException e) {
- throw new IllegalStateException(e);
- } catch (IllegalArgumentException e) {
- throw new IllegalStateException(e);
- } catch (IllegalAccessException e) {
- throw new IllegalStateException(e);
- } catch (InvocationTargetException e) {
- throw new IllegalStateException(e);
- }
- }
-
- /**
- * Check if log4j >= 1.2.15 library is present.
- */
- private static boolean testLog4jClasses() {
- Class<?> loggingEventClass;
-
- try {
- loggingEventClass = Class.forName("org.apache.log4j.spi.LoggingEvent");
- } catch (ClassNotFoundException e) {
- /*
- * Log4j classes not found, no need to create the relevant objects
- */
- return false;
- }
-
- /*
- * Detect capabilities of the log4j library. We only support log4j >=
- * 1.2.15. The getTimeStamp() method was introduced in log4j 1.2.15, so
- * verify that it is available.
- *
- * We can't rely on the getPackage().getImplementationVersion() call
- * that would retrieves information from the manifest file found in the
- * JAR since the manifest file shipped from upstream is known to be
- * broken in several versions of the library.
- *
- * More info: https://issues.apache.org/bugzilla/show_bug.cgi?id=44370
- */
- try {
- loggingEventClass.getDeclaredMethod("getTimeStamp");
- } catch (NoSuchMethodException e) {
- System.err.println(
- "Warning: The loaded log4j library is too old. Log4j tracing with LTTng will be disabled.");
- return false;
- } catch (SecurityException e) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Detach the JUL handler from its logger and close it.
- */
- private void disposeJulHandler() {
- if (julHandler == null) {
- /* The JUL handler was not activated, we have nothing to do */
- return;
- }
- Logger.getLogger("").removeHandler((Handler) julHandler);
- julHandler.close();
- julHandler = null;
- }
-
- /**
- * Detach the log4j appender from its logger and close it.
- */
- private void disposeLog4jAppender() {
- if (log4jAppender == null) {
- /* The log4j appender was not active, we have nothing to do */
- return;
- }
-
- /*
- * Detach the appender from the log4j root logger. Again, we have to do
- * this via reflection.
- */
- try {
- Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
- Class<?> appenderClass = Class.forName("org.apache.log4j.Appender");
-
- Method getRootLoggerMethod = loggerClass.getMethod("getRootLogger", (Class<?>[]) null);
- Method removeAppenderMethod = loggerClass.getMethod("removeAppender", appenderClass);
-
- Object rootLogger = getRootLoggerMethod.invoke(null, (Object[]) null);
- removeAppenderMethod.invoke(rootLogger, log4jAppender);
-
- /*
- * We were able to attach the appender previously, we should not
- * have problems here either!
- */
- } catch (SecurityException e) {
- throw new IllegalStateException(e);
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException(e);
- } catch (NoSuchMethodException e) {
- throw new IllegalStateException(e);
- } catch (IllegalArgumentException e) {
- throw new IllegalStateException(e);
- } catch (IllegalAccessException e) {
- throw new IllegalStateException(e);
- } catch (InvocationTargetException e) {
- throw new IllegalStateException(e);
- }
-
- /* Close the appender */
- log4jAppender.close();
- log4jAppender = null;
- }
-
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.util.Collection;
-
-import org.lttng.ust.agent.session.EventRule;
-
-/**
- * TCP client listener interface.
- *
- * This interface contains callbacks that are called when the TCP client
- * receives commands from the session daemon. These callbacks will define what
- * do to with each command.
- *
- * @author Alexandre Montplaisir
- */
-public interface ILttngTcpClientListener {
-
- /**
- * Callback for the TCP client to notify the listener agent that a request
- * for enabling an event rule was sent from the session daemon.
- *
- * @param eventRule
- * The event rule that was requested to be enabled
- * @return Since we do not track individual sessions, right now this command
- * cannot fail. It will always return true.
- */
- boolean eventEnabled(EventRule eventRule);
-
- /**
- * Callback for the TCP client to notify the listener agent that a request
- * for disabling an event was sent from the session daemon.
- *
- * @param eventName
- * The name of the event that was requested to be disabled.
- * @return True if the command completed successfully, false if we should
- * report an error (event was not enabled, etc.)
- */
- boolean eventDisabled(String eventName);
-
- /**
- * Callback for the TCP client to notify the listener agent that a request
- * for enabling an application-specific context was sent from the session
- * daemon.
- *
- * @param contextRetrieverName
- * The name of the retriever in which the context is present.
- * This is used to namespace the contexts.
- * @param contextName
- * The name of the context that was requested to be enabled
- * @return Since we do not track individual sessions, right now this command
- * cannot fail. It will always return true.
- */
- boolean appContextEnabled(String contextRetrieverName, String contextName);
-
- /**
- * Callback for the TCP client to notify the listener agent that a request
- * for disabling an application-specific context was sent from the session
- * daemon.
- *
- * @param contextRetrieverName
- * The name of the retriever in which the context is present.
- * This is used to namespace the contexts.
- * @param contextName
- * The name of the context that was requested to be disabled.
- * @return True if the command completed successfully, false if we should
- * report an error (context was not previously enabled for example)
- */
- boolean appContextDisabled(String contextRetrieverName, String contextName);
-
- /**
- * List the events that are available in the agent's tracing domain.
- *
- * In Java terms, this means loggers that have at least one LTTng log
- * handler of their corresponding domain attached.
- *
- * @return The list of available events
- */
- Collection<String> listAvailableEvents();
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * Interface for all response messages sent from the Java agent to the sessiond
- * daemon. Normally sent after a command coming from the session daemon was
- * executed.
- *
- * @author Alexandre Montplaisir
- */
-abstract class LttngAgentResponse {
-
- private static final int INT_SIZE = 4;
-
- public static final LttngAgentResponse SUCESS_RESPONSE = new LttngAgentResponse() {
- @Override
- public ReturnCode getReturnCode() {
- return ReturnCode.CODE_SUCCESS_CMD;
- }
- };
-
- public static final LttngAgentResponse FAILURE_RESPONSE = new LttngAgentResponse() {
- @Override
- public ReturnCode getReturnCode() {
- return ReturnCode.CODE_INVALID_CMD;
- }
- };
-
- /**
- * Return codes used in agent responses, to indicate success or different
- * types of failures of the commands.
- */
- protected enum ReturnCode {
-
- CODE_SUCCESS_CMD(1, "sucess"),
- CODE_INVALID_CMD(2, "invalid"),
- CODE_UNKNOWN_LOGGER_NAME(3, "unknown logger name");
-
- private final int code;
- private final String toString;
-
- private ReturnCode(int c, String str) {
- code = c;
- toString = str;
- }
-
- public int getCode() {
- return code;
- }
-
- /**
- * Mainly used for debugging. The strings are not sent through the
- * socket.
- */
- @Override
- public String toString() {
- return toString;
- }
- }
-
- /**
- * Get the {@link ReturnCode} that goes with this response. It is expected
- * by the session daemon, but some commands may require more than this
- * in their response.
- *
- * @return The return code
- */
- public abstract ReturnCode getReturnCode();
-
- /**
- * Gets a byte array of the response so that it may be streamed.
- *
- * @return The byte array of the response
- */
- public byte[] getBytes() {
- byte data[] = new byte[INT_SIZE];
- ByteBuffer buf = ByteBuffer.wrap(data);
- buf.order(ByteOrder.BIG_ENDIAN);
- buf.putInt(getReturnCode().getCode());
- return data;
- }
-
- @Override
- public String toString() {
- return "LttngAgentResponse["
- + "code=" + getReturnCode().getCode()
- + ", " + getReturnCode().toString()
- + "]";
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015-2016 EfficiOS Inc.
- * Copyright (C) 2015-2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.io.BufferedReader;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.lang.management.ManagementFactory;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.lttng.ust.agent.utils.LttngUstAgentLogger;
-
-/**
- * Client for agents to connect to a local session daemon, using a TCP socket.
- *
- * @author David Goulet
- */
-public class LttngTcpSessiondClient implements Runnable {
-
- private static final String SESSION_HOST = "127.0.0.1";
- private static final String ROOT_PORT_FILE = "/var/run/lttng/agent.port";
- private static final String USER_PORT_FILE = "/.lttng/agent.port";
- private static final Charset PORT_FILE_ENCODING = Charset.forName("UTF-8");
-
- private static final int PROTOCOL_MAJOR_VERSION = 2;
- private static final int PROTOCOL_MINOR_VERSION = 0;
-
- /** Command header from the session deamon. */
- private final CountDownLatch registrationLatch = new CountDownLatch(1);
-
- private Socket sessiondSock;
- private volatile boolean quit = false;
-
- private DataInputStream inFromSessiond;
- private DataOutputStream outToSessiond;
-
- private final ILttngTcpClientListener logAgent;
- private final int domainValue;
- private final boolean isRoot;
-
- /**
- * Constructor
- *
- * @param logAgent
- * The listener this client will operate on, typically an LTTng
- * agent.
- * @param domainValue
- * The integer to send to the session daemon representing the
- * tracing domain to handle.
- * @param isRoot
- * True if this client should connect to the root session daemon,
- * false if it should connect to the user one.
- */
- public LttngTcpSessiondClient(ILttngTcpClientListener logAgent, int domainValue, boolean isRoot) {
- this.logAgent = logAgent;
- this.domainValue = domainValue;
- this.isRoot = isRoot;
- }
-
- /**
- * Wait until this client has successfully established a connection to its
- * target session daemon.
- *
- * @param seconds
- * A timeout in seconds after which this method will return
- * anyway.
- * @return True if the the client actually established the connection, false
- * if we returned because the timeout has elapsed or the thread was
- * interrupted.
- */
- public boolean waitForConnection(int seconds) {
- try {
- return registrationLatch.await(seconds, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- return false;
- }
- }
-
- @Override
- public void run() {
- for (;;) {
- if (this.quit) {
- break;
- }
-
- try {
-
- /*
- * Connect to the session daemon before anything else.
- */
- log("Connecting to sessiond");
- connectToSessiond();
-
- /*
- * Register to the session daemon as the Java component of the
- * UST application.
- */
- log("Registering to sessiond");
- registerToSessiond();
-
- /*
- * Block on socket receive and wait for command from the
- * session daemon. This will return if and only if there is a
- * fatal error or the socket closes.
- */
- log("Waiting on sessiond commands...");
- handleSessiondCmd();
- } catch (UnknownHostException uhe) {
- uhe.printStackTrace();
- /*
- * Terminate agent thread.
- */
- close();
- } catch (IOException ioe) {
- /*
- * I/O exception may have been triggered by a session daemon
- * closing the socket. Close our own socket and
- * retry connecting after a delay.
- */
- try {
- if (this.sessiondSock != null) {
- this.sessiondSock.close();
- }
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- /*
- * Retry immediately if sleep is interrupted.
- */
- } catch (IOException closeioe) {
- closeioe.printStackTrace();
- /*
- * Terminate agent thread.
- */
- close();
- }
- }
- }
- }
-
- /**
- * Dispose this client and close any socket connection it may hold.
- */
- public void close() {
- log("Closing client");
- this.quit = true;
-
- try {
- if (this.sessiondSock != null) {
- this.sessiondSock.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- private void connectToSessiond() throws IOException {
- int rootPort = getPortFromFile(ROOT_PORT_FILE);
- int userPort = getPortFromFile(getHomePath() + USER_PORT_FILE);
-
- /*
- * Check for the edge case of both files existing but pointing to the
- * same port. In this case, let the root client handle it.
- */
- if ((rootPort != 0) && (rootPort == userPort) && (!isRoot)) {
- log("User and root config files both point to port " + rootPort +
- ". Letting the root client handle it.");
- throw new IOException();
- }
-
- int portToUse = (isRoot ? rootPort : userPort);
-
- if (portToUse == 0) {
- /* No session daemon available. Stop and retry later. */
- throw new IOException();
- }
-
- this.sessiondSock = new Socket(SESSION_HOST, portToUse);
- this.inFromSessiond = new DataInputStream(sessiondSock.getInputStream());
- this.outToSessiond = new DataOutputStream(sessiondSock.getOutputStream());
- }
-
- private static String getHomePath() {
- /*
- * The environment variable LTTNG_HOME overrides HOME if
- * defined.
- */
- String homePath = System.getenv("LTTNG_HOME");
-
- if (homePath == null) {
- homePath = System.getProperty("user.home");
- }
- return homePath;
- }
-
- /**
- * Read port number from file created by the session daemon.
- *
- * @return port value if found else 0.
- */
- private static int getPortFromFile(String path) throws IOException {
- BufferedReader br = null;
-
- try {
- br = new BufferedReader(new InputStreamReader(new FileInputStream(path), PORT_FILE_ENCODING));
- String line = br.readLine();
- if (line == null) {
- /* File exists but is empty. */
- return 0;
- }
-
- int port = Integer.parseInt(line, 10);
- if (port < 0 || port > 65535) {
- /* Invalid value. Ignore. */
- port = 0;
- }
- return port;
-
- } catch (NumberFormatException e) {
- /* File contained something that was not a number. */
- return 0;
- } catch (FileNotFoundException e) {
- /* No port available. */
- return 0;
- } finally {
- if (br != null) {
- br.close();
- }
- }
- }
-
- private void registerToSessiond() throws IOException {
- byte data[] = new byte[16];
- ByteBuffer buf = ByteBuffer.wrap(data);
- String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
-
- buf.putInt(domainValue);
- buf.putInt(Integer.parseInt(pid));
- buf.putInt(PROTOCOL_MAJOR_VERSION);
- buf.putInt(PROTOCOL_MINOR_VERSION);
- this.outToSessiond.write(data, 0, data.length);
- this.outToSessiond.flush();
- }
-
- /**
- * Handle session command from the session daemon.
- */
- private void handleSessiondCmd() throws IOException {
- /* Data read from the socket */
- byte inputData[] = null;
- /* Reply data written to the socket, sent to the sessiond */
- LttngAgentResponse response;
-
- while (true) {
- /* Get header from session daemon. */
- SessiondCommandHeader cmdHeader = recvHeader();
-
- if (cmdHeader.getDataSize() > 0) {
- inputData = recvPayload(cmdHeader);
- }
-
- switch (cmdHeader.getCommandType()) {
- case CMD_REG_DONE:
- {
- /*
- * Countdown the registration latch, meaning registration is
- * done and we can proceed to continue tracing.
- */
- registrationLatch.countDown();
- /*
- * We don't send any reply to the registration done command.
- * This just marks the end of the initial session setup.
- */
- log("Registration done");
- continue;
- }
- case CMD_LIST:
- {
- SessiondCommand listLoggerCmd = new SessiondListLoggersCommand();
- response = listLoggerCmd.execute(logAgent);
- log("Received list loggers command");
- break;
- }
- case CMD_EVENT_ENABLE:
- {
- if (inputData == null) {
- /* Invalid command */
- response = LttngAgentResponse.FAILURE_RESPONSE;
- break;
- }
- SessiondCommand enableEventCmd = new SessiondEnableEventCommand(inputData);
- response = enableEventCmd.execute(logAgent);
- log("Received enable event command: " + enableEventCmd.toString());
- break;
- }
- case CMD_EVENT_DISABLE:
- {
- if (inputData == null) {
- /* Invalid command */
- response = LttngAgentResponse.FAILURE_RESPONSE;
- break;
- }
- SessiondCommand disableEventCmd = new SessiondDisableEventCommand(inputData);
- response = disableEventCmd.execute(logAgent);
- log("Received disable event command: " + disableEventCmd.toString());
- break;
- }
- case CMD_APP_CTX_ENABLE:
- {
- if (inputData == null) {
- /* This commands expects a payload, invalid command */
- response = LttngAgentResponse.FAILURE_RESPONSE;
- break;
- }
- SessiondCommand enableAppCtxCmd = new SessiondEnableAppContextCommand(inputData);
- response = enableAppCtxCmd.execute(logAgent);
- log("Received enable app-context command");
- break;
- }
- case CMD_APP_CTX_DISABLE:
- {
- if (inputData == null) {
- /* This commands expects a payload, invalid command */
- response = LttngAgentResponse.FAILURE_RESPONSE;
- break;
- }
- SessiondCommand disableAppCtxCmd = new SessiondDisableAppContextCommand(inputData);
- response = disableAppCtxCmd.execute(logAgent);
- log("Received disable app-context command");
- break;
- }
- default:
- {
- /* Unknown command, send empty reply */
- response = null;
- log("Received unknown command, ignoring");
- break;
- }
- }
-
- /* Send response to the session daemon. */
- byte[] responseData;
- if (response == null) {
- responseData = new byte[4];
- ByteBuffer buf = ByteBuffer.wrap(responseData);
- buf.order(ByteOrder.BIG_ENDIAN);
- } else {
- log("Sending response: " + response.toString());
- responseData = response.getBytes();
- }
- this.outToSessiond.write(responseData, 0, responseData.length);
- this.outToSessiond.flush();
- }
- }
-
- /**
- * Receive header data from the session daemon using the LTTng command
- * static buffer of the right size.
- */
- private SessiondCommandHeader recvHeader() throws IOException {
- byte data[] = new byte[SessiondCommandHeader.HEADER_SIZE];
- int bytesLeft = data.length;
- int bytesOffset = 0;
-
- while (bytesLeft > 0) {
- int bytesRead = this.inFromSessiond.read(data, bytesOffset, bytesLeft);
-
- if (bytesRead < 0) {
- throw new IOException();
- }
- bytesLeft -= bytesRead;
- bytesOffset += bytesRead;
- }
- return new SessiondCommandHeader(data);
- }
-
- /**
- * Receive payload from the session daemon. This MUST be done after a
- * recvHeader() so the header value of a command are known.
- *
- * The caller SHOULD use isPayload() before which returns true if a payload
- * is expected after the header.
- */
- private byte[] recvPayload(SessiondCommandHeader headerCmd) throws IOException {
- byte payload[] = new byte[(int) headerCmd.getDataSize()];
- int bytesLeft = payload.length;
- int bytesOffset = 0;
-
- /* Failsafe check so we don't waste our time reading 0 bytes. */
- if (bytesLeft == 0) {
- return null;
- }
-
- while (bytesLeft > 0) {
- int bytesRead = inFromSessiond.read(payload, bytesOffset, bytesLeft);
-
- if (bytesRead < 0) {
- throw new IOException();
- }
- bytesLeft -= bytesRead;
- bytesOffset += bytesRead;
- }
- return payload;
- }
-
- /**
- * Wrapper for this class's logging, adds the connection's characteristics
- * to help differentiate between multiple TCP clients.
- */
- private void log(String message) {
- LttngUstAgentLogger.log(getClass(),
- "(root=" + isRoot + ", domain=" + domainValue + ") " + message);
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015-2016 EfficiOS Inc.
- * Copyright (C) 2015-2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-/**
- * Base class to represent all commands sent from the session daemon to the Java
- * agent. The agent is then expected to execute the command and provide a
- * response.
- *
- * @author Alexandre Montplaisir
- */
-abstract class SessiondCommand {
-
- /**
- * Encoding that should be used for the strings in the sessiond agent
- * protocol on the socket.
- */
- protected static final Charset SESSIOND_PROTOCOL_CHARSET = Charset.forName("UTF-8");
-
- enum CommandType {
- /** List logger(s). */
- CMD_LIST(1),
- /** Enable logger by name. */
- CMD_EVENT_ENABLE(2),
- /** Disable logger by name. */
- CMD_EVENT_DISABLE(3),
- /** Registration done */
- CMD_REG_DONE(4),
- /** Enable application context */
- CMD_APP_CTX_ENABLE(5),
- /** Disable application context */
- CMD_APP_CTX_DISABLE(6);
-
- private int code;
-
- private CommandType(int c) {
- code = c;
- }
-
- public int getCommandType() {
- return code;
- }
- }
-
- /**
- * Execute the command handler's action on the specified tracing agent.
- *
- * @param agent
- * The agent on which to execute the command
- * @return If the command completed successfully or not
- */
- public abstract LttngAgentResponse execute(ILttngTcpClientListener agent);
-
- /**
- * Utility method to read agent-protocol strings passed on the socket. The
- * buffer will contain a 32-bit integer representing the length, immediately
- * followed by the string itself.
- *
- * @param buffer
- * The ByteBuffer from which to read. It should already be setup
- * and positioned where the read should begin.
- * @return The string that was read, or <code>null</code> if it was badly
- * formatted.
- */
- protected static String readNextString(ByteBuffer buffer) {
- int nbBytes = buffer.getInt();
- if (nbBytes < 0) {
- /* The string length should be positive */
- return null;
- }
- if (nbBytes == 0) {
- /* The string is explicitly an empty string */
- return "";
- }
-
- byte[] stringBytes = new byte[nbBytes];
- buffer.get(stringBytes);
- return new String(stringBytes, SESSIOND_PROTOCOL_CHARSET).trim();
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-import org.lttng.ust.agent.client.SessiondCommand.CommandType;
-
-/**
- * Header of session daemon commands.
- *
- * @author Alexandre Montplaisir
- * @author David Goulet
- */
-class SessiondCommandHeader {
-
- /** ABI size of command header. */
- public static final int HEADER_SIZE = 16;
-
- /** Payload size in bytes following this header. */
- private final long dataSize;
-
- /** Command type. */
- private final CommandType cmd;
-
- public SessiondCommandHeader(byte[] data) {
- ByteBuffer buf = ByteBuffer.wrap(data);
- buf.order(ByteOrder.BIG_ENDIAN);
-
- dataSize = buf.getLong();
- cmd = CommandType.values()[buf.getInt() - 1];
- buf.getInt(); // command version, currently unused
- }
-
- public long getDataSize() {
- return dataSize;
- }
-
- public CommandType getCommandType() {
- return cmd;
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * Session daemon command indicating to the Java agent that an
- * application-specific context was disabled in the tracing session.
- *
- * @author Alexandre Montplaisir
- */
-class SessiondDisableAppContextCommand extends SessiondCommand {
-
- private final String retrieverName;
- private final String contextName;
-
- private final boolean commandIsValid;
-
- public SessiondDisableAppContextCommand(byte[] data) {
- if (data == null) {
- throw new IllegalArgumentException();
- }
- ByteBuffer buf = ByteBuffer.wrap(data);
- buf.order(ByteOrder.BIG_ENDIAN);
-
- /*
- * The buffer contains the retriever name first, followed by the
- * context's name.
- */
- retrieverName = readNextString(buf);
- contextName = readNextString(buf);
-
- /* If any of these strings were null then the command was invalid */
- commandIsValid = ((retrieverName != null) && (contextName != null));
- }
-
- @Override
- public LttngAgentResponse execute(ILttngTcpClientListener agent) {
- if (!commandIsValid) {
- return LttngAgentResponse.FAILURE_RESPONSE;
- }
-
- boolean success = agent.appContextDisabled(retrieverName, contextName);
- return (success ? LttngAgentResponse.SUCESS_RESPONSE : DISABLE_APP_CONTEXT_FAILURE_RESPONSE);
- }
-
- /**
- * Response sent when the disable-context command asks to disable an
- * unknown context name.
- */
- private static final LttngAgentResponse DISABLE_APP_CONTEXT_FAILURE_RESPONSE = new LttngAgentResponse() {
- @Override
- public ReturnCode getReturnCode() {
- /* Same return code used for unknown event/logger names */
- return ReturnCode.CODE_UNKNOWN_LOGGER_NAME;
- }
- };
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * Session daemon command indicating to the Java agent that some events were
- * disabled in the tracing session.
- *
- * @author Alexandre Montplaisir
- * @author David Goulet
- */
-class SessiondDisableEventCommand extends SessiondCommand {
-
- /**
- * Response sent when the disable-event command asks to disable an
- * unknown event.
- */
- private static final LttngAgentResponse DISABLE_EVENT_FAILURE_RESPONSE = new LttngAgentResponse() {
- @Override
- public ReturnCode getReturnCode() {
- return ReturnCode.CODE_UNKNOWN_LOGGER_NAME;
- }
- };
-
- /** Event name to disable from the tracing session */
- private final String eventName;
-
- public SessiondDisableEventCommand(byte[] data) {
- if (data == null) {
- throw new IllegalArgumentException();
- }
- ByteBuffer buf = ByteBuffer.wrap(data);
- buf.order(ByteOrder.BIG_ENDIAN);
- eventName = new String(data, SESSIOND_PROTOCOL_CHARSET).trim();
- }
-
- @Override
- public LttngAgentResponse execute(ILttngTcpClientListener agent) {
- boolean success = agent.eventDisabled(this.eventName);
- return (success ? LttngAgentResponse.SUCESS_RESPONSE : DISABLE_EVENT_FAILURE_RESPONSE);
- }
-
- @Override
- public String toString() {
- return "SessiondDisableEventCommand["
- + "eventName=" + eventName
- +"]";
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * Session daemon command indicating to the Java agent that an
- * application-specific context was enabled in the tracing session.
- *
- * @author Alexandre Montplaisir
- */
-class SessiondEnableAppContextCommand extends SessiondCommand {
-
- private final String retrieverName;
- private final String contextName;
-
- private final boolean commandIsValid;
-
- public SessiondEnableAppContextCommand(byte[] data) {
- if (data == null) {
- throw new IllegalArgumentException();
- }
- ByteBuffer buf = ByteBuffer.wrap(data);
- buf.order(ByteOrder.BIG_ENDIAN);
-
- /*
- * The buffer contains the retriever name first, followed by the
- * context's name.
- */
- retrieverName = readNextString(buf);
- contextName = readNextString(buf);
-
- /* If any of these strings were null then the command was invalid */
- commandIsValid = ((retrieverName != null) && (contextName != null));
- }
-
- @Override
- public LttngAgentResponse execute(ILttngTcpClientListener agent) {
- if (!commandIsValid) {
- return LttngAgentResponse.FAILURE_RESPONSE;
- }
-
- boolean success = agent.appContextEnabled(retrieverName, contextName);
- return (success ? LttngAgentResponse.SUCESS_RESPONSE : LttngAgentResponse.FAILURE_RESPONSE);
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-import org.lttng.ust.agent.session.EventRule;
-import org.lttng.ust.agent.session.LogLevelSelector;
-
-/**
- * Session daemon command indicating to the Java agent that some events were
- * enabled in the tracing session.
- *
- * @author Alexandre Montplaisir
- * @author David Goulet
- */
-class SessiondEnableEventCommand extends SessiondCommand {
-
- /** Fixed event name length. Value defined by the lttng agent protocol. */
- private static final int EVENT_NAME_LENGTH = 256;
-
- private final boolean commandIsValid;
-
- /* Parameters of the event rule being enabled */
- private final String eventName;
- private final LogLevelSelector logLevelFilter;
- private final String filterString;
-
- public SessiondEnableEventCommand(byte[] data) {
- if (data == null) {
- throw new IllegalArgumentException();
- }
- ByteBuffer buf = ByteBuffer.wrap(data);
- buf.order(ByteOrder.BIG_ENDIAN);
- int logLevel = buf.getInt();
- int logLevelType = buf.getInt();
- logLevelFilter = new LogLevelSelector(logLevel, logLevelType);
-
- /* Read the event name */
- byte[] eventNameBytes = new byte[EVENT_NAME_LENGTH];
- buf.get(eventNameBytes);
- eventName = new String(eventNameBytes, SESSIOND_PROTOCOL_CHARSET).trim();
-
- /* Read the filter string */
- filterString = readNextString(buf);
-
- /* The command was invalid if the string could not be read correctly */
- commandIsValid = (filterString != null);
- }
-
- @Override
- public LttngAgentResponse execute(ILttngTcpClientListener agent) {
- if (!commandIsValid) {
- return LttngAgentResponse.FAILURE_RESPONSE;
- }
-
- EventRule rule = new EventRule(eventName, logLevelFilter, filterString);
- boolean success = agent.eventEnabled(rule);
- return (success ? LttngAgentResponse.SUCESS_RESPONSE : LttngAgentResponse.FAILURE_RESPONSE);
- }
-
- @Override
- public String toString() {
- return "SessiondEnableEventCommand["
- + "eventName=" + eventName
- + ", logLevel=" + logLevelFilter.toString()
- + ", filterString=" + filterString
- +"]";
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent.client;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Collection;
-
-/**
- * Session daemon command asking the Java agent to list its registered loggers,
- * which corresponds to event names in the tracing session.
- *
- * @author Alexandre Montplaisir
- * @author David Goulet
- */
-class SessiondListLoggersCommand extends SessiondCommand {
-
- @Override
- public LttngAgentResponse execute(ILttngTcpClientListener agent) {
- final Collection<String> loggerList = agent.listAvailableEvents();
- return new SessiondListLoggersResponse(loggerList);
- }
-
- private static class SessiondListLoggersResponse extends LttngAgentResponse {
-
- private final static int SIZE = 12;
-
- private final Collection<String> loggers;
-
- public SessiondListLoggersResponse(Collection<String> loggers) {
- this.loggers = loggers;
- }
-
- @Override
- public ReturnCode getReturnCode() {
- /* This command can't really fail */
- return ReturnCode.CODE_SUCCESS_CMD;
- }
-
- @Override
- public byte[] getBytes() {
- /*
- * Compute the data size, which is the number of bytes of each
- * encoded string, +1 per string for the \0
- */
- int dataSize = 0;
- for (String logger : loggers) {
- dataSize += logger.getBytes(SESSIOND_PROTOCOL_CHARSET).length + 1;
- }
-
- /* Prepare the buffer */
- byte data[] = new byte[SIZE + dataSize];
- ByteBuffer buf = ByteBuffer.wrap(data);
- buf.order(ByteOrder.BIG_ENDIAN);
-
- /* Write the header section of the response */
- buf.putInt(getReturnCode().getCode());
- buf.putInt(dataSize);
- buf.putInt(loggers.size());
-
- /* Write the payload */
- for (String logger : loggers) {
- buf.put(logger.getBytes(SESSIOND_PROTOCOL_CHARSET));
- /* NULL terminated byte after the logger name. */
- buf.put((byte) 0x0);
- }
- return data;
- }
- }
-
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.context;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * The singleton manager of {@link IContextInfoRetriever} objects.
- *
- * @author Alexandre Montplaisir
- */
-public final class ContextInfoManager {
-
- private static final String SHARED_LIBRARY_NAME = "lttng-ust-context-jni";
-
- private static final Pattern VALID_CONTEXT_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_\\.]+$");
-
- private static ContextInfoManager instance;
-
- private final Map<String, IContextInfoRetriever> contextInfoRetrievers = new ConcurrentHashMap<String, IContextInfoRetriever>();
- private final Map<String, Long> contextInforRetrieverRefs = new HashMap<String, Long>();
-
- /**
- * Lock used to keep the two maps above in sync when retrievers are
- * registered or unregistered.
- */
- private final Object retrieverLock = new Object();
-
- /** Singleton class, constructor should not be accessed directly */
- private ContextInfoManager() {
- }
-
- /**
- * Get the singleton instance.
- *
- * <p>
- * Usage of this class requires the "liblttng-ust-context-jni.so" native
- * library to be present on the system and available (passing
- * -Djava.library.path=path to the JVM may be needed).
- * </p>
- *
- * @return The singleton instance
- * @throws IOException
- * If the shared library cannot be found.
- * @throws SecurityException
- * We will forward any SecurityExcepion that may be thrown when
- * trying to load the JNI library.
- */
- public static synchronized ContextInfoManager getInstance() throws IOException, SecurityException {
- if (instance == null) {
- try {
- System.loadLibrary(SHARED_LIBRARY_NAME);
- } catch (UnsatisfiedLinkError e) {
- throw new IOException(e);
- }
- instance = new ContextInfoManager();
- }
- return instance;
- }
-
- /**
- * Register a new context info retriever.
- *
- * <p>
- * Each context info retriever is registered with a given "retriever name",
- * which specifies the namespace of the context elements. This name is
- * specified separately from the retriever objects, which would allow
- * register the same retriever under different namespaces for example.
- * </p>
- *
- * <p>
- * If the method returns false (indicating registration failure), then the
- * retriever object will *not* be used for context information.
- * </p>
- *
- * @param retrieverName
- * The name to register to the context retriever object with.
- * @param contextInfoRetriever
- * The context info retriever to register
- * @return True if the retriever was successfully registered, false if there
- * was an error, for example if a retriever is already registered
- * with that name.
- */
- public boolean registerContextInfoRetriever(String retrieverName, IContextInfoRetriever contextInfoRetriever) {
- synchronized (retrieverLock) {
- if (!validateRetrieverName(retrieverName)) {
- return false;
- }
-
- if (contextInfoRetrievers.containsKey(retrieverName)) {
- /*
- * There is already a retriever registered with that name,
- * refuse the new registration.
- */
- return false;
- }
- /*
- * Inform LTTng-UST of the new retriever. The names have to start
- * with "$app." on the UST side!
- */
- long ref = LttngContextApi.registerProvider("$app." + retrieverName);
- if (ref == 0) {
- return false;
- }
-
- contextInfoRetrievers.put(retrieverName, contextInfoRetriever);
- contextInforRetrieverRefs.put(retrieverName, Long.valueOf(ref));
-
- return true;
- }
- }
-
- /**
- * Unregister a previously added context info retriever.
- *
- * This method has no effect if the retriever was not already registered.
- *
- * @param retrieverName
- * The context info retriever to unregister
- * @return True if unregistration was successful, false if there was an
- * error
- */
- public boolean unregisterContextInfoRetriever(String retrieverName) {
- synchronized (retrieverLock) {
- if (!contextInfoRetrievers.containsKey(retrieverName)) {
- /*
- * There was no retriever registered with that name.
- */
- return false;
- }
- contextInfoRetrievers.remove(retrieverName);
- long ref = contextInforRetrieverRefs.remove(retrieverName).longValue();
-
- /* Unregister the retriever on the UST side too */
- LttngContextApi.unregisterProvider(ref);
-
- return true;
- }
- }
-
- /**
- * Return the context info retriever object registered with the given name.
- *
- * @param retrieverName
- * The retriever name to look for
- * @return The corresponding retriever object, or <code>null</code> if there
- * was none
- */
- public IContextInfoRetriever getContextInfoRetriever(String retrieverName) {
- /*
- * Note that this method does not take the retrieverLock, it lets
- * concurrent threads access the ConcurrentHashMap directly.
- *
- * It's fine for a get() to happen during a registration or
- * unregistration, it's first-come-first-serve.
- */
- return contextInfoRetrievers.get(retrieverName);
- }
-
- /**
- * Validate that the given retriever name contains only the allowed
- * characters, which are alphanumerical characters, period "." and
- * underscore "_". The name must also not start with a number.
- */
- private static boolean validateRetrieverName(String contextName) {
- if (contextName.isEmpty()) {
- return false;
- }
-
- /* First character must not be a number */
- if (Character.isDigit(contextName.charAt(0))) {
- return false;
- }
-
- /* Validate the other characters of the string */
- Matcher matcher = VALID_CONTEXT_NAME_PATTERN.matcher(contextName);
- return matcher.matches();
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.context;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.util.Collection;
-import java.util.Map;
-
-import org.lttng.ust.agent.utils.LttngUstAgentLogger;
-
-/**
- * This class is used to serialize the list of "context info" objects to pass
- * through JNI.
- *
- * The protocol expects two byte array parameters, which are contained here in
- * the {@link SerializedContexts} inner class.
- *
- * The first byte array is called the "entries array", and contains fixed-size
- * entries, one per context element.
- *
- * The second one is the "strings array", it is of variable length and used to
- * hold the variable-length strings. Each one of these strings is formatted as a
- * UTF-8 C-string, meaning in will end with a "\0" byte to indicate its end.
- * Entries in the first array may refer to offsets in the second array to point
- * to relevant strings.
- *
- * The fixed-size entries in the entries array contain the following elements
- * (size in bytes in parentheses):
- *
- * <ul>
- * <li>The offset in the strings array pointing to the full context name, like
- * "$app.myprovider:mycontext" (4)</li>
- * <li>The context value type (1)</li>
- * <li>The context value itself (8)</li>
- * </ul>
- *
- * The context value type will indicate how many bytes are used for the value.
- * If the it is of String type, then we use 4 bytes to represent the offset in
- * the strings array.
- *
- * So the total size of each entry is 13 bytes. All unused bytes (for context
- * values shorter than 8 bytes for example) will be zero'ed.
- *
- * @author Alexandre Montplaisir
- */
-public class ContextInfoSerializer {
-
- private enum DataType {
- NULL(0),
- INTEGER(1),
- LONG(2),
- DOUBLE(3),
- FLOAT(4),
- BYTE(5),
- SHORT(6),
- BOOLEAN(7),
- STRING(8);
-
- private final byte value;
-
- private DataType(int value) {
- this.value = (byte) value;
- }
-
- public byte getValue() {
- return value;
- }
- }
-
- /**
- * Class used to wrap the two byte arrays returned by
- * {@link #queryAndSerializeRequestedContexts}.
- */
- public static class SerializedContexts {
-
- private final byte[] contextEntries;
- private final byte[] contextStrings;
-
- /**
- * Constructor
- *
- * @param entries
- * Arrays for the fixed-size context entries.
- * @param strings
- * Arrays for variable-length strings
- */
- public SerializedContexts(byte[] entries, byte[] strings) {
- contextEntries = entries;
- contextStrings = strings;
- }
-
- /**
- * @return The entries array
- */
- public byte[] getEntriesArray() {
- return contextEntries;
- }
-
- /**
- * @return The strings array
- */
- public byte[] getStringsArray() {
- return contextStrings;
- }
- }
-
- private static final String UST_APP_CTX_PREFIX = "$app.";
- private static final int ENTRY_LENGTH = 13;
- private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder();
- private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
- private static final SerializedContexts EMPTY_CONTEXTS = new SerializedContexts(new byte[0], new byte[0]);
-
- /**
- * From the list of requested contexts in the tracing session, look them up
- * in the {@link ContextInfoManager}, retrieve the available ones, and
- * serialize them into a byte array.
- *
- * @param enabledContexts
- * The contexts that are enabled in the tracing session (indexed
- * first by retriever name, then by index names). Should come
- * from the LTTng Agent.
- * @return The byte array representing the intersection of the requested and
- * available contexts.
- */
- public static SerializedContexts queryAndSerializeRequestedContexts(Collection<Map.Entry<String, Map<String, Integer>>> enabledContexts) {
- if (enabledContexts.isEmpty()) {
- /* Early return if there is no requested context information */
- return EMPTY_CONTEXTS;
- }
-
- ContextInfoManager contextManager;
- try {
- contextManager = ContextInfoManager.getInstance();
- } catch (IOException e) {
- /*
- * The JNI library is not available, do not send any context
- * information. No retriever could have been defined anyways.
- */
- return EMPTY_CONTEXTS;
- }
-
- /* Compute the total number of contexts (flatten the map) */
- int totalArraySize = 0;
- for (Map.Entry<String, Map<String, Integer>> contexts : enabledContexts) {
- totalArraySize += contexts.getValue().size() * ENTRY_LENGTH;
- }
-
- /* Prepare the ByteBuffer that will generate the "entries" array */
- ByteBuffer entriesBuffer = ByteBuffer.allocate(totalArraySize);
- entriesBuffer.order(NATIVE_ORDER);
- entriesBuffer.clear();
-
- /* Prepare the streams that will generate the "strings" array */
- ByteArrayOutputStream stringsBaos = new ByteArrayOutputStream();
- DataOutputStream stringsDos = new DataOutputStream(stringsBaos);
-
- try {
- for (Map.Entry<String, Map<String, Integer>> entry : enabledContexts) {
- String requestedRetrieverName = entry.getKey();
- Map<String, Integer> requestedContexts = entry.getValue();
-
- IContextInfoRetriever retriever = contextManager.getContextInfoRetriever(requestedRetrieverName);
-
- for (String requestedContext : requestedContexts.keySet()) {
- Object contextInfo;
- if (retriever == null) {
- contextInfo = null;
- } else {
- contextInfo = retriever.retrieveContextInfo(requestedContext);
- /*
- * 'contextInfo' can still be null here, which would
- * indicate the retriever does not supply this context.
- * We will still write this information so that the
- * tracer can know about it.
- */
- }
-
- /* Serialize the result to the buffers */
- // FIXME Eventually pass the retriever name only once?
- String fullContextName = (UST_APP_CTX_PREFIX + requestedRetrieverName + ':' + requestedContext);
- byte[] strArray = fullContextName.getBytes(UTF8_CHARSET);
-
- entriesBuffer.putInt(stringsDos.size());
- stringsDos.write(strArray);
- stringsDos.writeChar('\0');
-
- LttngUstAgentLogger.log(ContextInfoSerializer.class,
- "ContextInfoSerializer: Context to be sent through JNI: " + fullContextName + '=' +
- (contextInfo == null ? "null" : contextInfo.toString()));
-
- serializeContextInfo(entriesBuffer, stringsDos, contextInfo);
- }
- }
-
- stringsDos.flush();
- stringsBaos.flush();
-
- } catch (IOException e) {
- /*
- * Should not happen because we are wrapping a
- * ByteArrayOutputStream, which writes to memory
- */
- e.printStackTrace();
- }
-
- byte[] entriesArray = entriesBuffer.array();
- byte[] stringsArray = stringsBaos.toByteArray();
- return new SerializedContexts(entriesArray, stringsArray);
- }
-
- private static final int CONTEXT_VALUE_LENGTH = 8;
-
- private static void serializeContextInfo(ByteBuffer entriesBuffer, DataOutputStream stringsDos, Object contextInfo) throws IOException {
- int remainingBytes;
- if (contextInfo == null) {
- entriesBuffer.put(DataType.NULL.getValue());
- remainingBytes = CONTEXT_VALUE_LENGTH;
-
- } else if (contextInfo instanceof Integer) {
- entriesBuffer.put(DataType.INTEGER.getValue());
- entriesBuffer.putInt(((Integer) contextInfo).intValue());
- remainingBytes = CONTEXT_VALUE_LENGTH - 4;
-
- } else if (contextInfo instanceof Long) {
- entriesBuffer.put(DataType.LONG.getValue());
- entriesBuffer.putLong(((Long) contextInfo).longValue());
- remainingBytes = CONTEXT_VALUE_LENGTH - 8;
-
- } else if (contextInfo instanceof Double) {
- entriesBuffer.put(DataType.DOUBLE.getValue());
- entriesBuffer.putDouble(((Double) contextInfo).doubleValue());
- remainingBytes = CONTEXT_VALUE_LENGTH - 8;
-
- } else if (contextInfo instanceof Float) {
- entriesBuffer.put(DataType.FLOAT.getValue());
- entriesBuffer.putFloat(((Float) contextInfo).floatValue());
- remainingBytes = CONTEXT_VALUE_LENGTH - 4;
-
- } else if (contextInfo instanceof Byte) {
- entriesBuffer.put(DataType.BYTE.getValue());
- entriesBuffer.put(((Byte) contextInfo).byteValue());
- remainingBytes = CONTEXT_VALUE_LENGTH - 1;
-
- } else if (contextInfo instanceof Short) {
- entriesBuffer.put(DataType.SHORT.getValue());
- entriesBuffer.putShort(((Short) contextInfo).shortValue());
- remainingBytes = CONTEXT_VALUE_LENGTH - 2;
-
- } else if (contextInfo instanceof Boolean) {
- entriesBuffer.put(DataType.BOOLEAN.getValue());
- boolean b = ((Boolean) contextInfo).booleanValue();
- /* Converted to one byte, write 1 for true, 0 for false */
- entriesBuffer.put((byte) (b ? 1 : 0));
- remainingBytes = CONTEXT_VALUE_LENGTH - 1;
-
- } else {
- /* Also includes the case of Character. */
- /*
- * We'll write the object as a string, into the strings array. We
- * will write the corresponding offset to the entries array.
- */
- String str = contextInfo.toString();
- byte[] strArray = str.getBytes(UTF8_CHARSET);
-
- entriesBuffer.put(DataType.STRING.getValue());
-
- entriesBuffer.putInt(stringsDos.size());
- stringsDos.write(strArray);
- stringsDos.writeChar('\0');
-
- remainingBytes = CONTEXT_VALUE_LENGTH - 4;
- }
- entriesBuffer.position(entriesBuffer.position() + remainingBytes);
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.context;
-
-/**
- * Context-retrieving object specified by the application to extract
- * application-specific context information, which can then be passed on to the
- * Java agents and saved to a trace.
- *
- * Retriever objects should be registered to the {@link ContextInfoManager} to
- * make them available to the LTTng agents.
- *
- * @author Alexandre Montplaisir
- */
-public interface IContextInfoRetriever {
-
- /**
- * Retrieve a piece of context information from the application, identified
- * by a key.
- *
- * @param key
- * The key identifying the context information
- * @return The context information.
- */
- Object retrieveContextInfo(String key);
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.context;
-
-/**
- * Virtual class containing the Java side of the LTTng-UST context provider
- * registering/unregistering methods.
- *
- * @author Alexandre Montplaisir
- */
-final class LttngContextApi {
-
- private LttngContextApi() {}
-
- /**
- * Register a context provider to UST.
- *
- * The callbacks are the same for all providers, and are defined in the .c
- * file. The only needed information is the retriever (which is called
- * "provider" from UST'S point of view) name.
- *
- * @param provider_name
- * The name of the provider
- * @return The pointer to the created provider object. It's useless in the
- * Java space, but will be needed for
- * {@link #unregisterProvider(long)}.
- */
- static native long registerProvider(String provider_name);
-
- /**
- * Unregister a previously-registered context provider from UST.
- *
- * @param provider_ref
- * The pointer to the provider object, obtained from
- * {@link #registerProvider}
- */
- static native void unregisterProvider(long provider_ref);
-}
-
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.filter;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import org.lttng.ust.agent.session.EventRule;
-
-/**
- * Singleton class managing the filter notifications.
- *
- * Applications can register a {@link IFilterChangeListener} to be notified when
- * event filtering rules change in the tracing sessions.
- *
- * @author Alexandre Montplaisir
- */
-public final class FilterChangeNotifier {
-
- /** Lazy-loaded singleton instance object */
- private static FilterChangeNotifier instance = null;
-
- private final Map<EventRule, Integer> enabledEventRules = new HashMap<EventRule, Integer>();
- private final Collection<IFilterChangeListener> registeredListeners = new LinkedList<IFilterChangeListener>();
-
-
- /**
- * Private constructor, singleton class should not be instantiated directly.
- */
- private FilterChangeNotifier() {
- }
-
- /**
- * Get the singleton instance, initializing it if needed.
- *
- * @return The singleton instance
- */
- public static synchronized FilterChangeNotifier getInstance() {
- if (instance == null) {
- instance = new FilterChangeNotifier();
- }
- return instance;
- }
-
- /**
- * Notify the filter manager that a new rule was enabled in a tracing
- * session ("lttng enable-event ...")
- *
- * This is meant to be called by the LTTng Agent only. External Java
- * applications should not call this.
- *
- * @param rule
- * The rule that was added
- */
- public synchronized void addEventRule(EventRule rule) {
- Integer count = enabledEventRules.get(rule);
- if (count == null) {
- /*
- * This is the first instance of this rule being enabled. Add it to
- * the map and send notifications to the registered notifiers.
- */
- enabledEventRules.put(rule, Integer.valueOf(1));
- notifyForAddedRule(rule);
- return;
- }
- if (count.intValue() <= 0) {
- /* It should not have been in the map! */
- throw new IllegalStateException();
- }
- /*
- * This exact event rule was already enabled, just increment its
- * refcount without sending notifications
- */
- enabledEventRules.put(rule, Integer.valueOf(count.intValue() + 1));
- }
-
- /**
- * Notify the filter manager that an event name was disabled in the tracing
- * sessions ("lttng disable-event ...").
- *
- * The "disable-event" only specifies an event name. This means all the
- * rules containing this event name are to be disabled.
- *
- * This is meant to be called by the LTTng Agent only. External Java
- * applications should not call this.
- *
- * @param eventName
- * The event name to disable
- */
- public synchronized void removeEventRules(String eventName) {
- List<EventRule> rulesToRemove = new LinkedList<EventRule>();
-
- for (EventRule eventRule : enabledEventRules.keySet()) {
- if (eventRule.getEventName().equals(eventName)) {
- rulesToRemove.add(eventRule);
- }
- }
- /*
- * We cannot modify the map while iterating on it. We have to do the
- * removal separately from the iteration above.
- */
- for (EventRule rule : rulesToRemove) {
- removeEventRule(rule);
- }
- }
-
- private synchronized void removeEventRule(EventRule eventRule) {
- Integer count = enabledEventRules.get(eventRule);
- if (count == null || count.intValue() <= 0) {
- /*
- * We were asked us to disable an event rule that was not enabled
- * previously. Command error?
- */
- throw new IllegalStateException();
- }
- if (count.intValue() == 1) {
- /*
- * This is the last instance of this event rule being disabled,
- * remove it from the map and send notifications of this rule being
- * gone.
- */
- enabledEventRules.remove(eventRule);
- notifyForRemovedRule(eventRule);
- return;
- }
- /*
- * Other sessions/daemons are still looking for this event rule, simply
- * decrement its refcount, and do not send notifications.
- */
- enabledEventRules.put(eventRule, Integer.valueOf(count.intValue() - 1));
-
- }
-
- /**
- * Register a new listener to the manager.
- *
- * @param listener
- * The listener to add
- */
- public synchronized void registerListener(IFilterChangeListener listener) {
- registeredListeners.add(listener);
-
- /* Send the current rules to the new listener ("statedump") */
- for (EventRule rule : enabledEventRules.keySet()) {
- listener.eventRuleAdded(rule);
- }
- }
-
- /**
- * Unregister a listener from the manager.
- *
- * @param listener
- * The listener to remove
- */
- public synchronized void unregisterListener(IFilterChangeListener listener) {
- registeredListeners.remove(listener);
- }
-
- private void notifyForAddedRule(final EventRule rule) {
- for (IFilterChangeListener notifier : registeredListeners) {
- notifier.eventRuleAdded(rule);
- }
- }
-
- private void notifyForRemovedRule(final EventRule rule) {
- for (IFilterChangeListener notifier : registeredListeners) {
- notifier.eventRuleRemoved(rule);
- }
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.filter;
-
-import org.lttng.ust.agent.session.EventRule;
-
-/**
- * Filter notification listener interface.
- * <p>
- * Applications wanting to be notified of event filtering rule changes should
- * implement this interface, then register their listener using
- * {@link FilterChangeNotifier#registerListener}.
- * </p>
- * <p>
- * The callbacks defined in this interface will be called whenever an event rule
- * is added or removed. The manager will take care of the reference-counting in
- * case multiple tracing sessions enable the exact same rules. For example, the
- * {@link #eventRuleRemoved} callback is only called when there are no more
- * session interested into it.
- * </p>
- * <p>
- * Do not forget to unregister the listener after use, using
- * {@link FilterChangeNotifier#unregisterListener}. If you do not, or if
- * you use an anonymous listener for example, these will remain attached until
- * the complete shutdown of the application.
- * </p>
- * <p>
- * Only one thread is used to dispatch notifications, sequentially. This means
- * that if a callback hangs it will prevent other listeners from receiving
- * notifications. Please take care of not blocking inside the listener
- * callbacks, and use separate threads for potentially long or blocking
- * operations.
- * </p>
- *
- * @author Alexandre Montplaisir
- */
-public interface IFilterChangeListener {
-
- /**
- * Notification that a new event rule is now enabled in the tracing
- * sessions.
- *
- * @param rule
- * The event rule that was enabled
- */
- void eventRuleAdded(EventRule rule);
-
- /**
- * Notification that an existing event rule is now disabled in the tracing
- * sessions.
- *
- * @param rule
- * The event rule that was disabled
- */
- void eventRuleRemoved(EventRule rule);
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.session;
-
-/**
- * Event filtering rule present in a tracing session.
- *
- * It typically comes from a "lttng enable-event" command, and contains a
- * domain, event name, log level and filter string.
- *
- * @author Alexandre Montplaisir
- */
-public class EventRule {
-
- private final String eventName;
- private final LogLevelSelector logLevelSelector;
- private final String filterString;
-
- /**
- * Constructor.
- *
- * @param eventName
- * The name of the tracepoint
- * @param logLevelSelector
- * The log level of the event rule
- * @param filterString
- * The filtering string. May be null if there is no extra filter.
- */
- public EventRule(String eventName, LogLevelSelector logLevelSelector, String filterString) {
- this.eventName = eventName;
- this.logLevelSelector = logLevelSelector;
- this.filterString = filterString;
- }
-
- /**
- * Get the event name of this rule.
- *
- * @return The event name
- */
- public String getEventName() {
- return eventName;
- }
-
- /**
- * Get the log level filter configuration of the rule.
- *
- * @return The log level selector
- */
- public LogLevelSelector getLogLevelSelector() {
- return logLevelSelector;
- }
-
- /**
- * Get the filter string associated with this rule.
- *
- * @return The filter string, may be null for no filter string.
- */
- public String getFilterString() {
- return filterString;
- }
-
- // ------------------------------------------------------------------------
- // Methods from Object
- // ------------------------------------------------------------------------
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((eventName == null) ? 0 : eventName.hashCode());
- result = prime * result + ((filterString == null) ? 0 : filterString.hashCode());
- result = prime * result + ((logLevelSelector == null) ? 0 : logLevelSelector.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- EventRule other = (EventRule) obj;
-
- if (eventName == null) {
- if (other.eventName != null) {
- return false;
- }
- } else if (!eventName.equals(other.eventName)) {
- return false;
- }
- /* else, continue */
-
- if (filterString == null) {
- if (other.filterString != null) {
- return false;
- }
- } else if (!filterString.equals(other.filterString)) {
- return false;
- }
- /* else, continue */
-
- if (logLevelSelector == null) {
- if (other.logLevelSelector != null) {
- return false;
- }
- } else if (!logLevelSelector.equals(other.logLevelSelector)) {
- return false;
- }
- /* else, continue */
-
- return true;
- }
-
- @Override
- public String toString() {
- return "Event name = " + getEventName() +
- ", Log level selector = (" + getLogLevelSelector().toString() + ")" +
- ", Filter string = " + getFilterString();
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.session;
-
-/**
- * Log level filtering element, which is part of an {@link EventRule}.
- *
- * @author Alexandre Montplaisir
- */
-public class LogLevelSelector {
-
- /**
- * The type of log level filter that is enabled.
- *
- * Defined from lttng-tools' include/lttng/event.h.
- */
- public enum LogLevelType {
- /**
- * All log levels are enabled. This overrides the value of
- * {@link LogLevelSelector#getLogLevel}.
- */
- LTTNG_EVENT_LOGLEVEL_ALL(0),
-
- /** This log level along with all log levels of higher severity are enabled. */
- LTTNG_EVENT_LOGLEVEL_RANGE(1),
-
- /** Only this exact log level is enabled. */
- LTTNG_EVENT_LOGLEVEL_SINGLE(2);
-
- private final int value;
-
- private LogLevelType(int value) {
- this.value = value;
- }
-
- /**
- * Get the numerical (int) value representing this log level type in the
- * communication protocol.
- *
- * @return The int value
- */
- public int getValue() {
- return value;
- }
-
- static LogLevelType fromValue(int val) {
- switch (val) {
- case 0:
- return LTTNG_EVENT_LOGLEVEL_ALL;
- case 1:
- return LTTNG_EVENT_LOGLEVEL_RANGE;
- case 2:
- return LTTNG_EVENT_LOGLEVEL_SINGLE;
- default:
- throw new IllegalArgumentException();
- }
- }
- }
-
- private final int logLevel;
- private final LogLevelType logLevelType;
-
- /**
- * Constructor using numerical values straight from the communication
- * protocol.
- *
- * @param logLevel
- * The numerical value of the log level. The exact value depends
- * on the tracing domain, see include/lttng/event.h in the
- * lttng-tools tree for the complete enumeration.
- * @param logLevelType
- * The numerical value of the log level type. It will be
- * converted to a {@link LogLevelType} by this constructor.
- * @throws IllegalArgumentException
- * If the 'logLevelType' does not correspond to a valid value.
- */
- public LogLevelSelector(int logLevel, int logLevelType) {
- this.logLevel = logLevel;
- this.logLevelType = LogLevelType.fromValue(logLevelType);
- }
-
- /**
- * "Manual" constructor, specifying the {@link LogLevelType} directly.
- *
- * @param logLevel
- * The numerical value of the log level. The exact value depends
- * on the tracing domain, see include/lttng/event.h in the
- * lttng-tools tree for the complete enumeration.
- * @param type
- * The log level filter type.
- */
- public LogLevelSelector(int logLevel, LogLevelType type) {
- this.logLevel = logLevel;
- this.logLevelType = type;
- }
-
- /**
- * Get the numerical value of the log level element. Does not apply if
- * {@link #getLogLevelType} returns
- * {@link LogLevelType#LTTNG_EVENT_LOGLEVEL_ALL}.
- *
- * @return The numerical value of the log level
- */
- public int getLogLevel() {
- return logLevel;
- }
-
- /**
- * Get the log level filter type.
- *
- * @return The log level filter type
- */
- public LogLevelType getLogLevelType() {
- return logLevelType;
- }
-
- /**
- * Helper method to determine if an event with the given log level should be
- * traced when considering this filter.
- *
- * For example, if this filter object represents "higher severity than 5",
- * and the log level passed in parameter is "8", it will return that it
- * matches (higher value means higher severity).
- *
- * @param targetLogLevel
- * The log level value of the event to check for
- * @return Should this event be traced, or not
- */
- public boolean matches(int targetLogLevel) {
- switch (logLevelType) {
- case LTTNG_EVENT_LOGLEVEL_ALL:
- return true;
- case LTTNG_EVENT_LOGLEVEL_RANGE:
- return (targetLogLevel >= logLevel);
- case LTTNG_EVENT_LOGLEVEL_SINGLE:
- return (targetLogLevel == logLevel);
- default:
- throw new IllegalStateException();
- }
- }
-
- // ------------------------------------------------------------------------
- // Methods from Object
- // ------------------------------------------------------------------------
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + logLevel;
- result = prime * result + ((logLevelType == null) ? 0 : logLevelType.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- LogLevelSelector other = (LogLevelSelector) obj;
-
- if (logLevel != other.logLevel) {
- return false;
- }
- if (logLevelType != other.logLevelType) {
- return false;
- }
- return true;
- }
-
- @Override
- public String toString() {
- if (getLogLevelType() == LogLevelType.LTTNG_EVENT_LOGLEVEL_ALL) {
- return LogLevelType.LTTNG_EVENT_LOGLEVEL_ALL.toString();
- }
- return String.valueOf(getLogLevel()) + ", " + getLogLevelType().toString();
- }
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.utils;
-
-/**
- * Logging infrastructure for the lttng-ust Java agent. It prints log messages
- * to stderr but only when the environment variable LTTNG_UST_DEBUG is defined.
- *
- * @author Alexandre Montplaisir
- */
-public class LttngUstAgentLogger {
-
- private static final String ENV_VAR_NAME = "LTTNG_UST_DEBUG";
- private static final boolean LOGGING_ENABLED = (System.getenv(ENV_VAR_NAME) == null ? false : true);
-
- /**
- * Log event. Will be printed to stderr if the environment variable
- * "LTTNG_UST_DEBUG" is defined.
- *
- * @param c
- * The class logging the message (should normally be called with
- * {@link #getClass()}).
- * @param message
- * The message to print
- */
- public static void log(Class<?> c, String message) {
- if (LOGGING_ENABLED) {
- System.err.println(c.getSimpleName() + ": " + message);
- }
- }
-}
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-JAVAROOT = .
-AM_JAVACFLAGS = -classpath $(CLASSPATH):$(builddir)/../lttng-ust-agent-common/lttng-ust-agent-common.jar
-
-pkgpath = org/lttng/ust/agent/jul
-
-jarfile_version = 1.0.0
-jarfile_manifest = $(srcdir)/Manifest.txt
-jarfile_symlink = lttng-ust-agent-jul.jar
-jarfile = lttng-ust-agent-jul-$(jarfile_version).jar
-
-jardir = $(datadir)/java
-
-juljniout = ../../jni/jul
-
-dist_noinst_JAVA = $(pkgpath)/LttngJulAgent.java \
- $(pkgpath)/LttngJulApi.java \
- $(pkgpath)/LttngLogHandler.java
-
-dist_noinst_DATA = $(jarfile_manifest)
-
-jar_DATA = $(jarfile)
-
-stamp = jul-jni-header.stamp
-classes = $(pkgpath)/*.class
-
-$(jarfile): classnoinst.stamp
- $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink)
-
-if !HAVE_JAVAH
-# If we don't have javah, assume we are running openjdk >= 10 and use javac
-# to generate the jni header file.
-AM_JAVACFLAGS += -h $(juljniout)
-else
-jul-jni-header.stamp: $(dist_noinst_JAVA)
- $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(juljniout) $(JAVAHFLAGS) org.lttng.ust.agent.jul.LttngJulApi && \
- echo "JUL JNI header generated" > jul-jni-header.stamp
-
-all-local: $(stamp)
-endif
-
-install-data-hook:
- cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink)
-
-uninstall-hook:
- cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink)
-
-CLEANFILES = *.jar \
- $(pkgpath)/*.class \
- jul-jni-header.stamp \
- $(juljniout)/org_lttng_ust_agent_jul_LttngJulApi.h
+++ /dev/null
-Name: org/lttng/ust/agent/jul/
-Specification-Title: LTTng UST Java Agent JUL Integration
-Specification-Version: 1.0.0
-Specification-Vendor: LTTng Project
-Implementation-Title: org.lttng.ust.agent.jul
-Implementation-Version: 1.0.0
-Implementation-Vendor: LTTng Project
-Class-Path: lttng-ust-agent-common.jar
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.jul;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.logging.Handler;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-import org.lttng.ust.agent.AbstractLttngAgent;
-
-/**
- * Agent implementation for tracing from JUL loggers.
- *
- * @author Alexandre Montplaisir
- */
-class LttngJulAgent extends AbstractLttngAgent<LttngLogHandler> {
-
- private static LttngJulAgent instance = null;
-
- private LttngJulAgent() {
- super(Domain.JUL);
- }
-
- public static synchronized LttngJulAgent getInstance() {
- if (instance == null) {
- instance = new LttngJulAgent();
- }
- return instance;
- }
-
- @Override
- public Collection<String> listAvailableEvents() {
- Set<String> ret = new TreeSet<String>();
-
- List<String> loggersNames = Collections.list(LogManager.getLogManager().getLoggerNames());
- for (String name : loggersNames) {
- /*
- * Skip the root logger. An empty string is not a valid event name
- * in LTTng.
- */
- if (name.equals("") || name.equals("global")) {
- continue;
- }
-
- /*
- * Check if that logger has at least one LTTng JUL handler attached.
- */
- Logger logger = Logger.getLogger(name);
- if (hasLttngHandlerAttached(logger)) {
- ret.add(name);
- }
- }
-
- return ret;
- }
-
- private static boolean hasLttngHandlerAttached(Logger logger) {
- for (Handler handler : logger.getHandlers()) {
- if (handler instanceof LttngLogHandler) {
- return true;
- }
- }
-
- /*
- * A parent logger, if any, may be connected to an LTTng handler. In
- * this case, we will want to include this child logger in the output,
- * since it will be accessible by LTTng.
- */
- Logger parent = logger.getParent();
- if (parent != null) {
- return hasLttngHandlerAttached(parent);
- }
-
- /*
- * We have reached the root logger and have not found any LTTng handler,
- * this event will not be accessible.
- */
- return false;
- }
-
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.jul;
-
-/**
- * Virtual class containing the Java side of the LTTng-JUL JNI API methods.
- *
- * @author Alexandre Montplaisir
- */
-final class LttngJulApi {
-
- private LttngJulApi() {}
-
- static native void tracepoint(String msg,
- String logger_name,
- String class_name,
- String method_name,
- long millis,
- int log_level,
- int thread_id);
-
- static native void tracepointWithContext(String msg,
- String logger_name,
- String class_name,
- String method_name,
- long millis,
- int log_level,
- int thread_id,
- byte[] contextEntries,
- byte[] contextStrings);
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-package org.lttng.ust.agent.jul;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.logging.Formatter;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
-
-import org.lttng.ust.agent.ILttngAgent;
-import org.lttng.ust.agent.ILttngHandler;
-import org.lttng.ust.agent.context.ContextInfoSerializer;
-
-/**
- * LTTng-UST JUL log handler.
- *
- * Applications can attach this handler to their
- * {@link java.util.logging.Logger} to have it generate UST events from logging
- * events received through the logger.
- *
- * It sends its events to UST via the JNI library "liblttng-ust-jul-jni.so".
- * Make sure this library is available before using this handler.
- *
- * @author Alexandre Montplaisir
- * @author David Goulet
- */
-public class LttngLogHandler extends Handler implements ILttngHandler {
-
- private static final String SHARED_OBJECT_NAME = "lttng-ust-jul-jni";
-
- /**
- * Dummy Formatter object, so we can use its
- * {@link Formatter#formatMessage(LogRecord)} method.
- */
- private static final Formatter FORMATTER = new Formatter() {
- @Override
- public String format(LogRecord record) {
- throw new UnsupportedOperationException();
- }
- };
-
- private final ILttngAgent<LttngLogHandler> agent;
-
- /** Number of events logged (really sent through JNI) by this handler */
- private final AtomicLong eventCount = new AtomicLong(0);
-
- /**
- * Constructor
- *
- * @throws IOException
- * This handler requires the lttng-ust-jul-jni.so native
- * library, through which it will send the trace events. This
- * exception is throw is this library cannot be found.
- * @throws SecurityException
- * We will forward any SecurityExcepion that may be thrown when
- * trying to load the JNI library.
- */
- public LttngLogHandler() throws IOException, SecurityException {
- super();
- /* Initialize LTTng UST tracer. */
- try {
- System.loadLibrary(SHARED_OBJECT_NAME); //$NON-NLS-1$
- } catch (UnsatisfiedLinkError e) {
- throw new IOException(e);
- }
-
- /** Register to the relevant agent */
- agent = LttngJulAgent.getInstance();
- agent.registerHandler(this);
- }
-
- @Override
- public synchronized void close() {
- agent.unregisterHandler(this);
- }
-
- /**
- * Get the number of events logged by this handler so far. This means the
- * number of events actually sent through JNI to UST.
- *
- * @return The number of events logged so far
- */
- @Override
- public long getEventCount() {
- return eventCount.get();
- }
-
- @Override
- public void flush() {
- }
-
- @Override
- public void publish(LogRecord record) {
- /*
- * Check if the current message should be logged, according to the UST
- * session settings.
- */
- if (!agent.isEventEnabled(record.getLoggerName())) {
- return;
- }
-
- String formattedMessage = FORMATTER.formatMessage(record);
-
- /* Retrieve all the requested context information we can find */
- Collection<Entry<String, Map<String, Integer>>> enabledContexts = agent.getEnabledAppContexts();
- ContextInfoSerializer.SerializedContexts contextInfo = ContextInfoSerializer.queryAndSerializeRequestedContexts(enabledContexts);
-
- eventCount.incrementAndGet();
-
- /*
- * Specific tracepoint designed for JUL events. The source class of the
- * caller is used for the event name, the raw message is taken, the
- * loglevel of the record and the thread ID.
- */
- LttngJulApi.tracepointWithContext(formattedMessage,
- record.getLoggerName(),
- record.getSourceClassName(),
- record.getSourceMethodName(),
- record.getMillis(),
- record.getLevel().intValue(),
- record.getThreadID(),
- contextInfo.getEntriesArray(),
- contextInfo.getStringsArray());
- }
-
-}
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-JAVAROOT = .
-AM_JAVACFLAGS = -classpath $(CLASSPATH):$(builddir)/../lttng-ust-agent-common/lttng-ust-agent-common.jar
-
-pkgpath = org/lttng/ust/agent/log4j
-
-jarfile_version = 1.0.0
-jarfile_manifest = $(srcdir)/Manifest.txt
-jarfile_symlink = lttng-ust-agent-log4j.jar
-jarfile = lttng-ust-agent-log4j-$(jarfile_version).jar
-
-jardir = $(datadir)/java
-
-log4jjniout = ../../jni/log4j
-
-dist_noinst_JAVA = $(pkgpath)/LttngLog4jAgent.java \
- $(pkgpath)/LttngLog4jApi.java \
- $(pkgpath)/LttngLogAppender.java
-
-dist_noinst_DATA = $(jarfile_manifest)
-
-jar_DATA = $(jarfile)
-
-stamp = log4j-jni-header.stamp
-classes = $(pkgpath)/*.class
-
-$(jarfile): classnoinst.stamp
- $(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink)
-
-if !HAVE_JAVAH
-# If we don't have javah, assume we are running openjdk >= 10 and use javac
-# to generate the jni header file.
-AM_JAVACFLAGS += -h $(log4jjniout)
-else
-log4j-jni-header.stamp: $(dist_noinst_JAVA)
- $(JAVAH) -classpath $(CLASSPATH):$(srcdir) -d $(log4jjniout) $(JAVAHFLAGS) org.lttng.ust.agent.log4j.LttngLog4jApi && \
- echo "Log4j JNI header generated" > log4j-jni-header.stamp
-
-all-local: $(stamp)
-endif
-
-install-data-hook:
- cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink)
-
-uninstall-hook:
- cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink)
-
-CLEANFILES = *.jar \
- $(pkgpath)/*.class \
- log4j-jni-header.stamp \
- $(log4jjniout)/org_lttng_ust_agent_log4j_LttngLog4jApi.h
+++ /dev/null
-Name: org/lttng/ust/agent/log4j/
-Specification-Title: LTTng UST Java Agent Log4J 1.x Integration
-Specification-Version: 1.0.0
-Specification-Vendor: LTTng Project
-Implementation-Title: org.lttng.ust.agent.log4j
-Implementation-Version: 1.0.0
-Implementation-Vendor: LTTng Project
-Class-Path: lttng-ust-agent-common.jar
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.log4j;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.apache.log4j.Appender;
-import org.apache.log4j.Category;
-import org.apache.log4j.LogManager;
-import org.apache.log4j.Logger;
-import org.lttng.ust.agent.AbstractLttngAgent;
-
-/**
- * Agent implementation for using the Log4j logger, connecting to a root session
- * daemon.
- *
- * @author Alexandre Montplaisir
- */
-class LttngLog4jAgent extends AbstractLttngAgent<LttngLogAppender> {
-
- private static LttngLog4jAgent instance = null;
-
- private LttngLog4jAgent() {
- super(Domain.LOG4J);
- }
-
- public static synchronized LttngLog4jAgent getInstance() {
- if (instance == null) {
- instance = new LttngLog4jAgent();
- }
- return instance;
- }
-
- @Override
- public Collection<String> listAvailableEvents() {
- Set<String> ret = new TreeSet<String>();
-
- @SuppressWarnings("unchecked")
- List<Logger> loggers = Collections.list(LogManager.getCurrentLoggers());
- for (Logger logger : loggers) {
- if (logger == null) {
- continue;
- }
-
- /*
- * Check if that logger has at least one LTTng log4j appender
- * attached.
- */
- if (hasLttngAppenderAttached(logger)) {
- ret.add(logger.getName());
- }
- }
-
- return ret;
- }
-
- private static boolean hasLttngAppenderAttached(Category logger) {
- @SuppressWarnings("unchecked")
- Enumeration<Appender> appenders = logger.getAllAppenders();
- if (appenders != null) {
- for (Appender appender : Collections.list(appenders)) {
- if (appender instanceof LttngLogAppender) {
- return true;
- }
- }
- }
-
- /*
- * A parent logger, if any, may be connected to an LTTng handler. In
- * this case, we will want to include this child logger in the output,
- * since it will be accessible by LTTng.
- */
- Category parent = logger.getParent();
- if (parent != null) {
- return hasLttngAppenderAttached(parent);
- }
-
- /*
- * We have reached the root logger and have not found any LTTng handler,
- * this event will not be accessible.
- */
- return false;
- }
-
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-package org.lttng.ust.agent.log4j;
-
-/**
- * Virtual class containing the Java side of the LTTng-log4j JNI API methods.
- *
- * @author Alexandre Montplaisir
- */
-final class LttngLog4jApi {
-
- private LttngLog4jApi() {}
-
- static native void tracepoint(String msg,
- String logger_name,
- String class_name,
- String method_name,
- String file_name,
- int line_number,
- long timestamp,
- int loglevel,
- String thread_name);
-
- static native void tracepointWithContext(String msg,
- String logger_name,
- String class_name,
- String method_name,
- String file_name,
- int line_number,
- long timestamp,
- int loglevel,
- String thread_name,
- byte[] contextEntries,
- byte[] contextStrings);
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2015 EfficiOS Inc.
- * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2014 Christian Babeux <christian.babeux@efficios.com>
- */
-
-package org.lttng.ust.agent.log4j;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.log4j.AppenderSkeleton;
-import org.apache.log4j.spi.LoggingEvent;
-import org.lttng.ust.agent.ILttngAgent;
-import org.lttng.ust.agent.ILttngHandler;
-import org.lttng.ust.agent.context.ContextInfoSerializer;
-
-/**
- * LTTng-UST Log4j 1.x log handler.
- *
- * Applications can attach this appender to their
- * {@link org.apache.log4j.Logger} to have it generate UST events from logging
- * events received through the logger.
- *
- * It sends its events to UST via the JNI library "liblttng-ust-log4j-jni.so".
- * Make sure this library is available before using this appender.
- *
- * @author Alexandre Montplaisir
- * @author Christian Babeux
- */
-public class LttngLogAppender extends AppenderSkeleton implements ILttngHandler {
-
- private static final String SHARED_OBJECT_NAME = "lttng-ust-log4j-jni";
-
- private final AtomicLong eventCount = new AtomicLong(0);
-
- private final ILttngAgent<LttngLogAppender> agent;
-
-
- /**
- * Constructor
- *
- * @throws IOException
- * This handler requires the lttng-ust-log4j-jni.so native
- * library, through which it will send the trace events. This
- * exception is throw is this library cannot be found.
- * @throws SecurityException
- * We will forward any SecurityExcepion that may be thrown when
- * trying to load the JNI library.
- */
- public LttngLogAppender() throws IOException, SecurityException {
- super();
- /* Initialize LTTng UST tracer. */
- try {
- System.loadLibrary(SHARED_OBJECT_NAME); // $NON-NLS-1$
- } catch (UnsatisfiedLinkError e) {
- throw new IOException(e);
- }
-
- /** Register to the relevant agent */
- agent = LttngLog4jAgent.getInstance();
- agent.registerHandler(this);
- }
-
- @Override
- public synchronized void close() {
- agent.unregisterHandler(this);
- }
-
- /**
- * Get the number of events logged by this handler so far. This means the
- * number of events actually sent through JNI to UST.
- *
- * @return The number of events logged so far
- */
- @Override
- public long getEventCount() {
- return eventCount.get();
- }
-
- @Override
- public boolean requiresLayout() {
- return false;
- }
-
- @Override
- protected void append(LoggingEvent event) {
- /*
- * Check if the current message should be logged, according to the UST
- * session settings.
- */
- if (!agent.isEventEnabled(event.getLoggerName())) {
- return;
- }
-
- /*
- * The line number returned from LocationInformation is a string. At
- * least try to convert to a proper int.
- */
- int line;
- try {
- String lineString = event.getLocationInformation().getLineNumber();
- line = Integer.parseInt(lineString);
- } catch (NumberFormatException n) {
- line = -1;
- }
-
- /* Retrieve all the requested context information we can find */
- Collection<Entry<String, Map<String, Integer>>> enabledContexts = agent.getEnabledAppContexts();
- ContextInfoSerializer.SerializedContexts contextInfo = ContextInfoSerializer.queryAndSerializeRequestedContexts(enabledContexts);
-
- eventCount.incrementAndGet();
-
- LttngLog4jApi.tracepointWithContext(event.getRenderedMessage(),
- event.getLoggerName(),
- event.getLocationInformation().getClassName(),
- event.getLocationInformation().getMethodName(),
- event.getLocationInformation().getFileName(),
- line,
- event.getTimeStamp(),
- event.getLevel().toInt(),
- event.getThreadName(),
- contextInfo.getEntriesArray(),
- contextInfo.getStringsArray());
- }
-
-}
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-SUBDIRS = common
-
-if ENABLE_JAVA_AGENT_WITH_JUL
-SUBDIRS += jul
-endif
-
-if ENABLE_JAVA_AGENT_WITH_LOG4J
-SUBDIRS += log4j
-endif
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-AM_CPPFLAGS += -I$(builddir) -I$(srcdir) $(JNI_CPPFLAGS)
-
-lib_LTLIBRARIES = liblttng-ust-context-jni.la
-liblttng_ust_context_jni_la_SOURCES = lttng_ust_context.c lttng_ust_context.h
-
-nodist_liblttng_ust_context_jni_la_SOURCES = org_lttng_ust_agent_context_LttngContextApi.h
-
-liblttng_ust_context_jni_la_LIBADD = -lc \
- $(top_builddir)/src/liblttng-ust/liblttng-ust.la
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#include "org_lttng_ust_agent_context_LttngContextApi.h"
-
-#include <string.h>
-#include <inttypes.h>
-#include <lttng/ust-events.h>
-#include <lttng/ringbuffer-context.h>
-#include <common/ust-context-provider.h>
-
-#include "common/macros.h"
-#include "lttng_ust_context.h"
-
-enum lttng_ust_jni_type {
- JNI_TYPE_NULL = 0,
- JNI_TYPE_INTEGER = 1,
- JNI_TYPE_LONG = 2,
- JNI_TYPE_DOUBLE = 3,
- JNI_TYPE_FLOAT = 4,
- JNI_TYPE_BYTE = 5,
- JNI_TYPE_SHORT = 6,
- JNI_TYPE_BOOLEAN = 7,
- JNI_TYPE_STRING = 8,
-};
-
-struct lttng_ust_jni_ctx_entry {
- int32_t context_name_offset;
- char type; /* enum lttng_ust_jni_type */
- union {
- int32_t _integer;
- int64_t _long;
- double _double;
- float _float;
- signed char _byte;
- int16_t _short;
- signed char _boolean;
- int32_t _string_offset;
- } value;
-} __attribute__((packed));
-
-struct lttng_ust_jni_provider {
- struct lttng_ust_registered_context_provider *reg_provider;
- char *name;
- struct lttng_ust_context_provider provider;
-};
-
-/* TLS passing context info from JNI to callbacks. */
-__thread struct lttng_ust_jni_tls lttng_ust_context_info_tls;
-
-static const char *get_ctx_string_at_offset(int32_t offset)
-{
- signed char *ctx_strings_array = lttng_ust_context_info_tls.ctx_strings;
-
- if (offset < 0 || offset >= lttng_ust_context_info_tls.ctx_strings_len) {
- return NULL;
- }
- return (const char *) (ctx_strings_array + offset);
-}
-
-static struct lttng_ust_jni_ctx_entry *lookup_ctx_by_name(const char *ctx_name)
-{
- struct lttng_ust_jni_ctx_entry *ctx_entries_array = lttng_ust_context_info_tls.ctx_entries;
- int i, len = lttng_ust_context_info_tls.ctx_entries_len / sizeof(struct lttng_ust_jni_ctx_entry);
-
- for (i = 0; i < len; i++) {
- int32_t offset = ctx_entries_array[i].context_name_offset;
- const char *string = get_ctx_string_at_offset(offset);
-
- if (string && strcmp(string, ctx_name) == 0) {
- return &ctx_entries_array[i];
- }
- }
- return NULL;
-}
-
-static size_t get_size_cb(void *priv, size_t offset)
-{
- struct lttng_ust_jni_ctx_entry *jctx;
- size_t size = 0;
- struct lttng_ust_jni_provider *jni_provider = (struct lttng_ust_jni_provider *) priv;
- const char *ctx_name = jni_provider->name;
- enum lttng_ust_jni_type jni_type;
-
- size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(char));
- size += sizeof(char); /* tag */
- jctx = lookup_ctx_by_name(ctx_name);
- if (!jctx) {
- jni_type = JNI_TYPE_NULL;
- } else {
- jni_type = jctx->type;
- }
- switch (jni_type) {
- case JNI_TYPE_NULL:
- break;
- case JNI_TYPE_INTEGER:
- size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(int32_t));
- size += sizeof(int32_t); /* variant */
- break;
- case JNI_TYPE_LONG:
- size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(int64_t));
- size += sizeof(int64_t); /* variant */
- break;
- case JNI_TYPE_DOUBLE:
- size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(double));
- size += sizeof(double); /* variant */
- break;
- case JNI_TYPE_FLOAT:
- size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(float));
- size += sizeof(float); /* variant */
- break;
- case JNI_TYPE_SHORT:
- size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(int16_t));
- size += sizeof(int16_t); /* variant */
- break;
- case JNI_TYPE_BYTE: /* Fall-through. */
- case JNI_TYPE_BOOLEAN:
- size += lttng_ust_lib_ring_buffer_align(offset, lttng_ust_rb_alignof(char));
- size += sizeof(char); /* variant */
- break;
- case JNI_TYPE_STRING:
- {
- /* The value is an offset, the string is in the "strings" array */
- int32_t string_offset = jctx->value._string_offset;
- const char *string = get_ctx_string_at_offset(string_offset);
-
- if (string) {
- size += strlen(string) + 1;
- }
- break;
- }
- default:
- abort();
- }
- return size;
-
-}
-
-static void record_cb(void *priv,
- struct lttng_ust_lib_ring_buffer_ctx *ctx,
- struct lttng_ust_channel_buffer *lttng_chan_buf)
-{
- struct lttng_ust_jni_ctx_entry *jctx;
- struct lttng_ust_jni_provider *jni_provider = (struct lttng_ust_jni_provider *) priv;
- const char *ctx_name = jni_provider->name;
- enum lttng_ust_jni_type jni_type;
- char sel_char;
-
- jctx = lookup_ctx_by_name(ctx_name);
- if (!jctx) {
- jni_type = JNI_TYPE_NULL;
- } else {
- jni_type = jctx->type;
- }
-
- switch (jni_type) {
- case JNI_TYPE_NULL:
- sel_char = LTTNG_UST_DYNAMIC_TYPE_NONE;
- lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
- break;
- case JNI_TYPE_INTEGER:
- {
- int32_t v = jctx->value._integer;
-
- sel_char = LTTNG_UST_DYNAMIC_TYPE_S32;
- lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
- lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
- break;
- }
- case JNI_TYPE_LONG:
- {
- int64_t v = jctx->value._long;
-
- sel_char = LTTNG_UST_DYNAMIC_TYPE_S64;
- lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
- lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
- break;
- }
- case JNI_TYPE_DOUBLE:
- {
- double v = jctx->value._double;
-
- sel_char = LTTNG_UST_DYNAMIC_TYPE_DOUBLE;
- lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
- lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
- break;
- }
- case JNI_TYPE_FLOAT:
- {
- float v = jctx->value._float;
-
- sel_char = LTTNG_UST_DYNAMIC_TYPE_FLOAT;
- lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
- lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
- break;
- }
- case JNI_TYPE_SHORT:
- {
- int16_t v = jctx->value._short;
-
- sel_char = LTTNG_UST_DYNAMIC_TYPE_S16;
- lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
- lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
- break;
- }
- case JNI_TYPE_BYTE:
- {
- char v = jctx->value._byte;
-
- sel_char = LTTNG_UST_DYNAMIC_TYPE_S8;
- lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
- lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
- break;
- }
- case JNI_TYPE_BOOLEAN:
- {
- char v = jctx->value._boolean;
-
- sel_char = LTTNG_UST_DYNAMIC_TYPE_S8;
- lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
- lttng_chan_buf->ops->event_write(ctx, &v, sizeof(v), lttng_ust_rb_alignof(v));
- break;
- }
- case JNI_TYPE_STRING:
- {
- int32_t offset = jctx->value._string_offset;
- const char *str = get_ctx_string_at_offset(offset);
-
- if (str) {
- sel_char = LTTNG_UST_DYNAMIC_TYPE_STRING;
- } else {
- sel_char = LTTNG_UST_DYNAMIC_TYPE_NONE;
- }
- lttng_chan_buf->ops->event_write(ctx, &sel_char, sizeof(sel_char), lttng_ust_rb_alignof(char));
- if (str) {
- lttng_chan_buf->ops->event_write(ctx, str, strlen(str) + 1, 1);
- }
- break;
- }
- default:
- abort();
- }
-}
-
-static void get_value_cb(void *priv, struct lttng_ust_ctx_value *value)
-{
- struct lttng_ust_jni_provider *jni_provider = (struct lttng_ust_jni_provider *) priv;
- struct lttng_ust_jni_ctx_entry *jctx;
- const char *ctx_name = jni_provider->name;
- enum lttng_ust_jni_type jni_type;
-
- jctx = lookup_ctx_by_name(ctx_name);
- if (!jctx) {
- jni_type = JNI_TYPE_NULL;
- } else {
- jni_type = jctx->type;
- }
-
- switch (jni_type) {
- case JNI_TYPE_NULL:
- value->sel = LTTNG_UST_DYNAMIC_TYPE_NONE;
- break;
- case JNI_TYPE_INTEGER:
- value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
- value->u.s64 = (int64_t) jctx->value._integer;
- break;
- case JNI_TYPE_LONG:
- value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
- value->u.s64 = jctx->value._long;
- break;
- case JNI_TYPE_DOUBLE:
- value->sel = LTTNG_UST_DYNAMIC_TYPE_DOUBLE;
- value->u.d = jctx->value._double;
- break;
- case JNI_TYPE_FLOAT:
- value->sel = LTTNG_UST_DYNAMIC_TYPE_DOUBLE;
- value->u.d = (double) jctx->value._float;
- break;
- case JNI_TYPE_SHORT:
- value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
- value->u.s64 = (int64_t) jctx->value._short;
- break;
- case JNI_TYPE_BYTE:
- value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
- value->u.s64 = (int64_t) jctx->value._byte;
- break;
- case JNI_TYPE_BOOLEAN:
- value->sel = LTTNG_UST_DYNAMIC_TYPE_S64;
- value->u.s64 = (int64_t) jctx->value._boolean;
- break;
- case JNI_TYPE_STRING:
- {
- int32_t offset = jctx->value._string_offset;
- const char *str = get_ctx_string_at_offset(offset);
-
- if (str) {
- value->sel = LTTNG_UST_DYNAMIC_TYPE_STRING;
- value->u.str = str;
- } else {
- value->sel = LTTNG_UST_DYNAMIC_TYPE_NONE;
- }
- break;
- }
- default:
- abort();
- }
-}
-
-/*
- * Register a context provider to UST.
- *
- * Called from the Java side when an application registers a context retriever,
- * so we create and register a corresponding provider on the C side.
- */
-JNIEXPORT jlong JNICALL Java_org_lttng_ust_agent_context_LttngContextApi_registerProvider(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring provider_name)
-{
- jboolean iscopy;
- const char *provider_name_jstr;
- char *provider_name_cstr;
- struct lttng_ust_context_provider *provider;
- struct lttng_ust_jni_provider *jni_provider;
- /*
- * Note: a "jlong" is 8 bytes on all architectures, whereas a
- * C "long" varies.
- */
- jlong provider_ref;
-
- provider_name_jstr = (*env)->GetStringUTFChars(env, provider_name, &iscopy);
- if (!provider_name_jstr) {
- goto error_jstr;
- }
- /* Keep our own copy of the string so UST can use it. */
- provider_name_cstr = strdup(provider_name_jstr);
- (*env)->ReleaseStringUTFChars(env, provider_name, provider_name_jstr);
- if (!provider_name_cstr) {
- goto error_strdup;
- }
- jni_provider = zmalloc(sizeof(*jni_provider));
- if (!jni_provider) {
- goto error_provider;
- }
- provider = &jni_provider->provider;
- provider->struct_size = sizeof(*provider);
- jni_provider->name = provider_name_cstr;
- provider->name = jni_provider->name;
- provider->get_size = get_size_cb;
- provider->record = record_cb;
- provider->get_value = get_value_cb;
- provider->priv = jni_provider;
-
- jni_provider->reg_provider = lttng_ust_context_provider_register(provider);
- if (!jni_provider->reg_provider) {
- goto error_register;
- }
-
- provider_ref = (jlong) (long) jni_provider;
- return provider_ref;
-
- /* Error handling. */
-error_register:
- free(jni_provider);
-error_provider:
- free(provider_name_cstr);
-error_strdup:
-error_jstr:
- return 0;
-}
-
-/*
- * Unregister a previously-registered context provider.
- *
- * Called from the Java side when an application unregisters a context retriever,
- * so we unregister and delete the corresponding provider on the C side.
- */
-JNIEXPORT void JNICALL Java_org_lttng_ust_agent_context_LttngContextApi_unregisterProvider(JNIEnv *env __attribute__((unused)),
- jobject jobj __attribute__((unused)),
- jlong provider_ref)
-{
- struct lttng_ust_jni_provider *jni_provider =
- (struct lttng_ust_jni_provider *) (unsigned long) provider_ref;
-
- if (!jni_provider) {
- return;
- }
-
- lttng_ust_context_provider_unregister(jni_provider->reg_provider);
-
- free(jni_provider->name);
- free(jni_provider);
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- */
-
-#ifndef LIBLTTNG_UST_JAVA_AGENT_JNI_COMMON_LTTNG_UST_CONTEXT_H_
-#define LIBLTTNG_UST_JAVA_AGENT_JNI_COMMON_LTTNG_UST_CONTEXT_H_
-
-struct lttng_ust_jni_ctx_entry;
-
-struct lttng_ust_jni_tls {
- struct lttng_ust_jni_ctx_entry *ctx_entries;
- int32_t ctx_entries_len;
- signed char *ctx_strings;
- int32_t ctx_strings_len;
-};
-
-extern __thread struct lttng_ust_jni_tls lttng_ust_context_info_tls;
-
-#endif /* LIBLTTNG_UST_JAVA_AGENT_JNI_COMMON_LTTNG_UST_CONTEXT_H_ */
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-AM_CPPFLAGS += -I$(builddir) -I$(srcdir) $(JNI_CPPFLAGS)
-
-lib_LTLIBRARIES = liblttng-ust-jul-jni.la
-liblttng_ust_jul_jni_la_SOURCES = lttng_ust_jul.c \
- lttng_ust_jul.h
-
-nodist_liblttng_ust_jul_jni_la_SOURCES = org_lttng_ust_agent_jul_LttngJulApi.h
-
-liblttng_ust_jul_jni_la_LIBADD = -lc \
- $(top_builddir)/src/liblttng-ust/liblttng-ust.la \
- $(top_builddir)/src/liblttng-ust-java-agent/jni/common/liblttng-ust-context-jni.la
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#define _LGPL_SOURCE
-#include "org_lttng_ust_agent_jul_LttngJulApi.h"
-
-#define TRACEPOINT_DEFINE
-#define TRACEPOINT_CREATE_PROBES
-#include "lttng_ust_jul.h"
-#include "../common/lttng_ust_context.h"
-
-/*
- * Deprecated function from before the context information was passed.
- */
-JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LttngJulApi_tracepoint(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring msg,
- jstring logger_name,
- jstring class_name,
- jstring method_name,
- jlong millis,
- jint log_level,
- jint thread_id)
-{
- jboolean iscopy;
- const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
- const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy);
- const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy);
- const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy);
-
- tracepoint(lttng_jul, event, msg_cstr, logger_name_cstr,
- class_name_cstr, method_name_cstr, millis, log_level, thread_id);
-
- (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
- (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
- (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
- (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
-}
-
-/*
- * Tracepoint used by Java applications using the JUL handler.
- */
-JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LttngJulApi_tracepointWithContext(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring msg,
- jstring logger_name,
- jstring class_name,
- jstring method_name,
- jlong millis,
- jint log_level,
- jint thread_id,
- jbyteArray context_info_entries,
- jbyteArray context_info_strings)
-{
- jboolean iscopy;
- const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
- const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy);
- const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy);
- const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy);
- signed char *context_info_entries_array;
- signed char *context_info_strings_array;
-
- /*
- * Write these to the TLS variables, so that the UST callbacks in
- * lttng_ust_context.c can access them.
- */
- context_info_entries_array = (*env)->GetByteArrayElements(env, context_info_entries, &iscopy);
- lttng_ust_context_info_tls.ctx_entries = (struct lttng_ust_jni_ctx_entry *) context_info_entries_array;
- lttng_ust_context_info_tls.ctx_entries_len = (*env)->GetArrayLength(env, context_info_entries);
- context_info_strings_array = (*env)->GetByteArrayElements(env, context_info_strings, &iscopy);
- lttng_ust_context_info_tls.ctx_strings = context_info_strings_array;
- lttng_ust_context_info_tls.ctx_strings_len = (*env)->GetArrayLength(env, context_info_strings);
-
- tracepoint(lttng_jul, event, msg_cstr, logger_name_cstr,
- class_name_cstr, method_name_cstr, millis, log_level, thread_id);
-
- lttng_ust_context_info_tls.ctx_entries = NULL;
- lttng_ust_context_info_tls.ctx_entries_len = 0;
- lttng_ust_context_info_tls.ctx_strings = NULL;
- lttng_ust_context_info_tls.ctx_strings_len = 0;
- (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
- (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
- (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
- (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
- (*env)->ReleaseByteArrayElements(env, context_info_entries, context_info_entries_array, 0);
- (*env)->ReleaseByteArrayElements(env, context_info_strings, context_info_strings_array, 0);
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#undef TRACEPOINT_PROVIDER
-#define TRACEPOINT_PROVIDER lttng_jul
-
-#if !defined(_TRACEPOINT_LTTNG_UST_JUL_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
-#define _TRACEPOINT_LTTNG_UST_JUL_H
-
-#include <lttng/tracepoint.h>
-
-/*
- * Tracepoint used by Java applications using the JUL handler.
- */
-TRACEPOINT_EVENT(lttng_jul, event,
- TP_ARGS(
- const char *, msg,
- const char *, logger_name,
- const char *, class_name,
- const char *, method_name,
- long, millis,
- int, log_level,
- int, thread_id),
- TP_FIELDS(
- ctf_string(msg, msg)
- ctf_string(logger_name, logger_name)
- ctf_string(class_name, class_name)
- ctf_string(method_name, method_name)
- ctf_integer(long, long_millis, millis)
- ctf_integer(int, int_loglevel, log_level)
- ctf_integer(int, int_threadid, thread_id)
- )
-)
-
-#endif /* _TRACEPOINT_LTTNG_UST_JUL_H */
-
-#undef TRACEPOINT_INCLUDE
-#define TRACEPOINT_INCLUDE "./lttng_ust_jul.h"
-
-/* This part must be outside protection */
-#include <lttng/tracepoint-event.h>
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-AM_CPPFLAGS += -I$(builddir) -I$(srcdir) $(JNI_CPPFLAGS)
-
-lib_LTLIBRARIES = liblttng-ust-log4j-jni.la
-liblttng_ust_log4j_jni_la_SOURCES = lttng_ust_log4j.c \
- lttng_ust_log4j.h
-
-nodist_liblttng_ust_log4j_jni_la_SOURCES = org_lttng_ust_agent_log4j_LttngLog4jApi.h
-
-liblttng_ust_log4j_jni_la_LIBADD = -lc \
- $(top_builddir)/src/liblttng-ust/liblttng-ust.la \
- $(top_builddir)/src/liblttng-ust-java-agent/jni/common/liblttng-ust-context-jni.la
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2016 EfficiOS Inc.
- * Copyright (C) 2016 Alexandre Montplaisir <alexmonthy@efficios.com>
- * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#define _LGPL_SOURCE
-#include "org_lttng_ust_agent_log4j_LttngLog4jApi.h"
-
-#define TRACEPOINT_DEFINE
-#define TRACEPOINT_CREATE_PROBES
-#include "lttng_ust_log4j.h"
-#include "../common/lttng_ust_context.h"
-
-/*
- * Deprecated function from before the context information was passed.
- */
-JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LttngLog4jApi_tracepoint(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring msg,
- jstring logger_name,
- jstring class_name,
- jstring method_name,
- jstring file_name,
- jint line_number,
- jlong timestamp,
- jint loglevel,
- jstring thread_name)
-{
- jboolean iscopy;
- const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
- const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy);
- const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy);
- const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy);
- const char *file_name_cstr = (*env)->GetStringUTFChars(env, file_name, &iscopy);
- const char *thread_name_cstr = (*env)->GetStringUTFChars(env, thread_name, &iscopy);
-
- tracepoint(lttng_log4j, event, msg_cstr, logger_name_cstr,
- class_name_cstr, method_name_cstr, file_name_cstr,
- line_number, timestamp, loglevel, thread_name_cstr);
-
- (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
- (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
- (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
- (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
- (*env)->ReleaseStringUTFChars(env, file_name, file_name_cstr);
- (*env)->ReleaseStringUTFChars(env, thread_name, thread_name_cstr);
-}
-
-/*
- * Tracepoint used by Java applications using the log4j handler.
- */
-JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LttngLog4jApi_tracepointWithContext(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring msg,
- jstring logger_name,
- jstring class_name,
- jstring method_name,
- jstring file_name,
- jint line_number,
- jlong timestamp,
- jint loglevel,
- jstring thread_name,
- jbyteArray context_info_entries,
- jbyteArray context_info_strings)
-{
- jboolean iscopy;
- const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
- const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy);
- const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy);
- const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy);
- const char *file_name_cstr = (*env)->GetStringUTFChars(env, file_name, &iscopy);
- const char *thread_name_cstr = (*env)->GetStringUTFChars(env, thread_name, &iscopy);
- signed char *context_info_entries_array;
- signed char *context_info_strings_array;
-
- /*
- * Write these to the TLS variables, so that the UST callbacks in
- * lttng_ust_context.c can access them.
- */
- context_info_entries_array = (*env)->GetByteArrayElements(env, context_info_entries, &iscopy);
- lttng_ust_context_info_tls.ctx_entries = (struct lttng_ust_jni_ctx_entry *) context_info_entries_array;
- lttng_ust_context_info_tls.ctx_entries_len = (*env)->GetArrayLength(env, context_info_entries);
- context_info_strings_array = (*env)->GetByteArrayElements(env, context_info_strings, &iscopy);
- lttng_ust_context_info_tls.ctx_strings = context_info_strings_array;
- lttng_ust_context_info_tls.ctx_strings_len = (*env)->GetArrayLength(env, context_info_strings);
-
- tracepoint(lttng_log4j, event, msg_cstr, logger_name_cstr,
- class_name_cstr, method_name_cstr, file_name_cstr,
- line_number, timestamp, loglevel, thread_name_cstr);
-
- lttng_ust_context_info_tls.ctx_entries = NULL;
- lttng_ust_context_info_tls.ctx_entries_len = 0;
- lttng_ust_context_info_tls.ctx_strings = NULL;
- lttng_ust_context_info_tls.ctx_strings_len = 0;
- (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
- (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
- (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
- (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
- (*env)->ReleaseStringUTFChars(env, file_name, file_name_cstr);
- (*env)->ReleaseStringUTFChars(env, thread_name, thread_name_cstr);
- (*env)->ReleaseByteArrayElements(env, context_info_entries, context_info_entries_array, 0);
- (*env)->ReleaseByteArrayElements(env, context_info_strings, context_info_strings_array, 0);
-}
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#undef TRACEPOINT_PROVIDER
-#define TRACEPOINT_PROVIDER lttng_log4j
-
-#if !defined(_TRACEPOINT_LTTNG_UST_LOG4J_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
-#define _TRACEPOINT_LTTNG_UST_LOG4J_H
-
-#include <lttng/tracepoint.h>
-
-/*
- * Tracepoint used by Java applications using the log4j log appender.
- */
-TRACEPOINT_EVENT(lttng_log4j, event,
- TP_ARGS(
- const char *, msg,
- const char *, logger_name,
- const char *, class_name,
- const char *, method_name,
- const char *, file_name,
- int, line_number,
- long, timestamp,
- int, log_level,
- const char *, thread_name),
- TP_FIELDS(
- ctf_string(msg, msg)
- ctf_string(logger_name, logger_name)
- ctf_string(class_name, class_name)
- ctf_string(method_name, method_name)
- ctf_string(filename, file_name)
- ctf_integer(int, line_number, line_number)
- ctf_integer(long, timestamp, timestamp)
- ctf_integer(int, int_loglevel, log_level)
- ctf_string(thread_name, thread_name)
- )
-)
-
-#endif /* _TRACEPOINT_LTTNG_UST_LOG4J_H */
-
-#undef TRACEPOINT_INCLUDE
-#define TRACEPOINT_INCLUDE "./lttng_ust_log4j.h"
-
-/* This part must be outside protection */
-#include <lttng/tracepoint-event.h>
+++ /dev/null
-org_lttng_ust_LTTngUst.h
-org/
-liblttng-ust-java.jar
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#define _LGPL_SOURCE
-#include "org_lttng_ust_LTTngUst.h"
-
-#define TRACEPOINT_DEFINE
-#define TRACEPOINT_CREATE_PROBES
-#include "lttng_ust_java.h"
-
-JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointInt(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring ev_name,
- jint payload)
-{
- jboolean iscopy;
- const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
-
- tracepoint(lttng_ust_java, int_event, ev_name_cstr, payload);
-
- (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
-}
-
-JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointIntInt(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring ev_name,
- jint payload1,
- jint payload2)
-{
- jboolean iscopy;
- const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
-
- tracepoint(lttng_ust_java, int_int_event, ev_name_cstr, payload1, payload2);
-
- (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
-}
-
-JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointLong(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring ev_name,
- jlong payload)
-{
- jboolean iscopy;
- const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
-
- tracepoint(lttng_ust_java, long_event, ev_name_cstr, payload);
-
- (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
-}
-
-JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointLongLong(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring ev_name,
- jlong payload1,
- jlong payload2)
-{
- jboolean iscopy;
- const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
-
- tracepoint(lttng_ust_java, long_long_event, ev_name_cstr, payload1, payload2);
-
- (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
-}
-
-JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepointString(JNIEnv *env,
- jobject jobj __attribute__((unused)),
- jstring ev_name,
- jstring payload)
-{
- jboolean iscopy;
- const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name, &iscopy);
- const char *payload_cstr = (*env)->GetStringUTFChars(env, payload, &iscopy);
-
- tracepoint(lttng_ust_java, string_event, ev_name_cstr, payload_cstr);
-
- (*env)->ReleaseStringUTFChars(env, ev_name, ev_name_cstr);
- (*env)->ReleaseStringUTFChars(env, payload, payload_cstr);
-}
-
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-only
-
-JAVAROOT = .
-jarfile = liblttng-ust-java.jar
-jardir = $(datadir)/java
-pkgpath = org/lttng/ust
-
-dist_noinst_JAVA = $(pkgpath)/LTTngUst.java
-jar_DATA = $(jarfile)
-BUILT_SOURCES = org_lttng_ust_LTTngUst.h
-
-AM_CPPFLAGS += -I$(builddir) -I$(srcdir) $(JNI_CPPFLAGS)
-lib_LTLIBRARIES = liblttng-ust-java.la
-liblttng_ust_java_la_SOURCES = LTTngUst.c lttng_ust_java.h
-nodist_liblttng_ust_java_la_SOURCES = org_lttng_ust_LTTngUst.h
-
-liblttng_ust_java_la_LIBADD = -lc \
- $(top_builddir)/src/liblttng-ust/liblttng-ust.la
-
-$(jarfile): classnoinst.stamp
- $(JAR) cf $(JARFLAGS) $@ $(pkgpath)/*.class
-
-if !HAVE_JAVAH
-# If we don't have javah, assume we are running openjdk >= 10 and use javac
-# to generate the jni header file.
-AM_JAVACFLAGS = -h .
-
-org_lttng_ust_LTTngUst.h: $(jarfile)
-else
-org_lttng_ust_LTTngUst.h: jni-header.stamp
-
-jni-header.stamp: $(dist_noinst_JAVA)
- $(JAVAH) -classpath $(srcdir) $(JAVAHFLAGS) org.lttng.ust.LTTngUst && \
- echo "JNI header generated" > jni-header.stamp
-endif
-
-all-local: org_lttng_ust_LTTngUst.h
-
-EXTRA_DIST = README
-
-CLEANFILES = $(jarfile) $(pkgpath)/*.class jni-header.stamp org_lttng_ust_LTTngUst.h
+++ /dev/null
-This directory contains a simple API for instrumenting java applications.
-
-Configuration examples to build this library:
-
-dependency: openjdk-7-jdk
-./configure --enable-jni-interface
-
-Note that the OpenJDK 7 is used for development and continuous integration thus
-we directly support that version for this library. However, it has been tested
-with OpenJDK 6 also. Please let us know if other Java version (commercial or
-not) work with this library.
-
-After building, you can use the liblttng-ust-java.jar file in a Java project.
-It requires the liblttng-ust-java.so* files (which get installed when doing
-`make install') so make sure those are in the linker's library path.
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#undef TRACEPOINT_PROVIDER
-#define TRACEPOINT_PROVIDER lttng_ust_java
-
-#if !defined(_TRACEPOINT_LTTNG_UST_JAVA_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
-#define _TRACEPOINT_LTTNG_UST_JAVA_H
-
-#include <lttng/tracepoint.h>
-
-TRACEPOINT_EVENT(lttng_ust_java, int_event,
- TP_ARGS(const char *, name, int, payload),
- TP_FIELDS(
- ctf_string(name, name)
- ctf_integer(int, int_payload, payload)
- )
-)
-
-TRACEPOINT_EVENT(lttng_ust_java, int_int_event,
- TP_ARGS(const char *, name, int, payload1, int, payload2),
- TP_FIELDS(
- ctf_string(name, name)
- ctf_integer(int, int_payload1, payload1)
- ctf_integer(int, int_payload2, payload2)
- )
-)
-
-TRACEPOINT_EVENT(lttng_ust_java, long_event,
- TP_ARGS(const char *, name, long, payload),
- TP_FIELDS(
- ctf_string(name, name)
- ctf_integer(long, long_payload, payload)
- )
-)
-
-TRACEPOINT_EVENT(lttng_ust_java, long_long_event,
- TP_ARGS(const char *, name, long, payload1, long, payload2),
- TP_FIELDS(
- ctf_string(name, name)
- ctf_integer(long, long_payload1, payload1)
- ctf_integer(long, long_payload2, payload2)
- )
-)
-
-TRACEPOINT_EVENT(lttng_ust_java, string_event,
- TP_ARGS(const char *, name, const char *, payload),
- TP_FIELDS(
- ctf_string(name, name)
- ctf_string(string_payload, payload)
- )
-)
-
-#endif /* _TRACEPOINT_LTTNG_UST_JAVA_H */
-
-#undef TRACEPOINT_INCLUDE
-#define TRACEPOINT_INCLUDE "./lttng_ust_java.h"
-
-/* This part must be outside protection */
-#include <lttng/tracepoint-event.h>
+++ /dev/null
-/*
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright (C) 2012 Alexandre Montplaisir <alexandre.montplaisir@polymtl.ca>
- */
-
-package org.lttng.ust;
-
-/**
- * This class implements the the Java side of the LTTng-UST Java interface.
- *
- * First, make sure you have installed "liblttng-ust-java.so" where the linker
- * can find it. You can then call LTTngUst.init() from your Java program to
- * connect the methods exposed here to the native library.
- *
- * Because of limitations in the probe declaration, all trace events generated
- * by this library will have "lttng_ust_java" for domain, and "<type>_event" for
- * event name in the CTF trace files. The "name" parameter will instead appear
- * as the first element of the event's payload.
- *
- * @author Mathieu Desnoyers
- * @author Alexandre Montplaisir
- *
- */
-public abstract class LTTngUst {
-
- /**
- * Initialize the UST tracer. This should always be called first, before any
- * tracepoint* method.
- */
- public static void init() {
- System.loadLibrary("lttng-ust-java"); //$NON-NLS-1$
- }
-
- /**
- * Insert a tracepoint with a payload of type Integer.
- *
- * @param name
- * The name assigned to this event. For best performance, this
- * should be a statically-defined String, or a literal.
- * @param payload
- * The int payload
- */
- public static native void tracepointInt(String name, int payload);
-
- /**
- * Insert a tracepoint with a payload consisting of two integers.
- *
- * @param name
- * The name assigned to this event. For best performance, this
- * should be a statically-defined String, or a literal.
- * @param payload1
- * The first int payload
- * @param payload2
- * The second int payload
- */
- public static native void
- tracepointIntInt(String name, int payload1, int payload2);
-
- /**
- * Insert a tracepoint with a payload of type Long
- *
- * @param name
- * The name assigned to this event. For best performance, this
- * should be a statically-defined String, or a literal.
- * @param payload
- * The long payload
- */
- public static native void tracepointLong(String name, long payload);
-
- /**
- * Insert a tracepoint with a payload consisting of two longs.
- *
- * @param name
- * The name assigned to this event. For best performance, this
- * should be a statically-defined String, or a literal.
- * @param payload1
- * The first long payload
- * @param payload2
- * The second long payload
- */
- public static native void
- tracepointLongLong(String name, long payload1, long payload2);
-
- /**
- * Insert a tracepoint with a String payload.
- *
- * @param name
- * The name assigned to this event. For best performance, this
- * should be a statically-defined String, or a literal.
- * @param payload
- * The String payload
- */
- public static native void tracepointString(String name, String payload);
-
-}
-