Implement Java agent application context retrieval
[deliverable/lttng-ust.git] / liblttng-ust-java-agent / jni / common / lttng_ust_context.c
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 (file)
index 0000000..8cc4087
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *               2016 - EfficiOS Inc., Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <string.h>
+#include <inttypes.h>
+#include <lttng/ust-events.h>
+#include <lttng/ringbuffer-config.h>
+#include <lttng/ust-context-provider.h>
+
+#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);
+}
This page took 0.029245 seconds and 5 git commands to generate.