/* * 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" 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_entry { int32_t context_name_offset; 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; 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 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_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++) { 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_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); 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: { /* 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(); } 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_entry *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: { 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)); if (str) { 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_entry *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: { 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(); } } /* * 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) (long) 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); }