From 43e5396b3b2247a3f57a8a797239359df3ff083f Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 7 Nov 2013 16:34:23 -0500 Subject: [PATCH] Add liblttng-ust-jul for JUL support The build system creates a jar file named liblttng-ust-jul.jar to be linked with the Java application. A public library named liblttng-ust-jul.so is also created and must be in the library path of the Java application in order for the LTTngAgent to use it for the tracer's JNI call. A unit test is also added and integrated with the "make check" command. Signed-off-by: David Goulet Signed-off-by: Mathieu Desnoyers --- .gitignore | 5 + Makefile.am | 5 +- configure.ac | 5 + liblttng-ust-jul/LTTngUst.c | 48 +++ liblttng-ust-jul/Makefile.am | 43 +++ liblttng-ust-jul/README | 38 +++ liblttng-ust-jul/lttng_ust_jul.h | 53 ++++ .../org/lttng/ust/jul/LTTngAgent.java | 175 +++++++++++ .../org/lttng/ust/jul/LTTngLogHandler.java | 57 ++++ .../lttng/ust/jul/LTTngSessiondCmd2_4.java | 289 ++++++++++++++++++ .../lttng/ust/jul/LTTngTCPSessiondClient.java | 282 +++++++++++++++++ .../org/lttng/ust/jul/LTTngThread.java | 45 +++ .../org/lttng/ust/jul/LTTngUst.java | 68 +++++ tests/Makefile.am | 4 + tests/java-jul/JULTest.java | 62 ++++ tests/java-jul/Makefile.am | 25 ++ tests/java-jul/test_jul | 12 + tests/unit_tests | 1 + 18 files changed, 1215 insertions(+), 2 deletions(-) create mode 100644 liblttng-ust-jul/LTTngUst.c create mode 100644 liblttng-ust-jul/Makefile.am create mode 100644 liblttng-ust-jul/README create mode 100644 liblttng-ust-jul/lttng_ust_jul.h create mode 100644 liblttng-ust-jul/org/lttng/ust/jul/LTTngAgent.java create mode 100644 liblttng-ust-jul/org/lttng/ust/jul/LTTngLogHandler.java create mode 100644 liblttng-ust-jul/org/lttng/ust/jul/LTTngSessiondCmd2_4.java create mode 100644 liblttng-ust-jul/org/lttng/ust/jul/LTTngTCPSessiondClient.java create mode 100644 liblttng-ust-jul/org/lttng/ust/jul/LTTngThread.java create mode 100644 liblttng-ust-jul/org/lttng/ust/jul/LTTngUst.java create mode 100644 tests/java-jul/JULTest.java create mode 100644 tests/java-jul/Makefile.am create mode 100755 tests/java-jul/test_jul diff --git a/.gitignore b/.gitignore index 07d13062..ce7c5729 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,8 @@ tests/tracepoint/tracepoint_test tests/snprintf/prog tests/benchmark/bench1 tests/benchmark/bench2 + +# Java JUL library +*.class +liblttng-ust-jul.jar +org_lttng_ust_jul_LTTngUst.h diff --git a/Makefile.am b/Makefile.am index dc88c46e..edb1a857 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,13 +7,14 @@ SUBDIRS = . include snprintf libringbuffer liblttng-ust-comm \ liblttng-ust-libc-wrapper \ liblttng-ust-cyg-profile \ tools \ - tests \ doc if BUILD_JNI_INTERFACE -SUBDIRS += liblttng-ust-java +SUBDIRS += liblttng-ust-java liblttng-ust-jul endif +SUBDIRS += tests + #temporarily disabled # liblttng-ust-malloc diff --git a/configure.ac b/configure.ac index cc28205c..c5802ee2 100644 --- a/configure.ac +++ b/configure.ac @@ -209,6 +209,9 @@ AC_ARG_WITH([java-jdk], [JAVA_JDK=$withval], [JAVA_JDK=""] ) +AM_CONDITIONAL([HAVE_JAVA_JDK], [test $JAVA_JDK], [Java JDK path]) +AC_SUBST([JAVA_JDK]) + AS_IF([test $JAVA_JDK],[ AS_IF([test -d $JAVA_JDK],[ AC_MSG_RESULT([using Java includes in $JAVA_SDK]) @@ -283,6 +286,7 @@ AC_CONFIG_FILES([ liblttng-ust-ctl/Makefile liblttng-ust-fork/Makefile liblttng-ust-java/Makefile + liblttng-ust-jul/Makefile liblttng-ust-libc-wrapper/Makefile liblttng-ust-cyg-profile/Makefile tools/Makefile @@ -293,6 +297,7 @@ AC_CONFIG_FILES([ tests/snprintf/Makefile tests/benchmark/Makefile tests/utils/Makefile + tests/java-jul/Makefile lttng-ust.pc ]) diff --git a/liblttng-ust-jul/LTTngUst.c b/liblttng-ust-jul/LTTngUst.c new file mode 100644 index 00000000..69bd83d3 --- /dev/null +++ b/liblttng-ust-jul/LTTngUst.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011-2012 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_jul_LTTngUst.h" + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES +#include "lttng_ust_jul.h" + +JNIEXPORT void JNICALL Java_org_lttng_ust_LTTngUst_tracepoint(JNIEnv *env, + jobject jobj, + jstring msg, + jstring logger_name, + jstring class_name, + jstring method_name, + jlong millis, + jint log_level, + jint thread_id) +{ + jboolean iscopy; + const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy); + const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy); + const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy); + const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy); + + tracepoint(lttng_jul, jul_event, msg_cstr, logger_name_cstr, + class_name_cstr, method_name_cstr, millis, log_level, thread_id); + + (*env)->ReleaseStringUTFChars(env, msg, msg_cstr); + (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr); + (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr); + (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr); +} diff --git a/liblttng-ust-jul/Makefile.am b/liblttng-ust-jul/Makefile.am new file mode 100644 index 00000000..22ba89c3 --- /dev/null +++ b/liblttng-ust-jul/Makefile.am @@ -0,0 +1,43 @@ +if BUILD_JNI_INTERFACE + +AM_CPPFLAGS = -I$(top_srcdir)/include + +lib_LTLIBRARIES = liblttng-ust-jul-jni.la +liblttng_ust_jul_jni_la_SOURCES = LTTngUst.c lttng_ust_jul.h +nodist_liblttng_ust_jul_jni_la_SOURCES = org_lttng_ust_LTTngUst.h +dist_noinst_DATA = $(LTTNG_JUL_SRCDIR)/LTTngUst.java +liblttng_ust_jul_jni_la_LIBADD = -lc -L$(top_builddir)/liblttng-ust/.libs -llttng-ust + +LTTNG_JUL_SRCDIR = $(srcdir)/org/lttng/ust/jul +LTTNG_JUL_DESTDIR = $(builddir)/org/lttng/ust/jul + +if HAVE_JAVA_JDK +JCC=$(JAVA_JDK)/bin +else +JCC=javac +endif + +all-local: $(LTTNG_JUL_DESTDIR)/LTTngAgent.class $(LTTNG_JUL_DESTDIR)/LTTngUst.class \ + org_lttng_ust_LTTngUst.h liblttng-ust-jul.jar + +clean-local: + rm -f org_lttng_ust_jul_LTTngUst.h + rm -f liblttng-ust-jul.jar + rm -f org/lttng/ust/jul/*.class + +LTTngUst.c: org_lttng_ust_LTTngUst.h + +$(LTTNG_JUL_DESTDIR)/LTTngUst.class: $(LTTNG_JUL_SRCDIR)/LTTngUst.java + $(JCC)/javac -d "$(builddir)" "$(LTTNG_JUL_SRCDIR)/LTTngUst.java" + +$(LTTNG_JUL_DESTDIR)/LTTngAgent.class: $(LTTNG_JUL_SRCDIR)/LTTngAgent.java + $(JCC)/javac -d "$(builddir)" "$(LTTNG_JUL_SRCDIR)/LTTngAgent.java" + +org_lttng_ust_LTTngUst.h: $(LTTNG_JUL_DESTDIR)/LTTngUst.class + $(JCC)/javah org.lttng.ust.jul.LTTngUst + +liblttng-ust-jul.jar: $(LTTNG_JUL_DESTDIR)/LTTngUst.class $(LTTNG_JUL_DESTDIR)/LTTngAgent.class + $(JCC)/jar cf liblttng-ust-jul.jar \ + $(LTTNG_JUL_DESTDIR)/*.class + +endif diff --git a/liblttng-ust-jul/README b/liblttng-ust-jul/README new file mode 100644 index 00000000..6e2ee98b --- /dev/null +++ b/liblttng-ust-jul/README @@ -0,0 +1,38 @@ +This directory contains the LTTng Java Agent for JUL support. + +Configuration examples to build this library: + +dependency: openjdk-7-jdk + ./configure --with-java-jdk=/usr/lib/jvm/java-7-openjdk --with-jni-interface + +On Debian system for instance you can simply use the "default-java" path: + + ./configure --with-java-jdk=/usr/lib/jvm/default-java --with-jni-interface + +Note that the OpenJDK 7 is used for development and continuous integration thus +we directly support that version for this library. However, it has been tested +with OpenJDK 6 also. Please let us know if other Java version (commercial or +not) work with this library. + +After building, you can use the "liblttng-ust-jul.jar" file in a Java project. +It requires "liblttng-ust-jul.so" which is installed by the build system when +doing "make install". Make sure that your Java application can find this shared +object with the "java.library.path". + +In order to enable the agent in your Java application, you simply have to add +this as early as you can in the runtime process. + +import org.lttng.ust.jul.LTTngAgent; +[...] + private static LTTngAgent lttngAgent; + [...] + lttngAgent = LTTngAgent.getLTTngAgent(); + +This will initialize automatically the singleton LTTngAgent, it will stall +your application until the session daemon registration is done. If no session +daemon is available, the execution will continue and the agent will retry at +each 3 seconds. + +Once registered, it is adds a thread inside your Java application and will be +able to automatically use every Logger object and map them to the jul_event +tracepoint of the JNI interface (see LTTngUst.c/.java). diff --git a/liblttng-ust-jul/lttng_ust_jul.h b/liblttng-ust-jul/lttng_ust_jul.h new file mode 100644 index 00000000..442eda54 --- /dev/null +++ b/liblttng-ust-jul/lttng_ust_jul.h @@ -0,0 +1,53 @@ +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER lttng_jul + +#if !defined(_TRACEPOINT_LTTNG_UST_JUL_H) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_LTTNG_UST_JUL_H + +/* + * Copyright (C) 2011 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; 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 + +TRACEPOINT_EVENT(lttng_jul, jul_event, + TP_ARGS( + const char *, msg, + const char *, logger_name, + const char *, class_name, + const char *, method_name, + long, millis, + int, log_level, + int, thread_id), + TP_FIELDS( + ctf_string(msg, msg) + ctf_string(logger_name, logger_name) + ctf_string(class_name, class_name) + ctf_string(method_name, method_name) + ctf_integer(long, long_millis, millis) + ctf_integer(int, int_loglevel, log_level) + ctf_integer(int, int_threadid, thread_id) + ) +) + +#endif /* _TRACEPOINT_LTTNG_UST_JUL_H */ + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./lttng_ust_jul.h" + +/* This part must be outside protection */ +#include diff --git a/liblttng-ust-jul/org/lttng/ust/jul/LTTngAgent.java b/liblttng-ust-jul/org/lttng/ust/jul/LTTngAgent.java new file mode 100644 index 00000000..c1834f58 --- /dev/null +++ b/liblttng-ust-jul/org/lttng/ust/jul/LTTngAgent.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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.jul; + +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.concurrent.Semaphore; +import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.LogManager; +import java.util.Enumeration; + +public class LTTngAgent { + private static LTTngLogHandler lttngHandler; + private static LogManager logManager; + private static LTTngThread lttngThread; + private static Thread sessiondTh; + + /* Singleton agent object. */ + private static LTTngAgent curAgent = null; + + /* Indicate if this object has been initialized. */ + private static boolean initialized = false; + + private static Semaphore registerSem; + + private static final String sessiondAddr = "127.0.0.1"; + private static final int sessiondPort = 5345; + + private static final String rootPortFile = "/var/run/lttng/jul.port"; + private static final String userPortFile = "/.lttng/jul.port"; + + /* + * Constructor is private. This is a singleton and a reference should be + * acquired using getLTTngAgent(). + */ + private LTTngAgent() throws IOException { + this.logManager = LogManager.getLogManager(); + this.lttngHandler = new LTTngLogHandler(this.logManager); + this.registerSem = new Semaphore(0, true); + } + + private void removeHandlers() throws SecurityException, IOException { + String loggerName; + Logger logger; + + Enumeration list = this.logManager.getLoggerNames(); + while (list.hasMoreElements()) { + loggerName = list.nextElement().toString(); + /* Somehow there is always an empty string at the end. */ + if (loggerName == "") { + continue; + } + + logger = this.logManager.getLogger(loggerName); + logger.removeHandler(this.lttngHandler); + } + } + + private int getUID() throws IOException { + int uid; + byte b[] = new byte[4]; + String userName = System.getProperty("user.name"); + String command = "id -u " + userName; + Process child = Runtime.getRuntime().exec(command); + InputStream in = child.getInputStream(); + + in.read(b); + uid = Integer.parseInt(new String(b).trim(), 10); + in.close(); + + return uid; + } + + private String getHomePath() { + return System.getProperty("user.home"); + } + + private int getPortFromFile() throws IOException { + int port; + int uid = getUID(); + String path; + BufferedReader br; + + /* Check if root or not, it tells where to get the port file. */ + if (uid == 0) { + path = rootPortFile; + } else { + path = new String(getHomePath() + userPortFile); + } + + try { + br = new BufferedReader(new FileReader(path)); + String line = br.readLine(); + port = Integer.parseInt(line, 10); + if (port < 0 || port > 65535) { + port = sessiondPort; + } + br.close(); + } catch (FileNotFoundException e) { + port = sessiondPort; + } + + return port; + } + + /* + * Public getter to acquire a reference to this singleton object. + */ + public static synchronized LTTngAgent getLTTngAgent() throws IOException { + if (curAgent == null) { + curAgent = new LTTngAgent(); + curAgent.init(); + } + + return curAgent; + } + + /* + * Initialize LTTngAgent. This will attach the log handler to all Logger + * returned by the logManager. + */ + private synchronized void init() throws SecurityException, IOException { + if (this.initialized) { + return; + } + + this.lttngThread = new LTTngThread(this.sessiondAddr, + getPortFromFile(), this.lttngHandler, this.registerSem); + this.sessiondTh = new Thread(lttngThread); + this.sessiondTh.start(); + + this.initialized = true; + + /* Wait for the registration to end. */ + try { + this.registerSem.acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public void dispose() throws IOException { + this.lttngThread.dispose(); + + /* Make sure there is no more LTTng handler attach to logger(s). */ + this.removeHandlers(); + + try { + this.sessiondTh.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/liblttng-ust-jul/org/lttng/ust/jul/LTTngLogHandler.java b/liblttng-ust-jul/org/lttng/ust/jul/LTTngLogHandler.java new file mode 100644 index 00000000..dfc15b3d --- /dev/null +++ b/liblttng-ust-jul/org/lttng/ust/jul/LTTngLogHandler.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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.jul; + +import java.lang.String; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.LogManager; + +import org.lttng.ust.jul.LTTngUst; + +public class LTTngLogHandler extends Handler { + public LogManager logManager; + + public LTTngLogHandler(LogManager logManager) { + super(); + + this.logManager = logManager; + + /* Initialize LTTng UST tracer. */ + LTTngUst.init(); + } + + @Override + public void close() throws SecurityException {} + + @Override + public void flush() {} + + @Override + public void publish(LogRecord record) { + /* + * Specific tracepoing 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. + */ + LTTngUst.tracepoint(record.getMessage(), record.getLoggerName(), + record.getSourceClassName(), record.getSourceMethodName(), + record.getMillis(), record.getLevel().intValue(), + record.getThreadID()); + } +} diff --git a/liblttng-ust-jul/org/lttng/ust/jul/LTTngSessiondCmd2_4.java b/liblttng-ust-jul/org/lttng/ust/jul/LTTngSessiondCmd2_4.java new file mode 100644 index 00000000..4b893e0b --- /dev/null +++ b/liblttng-ust-jul/org/lttng/ust/jul/LTTngSessiondCmd2_4.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * + * 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.jul; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.logging.Logger; +import java.util.ArrayList; +import java.util.List; +import java.util.Enumeration; + +public interface LTTngSessiondCmd2_4 { + /** + * Maximum name length for a logger name to be send to sessiond. + */ + final static int NAME_MAX = 255; + + public interface SessiondResponse { + /** + * Gets a byte array of the command so that it may be streamed + * + * @return the byte array of the command + */ + public byte[] getBytes(); + } + + public interface SessiondCommand { + /** + * Populate the class from a byte array + * + * @param data + * the byte array containing the streamed command + */ + public void populate(byte[] data); + } + + public enum lttng_jul_command { + /** List logger(s). */ + CMD_LIST(1), + /** Enable logger by name. */ + CMD_ENABLE(2), + /** Disable logger by name. */ + CMD_DISABLE(3); + private int code; + + private lttng_jul_command(int c) { + code = c; + } + + public int getCommand() { + return code; + } + } + + enum lttng_jul_ret_code { + CODE_SUCCESS_CMD(1), + CODE_INVALID_CMD(2), + CODE_UNK_LOGGER_NAME(3); + private int code; + + private lttng_jul_ret_code(int c) { + code = c; + } + + public int getCode() { + return code; + } + } + + public class sessiond_hdr implements SessiondCommand { + /** ABI size of command header. */ + public final static int SIZE = 16; + /** Payload size in bytes following this header. */ + public long data_size; + /** Command type. */ + public lttng_jul_command cmd; + /** Command version. */ + public int cmd_version; + + public void populate(byte[] data) { + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + + data_size = buf.getLong(); + cmd = lttng_jul_command.values()[buf.getInt() - 1]; + cmd_version = buf.getInt(); + } + } + + public class sessiond_enable_handler implements SessiondResponse, SessiondCommand { + private final static int SIZE = 4; + public String name; + + /** Return status code to the session daemon. */ + public lttng_jul_ret_code code; + + @Override + public void populate(byte[] data) { + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.LITTLE_ENDIAN); + name = new String(data, 0, data.length); + } + + @Override + public byte[] getBytes() { + byte data[] = new byte[SIZE]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putInt(code.getCode()); + return data; + } + + /** + * Execute enable handler action which is to enable the given handler + * to the received name. + * + * @return Event name as a string if the event is NOT found thus was + * not enabled. + */ + public String execute(LTTngLogHandler handler) { + Logger logger; + + if (name == null) { + this.code = lttng_jul_ret_code.CODE_INVALID_CMD; + return null; + } + + /* Wild card to enable ALL logger. */ + if (name.trim().equals("*")) { + String loggerName; + Enumeration loggers = handler.logManager.getLoggerNames(); + while (loggers.hasMoreElements()) { + loggerName = loggers.nextElement().toString(); + /* Somehow there is always an empty string at the end. */ + if (loggerName == "") { + continue; + } + + logger = handler.logManager.getLogger(loggerName); + logger.addHandler(handler); + } + this.code = lttng_jul_ret_code.CODE_SUCCESS_CMD; + return null; + } + + this.code = lttng_jul_ret_code.CODE_SUCCESS_CMD; + logger = handler.logManager.getLogger(name.trim()); + if (logger != null) { + logger.addHandler(handler); + return null; + } else { + return new String(name); + } + } + } + + public class sessiond_disable_handler implements SessiondResponse, SessiondCommand { + private final static int SIZE = 4; + public String name; + + /** Return status code to the session daemon. */ + public lttng_jul_ret_code code; + + @Override + public void populate(byte[] data) { + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + name = new String(data, 0, data.length); + } + + @Override + public byte[] getBytes() { + byte data[] = new byte[SIZE]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putInt(code.getCode()); + return data; + } + + /** + * Execute disable handler action which is to disable the given handler + * to the received name. + */ + public void execute(LTTngLogHandler handler) { + Logger logger; + + if (name == null) { + this.code = lttng_jul_ret_code.CODE_INVALID_CMD; + return; + } + + /* Wild card to disable ALL logger. */ + if (name.trim().equals("*")) { + String loggerName; + Enumeration loggers = handler.logManager.getLoggerNames(); + while (loggers.hasMoreElements()) { + loggerName = loggers.nextElement().toString(); + /* Somehow there is always an empty string at the end. */ + if (loggerName == "") { + continue; + } + + logger = handler.logManager.getLogger(loggerName); + logger.removeHandler(handler); + } + this.code = lttng_jul_ret_code.CODE_SUCCESS_CMD; + return; + } + + logger = handler.logManager.getLogger(name.trim()); + if (logger == null) { + this.code = lttng_jul_ret_code.CODE_UNK_LOGGER_NAME; + } else { + logger.removeHandler(handler); + this.code = lttng_jul_ret_code.CODE_SUCCESS_CMD; + } + } + } + + public class sessiond_list_logger implements SessiondResponse { + private final static int SIZE = 12; + + private int data_size = 0; + private int nb_logger = 0; + + List logger_list = new ArrayList(); + + /** Return status code to the session daemon. */ + public lttng_jul_ret_code code; + + @Override + public byte[] getBytes() { + byte data[] = new byte[SIZE + data_size]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + + /* Returned code */ + buf.putInt(code.getCode()); + buf.putInt(data_size); + buf.putInt(nb_logger); + + for (String logger: logger_list) { + buf.put(logger.getBytes()); + /* NULL terminated byte after the logger name. */ + buf.put((byte) 0x0); + } + return data; + } + + /** + * Execute enable handler action which is to enable the given handler + * to the received name. + */ + public void execute(LTTngLogHandler handler) { + String loggerName; + + Enumeration loggers = handler.logManager.getLoggerNames(); + while (loggers.hasMoreElements()) { + loggerName = loggers.nextElement().toString(); + /* Somehow there is always an empty string at the end. */ + if (loggerName == "") { + continue; + } + + this.logger_list.add(loggerName); + this.nb_logger++; + this.data_size += loggerName.length() + 1; + } + + this.code = lttng_jul_ret_code.CODE_SUCCESS_CMD; + } + } +} diff --git a/liblttng-ust-jul/org/lttng/ust/jul/LTTngTCPSessiondClient.java b/liblttng-ust-jul/org/lttng/ust/jul/LTTngTCPSessiondClient.java new file mode 100644 index 00000000..b89deb81 --- /dev/null +++ b/liblttng-ust-jul/org/lttng/ust/jul/LTTngTCPSessiondClient.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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.jul; + +import java.util.concurrent.Semaphore; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.lang.Integer; +import java.io.IOException; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.net.*; +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +class USTRegisterMsg { + public static int pid; +} + +public class LTTngTCPSessiondClient { + /* Command header from the session deamon. */ + private LTTngSessiondCmd2_4.sessiond_hdr headerCmd = + new LTTngSessiondCmd2_4.sessiond_hdr(); + + private final String sessiondHost; + private final int sessiondPort; + private Socket sessiondSock; + private boolean quit = false; + + private DataInputStream inFromSessiond; + private DataOutputStream outToSessiond; + + private LTTngLogHandler handler; + + private Semaphore registerSem; + + private Timer eventTimer; + private List enabledEventList = new ArrayList(); + /* Timer delay at each 5 seconds. */ + private final static long timerDelay = 5 * 1000; + private static boolean timerInitialized; + + public LTTngTCPSessiondClient(String host, int port, Semaphore sem) { + this.sessiondHost = host; + this.sessiondPort = port; + this.registerSem = sem; + this.eventTimer = new Timer(); + this.timerInitialized = false; + } + + private void setupEventTimer() { + if (this.timerInitialized) { + return; + } + + this.eventTimer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + /* + * We have to make a copy here since it is possible that the + * enabled event list is changed during an iteration on it. + */ + List tmpList = new ArrayList(enabledEventList); + + LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = new + LTTngSessiondCmd2_4.sessiond_enable_handler(); + for (String strEventName: tmpList) { + enableCmd.name = strEventName; + if (enableCmd.execute(handler) == null) { + enabledEventList.remove(strEventName); + } + } + } + }, this.timerDelay, this.timerDelay); + + this.timerInitialized = true; + } + + public void init(LTTngLogHandler handler) throws InterruptedException { + this.handler = handler; + + for (;;) { + if (this.quit) { + break; + } + + try { + + /* + * Connect to the session daemon before anything else. + */ + connectToSessiond(); + + /* + * Register to the session daemon as the Java component of the + * UST application. + */ + registerToSessiond(); + this.registerSem.release(); + + setupEventTimer(); + + /* + * Block on socket receive and wait for command from the + * session daemon. This will return if and only if there is a + * fatal error or the socket closes. + */ + handleSessiondCmd(); + } catch (UnknownHostException uhe) { + this.registerSem.release(); + System.out.println(uhe); + } catch (IOException ioe) { + this.registerSem.release(); + Thread.sleep(3000); + } catch (Exception e) { + this.registerSem.release(); + e.printStackTrace(); + } + } + } + + public void destroy() { + this.quit = true; + this.eventTimer.cancel(); + + try { + if (this.sessiondSock != null) { + this.sessiondSock.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /* + * Receive header data from the session daemon using the LTTng command + * static buffer of the right size. + */ + private void recvHeader() throws Exception { + int read_len; + byte data[] = new byte[this.headerCmd.SIZE]; + + read_len = this.inFromSessiond.read(data, 0, data.length); + if (read_len != data.length) { + throw new IOException(); + } + this.headerCmd.populate(data); + } + + /* + * Receive payload from the session daemon. This MUST be done after a + * recvHeader() so the header value of a command are known. + * + * The caller SHOULD use isPayload() before which returns true if a payload + * is expected after the header. + */ + private byte[] recvPayload() throws Exception { + byte payload[] = new byte[(int) this.headerCmd.data_size]; + + /* Failsafe check so we don't waste our time reading 0 bytes. */ + if (payload.length == 0) { + return null; + } + + this.inFromSessiond.read(payload, 0, payload.length); + return payload; + } + + /* + * Handle session command from the session daemon. + */ + private void handleSessiondCmd() throws Exception { + int ret_code; + byte data[] = null; + + while (true) { + /* Get header from session daemon. */ + recvHeader(); + + if (headerCmd.data_size > 0) { + data = recvPayload(); + } + + switch (headerCmd.cmd) { + case CMD_LIST: + { + LTTngSessiondCmd2_4.sessiond_list_logger listLoggerCmd = + new LTTngSessiondCmd2_4.sessiond_list_logger(); + listLoggerCmd.execute(this.handler); + data = listLoggerCmd.getBytes(); + break; + } + case CMD_ENABLE: + { + String event_name; + LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = + new LTTngSessiondCmd2_4.sessiond_enable_handler(); + if (data == null) { + enableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD; + break; + } + enableCmd.populate(data); + event_name = enableCmd.execute(this.handler); + if (event_name != null) { + /* + * Add the event to the list so it can be enabled if + * the logger appears at some point in time. + */ + enabledEventList.add(event_name); + } + data = enableCmd.getBytes(); + break; + } + case CMD_DISABLE: + { + LTTngSessiondCmd2_4.sessiond_disable_handler disableCmd = + new LTTngSessiondCmd2_4.sessiond_disable_handler(); + if (data == null) { + disableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD; + break; + } + disableCmd.populate(data); + disableCmd.execute(this.handler); + data = disableCmd.getBytes(); + break; + } + default: + { + data = new byte[4]; + ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.BIG_ENDIAN); + LTTngSessiondCmd2_4.lttng_jul_ret_code code = + LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD; + buf.putInt(code.getCode()); + break; + } + } + + /* Send payload to session daemon. */ + this.outToSessiond.write(data, 0, data.length); + this.outToSessiond.flush(); + } + } + + private void connectToSessiond() throws Exception { + this.sessiondSock = new Socket(this.sessiondHost, this.sessiondPort); + this.inFromSessiond = new DataInputStream( + sessiondSock.getInputStream()); + this.outToSessiond = new DataOutputStream( + sessiondSock.getOutputStream()); + } + + private void registerToSessiond() throws Exception { + byte data[] = new byte[4]; + ByteBuffer buf = ByteBuffer.wrap(data); + String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; + + buf.putInt(Integer.parseInt(pid)); + this.outToSessiond.write(data, 0, data.length); + this.outToSessiond.flush(); + } +} diff --git a/liblttng-ust-jul/org/lttng/ust/jul/LTTngThread.java b/liblttng-ust-jul/org/lttng/ust/jul/LTTngThread.java new file mode 100644 index 00000000..a64c6a9b --- /dev/null +++ b/liblttng-ust-jul/org/lttng/ust/jul/LTTngThread.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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.jul; + +import java.util.concurrent.Semaphore; + +public class LTTngThread implements Runnable { + private LTTngLogHandler handler; + private LTTngTCPSessiondClient sessiondClient; + + public LTTngThread(String host, int port, LTTngLogHandler handler, + Semaphore registerSem) { + this.handler = handler; + this.sessiondClient = new LTTngTCPSessiondClient(host, port, + registerSem); + } + + @Override + public void run() { + try { + this.sessiondClient.init(this.handler); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void dispose() { + this.sessiondClient.destroy(); + } +} diff --git a/liblttng-ust-jul/org/lttng/ust/jul/LTTngUst.java b/liblttng-ust-jul/org/lttng/ust/jul/LTTngUst.java new file mode 100644 index 00000000..33c15eb7 --- /dev/null +++ b/liblttng-ust-jul/org/lttng/ust/jul/LTTngUst.java @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2011-2012 Mathieu Desnoyers + * Copyright (C) 2012 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 + */ + +package org.lttng.ust.jul; + +/** + * This class implements the the Java side of the LTTng-UST Java interface. + * + * First, make sure you have installed "liblttng-ust-java.so" where the linker + * can find it. You can then call LTTngUst.init() from your Java program to + * connect the methods exposed here to the native library. + * + * Because of limitations in the probe declaration, all trace events generated + * by this library will have "lttng_ust_java" for domain, and "_event" for + * event name in the CTF trace files. The "name" parameter will instead appear + * as the first element of the event's payload. + * + * @author Mathieu Desnoyers + * @author Alexandre Montplaisir + * @author David Goulet + * + */ +public abstract class LTTngUst { + /** + * Initialize the UST tracer. This should always be called first, before any + * tracepoint* method. + */ + public static void init() { + System.loadLibrary("lttng-ust-jul-jni"); //$NON-NLS-1$ + } + + /** + * Insert a tracepoint for JUL event. + * + * @param msg + * Raw message provided by the JUL API. + * @param logger_name + * Logger name that trigger this event. + * @param class_name + * Name of the class that (allegedly) issued the logging request. + * @param method_name + * Name of the method that (allegedly) issued the logging request. + * @param millis + * Event time in milliseconds since 1970. + * @param log_level + * Log level of the event from JUL. + * @param thread_id + * Identifier for the thread where the message originated. + */ + public static native void tracepoint(String msg, String logger_name, String class_name, + String method_name, long millis, int log_level, int thread_id); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 30b0e9a0..b5166f98 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,6 +4,10 @@ if CXX_WORKS SUBDIRS += hello.cxx endif +if BUILD_JNI_INTERFACE +SUBDIRS += java-jul +endif + SCRIPT_LIST = test_loop run.sh unit_tests dist_noinst_SCRIPTS = $(SCRIPT_LIST) diff --git a/tests/java-jul/JULTest.java b/tests/java-jul/JULTest.java new file mode 100644 index 00000000..0a2e8540 --- /dev/null +++ b/tests/java-jul/JULTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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 + */ + +import java.io.IOException; +import java.util.concurrent.Semaphore; +import java.util.logging.LogManager; + +import org.lttng.ust.jul.*; + +public class JULTest { + private final static int NUM_TESTS = 5; + private static int testCount = 1; + + /* Singleton agent object. */ + private static LTTngAgent agent; + private static LTTngLogHandler handler; + private static LTTngTCPSessiondClient client; + + private static Semaphore sem; + + private static void ok(String desc) { + System.out.println("ok " + testCount + " - " + desc); + testCount++; + } + + public static void go() throws IOException { + handler = new LTTngLogHandler(LogManager.getLogManager()); + assert handler.logManager == LogManager.getLogManager(); + ok("Log handler logManager is valid"); + + client = new LTTngTCPSessiondClient("127.0.0.1", 5345, sem); + assert client != null; + ok("TCP client is valid"); + client.destroy(); + ok("TCP client destroyed"); + + agent = LTTngAgent.getLTTngAgent(); + assert agent != null; + ok("LTTngAgent is valid"); + agent.dispose(); + ok("LTTngAgent disposed"); + } + + public static void main(String args[]) throws Exception { + System.out.println("1.." + NUM_TESTS); + go(); + } +} diff --git a/tests/java-jul/Makefile.am b/tests/java-jul/Makefile.am new file mode 100644 index 00000000..6d3e2aab --- /dev/null +++ b/tests/java-jul/Makefile.am @@ -0,0 +1,25 @@ +if BUILD_JNI_INTERFACE + +if HAVE_JAVA_JDK +JCC=$(JAVA_JDK)/bin/javac +else +JCC=javac +endif + +AM_CPPFLAGS = -I$(top_srcdir)/include + +EXTRA_DIST = test_jul + +JUL_jar_file = "$(srcdir)/../../liblttng-ust-jul/liblttng-ust-jul.jar" + +default: all + +all: JULTest.class + +clean-local: + rm -f *.class + +JULTest.class: JULTest.java + $(JCC) -cp $(JUL_jar_file) -d "$(builddir)" "$(srcdir)/JULTest.java" + +endif diff --git a/tests/java-jul/test_jul b/tests/java-jul/test_jul new file mode 100755 index 00000000..192dfdac --- /dev/null +++ b/tests/java-jul/test_jul @@ -0,0 +1,12 @@ +#!/bin/bash + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/.. +TESTCLASS="JULTest" + +if [ ! -f "$CURDIR/$TESTCLASS.class" ]; then + echo "1..0 # Skipped: Java support not build" + exit 0 +fi + +java -ea -cp "$CURDIR:$TESTDIR/../liblttng-ust-jul/liblttng-ust-jul.jar" -Djava.library.path="$TESTDIR/../liblttng-ust-jul/.libs" $TESTCLASS diff --git a/tests/unit_tests b/tests/unit_tests index 91101383..2520c0d8 100644 --- a/tests/unit_tests +++ b/tests/unit_tests @@ -1 +1,2 @@ snprintf/test_snprintf +java-jul/test_jul -- 2.34.1