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); +}