Commit | Line | Data |
---|---|---|
8ab5c06b AM |
1 | /* |
2 | * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com> | |
3 | * 2016 - EfficiOS Inc., Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
4 | * | |
5 | * This library is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU Lesser General Public | |
7 | * License as published by the Free Software Foundation; only | |
8 | * version 2.1 of the License. | |
9 | * | |
10 | * This library is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * Lesser General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU Lesser General Public | |
16 | * License along with this library; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
18 | */ | |
19 | ||
20 | #include "org_lttng_ust_agent_context_LttngContextApi.h" | |
21 | ||
22 | #include <string.h> | |
23 | #include <inttypes.h> | |
24 | #include <lttng/ust-events.h> | |
25 | #include <lttng/ringbuffer-config.h> | |
26 | #include <lttng/ust-context-provider.h> | |
27 | ||
28 | #include "helper.h" | |
29 | #include "lttng_ust_context.h" | |
30 | ||
8ab5c06b AM |
31 | enum lttng_ust_jni_type { |
32 | JNI_TYPE_NULL = 0, | |
33 | JNI_TYPE_INTEGER = 1, | |
34 | JNI_TYPE_LONG = 2, | |
35 | JNI_TYPE_DOUBLE = 3, | |
36 | JNI_TYPE_FLOAT = 4, | |
37 | JNI_TYPE_BYTE = 5, | |
38 | JNI_TYPE_SHORT = 6, | |
39 | JNI_TYPE_BOOLEAN = 7, | |
40 | JNI_TYPE_STRING = 8, | |
41 | }; | |
42 | ||
b1ca4c5f AM |
43 | struct lttng_ust_jni_ctx_entry { |
44 | int32_t context_name_offset; | |
8ab5c06b AM |
45 | char type; /* enum lttng_ust_jni_type */ |
46 | union { | |
47 | int32_t _integer; | |
48 | int64_t _long; | |
49 | double _double; | |
50 | float _float; | |
51 | signed char _byte; | |
52 | int16_t _short; | |
53 | signed char _boolean; | |
b1ca4c5f | 54 | int32_t _string_offset; |
8ab5c06b AM |
55 | } value; |
56 | } __attribute__((packed)); | |
57 | ||
58 | /* TLS passing context info from JNI to callbacks. */ | |
59 | __thread struct lttng_ust_jni_tls lttng_ust_context_info_tls; | |
60 | ||
b1ca4c5f AM |
61 | static const char *get_ctx_string_at_offset(int32_t offset) |
62 | { | |
63 | signed char *ctx_strings_array = lttng_ust_context_info_tls.ctx_strings; | |
64 | ||
65 | if (offset < 0 || offset >= lttng_ust_context_info_tls.ctx_strings_len) { | |
66 | return NULL; | |
67 | } | |
68 | return (const char *) (ctx_strings_array + offset); | |
69 | } | |
70 | ||
71 | static struct lttng_ust_jni_ctx_entry *lookup_ctx_by_name(const char *ctx_name) | |
8ab5c06b | 72 | { |
b1ca4c5f AM |
73 | struct lttng_ust_jni_ctx_entry *ctx_entries_array = lttng_ust_context_info_tls.ctx_entries; |
74 | int i, len = lttng_ust_context_info_tls.ctx_entries_len / sizeof(struct lttng_ust_jni_ctx_entry); | |
8ab5c06b AM |
75 | |
76 | for (i = 0; i < len; i++) { | |
b1ca4c5f AM |
77 | int32_t offset = ctx_entries_array[i].context_name_offset; |
78 | const char *string = get_ctx_string_at_offset(offset); | |
79 | ||
80 | if (string && strcmp(string, ctx_name) == 0) { | |
81 | return &ctx_entries_array[i]; | |
82 | } | |
8ab5c06b AM |
83 | } |
84 | return NULL; | |
8ab5c06b AM |
85 | } |
86 | ||
87 | static size_t get_size_cb(struct lttng_ctx_field *field, size_t offset) | |
88 | { | |
b1ca4c5f | 89 | struct lttng_ust_jni_ctx_entry *jctx; |
8ab5c06b AM |
90 | size_t size = 0; |
91 | const char *ctx_name = field->event_field.name; | |
92 | enum lttng_ust_jni_type jni_type; | |
93 | ||
b1ca4c5f | 94 | |
8ab5c06b AM |
95 | size += lib_ring_buffer_align(offset, lttng_alignof(char)); |
96 | size += sizeof(char); /* tag */ | |
97 | jctx = lookup_ctx_by_name(ctx_name); | |
98 | if (!jctx) { | |
99 | jni_type = JNI_TYPE_NULL; | |
100 | } else { | |
101 | jni_type = jctx->type; | |
102 | } | |
103 | switch (jni_type) { | |
104 | case JNI_TYPE_NULL: | |
105 | break; | |
106 | case JNI_TYPE_INTEGER: | |
107 | size += lib_ring_buffer_align(offset, lttng_alignof(int32_t)); | |
108 | size += sizeof(int32_t); /* variant */ | |
109 | break; | |
110 | case JNI_TYPE_LONG: | |
111 | size += lib_ring_buffer_align(offset, lttng_alignof(int64_t)); | |
112 | size += sizeof(int64_t); /* variant */ | |
113 | break; | |
114 | case JNI_TYPE_DOUBLE: | |
115 | size += lib_ring_buffer_align(offset, lttng_alignof(double)); | |
116 | size += sizeof(double); /* variant */ | |
117 | break; | |
118 | case JNI_TYPE_FLOAT: | |
119 | size += lib_ring_buffer_align(offset, lttng_alignof(float)); | |
120 | size += sizeof(float); /* variant */ | |
121 | break; | |
122 | case JNI_TYPE_SHORT: | |
123 | size += lib_ring_buffer_align(offset, lttng_alignof(int16_t)); | |
124 | size += sizeof(int16_t); /* variant */ | |
125 | break; | |
126 | case JNI_TYPE_BYTE: /* Fall-through. */ | |
127 | case JNI_TYPE_BOOLEAN: | |
128 | size += lib_ring_buffer_align(offset, lttng_alignof(char)); | |
129 | size += sizeof(char); /* variant */ | |
130 | break; | |
131 | case JNI_TYPE_STRING: | |
b1ca4c5f AM |
132 | { |
133 | /* The value is an offset, the string is in the "strings" array */ | |
134 | int32_t string_offset = jctx->value._string_offset; | |
135 | const char *string = get_ctx_string_at_offset(string_offset); | |
136 | ||
137 | if (string) { | |
138 | size += strlen(string) + 1; | |
139 | } | |
8ab5c06b | 140 | break; |
b1ca4c5f | 141 | } |
8ab5c06b AM |
142 | default: |
143 | abort(); | |
144 | } | |
145 | return size; | |
146 | ||
147 | } | |
148 | ||
149 | static void record_cb(struct lttng_ctx_field *field, | |
150 | struct lttng_ust_lib_ring_buffer_ctx *ctx, | |
151 | struct lttng_channel *chan) | |
152 | { | |
b1ca4c5f | 153 | struct lttng_ust_jni_ctx_entry *jctx; |
8ab5c06b AM |
154 | const char *ctx_name = field->event_field.name; |
155 | enum lttng_ust_jni_type jni_type; | |
156 | char sel_char; | |
157 | ||
158 | jctx = lookup_ctx_by_name(ctx_name); | |
159 | if (!jctx) { | |
160 | jni_type = JNI_TYPE_NULL; | |
161 | } else { | |
162 | jni_type = jctx->type; | |
163 | } | |
164 | ||
165 | switch (jni_type) { | |
166 | case JNI_TYPE_NULL: | |
167 | sel_char = LTTNG_UST_DYNAMIC_TYPE_NONE; | |
168 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); | |
169 | chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); | |
170 | break; | |
171 | case JNI_TYPE_INTEGER: | |
172 | { | |
173 | int32_t v = jctx->value._integer; | |
174 | ||
175 | sel_char = LTTNG_UST_DYNAMIC_TYPE_S32; | |
176 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); | |
177 | chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); | |
178 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); | |
179 | chan->ops->event_write(ctx, &v, sizeof(v)); | |
180 | break; | |
181 | } | |
182 | case JNI_TYPE_LONG: | |
183 | { | |
184 | int64_t v = jctx->value._long; | |
185 | ||
186 | sel_char = LTTNG_UST_DYNAMIC_TYPE_S64; | |
187 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); | |
188 | chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); | |
189 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); | |
190 | chan->ops->event_write(ctx, &v, sizeof(v)); | |
191 | break; | |
192 | } | |
193 | case JNI_TYPE_DOUBLE: | |
194 | { | |
195 | double v = jctx->value._double; | |
196 | ||
197 | sel_char = LTTNG_UST_DYNAMIC_TYPE_DOUBLE; | |
198 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); | |
199 | chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); | |
200 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); | |
201 | chan->ops->event_write(ctx, &v, sizeof(v)); | |
202 | break; | |
203 | } | |
204 | case JNI_TYPE_FLOAT: | |
205 | { | |
206 | float v = jctx->value._float; | |
207 | ||
208 | sel_char = LTTNG_UST_DYNAMIC_TYPE_FLOAT; | |
209 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); | |
210 | chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); | |
211 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); | |
212 | chan->ops->event_write(ctx, &v, sizeof(v)); | |
213 | break; | |
214 | } | |
215 | case JNI_TYPE_SHORT: | |
216 | { | |
217 | int16_t v = jctx->value._short; | |
218 | ||
219 | sel_char = LTTNG_UST_DYNAMIC_TYPE_S16; | |
220 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); | |
221 | chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); | |
222 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); | |
223 | chan->ops->event_write(ctx, &v, sizeof(v)); | |
224 | break; | |
225 | } | |
226 | case JNI_TYPE_BYTE: | |
227 | { | |
228 | char v = jctx->value._byte; | |
229 | ||
230 | sel_char = LTTNG_UST_DYNAMIC_TYPE_S8; | |
231 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); | |
232 | chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); | |
233 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); | |
234 | chan->ops->event_write(ctx, &v, sizeof(v)); | |
235 | break; | |
236 | } | |
237 | case JNI_TYPE_BOOLEAN: | |
238 | { | |
239 | char v = jctx->value._boolean; | |
240 | ||
241 | sel_char = LTTNG_UST_DYNAMIC_TYPE_S8; | |
242 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); | |
243 | chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); | |
244 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(v)); | |
245 | chan->ops->event_write(ctx, &v, sizeof(v)); | |
246 | break; | |
247 | } | |
248 | case JNI_TYPE_STRING: | |
249 | { | |
b1ca4c5f AM |
250 | int32_t offset = jctx->value._string_offset; |
251 | const char *str = get_ctx_string_at_offset(offset); | |
8ab5c06b | 252 | |
b1ca4c5f AM |
253 | if (str) { |
254 | sel_char = LTTNG_UST_DYNAMIC_TYPE_STRING; | |
255 | } else { | |
256 | sel_char = LTTNG_UST_DYNAMIC_TYPE_NONE; | |
257 | } | |
8ab5c06b AM |
258 | lib_ring_buffer_align_ctx(ctx, lttng_alignof(char)); |
259 | chan->ops->event_write(ctx, &sel_char, sizeof(sel_char)); | |
b1ca4c5f AM |
260 | if (str) { |
261 | chan->ops->event_write(ctx, str, strlen(str) + 1); | |
262 | } | |
8ab5c06b AM |
263 | break; |
264 | } | |
265 | default: | |
266 | abort(); | |
267 | } | |
268 | } | |
269 | ||
270 | static void get_value_cb(struct lttng_ctx_field *field, | |
271 | struct lttng_ctx_value *value) | |
272 | { | |
b1ca4c5f | 273 | struct lttng_ust_jni_ctx_entry *jctx; |
8ab5c06b AM |
274 | const char *ctx_name = field->event_field.name; |
275 | enum lttng_ust_jni_type jni_type; | |
276 | ||
277 | jctx = lookup_ctx_by_name(ctx_name); | |
278 | if (!jctx) { | |
279 | jni_type = JNI_TYPE_NULL; | |
280 | } else { | |
281 | jni_type = jctx->type; | |
282 | } | |
283 | ||
284 | switch (jni_type) { | |
285 | case JNI_TYPE_NULL: | |
286 | value->sel = LTTNG_UST_DYNAMIC_TYPE_NONE; | |
287 | break; | |
288 | case JNI_TYPE_INTEGER: | |
289 | value->sel = LTTNG_UST_DYNAMIC_TYPE_S64; | |
290 | value->u.s64 = (int64_t) jctx->value._integer; | |
291 | break; | |
292 | case JNI_TYPE_LONG: | |
293 | value->sel = LTTNG_UST_DYNAMIC_TYPE_S64; | |
294 | value->u.s64 = jctx->value._long; | |
295 | break; | |
296 | case JNI_TYPE_DOUBLE: | |
297 | value->sel = LTTNG_UST_DYNAMIC_TYPE_DOUBLE; | |
298 | value->u.d = jctx->value._double; | |
299 | break; | |
300 | case JNI_TYPE_FLOAT: | |
301 | value->sel = LTTNG_UST_DYNAMIC_TYPE_DOUBLE; | |
302 | value->u.d = (double) jctx->value._float; | |
303 | break; | |
304 | case JNI_TYPE_SHORT: | |
305 | value->sel = LTTNG_UST_DYNAMIC_TYPE_S64; | |
306 | value->u.s64 = (int64_t) jctx->value._short; | |
307 | break; | |
308 | case JNI_TYPE_BYTE: | |
309 | value->sel = LTTNG_UST_DYNAMIC_TYPE_S64; | |
310 | value->u.s64 = (int64_t) jctx->value._byte; | |
311 | break; | |
312 | case JNI_TYPE_BOOLEAN: | |
313 | value->sel = LTTNG_UST_DYNAMIC_TYPE_S64; | |
314 | value->u.s64 = (int64_t) jctx->value._boolean; | |
315 | break; | |
316 | case JNI_TYPE_STRING: | |
b1ca4c5f AM |
317 | { |
318 | int32_t offset = jctx->value._string_offset; | |
319 | const char *str = get_ctx_string_at_offset(offset); | |
320 | ||
321 | if (str) { | |
322 | value->sel = LTTNG_UST_DYNAMIC_TYPE_STRING; | |
323 | value->u.str = str; | |
324 | } else { | |
325 | value->sel = LTTNG_UST_DYNAMIC_TYPE_NONE; | |
326 | } | |
8ab5c06b | 327 | break; |
b1ca4c5f | 328 | } |
8ab5c06b AM |
329 | default: |
330 | abort(); | |
331 | } | |
332 | } | |
333 | ||
334 | /* | |
335 | * Register a context provider to UST. | |
336 | * | |
337 | * Called from the Java side when an application registers a context retriever, | |
338 | * so we create and register a corresponding provider on the C side. | |
339 | */ | |
340 | JNIEXPORT jlong JNICALL Java_org_lttng_ust_agent_context_LttngContextApi_registerProvider(JNIEnv *env, | |
341 | jobject jobj, | |
342 | jstring provider_name) | |
343 | { | |
344 | jboolean iscopy; | |
345 | const char *provider_name_jstr; | |
346 | char *provider_name_cstr; | |
347 | struct lttng_ust_context_provider *provider; | |
348 | /* | |
349 | * Note: a "jlong" is 8 bytes on all architectures, whereas a | |
350 | * C "long" varies. | |
351 | */ | |
352 | jlong provider_ref; | |
353 | ||
354 | provider_name_jstr = (*env)->GetStringUTFChars(env, provider_name, &iscopy); | |
355 | if (!provider_name_jstr) { | |
356 | goto error_jstr; | |
357 | } | |
358 | /* Keep our own copy of the string so UST can use it. */ | |
359 | provider_name_cstr = strdup(provider_name_jstr); | |
360 | (*env)->ReleaseStringUTFChars(env, provider_name, provider_name_jstr); | |
361 | if (!provider_name_cstr) { | |
362 | goto error_strdup; | |
363 | } | |
364 | provider = zmalloc(sizeof(*provider)); | |
365 | if (!provider) { | |
366 | goto error_provider; | |
367 | } | |
368 | provider->name = provider_name_cstr; | |
369 | provider->get_size = get_size_cb; | |
370 | provider->record = record_cb; | |
371 | provider->get_value = get_value_cb; | |
372 | ||
373 | if (lttng_ust_context_provider_register(provider)) { | |
374 | goto error_register; | |
375 | } | |
376 | ||
377 | provider_ref = (jlong) provider; | |
378 | return provider_ref; | |
379 | ||
380 | /* Error handling. */ | |
381 | error_register: | |
382 | free(provider); | |
383 | error_provider: | |
384 | free(provider_name_cstr); | |
385 | error_strdup: | |
386 | error_jstr: | |
387 | return 0; | |
388 | } | |
389 | ||
390 | /* | |
391 | * Unregister a previously-registered context provider. | |
392 | * | |
393 | * Called from the Java side when an application unregisters a context retriever, | |
394 | * so we unregister and delete the corresponding provider on the C side. | |
395 | */ | |
396 | JNIEXPORT void JNICALL Java_org_lttng_ust_agent_context_LttngContextApi_unregisterProvider(JNIEnv *env, | |
397 | jobject jobj, | |
398 | jlong provider_ref) | |
399 | { | |
400 | struct lttng_ust_context_provider *provider = | |
401 | (struct lttng_ust_context_provider*) (unsigned long) provider_ref; | |
402 | ||
403 | if (!provider) { | |
404 | return; | |
405 | } | |
406 | ||
407 | lttng_ust_context_provider_unregister(provider); | |
408 | ||
409 | free(provider->name); | |
410 | free(provider); | |
411 | } |