Pass the Java app context information using two separate arrays
authorAlexandre Montplaisir <alexmonthy@efficios.com>
Fri, 12 Feb 2016 20:28:07 +0000 (15:28 -0500)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 12 Feb 2016 22:54:29 +0000 (17:54 -0500)
Instead of using one array with length limits for strings, we can
pass two separate arrays: the first will continue to contain fixed-
size entries, but instead of 256 bytes for strings, we will use
4-byte offsets to a second array, which will contain only those
variable-length strings.

The advantage is that we pass less bytes overall, and we don't
limit the context names or values to 256 bytes anymore.

Signed-off-by: Alexandre Montplaisir <alexmonthy@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
liblttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/context/ContextInfoSerializer.java
liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngJulApi.java
liblttng-ust-java-agent/java/lttng-ust-agent-jul/org/lttng/ust/agent/jul/LttngLogHandler.java
liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLog4jApi.java
liblttng-ust-java-agent/java/lttng-ust-agent-log4j/org/lttng/ust/agent/log4j/LttngLogAppender.java
liblttng-ust-java-agent/jni/common/lttng_ust_context.c
liblttng-ust-java-agent/jni/common/lttng_ust_context.h
liblttng-ust-java-agent/jni/jul/lttng_ust_jul.c
liblttng-ust-java-agent/jni/log4j/lttng_ust_log4j.c

index be613043920af9b63290962df6c076879c43aa43..878f44ef90bb8e3ae8e54cd4875e3abfc9fd0164 100644 (file)
@@ -17,6 +17,8 @@
 
 package org.lttng.ust.agent.context;
 
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -30,18 +32,34 @@ import org.lttng.ust.agent.utils.LttngUstAgentLogger;
  * This class is used to serialize the list of "context info" objects to pass
  * through JNI.
  *
- * The protocol expects 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 protocol expects two byte array parameters, which are contained here in
+ * the {@link SerializedContexts} inner class.
+ *
+ * The first byte array is called the "entries array", and contains fixed-size
+ * entries, one per context element.
+ *
+ * The second one is the "strings array", it is of variable length and used to
+ * hold the variable-length strings. Each one of these strings is formatted as a
+ * UTF-8 C-string, meaning in will end with a "\0" byte to indicate its end.
+ * Entries in the first array may refer to offsets in the second array to point
+ * to relevant strings.
+ *
+ * The fixed-size entries in the entries array contain the following elements
+ * (size in bytes in parentheses):
  *
  * <ul>
- * <li>The full context name, like "$app.myprovider:mycontext" (256)</li>
+ * <li>The offset in the strings array pointing to the full context name, like
+ * "$app.myprovider:mycontext" (4)</li>
  * <li>The context value type (1)</li>
- * <li>The context value itself(256)</li>
+ * <li>The context value itself (8)</li>
  * </ul>
  *
- * So the total size of each entry is 513 bytes. All unused bytes will be
- * zero'ed.
+ * The context value type will indicate how many bytes are used for the value.
+ * If the it is of String type, then we use 4 bytes to represent the offset in
+ * the strings array.
+ *
+ * So the total size of each entry is 13 bytes. All unused bytes (for context
+ * values shorter than 8 bytes for example) will be zero'ed.
  *
  * @author Alexandre Montplaisir
  */
@@ -69,12 +87,48 @@ public class ContextInfoSerializer {
                }
        }
 
+       /**
+        * Class used to wrap the two byte arrays returned by
+        * {@link #queryAndSerializeRequestedContexts}.
+        */
+       public static class SerializedContexts {
+
+               private final byte[] contextEntries;
+               private final byte[] contextStrings;
+
+               /**
+                * Constructor
+                *
+                * @param entries
+                *            Arrays for the fixed-size context entries.
+                * @param strings
+                *            Arrays for variable-length strings
+                */
+               public SerializedContexts(byte[] entries, byte[] strings) {
+                       contextEntries = entries;
+                       contextStrings = strings;
+               }
+
+               /**
+                * @return The entries array
+                */
+               public byte[] getEntriesArray() {
+                       return contextEntries;
+               }
+
+               /**
+                * @return The strings array
+                */
+               public byte[] getStringsArray() {
+                       return contextStrings;
+               }
+       }
+
        private static final String UST_APP_CTX_PREFIX = "$app.";
-       private static final int ELEMENT_LENGTH = 256;
-       private static final int ENTRY_LENGTH = 513;
+       private static final int ENTRY_LENGTH = 13;
        private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder();
        private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
-       private static final byte[] EMPTY_ARRAY = new byte[0];
+       private static final SerializedContexts EMPTY_CONTEXTS = new SerializedContexts(new byte[0], new byte[0]);
 
        /**
         * From the list of requested contexts in the tracing session, look them up
@@ -88,16 +142,10 @@ public class ContextInfoSerializer {
         * @return The byte array representing the intersection of the requested and
         *         available contexts.
         */
-       public static byte[] queryAndSerializeRequestedContexts(Collection<Map.Entry<String, Map<String, Integer>>> enabledContexts) {
+       public static SerializedContexts queryAndSerializeRequestedContexts(Collection<Map.Entry<String, Map<String, Integer>>> enabledContexts) {
                if (enabledContexts.isEmpty()) {
                        /* Early return if there is no requested context information */
-                       return EMPTY_ARRAY;
-               }
-
-               /* Compute the total number of contexts (flatten the map) */
-               int totalArraySize = 0;
-               for (Map.Entry<String, Map<String, Integer>> contexts : enabledContexts) {
-                       totalArraySize += contexts.getValue().size() * ENTRY_LENGTH;
+                       return EMPTY_CONTEXTS;
                }
 
                ContextInfoManager contextManager;
@@ -108,110 +156,140 @@ public class ContextInfoSerializer {
                         * The JNI library is not available, do not send any context
                         * information. No retriever could have been defined anyways.
                         */
-                       return EMPTY_ARRAY;
+                       return EMPTY_CONTEXTS;
                }
 
-               ByteBuffer buffer = ByteBuffer.allocate(totalArraySize);
-               buffer.order(NATIVE_ORDER);
-               buffer.clear();
-
-               for (Map.Entry<String, Map<String, Integer>> entry : enabledContexts) {
-                       String requestedRetrieverName = entry.getKey();
-                       Map<String, Integer> requestedContexts = entry.getValue();
-
-                       IContextInfoRetriever retriever = contextManager.getContextInfoRetriever(requestedRetrieverName);
-
-                       for (String requestedContext : requestedContexts.keySet()) {
-                               Object contextInfo;
-                               if (retriever == null) {
-                                       contextInfo = null;
-                               } else {
-                                       contextInfo = retriever.retrieveContextInfo(requestedContext);
-                                       /*
-                                        * 'contextInfo' can still be null here, which would
-                                        * indicate the retriever does not supply this context. We
-                                        * will still write this information so that the tracer can
-                                        * know about it.
-                                        */
-                               }
+               /* Compute the total number of contexts (flatten the map) */
+               int totalArraySize = 0;
+               for (Map.Entry<String, Map<String, Integer>> contexts : enabledContexts) {
+                       totalArraySize += contexts.getValue().size() * ENTRY_LENGTH;
+               }
+
+               /* Prepare the ByteBuffer that will generate the "entries" array */
+               ByteBuffer entriesBuffer = ByteBuffer.allocate(totalArraySize);
+               entriesBuffer.order(NATIVE_ORDER);
+               entriesBuffer.clear();
+
+               /* Prepare the streams that will generate the "strings" array */
+               ByteArrayOutputStream stringsBaos = new ByteArrayOutputStream();
+               DataOutputStream stringsDos = new DataOutputStream(stringsBaos);
+
+               try {
+                       for (Map.Entry<String, Map<String, Integer>> entry : enabledContexts) {
+                               String requestedRetrieverName = entry.getKey();
+                               Map<String, Integer> requestedContexts = entry.getValue();
+
+                               IContextInfoRetriever retriever = contextManager.getContextInfoRetriever(requestedRetrieverName);
+
+                               for (String requestedContext : requestedContexts.keySet()) {
+                                       Object contextInfo;
+                                       if (retriever == null) {
+                                               contextInfo = null;
+                                       } else {
+                                               contextInfo = retriever.retrieveContextInfo(requestedContext);
+                                               /*
+                                                * 'contextInfo' can still be null here, which would
+                                                * indicate the retriever does not supply this context.
+                                                * We will still write this information so that the
+                                                * tracer can know about it.
+                                                */
+                                       }
 
-                               /* Serialize the result to the 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);
+                                       /* Serialize the result to the buffers */
+                                       // FIXME Eventually pass the retriever name only once?
+                                       String fullContextName = (UST_APP_CTX_PREFIX + requestedRetrieverName + ':' + requestedContext);
+                                       byte[] strArray = fullContextName.getBytes(UTF8_CHARSET);
 
-                               LttngUstAgentLogger.log(ContextInfoSerializer.class,
-                                               "ContextInfoSerializer: Context to be sent through JNI: " + fullContextName + '=' +
-                                               (contextInfo == null ? "null" : contextInfo.toString()));
+                                       entriesBuffer.putInt(stringsDos.size());
+                                       stringsDos.write(strArray);
+                                       stringsDos.writeChar('\0');
 
-                               serializeContextInfo(buffer, contextInfo);
+                                       LttngUstAgentLogger.log(ContextInfoSerializer.class,
+                                                       "ContextInfoSerializer: Context to be sent through JNI: " + fullContextName + '=' +
+                                                                       (contextInfo == null ? "null" : contextInfo.toString()));
+
+                                       serializeContextInfo(entriesBuffer, stringsDos, contextInfo);
+                               }
                        }
+
+                       stringsDos.flush();
+                       stringsBaos.flush();
+
+               } catch (IOException e) {
+                       /*
+                        * Should not happen because we are wrapping a
+                        * ByteArrayOutputStream, which writes to memory
+                        */
+                       e.printStackTrace();
                }
-               return buffer.array();
+
+               byte[] entriesArray = entriesBuffer.array();
+               byte[] stringsArray = stringsBaos.toByteArray();
+               return new SerializedContexts(entriesArray, stringsArray);
        }
 
-       private static void serializeContextInfo(ByteBuffer buffer, Object contextInfo) {
+       private static final int CONTEXT_VALUE_LENGTH = 8;
+
+       private static void serializeContextInfo(ByteBuffer entriesBuffer, DataOutputStream stringsDos, Object contextInfo) throws IOException {
                int remainingBytes;
                if (contextInfo == null) {
-                       buffer.put(DataType.NULL.getValue());
-                       remainingBytes = ELEMENT_LENGTH;
+                       entriesBuffer.put(DataType.NULL.getValue());
+                       remainingBytes = CONTEXT_VALUE_LENGTH;
 
                } else if (contextInfo instanceof Integer) {
-                       buffer.put(DataType.INTEGER.getValue());
-                       buffer.putInt(((Integer) contextInfo).intValue());
-                       remainingBytes = ELEMENT_LENGTH - 4;
+                       entriesBuffer.put(DataType.INTEGER.getValue());
+                       entriesBuffer.putInt(((Integer) contextInfo).intValue());
+                       remainingBytes = CONTEXT_VALUE_LENGTH - 4;
 
                } else if (contextInfo instanceof Long) {
-                       buffer.put(DataType.LONG.getValue());
-                       buffer.putLong(((Long) contextInfo).longValue());
-                       remainingBytes = ELEMENT_LENGTH - 8;
+                       entriesBuffer.put(DataType.LONG.getValue());
+                       entriesBuffer.putLong(((Long) contextInfo).longValue());
+                       remainingBytes = CONTEXT_VALUE_LENGTH - 8;
 
                } else if (contextInfo instanceof Double) {
-                       buffer.put(DataType.DOUBLE.getValue());
-                       buffer.putDouble(((Double) contextInfo).doubleValue());
-                       remainingBytes = ELEMENT_LENGTH - 8;
+                       entriesBuffer.put(DataType.DOUBLE.getValue());
+                       entriesBuffer.putDouble(((Double) contextInfo).doubleValue());
+                       remainingBytes = CONTEXT_VALUE_LENGTH - 8;
 
                } else if (contextInfo instanceof Float) {
-                       buffer.put(DataType.FLOAT.getValue());
-                       buffer.putFloat(((Float) contextInfo).floatValue());
-                       remainingBytes = ELEMENT_LENGTH - 4;
+                       entriesBuffer.put(DataType.FLOAT.getValue());
+                       entriesBuffer.putFloat(((Float) contextInfo).floatValue());
+                       remainingBytes = CONTEXT_VALUE_LENGTH - 4;
 
                } else if (contextInfo instanceof Byte) {
-                       buffer.put(DataType.BYTE.getValue());
-                       buffer.put(((Byte) contextInfo).byteValue());
-                       remainingBytes = ELEMENT_LENGTH - 1;
+                       entriesBuffer.put(DataType.BYTE.getValue());
+                       entriesBuffer.put(((Byte) contextInfo).byteValue());
+                       remainingBytes = CONTEXT_VALUE_LENGTH - 1;
 
                } else if (contextInfo instanceof Short) {
-                       buffer.put(DataType.SHORT.getValue());
-                       buffer.putShort(((Short) contextInfo).shortValue());
-                       remainingBytes = ELEMENT_LENGTH - 2;
+                       entriesBuffer.put(DataType.SHORT.getValue());
+                       entriesBuffer.putShort(((Short) contextInfo).shortValue());
+                       remainingBytes = CONTEXT_VALUE_LENGTH - 2;
 
                } else if (contextInfo instanceof Boolean) {
-                       buffer.put(DataType.BOOLEAN.getValue());
+                       entriesBuffer.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;
+                       entriesBuffer.put((byte) (b ? 1 : 0));
+                       remainingBytes = CONTEXT_VALUE_LENGTH - 1;
 
                } else {
-                       /* We'll write the object as a string. Also includes the case of Character. */
+                       /* Also includes the case of Character. */
+                       /*
+                        * We'll write the object as a string, into the strings array. We
+                        * will write the corresponding offset to the entries array.
+                        */
                        String str = contextInfo.toString();
                        byte[] strArray = str.getBytes(UTF8_CHARSET);
 
-                       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;
-                       }
+                       entriesBuffer.put(DataType.STRING.getValue());
+
+                       entriesBuffer.putInt(stringsDos.size());
+                       stringsDos.write(strArray);
+                       stringsDos.writeChar('\0');
+
+                       remainingBytes = CONTEXT_VALUE_LENGTH - 4;
                }
-               buffer.position(buffer.position() + remainingBytes);
+               entriesBuffer.position(entriesBuffer.position() + remainingBytes);
        }
 }
index b1f0d0193f615983515d0bf7ad2c1604c958953e..e4d399f795af5a3e5d68f9110d50c277b5ec17bd 100644 (file)
@@ -41,5 +41,6 @@ final class LttngJulApi {
                        long millis,
                        int log_level,
                        int thread_id,
-                       byte[] contextInformation);
+                       byte[] contextEntries,
+                       byte[] contextStrings);
 }
index 535a2a3f57fd0d54e9216891354f7fe28a373e13..00c5c48cc77cb0ac352f57e1f5e7e0dffe6770fc 100644 (file)
@@ -123,7 +123,7 @@ public class LttngLogHandler extends Handler implements ILttngHandler {
 
                /* Retrieve all the requested context information we can find */
                Collection<Entry<String, Map<String, Integer>>> enabledContexts = agent.getEnabledAppContexts();
-               byte[] contextInfo = ContextInfoSerializer.queryAndSerializeRequestedContexts(enabledContexts);
+               ContextInfoSerializer.SerializedContexts contextInfo = ContextInfoSerializer.queryAndSerializeRequestedContexts(enabledContexts);
 
                eventCount.incrementAndGet();
 
@@ -139,7 +139,8 @@ public class LttngLogHandler extends Handler implements ILttngHandler {
                                record.getMillis(),
                                record.getLevel().intValue(),
                                record.getThreadID(),
-                               contextInfo);
+                               contextInfo.getEntriesArray(),
+                               contextInfo.getStringsArray());
        }
 
 }
index 61a041879d44a1adfc52dc53b2fbc0de7dd28b9c..035864c2c2fea7ab2afaa75c029cc53b73c01b94 100644 (file)
@@ -45,5 +45,6 @@ final class LttngLog4jApi {
                        long timestamp,
                        int loglevel,
                        String thread_name,
-                       byte[] contextInformation);
+                       byte[] contextEntries,
+                       byte[] contextStrings);
 }
index 5c6df4dff4e9c8e213c9fb3c2fa5a83d7827848d..92b76de2594446e4afc67a559c30f931b487cb8e 100644 (file)
@@ -122,7 +122,7 @@ public class LttngLogAppender extends AppenderSkeleton implements ILttngHandler
 
                /* Retrieve all the requested context information we can find */
                Collection<Entry<String, Map<String, Integer>>> enabledContexts = agent.getEnabledAppContexts();
-               byte[] contextInfo = ContextInfoSerializer.queryAndSerializeRequestedContexts(enabledContexts);
+               ContextInfoSerializer.SerializedContexts contextInfo = ContextInfoSerializer.queryAndSerializeRequestedContexts(enabledContexts);
 
                eventCount.incrementAndGet();
 
@@ -135,7 +135,8 @@ public class LttngLogAppender extends AppenderSkeleton implements ILttngHandler
                                event.getTimeStamp(),
                                event.getLevel().toInt(),
                                event.getThreadName(),
-                               contextInfo);
+                               contextInfo.getEntriesArray(),
+                               contextInfo.getStringsArray());
        }
 
 }
index 8cc4087f07548585f37254a3d79cf1d3146b6ffb..f412cc18fcb6c1b2eb33efe4526f78425401de55 100644 (file)
 #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,
@@ -44,8 +40,8 @@ enum lttng_ust_jni_type {
        JNI_TYPE_STRING = 8,
 };
 
-struct lttng_ust_jni_ctx {
-       char context_name[LTTNG_UST_JNI_CONTEXT_NAME_LEN];
+struct lttng_ust_jni_ctx_entry {
+       int32_t context_name_offset;
        char type;      /* enum lttng_ust_jni_type */
        union {
                int32_t _integer;
@@ -55,33 +51,47 @@ struct lttng_ust_jni_ctx {
                signed char _byte;
                int16_t _short;
                signed char _boolean;
-               char _string[LTTNG_UST_JNI_VALUE_LEN];
+               int32_t _string_offset;
        } 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)
+static const char *get_ctx_string_at_offset(int32_t offset)
+{
+       signed char *ctx_strings_array = lttng_ust_context_info_tls.ctx_strings;
+
+       if (offset < 0 || offset >= lttng_ust_context_info_tls.ctx_strings_len) {
+               return NULL;
+       }
+       return (const char *) (ctx_strings_array + offset);
+}
+
+static struct lttng_ust_jni_ctx_entry *lookup_ctx_by_name(const char *ctx_name)
 {
-       struct lttng_ust_jni_ctx *ctx_array = lttng_ust_context_info_tls.ctx;
-       int i, len = lttng_ust_context_info_tls.len / sizeof(struct lttng_ust_jni_ctx);
+       struct lttng_ust_jni_ctx_entry *ctx_entries_array = lttng_ust_context_info_tls.ctx_entries;
+       int i, len = lttng_ust_context_info_tls.ctx_entries_len / sizeof(struct lttng_ust_jni_ctx_entry);
 
        for (i = 0; i < len; i++) {
-               if (strcmp(ctx_array[i].context_name, ctx_name) == 0)
-                       return &ctx_array[i];
+               int32_t offset = ctx_entries_array[i].context_name_offset;
+               const char *string = get_ctx_string_at_offset(offset);
+
+               if (string && strcmp(string, ctx_name) == 0) {
+                       return &ctx_entries_array[i];
+               }
        }
        return NULL;
-
 }
 
 static size_t get_size_cb(struct lttng_ctx_field *field, size_t offset)
 {
-       struct lttng_ust_jni_ctx *jctx;
+       struct lttng_ust_jni_ctx_entry *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);
@@ -119,8 +129,16 @@ static size_t get_size_cb(struct lttng_ctx_field *field, size_t offset)
                size += sizeof(char);           /* variant */
                break;
        case JNI_TYPE_STRING:
-               size += strlen(jctx->value._string) + 1;
+       {
+               /* The value is an offset, the string is in the "strings" array */
+               int32_t string_offset = jctx->value._string_offset;
+               const char *string = get_ctx_string_at_offset(string_offset);
+
+               if (string) {
+                       size += strlen(string) + 1;
+               }
                break;
+       }
        default:
                abort();
        }
@@ -132,7 +150,7 @@ 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;
+       struct lttng_ust_jni_ctx_entry *jctx;
        const char *ctx_name = field->event_field.name;
        enum lttng_ust_jni_type jni_type;
        char sel_char;
@@ -229,12 +247,19 @@ static void record_cb(struct lttng_ctx_field *field,
        }
        case JNI_TYPE_STRING:
        {
-                       const char *str = jctx->value._string;
-
-                       sel_char = LTTNG_UST_DYNAMIC_TYPE_STRING;
+                       int32_t offset = jctx->value._string_offset;
+                       const char *str = get_ctx_string_at_offset(offset);
+
+                       if (str) {
+                               sel_char = LTTNG_UST_DYNAMIC_TYPE_STRING;
+                       } else {
+                               sel_char = LTTNG_UST_DYNAMIC_TYPE_NONE;
+                       }
                        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);
+                       if (str) {
+                               chan->ops->event_write(ctx, str, strlen(str) + 1);
+                       }
                        break;
        }
        default:
@@ -245,7 +270,7 @@ static void record_cb(struct lttng_ctx_field *field,
 static void get_value_cb(struct lttng_ctx_field *field,
                struct lttng_ctx_value *value)
 {
-       struct lttng_ust_jni_ctx *jctx;
+       struct lttng_ust_jni_ctx_entry *jctx;
        const char *ctx_name = field->event_field.name;
        enum lttng_ust_jni_type jni_type;
 
@@ -289,9 +314,18 @@ static void get_value_cb(struct lttng_ctx_field *field,
                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;
+       {
+               int32_t offset = jctx->value._string_offset;
+               const char *str = get_ctx_string_at_offset(offset);
+
+               if (str) {
+                       value->sel = LTTNG_UST_DYNAMIC_TYPE_STRING;
+                       value->u.str = str;
+               } else {
+                       value->sel = LTTNG_UST_DYNAMIC_TYPE_NONE;
+               }
                break;
+       }
        default:
                abort();
        }
index 2bdef3ae251cdb984ff86798bb420f432c032cc5..415f7de228fa9bcbc500dc28929a978272ef0b76 100644 (file)
 #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_ctx_entry;
 
 struct lttng_ust_jni_tls {
-       struct lttng_ust_jni_ctx *ctx;
-       int32_t len;
+       struct lttng_ust_jni_ctx_entry *ctx_entries;
+       int32_t ctx_entries_len;
+       signed char *ctx_strings;
+       int32_t ctx_strings_len;
 };
 
 extern __thread struct lttng_ust_jni_tls lttng_ust_context_info_tls;
index 62d820b2e3ed45466eeb330b19da1c2812ade159..562da6ba2a42c97e7e2d03b3983897f52c7b8351 100644 (file)
@@ -64,31 +64,39 @@ JNIEXPORT void JNICALL Java_org_lttng_ust_agent_jul_LttngJulApi_tracepointWithCo
                                                jlong millis,
                                                jint log_level,
                                                jint thread_id,
-                                               jbyteArray context_info)
+                                               jbyteArray context_info_entries,
+                                               jbyteArray context_info_strings)
 {
        jboolean iscopy;
        const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
        const char *logger_name_cstr = (*env)->GetStringUTFChars(env, logger_name, &iscopy);
        const char *class_name_cstr = (*env)->GetStringUTFChars(env, class_name, &iscopy);
        const char *method_name_cstr = (*env)->GetStringUTFChars(env, method_name, &iscopy);
-       signed char *context_info_array;
+       signed char *context_info_entries_array;
+       signed char *context_info_strings_array;
 
        /*
         * Write these to the TLS variables, so that the UST callbacks in
         * lttng_ust_context.c can access them.
         */
-       context_info_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);
+       context_info_entries_array = (*env)->GetByteArrayElements(env, context_info_entries, &iscopy);
+       lttng_ust_context_info_tls.ctx_entries = (struct lttng_ust_jni_ctx_entry *) context_info_entries_array;
+       lttng_ust_context_info_tls.ctx_entries_len = (*env)->GetArrayLength(env, context_info_entries);
+       context_info_strings_array = (*env)->GetByteArrayElements(env, context_info_strings, &iscopy);
+       lttng_ust_context_info_tls.ctx_strings = context_info_strings_array;
+       lttng_ust_context_info_tls.ctx_strings_len = (*env)->GetArrayLength(env, context_info_strings);
 
        tracepoint(lttng_jul, event, msg_cstr, logger_name_cstr,
                        class_name_cstr, method_name_cstr, millis, log_level, thread_id);
 
-       lttng_ust_context_info_tls.ctx = NULL;
-       lttng_ust_context_info_tls.len = 0;
+       lttng_ust_context_info_tls.ctx_entries = NULL;
+       lttng_ust_context_info_tls.ctx_entries_len = 0;
+       lttng_ust_context_info_tls.ctx_strings = NULL;
+       lttng_ust_context_info_tls.ctx_strings_len = 0;
        (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
        (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
        (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
        (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
-       (*env)->ReleaseByteArrayElements(env, context_info, context_info_array, 0);
+       (*env)->ReleaseByteArrayElements(env, context_info_entries, context_info_entries_array, 0);
+       (*env)->ReleaseByteArrayElements(env, context_info_strings, context_info_strings_array, 0);
 }
index b80312e2d3c72f6379d9d60d82ed8407fceeb9b4..2537922d106d76b81d42d402a56d4d430d714691 100644 (file)
@@ -73,7 +73,8 @@ JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LttngLog4jApi_tracepointWi
                                                jlong timestamp,
                                                jint loglevel,
                                                jstring thread_name,
-                                               jbyteArray context_info)
+                                               jbyteArray context_info_entries,
+                                               jbyteArray context_info_strings)
 {
        jboolean iscopy;
        const char *msg_cstr = (*env)->GetStringUTFChars(env, msg, &iscopy);
@@ -82,27 +83,34 @@ JNIEXPORT void JNICALL Java_org_lttng_ust_agent_log4j_LttngLog4jApi_tracepointWi
        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;
+       signed char *context_info_entries_array;
+       signed char *context_info_strings_array;
 
        /*
         * Write these to the TLS variables, so that the UST callbacks in
         * lttng_ust_context.c can access them.
         */
-       context_info_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);
+       context_info_entries_array = (*env)->GetByteArrayElements(env, context_info_entries, &iscopy);
+       lttng_ust_context_info_tls.ctx_entries = (struct lttng_ust_jni_ctx_entry *) context_info_entries_array;
+       lttng_ust_context_info_tls.ctx_entries_len = (*env)->GetArrayLength(env, context_info_entries);
+       context_info_strings_array = (*env)->GetByteArrayElements(env, context_info_strings, &iscopy);
+       lttng_ust_context_info_tls.ctx_strings = context_info_strings_array;
+       lttng_ust_context_info_tls.ctx_strings_len = (*env)->GetArrayLength(env, context_info_strings);
 
        tracepoint(lttng_log4j, event, msg_cstr, logger_name_cstr,
                   class_name_cstr, method_name_cstr, file_name_cstr,
                   line_number, timestamp, loglevel, thread_name_cstr);
 
-       lttng_ust_context_info_tls.ctx = NULL;
-       lttng_ust_context_info_tls.len = 0;
+       lttng_ust_context_info_tls.ctx_entries = NULL;
+       lttng_ust_context_info_tls.ctx_entries_len = 0;
+       lttng_ust_context_info_tls.ctx_strings = NULL;
+       lttng_ust_context_info_tls.ctx_strings_len = 0;
        (*env)->ReleaseStringUTFChars(env, msg, msg_cstr);
        (*env)->ReleaseStringUTFChars(env, logger_name, logger_name_cstr);
        (*env)->ReleaseStringUTFChars(env, class_name, class_name_cstr);
        (*env)->ReleaseStringUTFChars(env, method_name, method_name_cstr);
        (*env)->ReleaseStringUTFChars(env, file_name, file_name_cstr);
        (*env)->ReleaseStringUTFChars(env, thread_name, thread_name_cstr);
-       (*env)->ReleaseByteArrayElements(env, context_info, context_info_array, 0);
+       (*env)->ReleaseByteArrayElements(env, context_info_entries, context_info_entries_array, 0);
+       (*env)->ReleaseByteArrayElements(env, context_info_strings, context_info_strings_array, 0);
 }
This page took 0.037473 seconds and 4 git commands to generate.