From: Alexandre Montplaisir
Date: Thu, 7 Jan 2016 22:43:34 +0000 (-0500)
Subject: Implement Java agent application context retrieval
X-Git-Tag: v2.8.0-rc1~39
X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=8ab5c06b92ac9a06ba2743470a38e4e1cfc6a3c9;p=lttng-ust.git
Implement Java agent application context retrieval
Java application can now register an IContextInfoRetriever to provide
context information. This information can be used for filtering:
lttng enable-event -j myevent --filter '$app.retriever:context=="something"'
or for saving in the trace directly by enabling the context:
lttng add-context -j -t '$app.retriever:context'
See the "ApplicationContextExample.java" program for an example of
utilization.
Signed-off-by: Alexandre Montplaisir
Signed-off-by: Mathieu Desnoyers
---
diff --git a/.gitignore b/.gitignore
index d480867e..87d55d9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,8 +59,10 @@ tests/benchmark/bench2
lttng-ust-agent*.jar
classnoinst.stamp
jni-header.stamp
+context-jni-header.stamp
jul-jni-header.stamp
log4j-jni-header.stamp
+org_lttng_ust_agent_context_LttngContextApi.h
org_lttng_ust_agent_jul_LttngJulApi.h
org_lttng_ust_agent_log4j_LttngLog4jApi.h
diff --git a/configure.ac b/configure.ac
index 9d972ccf..7dcaab85 100644
--- a/configure.ac
+++ b/configure.ac
@@ -389,6 +389,7 @@ AC_CONFIG_FILES([
liblttng-ust-java-agent/java/lttng-ust-agent-jul/Makefile
liblttng-ust-java-agent/java/lttng-ust-agent-log4j/Makefile
liblttng-ust-java-agent/jni/Makefile
+ liblttng-ust-java-agent/jni/common/Makefile
liblttng-ust-java-agent/jni/jul/Makefile
liblttng-ust-java-agent/jni/log4j/Makefile
liblttng-ust-libc-wrapper/Makefile
diff --git a/doc/examples/java-jul/ApplicationContextExample.java b/doc/examples/java-jul/ApplicationContextExample.java
new file mode 100644
index 00000000..3da3ac82
--- /dev/null
+++ b/doc/examples/java-jul/ApplicationContextExample.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+import java.io.IOException;
+import java.util.logging.Handler;
+import java.util.logging.Logger;
+
+import org.lttng.ust.agent.context.ContextInfoManager;
+import org.lttng.ust.agent.context.IContextInfoRetriever;
+import org.lttng.ust.agent.jul.LttngLogHandler;
+
+/**
+ * Example program defining a application context retriever, which allows
+ * attaching application-defined contexts to trace events.
+ *
+ * FIXME Use custom context names, and several names/types
+ *
+ *
+ * Usage:
+ *
+ * - $ lttng create
+ * - $ lttng enable-event -j -a
+ * - $ lttng add-context -j -t '$app.myprovider:mystringcontext'
+ * - $ lttng add-context -j -t '$app.myprovider:myshortcontext'
+ * - $ lttng start
+ * - (run this program)
+ * - $ lttng stop
+ * - $ lttng view
+ * - $ lttng destroy
+ *
+ *
+ *
+ * The events present in the resulting trace should carry the context
+ * information defined in the example retriever.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class ApplicationContextExample {
+
+ /** Class-wide JUL logger object */
+ private static final Logger LOGGER = Logger.getLogger(ApplicationContextExample.class.getName());
+
+ private static final String RETRIEVER_NAME = "myprovider";
+ private static final String CONTEXT_NAME_STRING = "mystringcontext";
+ private static final String CONTEXT_NAME_SHORT = "myshortcontext";
+
+ private static class ExampleContextInfoRetriever implements IContextInfoRetriever {
+
+ @Override
+ public Object retrieveContextInfo(String key) {
+ switch (key) {
+ case CONTEXT_NAME_SHORT:
+ return (short) 42;
+ case CONTEXT_NAME_STRING:
+ return "context-value!";
+ default:
+ return null;
+ }
+ }
+
+ }
+
+ /**
+ * Application start
+ *
+ * @param args
+ * Command-line arguments
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public static void main(String args[]) throws IOException, InterruptedException {
+ /* Instantiate and attach a logger object */
+ Handler lttngHandler = new LttngLogHandler();
+ LOGGER.addHandler(lttngHandler);
+
+ /* Instantiate and register the context retriever */
+ IContextInfoRetriever cir = new ExampleContextInfoRetriever();
+ ContextInfoManager.getInstance().registerContextInfoRetriever(RETRIEVER_NAME, cir);
+
+ /*
+ * Make sure you have a LTTng session running with the appropriate
+ * events and contexts enabled! See the class Javadoc.
+ */
+
+ /* Trigger a tracing event using the JUL Logger created before. */
+ LOGGER.info("Log event #1");
+ LOGGER.warning("Log event #2");
+ LOGGER.severe("Log event #3");
+
+ /* Unregister our context retriever, and dispose the log handler */
+ ContextInfoManager.getInstance().unregisterContextInfoRetriever(RETRIEVER_NAME);
+ lttngHandler.close();
+ }
+}
diff --git a/doc/examples/java-jul/Makefile b/doc/examples/java-jul/Makefile
index f4dacdd6..3eb68708 100644
--- a/doc/examples/java-jul/Makefile
+++ b/doc/examples/java-jul/Makefile
@@ -40,7 +40,7 @@ JC = javac -classpath "$(CLASSPATH):."
.java.class:
$(JC) $(JFLAGS) $*.java
-CLASSES = Hello.java FilterChangeListenerExample.java
+CLASSES = Hello.java FilterChangeListenerExample.java ApplicationContextExample.java
all: classes
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile.am b/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile.am
index 33476ae3..683e451b 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile.am
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/Makefile.am
@@ -7,8 +7,8 @@ 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)/ILttngAgent.java \
@@ -19,10 +19,13 @@ dist_noinst_JAVA = $(pkgpath)/AbstractLttngAgent.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/ContextInfoManager.java \
+ $(pkgpath)/context/ContextInfoSerializer.java \
$(pkgpath)/context/IContextInfoRetriever.java \
$(pkgpath)/filter/FilterChangeNotifier.java \
$(pkgpath)/filter/IFilterChangeListener.java \
@@ -34,12 +37,20 @@ dist_noinst_DATA = $(jarfile_manifest)
jar_DATA = $(jarfile)
-classes = $(pkgpath)/*.class $(pkgpath)/client/*.class $(pkgpath)/context/*.class $(pkgpath)/filter/*.class $(pkgpath)/session/*.class
+classes = $(pkgpath)/*.class \
+ $(pkgpath)/client/*.class \
+ $(pkgpath)/context/*.class \
+ $(pkgpath)/filter/*.class \
+ $(pkgpath)/session/*.class
$(jarfile): classnoinst.stamp
$(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink)
-all-local:
+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
install-data-hook:
cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink) && $(LN_S) $(jarfile) $(jarfile_symlink)
@@ -47,4 +58,9 @@ install-data-hook:
uninstall-hook:
cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink)
-CLEANFILES = $(jarfile) $(pkgpath)/*.class $(pkgpath)/client/*.class $(pkgpath)/context/*.class $(pkgpath)/filter/*.class $(pkgpath)/session/*.class
+CLEANFILES = $(jarfile) $(pkgpath)/*.class \
+ $(pkgpath)/client/*.class \
+ $(pkgpath)/context/*.class \
+ $(pkgpath)/filter/*.class \
+ $(pkgpath)/session/*.class \
+ $(juljniout)/org_lttng_ust_agent_context_LttngContextApi.h
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java
index e97a7bdb..22c58265 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/AbstractLttngAgent.java
@@ -75,6 +75,19 @@ public abstract class AbstractLttngAgent
/** Number of sessions currently enabling the wildcard "*" event */
private final AtomicInteger enabledWildcards = new AtomicInteger(0);
+ /**
+ * 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> enabledAppContexts = new ConcurrentHashMap>();
+
/** Tracing domain. Defined by the sub-classes via the constructor. */
private final Domain domain;
@@ -186,7 +199,6 @@ public abstract class AbstractLttngAgent
enabledWildcards.set(0);
initialized = false;
-
}
@Override
@@ -203,10 +215,10 @@ public abstract class AbstractLttngAgent
if (eventName.endsWith(WILDCARD)) {
/* Strip the "*" from the name. */
String prefix = eventName.substring(0, eventName.length() - 1);
- return incrementEventCount(prefix, enabledEventPrefixes);
+ return incrementRefCount(prefix, enabledEventPrefixes);
}
- return incrementEventCount(eventName, enabledEvents);
+ return incrementRefCount(eventName, enabledEvents);
}
@Override
@@ -227,10 +239,44 @@ public abstract class AbstractLttngAgent
if (eventName.endsWith(WILDCARD)) {
/* Strip the "*" from the name. */
String prefix = eventName.substring(0, eventName.length() - 1);
- return decrementEventCount(prefix, enabledEventPrefixes);
+ return decrementRefCount(prefix, enabledEventPrefixes);
+ }
+
+ return decrementRefCount(eventName, enabledEvents);
+ }
+
+ @Override
+ public boolean appContextEnabled(String contextRetrieverName, String contextName) {
+ synchronized (enabledAppContexts) {
+ Map retrieverMap = enabledAppContexts.get(contextRetrieverName);
+ if (retrieverMap == null) {
+ /* There is no submap for this retriever, let's create one. */
+ retrieverMap = new ConcurrentHashMap();
+ enabledAppContexts.put(contextRetrieverName, retrieverMap);
+ }
+
+ return incrementRefCount(contextName, retrieverMap);
}
+ }
+
+ @Override
+ public boolean appContextDisabled(String contextRetrieverName, String contextName) {
+ synchronized (enabledAppContexts) {
+ Map retrieverMap = enabledAppContexts.get(contextRetrieverName);
+ if (retrieverMap == null) {
+ /* There was no submap for this retriever, invalid command? */
+ return false;
+ }
+
+ boolean ret = decrementRefCount(contextName, retrieverMap);
- return decrementEventCount(eventName, enabledEvents);
+ /* If the submap is now empty we can remove it from the main map. */
+ if (retrieverMap.isEmpty()) {
+ enabledAppContexts.remove(contextRetrieverName);
+ }
+
+ return ret;
+ }
}
/*
@@ -260,12 +306,17 @@ public abstract class AbstractLttngAgent
return false;
}
- private static boolean incrementEventCount(String eventName, Map eventMap) {
- synchronized (eventMap) {
- Integer count = eventMap.get(eventName);
+ @Override
+ public Collection>> getEnabledAppContexts() {
+ return enabledAppContexts.entrySet();
+ }
+
+ private static boolean incrementRefCount(String key, Map refCountMap) {
+ synchronized (refCountMap) {
+ Integer count = refCountMap.get(key);
if (count == null) {
/* This is the first instance of this event being enabled */
- eventMap.put(eventName, Integer.valueOf(1));
+ refCountMap.put(key, Integer.valueOf(1));
return true;
}
if (count.intValue() <= 0) {
@@ -273,14 +324,14 @@ public abstract class AbstractLttngAgent
throw new IllegalStateException();
}
/* The event was already enabled, increment its refcount */
- eventMap.put(eventName, Integer.valueOf(count.intValue() + 1));
+ refCountMap.put(key, Integer.valueOf(count.intValue() + 1));
return true;
}
}
- private static boolean decrementEventCount(String eventName, Map eventMap) {
- synchronized (eventMap) {
- Integer count = eventMap.get(eventName);
+ private static boolean decrementRefCount(String key, Map 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
@@ -293,14 +344,14 @@ public abstract class AbstractLttngAgent
* This is the last instance of this event being disabled,
* remove it from the map so that we stop sending it.
*/
- eventMap.remove(eventName);
+ refCountMap.remove(key);
return true;
}
/*
* Other sessions are still looking for this event, simply decrement
* its refcount.
*/
- eventMap.put(eventName, Integer.valueOf(count.intValue() - 1));
+ refCountMap.put(key, Integer.valueOf(count.intValue() - 1));
return true;
}
}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngAgent.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngAgent.java
index fd20a9eb..5de09242 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngAgent.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/ILttngAgent.java
@@ -17,6 +17,9 @@
package org.lttng.ust.agent;
+import java.util.Collection;
+import java.util.Map;
+
/**
* Interface to define LTTng Java agents.
*
@@ -93,4 +96,12 @@ public interface ILttngAgent {
* @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>> getEnabledAppContexts();
}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ILttngTcpClientListener.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ILttngTcpClientListener.java
index ef0c11d9..21189730 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ILttngTcpClientListener.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/ILttngTcpClientListener.java
@@ -54,6 +54,36 @@ public interface ILttngTcpClientListener {
*/
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.
*
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngAgentResponse.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngAgentResponse.java
index 3c5a27e5..40c38a55 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngAgentResponse.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngAgentResponse.java
@@ -40,7 +40,7 @@ abstract class LttngAgentResponse {
CODE_SUCCESS_CMD(1),
CODE_INVALID_CMD(2),
- CODE_UNK_LOGGER_NAME(3);
+ CODE_UNKNOWN_LOGGER_NAME(3);
private int code;
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java
index 2b31889c..5b5d50c4 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2015-2016 EfficiOS Inc., Alexandre Montplaisir
* Copyright (C) 2013 - David Goulet
*
* This library is free software; you can redistribute it and/or modify it
@@ -255,27 +256,51 @@ public class LttngTcpSessiondClient implements Runnable {
responseData = response.getBytes();
break;
}
- case CMD_ENABLE:
+ case CMD_EVENT_ENABLE:
{
if (inputData == null) {
/* Invalid command */
responseData = LttngAgentResponse.FAILURE_RESPONSE.getBytes();
break;
}
- SessiondCommand enableCmd = new SessiondEnableEventCommand(inputData);
- LttngAgentResponse response = enableCmd.execute(logAgent);
+ SessiondCommand enableEventCmd = new SessiondEnableEventCommand(inputData);
+ LttngAgentResponse response = enableEventCmd.execute(logAgent);
responseData = response.getBytes();
break;
}
- case CMD_DISABLE:
+ case CMD_EVENT_DISABLE:
{
if (inputData == null) {
/* Invalid command */
responseData = LttngAgentResponse.FAILURE_RESPONSE.getBytes();
break;
}
- SessiondCommand disableCmd = new SessiondDisableEventCommand(inputData);
- LttngAgentResponse response = disableCmd.execute(logAgent);
+ SessiondCommand disableEventCmd = new SessiondDisableEventCommand(inputData);
+ LttngAgentResponse response = disableEventCmd.execute(logAgent);
+ responseData = response.getBytes();
+ break;
+ }
+ case CMD_APP_CTX_ENABLE:
+ {
+ if (inputData == null) {
+ /* This commands expects a payload, invalid command */
+ responseData = LttngAgentResponse.FAILURE_RESPONSE.getBytes();
+ break;
+ }
+ SessiondCommand enableAppCtxCmd = new SessiondEnableAppContextCommand(inputData);
+ LttngAgentResponse response = enableAppCtxCmd.execute(logAgent);
+ responseData = response.getBytes();
+ break;
+ }
+ case CMD_APP_CTX_DISABLE:
+ {
+ if (inputData == null) {
+ /* This commands expects a payload, invalid command */
+ responseData = LttngAgentResponse.FAILURE_RESPONSE.getBytes();
+ break;
+ }
+ SessiondCommand disableAppCtxCmd = new SessiondDisableAppContextCommand(inputData);
+ LttngAgentResponse response = disableAppCtxCmd.execute(logAgent);
responseData = response.getBytes();
break;
}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondCommand.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondCommand.java
index ee191da1..fd5bb1de 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondCommand.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondCommand.java
@@ -30,15 +30,18 @@ import java.nio.ByteBuffer;
abstract class SessiondCommand {
enum CommandType {
-
/** List logger(s). */
CMD_LIST(1),
/** Enable logger by name. */
- CMD_ENABLE(2),
+ CMD_EVENT_ENABLE(2),
/** Disable logger by name. */
- CMD_DISABLE(3),
+ CMD_EVENT_DISABLE(3),
/** Registration done */
- CMD_REG_DONE(4);
+ CMD_REG_DONE(4),
+ /** Enable application context */
+ CMD_APP_CTX_ENABLE(5),
+ /** Disable application context */
+ CMD_APP_CTX_DISABLE(6);
private int code;
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableAppContextCommand.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableAppContextCommand.java
new file mode 100644
index 00000000..157ad756
--- /dev/null
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableAppContextCommand.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+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;
+ }
+ };
+}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableEventCommand.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableEventCommand.java
index 920a2bf1..4a19c22e 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableEventCommand.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondDisableEventCommand.java
@@ -55,7 +55,7 @@ class SessiondDisableEventCommand extends SessiondCommand {
private static final LttngAgentResponse DISABLE_EVENT_FAILURE_RESPONSE = new LttngAgentResponse() {
@Override
public ReturnCode getReturnCode() {
- return ReturnCode.CODE_UNK_LOGGER_NAME;
+ return ReturnCode.CODE_UNKNOWN_LOGGER_NAME;
}
};
}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondEnableAppContextCommand.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondEnableAppContextCommand.java
new file mode 100644
index 00000000..a06db881
--- /dev/null
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/SessiondEnableAppContextCommand.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+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);
+ }
+}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/ContextInfoManager.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/ContextInfoManager.java
index 90a79acc..c3999b5d 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/ContextInfoManager.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/ContextInfoManager.java
@@ -17,8 +17,10 @@
package org.lttng.ust.agent.context;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* The singleton manager of {@link IContextInfoRetriever} objects.
@@ -27,9 +29,14 @@ import java.util.concurrent.CopyOnWriteArraySet;
*/
public final class ContextInfoManager {
- private static final ContextInfoManager INSTANCE = new ContextInfoManager();
+ private static final String SHARED_LIBRARY_NAME = "lttng-ust-context-jni";
- private final Set cirs = new CopyOnWriteArraySet();
+ private static ContextInfoManager instance;
+
+ private final Map contextInfoRetrievers = new ConcurrentHashMap();
+ private final Map contextInforRetrieverRefs = new HashMap();
+
+ private final Object retrieverLock = new Object();
/** Singleton class, constructor should not be accessed directly */
private ContextInfoManager() {
@@ -38,25 +45,77 @@ public final class ContextInfoManager {
/**
* Get the singleton instance.
*
+ *
+ * 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).
+ *
+ *
* @return The singleton instance
- * @deprecated The context-retrieving facilities are not yet implemented.
+ * @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.
*/
- @Deprecated
- public static ContextInfoManager getInstance() {
- return INSTANCE;
+ 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.
*
- * This method has no effect if the exact same retriever is already
- * registered.
+ *
+ * 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.
+ *
+ *
+ *
+ * If the method returns false (indicating registration failure), then the
+ * retriever object will *not* be used for context information.
+ *
*
- * @param cir
+ * @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 void addContextInfoRetriever(IContextInfoRetriever cir) {
- cirs.add(cir);
+ public boolean registerContextInfoRetriever(String retrieverName, IContextInfoRetriever contextInfoRetriever) {
+ synchronized (retrieverLock) {
+ 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;
+ }
}
/**
@@ -64,21 +123,38 @@ public final class ContextInfoManager {
*
* This method has no effect if the retriever was not already registered.
*
- * @param cir
+ * @param retrieverName
* The context info retriever to unregister
+ * @return True if unregistration was successful, false if there was an
+ * error
*/
- public void removeContextInfoRetriever(IContextInfoRetriever cir) {
- cirs.remove(cir);
+ 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 a read-only view (does not support
- * {@link java.util.Iterator#remove}) of the currently registered context
- * info retrievers.
+ * Return the context info retriever object registered with the given name.
*
- * @return The current context info retrievers
+ * @param retrieverName
+ * The retriever name to look for
+ * @return The corresponding retriever object, or null
if there
+ * was none
*/
- public Iterable getContextInfoRetrievers() {
- return cirs;
+ public IContextInfoRetriever getContextInfoRetriever(String retrieverName) {
+ return contextInfoRetrievers.get(retrieverName);
}
}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/ContextInfoSerializer.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/ContextInfoSerializer.java
new file mode 100644
index 00000000..57382bd1
--- /dev/null
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/ContextInfoSerializer.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.lttng.ust.agent.context;
+
+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;
+
+/**
+ * This class is used to serialize the list of "context info" objects to pass
+ * through JNI.
+ *
+ * The protocol expects a single byte array parameter. This byte array consists
+ * of a series of fixed-size entries, where each entry contains the following
+ * elements (with their size in bytes in parenthesis):
+ *
+ *
+ * - The full context name, like "$app.myprovider:mycontext" (256)
+ * - The context value type (1)
+ * - The context value itself(256)
+ *
+ *
+ * So the total size of each entry is 513 bytes. All unused bytes 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;
+ }
+ }
+
+ private static final String UST_APP_CTX_PREFIX = "$app.";
+ private static final int ELEMENT_LENGTH = 256;
+ private static final int ENTRY_LENGTH = 513;
+ private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder();
+ private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+ private static final byte[] EMPTY_ARRAY = 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 byte[] queryAndSerializeRequestedContexts(Collection>> enabledContexts) {
+ if (enabledContexts.isEmpty()) {
+ /* Early return if there is no requested context information */
+ return EMPTY_ARRAY;
+ }
+
+ /* Compute the total number of contexts (flatten the map) */
+ int totalArraySize = 0;
+ for (Map.Entry> contexts : enabledContexts) {
+ totalArraySize += contexts.getValue().size() * ENTRY_LENGTH;
+ }
+
+ 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_ARRAY;
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(totalArraySize);
+ buffer.order(NATIVE_ORDER);
+ buffer.clear();
+
+ for (Map.Entry> entry : enabledContexts) {
+ String requestedRetrieverName = entry.getKey();
+ Map 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 buffer */
+ // FIXME Eventually pass the retriever name only once?
+ String fullContextName = (UST_APP_CTX_PREFIX + requestedRetrieverName + ':' + requestedContext);
+ byte[] strArray = fullContextName.getBytes(UTF8_CHARSET);
+ int remainingBytes = ELEMENT_LENGTH - strArray.length;
+ // FIXME Handle case where name is too long...
+ buffer.put(strArray);
+ buffer.position(buffer.position() + remainingBytes);
+
+ serializeContextInfo(buffer, contextInfo);
+ }
+ }
+ return buffer.array();
+ }
+
+ private static void serializeContextInfo(ByteBuffer buffer, Object contextInfo) {
+ int remainingBytes;
+ if (contextInfo == null) {
+ buffer.put(DataType.NULL.getValue());
+ remainingBytes = ELEMENT_LENGTH;
+
+ } else if (contextInfo instanceof Integer) {
+ buffer.put(DataType.INTEGER.getValue());
+ buffer.putInt(((Integer) contextInfo).intValue());
+ remainingBytes = ELEMENT_LENGTH - 4;
+
+ } else if (contextInfo instanceof Long) {
+ buffer.put(DataType.LONG.getValue());
+ buffer.putLong(((Long) contextInfo).longValue());
+ remainingBytes = ELEMENT_LENGTH - 8;
+
+ } else if (contextInfo instanceof Double) {
+ buffer.put(DataType.DOUBLE.getValue());
+ buffer.putDouble(((Double) contextInfo).doubleValue());
+ remainingBytes = ELEMENT_LENGTH - 8;
+
+ } else if (contextInfo instanceof Float) {
+ buffer.put(DataType.FLOAT.getValue());
+ buffer.putFloat(((Float) contextInfo).floatValue());
+ remainingBytes = ELEMENT_LENGTH - 4;
+
+ } else if (contextInfo instanceof Byte) {
+ buffer.put(DataType.BYTE.getValue());
+ buffer.put(((Byte) contextInfo).byteValue());
+ remainingBytes = ELEMENT_LENGTH - 1;
+
+ } else if (contextInfo instanceof Short) {
+ buffer.put(DataType.SHORT.getValue());
+ buffer.putShort(((Short) contextInfo).shortValue());
+ remainingBytes = ELEMENT_LENGTH - 2;
+
+ } else if (contextInfo instanceof Boolean) {
+ buffer.put(DataType.BOOLEAN.getValue());
+ boolean b = ((Boolean) contextInfo).booleanValue();
+ /* Converted to one byte, write 1 for true, 0 for false */
+ buffer.put((byte) (b ? 1 : 0));
+ remainingBytes = ELEMENT_LENGTH - 1;
+
+ } else {
+ /* We'll write the object as a string. Also includes the case of Character. */
+ String str = contextInfo.toString();
+ byte[] strArray = str.getBytes(UTF8_CHARSET);
+
+ buffer.put(DataType.STRING.getValue());
+ if (strArray.length >= ELEMENT_LENGTH) {
+ /* Trim the string to the max allowed length */
+ buffer.put(strArray, 0, ELEMENT_LENGTH);
+ remainingBytes = 0;
+ } else {
+ buffer.put(strArray);
+ remainingBytes = ELEMENT_LENGTH - strArray.length;
+ }
+ }
+ buffer.position(buffer.position() + remainingBytes);
+ }
+}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/LttngContextApi.java b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/LttngContextApi.java
new file mode 100644
index 00000000..15062c9d
--- /dev/null
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/LttngContextApi.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+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);
+}
+
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulApi.java b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulApi.java
index f8a29d6d..b1f0d019 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulApi.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulApi.java
@@ -33,4 +33,13 @@ final class LttngJulApi {
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[] contextInformation);
}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java
index 3e61fe95..535a2a3f 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java
@@ -19,6 +19,9 @@
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;
@@ -26,6 +29,7 @@ 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.
@@ -117,19 +121,25 @@ public class LttngLogHandler extends Handler implements ILttngHandler {
String formattedMessage = FORMATTER.formatMessage(record);
+ /* Retrieve all the requested context information we can find */
+ Collection>> enabledContexts = agent.getEnabledAppContexts();
+ byte[] 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.tracepoint(formattedMessage,
+ LttngJulApi.tracepointWithContext(formattedMessage,
record.getLoggerName(),
record.getSourceClassName(),
record.getSourceMethodName(),
record.getMillis(),
record.getLevel().intValue(),
- record.getThreadID());
+ record.getThreadID(),
+ contextInfo);
}
}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jApi.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jApi.java
index 2521e9f0..61a04187 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jApi.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jApi.java
@@ -35,4 +35,15 @@ final class LttngLog4jApi {
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[] contextInformation);
}
diff --git a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java
index 753a5df3..5c6df4df 100644
--- a/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java
+++ b/liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java
@@ -19,12 +19,16 @@
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.
@@ -116,9 +120,13 @@ public class LttngLogAppender extends AppenderSkeleton implements ILttngHandler
line = -1;
}
+ /* Retrieve all the requested context information we can find */
+ Collection>> enabledContexts = agent.getEnabledAppContexts();
+ byte[] contextInfo = ContextInfoSerializer.queryAndSerializeRequestedContexts(enabledContexts);
+
eventCount.incrementAndGet();
- LttngLog4jApi.tracepoint(event.getRenderedMessage(),
+ LttngLog4jApi.tracepointWithContext(event.getRenderedMessage(),
event.getLoggerName(),
event.getLocationInformation().getClassName(),
event.getLocationInformation().getMethodName(),
@@ -126,7 +134,8 @@ public class LttngLogAppender extends AppenderSkeleton implements ILttngHandler
line,
event.getTimeStamp(),
event.getLevel().toInt(),
- event.getThreadName());
+ event.getThreadName(),
+ contextInfo);
}
}
diff --git a/liblttng-ust-java-agent/jni/Makefile.am b/liblttng-ust-java-agent/jni/Makefile.am
index 5310c339..dae51200 100644
--- a/liblttng-ust-java-agent/jni/Makefile.am
+++ b/liblttng-ust-java-agent/jni/Makefile.am
@@ -1,4 +1,5 @@
-SUBDIRS=
+SUBDIRS = common
+
if BUILD_JAVA_AGENT_WITH_JUL
SUBDIRS += jul
endif
diff --git a/liblttng-ust-java-agent/jni/common/Makefile.am b/liblttng-ust-java-agent/jni/common/Makefile.am
new file mode 100644
index 00000000..9e440cdc
--- /dev/null
+++ b/liblttng-ust-java-agent/jni/common/Makefile.am
@@ -0,0 +1,8 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+
+lib_LTLIBRARIES = liblttng-ust-context-jni.la
+liblttng_ust_context_jni_la_SOURCES = lttng_ust_context.c
+
+nodist_liblttng_ust_context_jni_la_SOURCES = org_lttng_ust_agent_context_LttngContextApi.h
+
+liblttng_ust_context_jni_la_LIBADD = -lc -L$(top_builddir)/liblttng-ust/.libs -llttng-ust
diff --git a/liblttng-ust-java-agent/jni/common/lttng_ust_context.c b/liblttng-ust-java-agent/jni/common/lttng_ust_context.c
new file mode 100644
index 00000000..8cc4087f
--- /dev/null
+++ b/liblttng-ust-java-agent/jni/common/lttng_ust_context.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir
+ * 2016 - EfficiOS Inc., Mathieu Desnoyers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "org_lttng_ust_agent_context_LttngContextApi.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "helper.h"
+#include "lttng_ust_context.h"
+
+#define LTTNG_UST_JNI_CONTEXT_NAME_LEN 256
+/* TODO: the value should be variable length. */
+#define LTTNG_UST_JNI_VALUE_LEN 256
+
+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 {
+ char context_name[LTTNG_UST_JNI_CONTEXT_NAME_LEN];
+ 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;
+ char _string[LTTNG_UST_JNI_VALUE_LEN];
+ } value;
+} __attribute__((packed));
+
+/* TLS passing context info from JNI to callbacks. */
+__thread struct lttng_ust_jni_tls lttng_ust_context_info_tls;
+
+static struct lttng_ust_jni_ctx *lookup_ctx_by_name(const char *ctx_name)
+{
+ struct lttng_ust_jni_ctx *ctx_array = lttng_ust_context_info_tls.ctx;
+ int i, len = lttng_ust_context_info_tls.len / sizeof(struct lttng_ust_jni_ctx);
+
+ for (i = 0; i < len; i++) {
+ if (strcmp(ctx_array[i].context_name, ctx_name) == 0)
+ return &ctx_array[i];
+ }
+ return NULL;
+
+}
+
+static size_t get_size_cb(struct lttng_ctx_field *field, size_t offset)
+{
+ struct lttng_ust_jni_ctx *jctx;
+ size_t size = 0;
+ const char *ctx_name = field->event_field.name;
+ enum lttng_ust_jni_type jni_type;
+
+ size += lib_ring_buffer_align(offset, lttng_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 += lib_ring_buffer_align(offset, lttng_alignof(int32_t));
+ size += sizeof(int32_t); /* variant */
+ break;
+ case JNI_TYPE_LONG:
+ size += lib_ring_buffer_align(offset, lttng_alignof(int64_t));
+ size += sizeof(int64_t); /* variant */
+ break;
+ case JNI_TYPE_DOUBLE:
+ size += lib_ring_buffer_align(offset, lttng_alignof(double));
+ size += sizeof(double); /* variant */
+ break;
+ case JNI_TYPE_FLOAT:
+ size += lib_ring_buffer_align(offset, lttng_alignof(float));
+ size += sizeof(float); /* variant */
+ break;
+ case JNI_TYPE_SHORT:
+ size += lib_ring_buffer_align(offset, lttng_alignof(int16_t));
+ size += sizeof(int16_t); /* variant */
+ break;
+ case JNI_TYPE_BYTE: /* Fall-through. */
+ case JNI_TYPE_BOOLEAN:
+ size += lib_ring_buffer_align(offset, lttng_alignof(char));
+ size += sizeof(char); /* variant */
+ break;
+ case JNI_TYPE_STRING:
+ size += strlen(jctx->value._string) + 1;
+ break;
+ default:
+ abort();
+ }
+ return size;
+
+}
+
+static void record_cb(struct lttng_ctx_field *field,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ struct lttng_channel *chan)
+{
+ struct lttng_ust_jni_ctx *jctx;
+ const char *ctx_name = field->event_field.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;
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(char));
+ chan->ops->event_write(ctx, &sel_char, sizeof(sel_char));
+ break;
+ case JNI_TYPE_INTEGER:
+ {
+ int32_t v = jctx->value._integer;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S32;
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(char));
+ chan->ops->event_write(ctx, &sel_char, sizeof(sel_char));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(v));
+ chan->ops->event_write(ctx, &v, sizeof(v));
+ break;
+ }
+ case JNI_TYPE_LONG:
+ {
+ int64_t v = jctx->value._long;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S64;
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(char));
+ chan->ops->event_write(ctx, &sel_char, sizeof(sel_char));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(v));
+ chan->ops->event_write(ctx, &v, sizeof(v));
+ break;
+ }
+ case JNI_TYPE_DOUBLE:
+ {
+ double v = jctx->value._double;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_DOUBLE;
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(char));
+ chan->ops->event_write(ctx, &sel_char, sizeof(sel_char));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(v));
+ chan->ops->event_write(ctx, &v, sizeof(v));
+ break;
+ }
+ case JNI_TYPE_FLOAT:
+ {
+ float v = jctx->value._float;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_FLOAT;
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(char));
+ chan->ops->event_write(ctx, &sel_char, sizeof(sel_char));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(v));
+ chan->ops->event_write(ctx, &v, sizeof(v));
+ break;
+ }
+ case JNI_TYPE_SHORT:
+ {
+ int16_t v = jctx->value._short;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S16;
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(char));
+ chan->ops->event_write(ctx, &sel_char, sizeof(sel_char));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(v));
+ chan->ops->event_write(ctx, &v, sizeof(v));
+ break;
+ }
+ case JNI_TYPE_BYTE:
+ {
+ char v = jctx->value._byte;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S8;
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(char));
+ chan->ops->event_write(ctx, &sel_char, sizeof(sel_char));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(v));
+ chan->ops->event_write(ctx, &v, sizeof(v));
+ break;
+ }
+ case JNI_TYPE_BOOLEAN:
+ {
+ char v = jctx->value._boolean;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_S8;
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(char));
+ chan->ops->event_write(ctx, &sel_char, sizeof(sel_char));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(v));
+ chan->ops->event_write(ctx, &v, sizeof(v));
+ break;
+ }
+ case JNI_TYPE_STRING:
+ {
+ const char *str = jctx->value._string;
+
+ sel_char = LTTNG_UST_DYNAMIC_TYPE_STRING;
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(char));
+ chan->ops->event_write(ctx, &sel_char, sizeof(sel_char));
+ chan->ops->event_write(ctx, str, strlen(str) + 1);
+ break;
+ }
+ default:
+ abort();
+ }
+}
+
+static void get_value_cb(struct lttng_ctx_field *field,
+ struct lttng_ctx_value *value)
+{
+ struct lttng_ust_jni_ctx *jctx;
+ const char *ctx_name = field->event_field.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:
+ value->sel = LTTNG_UST_DYNAMIC_TYPE_STRING;
+ value->u.str = jctx->value._string;
+ 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,
+ jstring provider_name)
+{
+ jboolean iscopy;
+ const char *provider_name_jstr;
+ char *provider_name_cstr;
+ struct lttng_ust_context_provider *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;
+ }
+ provider = zmalloc(sizeof(*provider));
+ if (!provider) {
+ goto error_provider;
+ }
+ provider->name = provider_name_cstr;
+ provider->get_size = get_size_cb;
+ provider->record = record_cb;
+ provider->get_value = get_value_cb;
+
+ if (lttng_ust_context_provider_register(provider)) {
+ goto error_register;
+ }
+
+ provider_ref = (jlong) provider;
+ return provider_ref;
+
+ /* Error handling. */
+error_register:
+ free(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,
+ jobject jobj,
+ jlong provider_ref)
+{
+ struct lttng_ust_context_provider *provider =
+ (struct lttng_ust_context_provider*) (unsigned long) provider_ref;
+
+ if (!provider) {
+ return;
+ }
+
+ lttng_ust_context_provider_unregister(provider);
+
+ free(provider->name);
+ free(provider);
+}
diff --git a/liblttng-ust-java-agent/jni/common/lttng_ust_context.h b/liblttng-ust-java-agent/jni/common/lttng_ust_context.h
new file mode 100644
index 00000000..2bdef3ae
--- /dev/null
+++ b/liblttng-ust-java-agent/jni/common/lttng_ust_context.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#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;
+
+struct lttng_ust_jni_tls {
+ struct lttng_ust_jni_ctx *ctx;
+ int32_t len;
+};
+
+extern __thread struct lttng_ust_jni_tls lttng_ust_context_info_tls;
+
+#endif /* LIBLTTNG_UST_JAVA_AGENT_JNI_COMMON_LTTNG_UST_CONTEXT_H_ */
diff --git a/liblttng-ust-java-agent/jni/jul/Makefile.am b/liblttng-ust-java-agent/jni/jul/Makefile.am
index cd0b7dc3..111c5595 100644
--- a/liblttng-ust-java-agent/jni/jul/Makefile.am
+++ b/liblttng-ust-java-agent/jni/jul/Makefile.am
@@ -6,4 +6,8 @@ liblttng_ust_jul_jni_la_SOURCES = lttng_ust_jul.c \
nodist_liblttng_ust_jul_jni_la_SOURCES = org_lttng_ust_agent_jul_LttngJulApi.h
-liblttng_ust_jul_jni_la_LIBADD = -lc -L$(top_builddir)/liblttng-ust/.libs -llttng-ust
+liblttng_ust_jul_jni_la_LIBADD = -lc \
+ -L$(top_builddir)/liblttng-ust/.libs \
+ -L$(top_builddir)/liblttng-ust-java-agent/jni/common/.libs \
+ -llttng-ust-context-jni
+ -llttng-ust
diff --git a/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c b/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c
index a0e893e5..62d820b2 100644
--- a/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c
+++ b/liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir
* Copyright (C) 2011-2012 Mathieu Desnoyers
*
* This library is free software; you can redistribute it and/or
@@ -21,9 +22,10 @@
#define TRACEPOINT_DEFINE
#define TRACEPOINT_CREATE_PROBES
#include "lttng_ust_jul.h"
+#include "../common/lttng_ust_context.h"
/*
- * Tracepoint used by Java applications using the JUL handler.
+ * Deprecated function from before the context information was passed.
*/
JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LttngJulApi_tracepoint(JNIEnv *env,
jobject jobj,
@@ -49,3 +51,44 @@ JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LttngJulApi_tracepoint(JNIEn
(*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,
+ jstring msg,
+ jstring logger_name,
+ jstring class_name,
+ jstring method_name,
+ jlong millis,
+ jint log_level,
+ jint thread_id,
+ jbyteArray context_info)
+{
+ 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_array;
+
+ /*
+ * Write these to the TLS variables, so that the UST callbacks in
+ * lttng_ust_context.c can access them.
+ */
+ context_info_array = (*env)->GetByteArrayElements(env, context_info, &iscopy);
+ lttng_ust_context_info_tls.ctx = (struct lttng_ust_jni_ctx *) context_info_array;
+ lttng_ust_context_info_tls.len = (*env)->GetArrayLength(env, context_info);
+
+ 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 = NULL;
+ lttng_ust_context_info_tls.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, context_info_array, 0);
+}
diff --git a/liblttng-ust-java-agent/jni/log4j/Makefile.am b/liblttng-ust-java-agent/jni/log4j/Makefile.am
index 5030a03f..a94479c5 100644
--- a/liblttng-ust-java-agent/jni/log4j/Makefile.am
+++ b/liblttng-ust-java-agent/jni/log4j/Makefile.am
@@ -1,8 +1,13 @@
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+
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 -L$(top_builddir)/liblttng-ust/.libs -llttng-ust
+liblttng_ust_log4j_jni_la_LIBADD = -lc \
+ -L$(top_builddir)/liblttng-ust/.libs \
+ -L$(top_builddir)/liblttng-ust-java-agent/jni/common/.libs \
+ -llttng-ust-context-jni
+ -llttng-ust
diff --git a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c
index 3b78c8e5..b80312e2 100644
--- a/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c
+++ b/liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir
* Copyright (C) 2011-2012 Mathieu Desnoyers
*
* This library is free software; you can redistribute it and/or
@@ -21,9 +22,10 @@
#define TRACEPOINT_DEFINE
#define TRACEPOINT_CREATE_PROBES
#include "lttng_ust_log4j.h"
+#include "../common/lttng_ust_context.h"
/*
- * System tracepoint meaning only root agent will fire this.
+ * Deprecated function from before the context information was passed.
*/
JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LttngLog4jApi_tracepoint(JNIEnv *env,
jobject jobj,
@@ -57,3 +59,50 @@ JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LttngLog4jApi_tracepoint(J
(*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,
+ 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)
+{
+ 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_array;
+
+ /*
+ * Write these to the TLS variables, so that the UST callbacks in
+ * lttng_ust_context.c can access them.
+ */
+ context_info_array = (*env)->GetByteArrayElements(env, context_info, &iscopy);
+ lttng_ust_context_info_tls.ctx = (struct lttng_ust_jni_ctx *) context_info_array;
+ lttng_ust_context_info_tls.len = (*env)->GetArrayLength(env, context_info);
+
+ 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 = NULL;
+ lttng_ust_context_info_tls.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, context_info_array, 0);
+}