1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #ifndef	_DTRACE_JNI_H
28 #define	_DTRACE_JNI_H
29 
30 #include <libuutil.h>
31 #include <jni.h>
32 #include <dtrace.h>
33 #include <dtj_util.h>
34 
35 #ifdef	__cplusplus
36 extern "C" {
37 #endif
38 
39 /* Java DTrace API native library */
40 
41 
42 /*
43  * Thread-specific data key used to obtain JNI state specific to either the
44  * consumer loop (calls dtrace_work()) or the getAggregate() method (calls
45  * dtrace_aggregate_print()).
46  */
47 extern pthread_key_t g_dtj_consumer_key;
48 
49 typedef enum dtj_consumer_state {
50 	DTJ_CONSUMER_INIT,
51 	DTJ_CONSUMER_GO,
52 	DTJ_CONSUMER_START,
53 	DTJ_CONSUMER_STOP
54 } dtj_consumer_state_t;
55 
56 typedef struct dtj_error {
57 	int dtje_number;		/* dtrace_errno() */
58 	const char *dtje_message;	/* dtrace_errmsg() */
59 } dtj_error_t;
60 
61 /*
62  * Identifies which function should handle a request dequeued after
63  * dtrace_sleep().
64  */
65 typedef enum dtj_request_type {
66 	DTJ_REQUEST_OPTION		/* set DTrace runtime option */
67 } dtj_request_type_t;
68 
69 /*
70  * A request made from Java (by native method call) that is unsafe to process
71  * until just after the consumer loop wakes up from dtrace_sleep().
72  */
73 typedef struct dtj_request {
74 	dtj_request_type_t dtjr_type;	/* request handler ID */
75 	uu_list_t *dtjr_args;		/* string args to request handler */
76 	uu_list_node_t dtjr_node;	/* points to next and prev requests */
77 } dtj_request_t;
78 
79 typedef enum dtj_program_type {
80 	DTJ_PROGRAM_NONE,
81 	DTJ_PROGRAM_STRING,		/* dtrace_program_strcompile() */
82 	DTJ_PROGRAM_FILE		/* dtrace_program_fcompile() */
83 } dtj_program_type_t;
84 
85 /* Identifier and description of a compiled DTrace program */
86 typedef struct dtj_program {
87 	dtj_program_type_t dtjp_type;	/* string or file */
88 	const char *dtjp_name;		/* string or filename for err msg */
89 	dtrace_prog_t *dtjp_program;	/* libdtrace program handle */
90 	dtrace_proginfo_t dtjp_info;	/* program attributes */
91 	boolean_t dtjp_enabled;		/* dtrace_program_exec() flag */
92 	uu_list_node_t dtjp_node;	/* points to next and prev programs */
93 } dtj_program_t;
94 
95 /*
96  * An entry used to maintain the association between the value of an aggregating
97  * action (such as count()) and the aggregation to which the value belongs until
98  * all the data associated with a single tuple is available to the callback
99  * handler.
100  */
101 typedef struct dtj_aggval {
102 	jobject dtja_value;		/* value of aggregating action */
103 	const char *dtja_aggname;	/* aggregation name */
104 	int64_t dtja_aggid;		/* libdtrace aggregation ID */
105 	uu_list_node_t dtja_node;	/* points to next and prev aggvals */
106 } dtj_aggval_t;
107 
108 /*
109  * Per-consumer state, including the libdtrace consumer handle, is valid across
110  * multiple threads.  One consumer entry is added to a global table per
111  * dtrace_open().
112  */
113 typedef struct dtj_consumer {
114 	/* Consumer state */
115 
116 	dtrace_hdl_t *dtjc_dtp;		/* libdtrace consumer handle */
117 	uu_list_t *dtjc_program_list;	/* program_t list */
118 	uu_list_t *dtjc_process_list;	/* proc handle list */
119 
120 	/*
121 	 * Count of processes that have ended.  The consumer is stopped when
122 	 * this count equals the number of outstanding target processes and
123 	 * grabbed processes (see the Java Consumer createProcess() and
124 	 * grabProcess() methods).
125 	 */
126 	int dtjc_procs_ended;
127 
128 	/*
129 	 * Bit-field passed to dtrace_program_strcompile() and
130 	 * dtrace_program_fcompile() containing compile flags.  The flags are
131 	 * set from Java by the setOption() Consumer method (just like the
132 	 * runtime options handled by dtrace_setopt(), except that they must be
133 	 * set before program compilation to have any effect).
134 	 */
135 	uint_t dtjc_cflags;
136 
137 	boolean_t dtjc_flow;	/* current value of the flowindent option */
138 	dtj_consumer_state_t dtjc_state; /* execution state */
139 	boolean_t dtjc_interrupt;	/* flag that stops consumer */
140 
141 	/* Pending requests */
142 	uu_list_t *dtjc_request_list;	/* request_t queue */
143 	pthread_mutex_t dtjc_request_list_lock;
144 
145 
146 	/* Cached for optimization and for use across functions */
147 
148 	/*
149 	 * Nanosecond timestamp cached in the consumer loop just before
150 	 * dtrace_work().  The timestamp is applied to each Java PrintaRecord
151 	 * generated in that iteration of the consumer loop.  A value of zero
152 	 * indicates that we are not in the consumer loop, but that the
153 	 * callback was triggered instead by the Consumer getAggregate() method
154 	 * (from dtrace_aggregate_print()).
155 	 */
156 	hrtime_t dtjc_printa_snaptime;
157 
158 	/*
159 	 * The aggregation ID is used to optimize aggregation inclusion by
160 	 * testing for inclusion only when the aggregation has changed.
161 	 */
162 	int64_t dtjc_aggid;
163 	boolean_t dtjc_included;
164 
165 	/*
166 	 * The expected tuple member count is used to determine whether or not
167 	 * the aggregation tuple values are completely specified in the printa()
168 	 * format string.
169 	 */
170 	int dtjc_expected;
171 
172 	int dtjc_probedata_rec_i;	/* probe data record index */
173 
174 	/*
175 	 * The current DTrace action may apply across multiple libdtrace probe
176 	 * data records.
177 	 */
178 	dtrace_actkind_t dtjc_probedata_act;
179 
180 	/* Placeholder used when listing probes */
181 	dtrace_ecbdesc_t *dtjc_last_probe;
182 
183 	/* Function used by statement iterator when listing probes */
184 	dtrace_probe_f *dtjc_plistfunc;
185 } dtj_consumer_t;
186 
187 /*
188  * A view of a dtj_consumer_t that lasts only as long as a single native method
189  * call.  This view attaches state needed for interaction with Java and specific
190  * to the JNI.
191  */
192 typedef struct dtj_java_consumer {
193 	/* Per-consumer state in global consumer table */
194 	dtj_consumer_t *dtjj_consumer;
195 
196 	JNIEnv *dtjj_jenv;	/* Java environment pointer */
197 	jobject dtjj_caller;	/* Java Consumer to call back with probe data */
198 
199 	/*
200 	 * Java Object references used across function boundaries, valid only
201 	 * within the current native method call.
202 	 */
203 
204 	jobject dtjj_probedata;	/* instance of class ProbeData */
205 
206 	/*
207 	 * StringBuffer used to concatenate buffered printa() output associated
208 	 * with the current tuple.
209 	 */
210 	jobject dtjj_printa_buffer;
211 
212 	jobject dtjj_aggregate;	/* instance of class Aggregate */
213 	jobject dtjj_tuple;	/* instance of class Tuple */
214 
215 	/*
216 	 * AggregationValue instances cached until we receive the
217 	 * DTRACE_BUFDATA_AGGLAST flag indicating the last callback associated
218 	 * with the current tuple.
219 	 */
220 	uu_list_t *dtjj_aggval_list;
221 
222 	/* AggregateSpec used by get_aggregate() */
223 	jobject dtjj_aggregate_spec;
224 
225 	jobject dtjj_probelist;	/* java.util.List returned by listProbes() */
226 
227 	/*
228 	 * Exception temporarily cleared by callback handlers who cannot return
229 	 * a signal to abort the consumer.  At a safe point when the consumer
230 	 * loop gets control back from libdtrace, the exception is rethrown.
231 	 */
232 	jthrowable dtjj_exception;
233 
234 	jobject dtjj_consumer_lock; /* per-consumer lock */
235 
236 } dtj_java_consumer_t;
237 
238 /*
239  * Cache of jclass, jmethodID, and jfieldID values, usable across multiple
240  * native method calls and multiple threads.  Caching all of them up front
241  * rather than as-needed guarantees early detection of incorrect class, method,
242  * or field definitions, and eliminates the need for test cases to cover
243  * seldom-used definitions.
244  *
245  * Suffix conventions:
246  *   jc  java class
247  *   jm  java method
248  *   jsm java static method
249  *   jf  java field
250  *   jsf java static field
251  */
252 
253 /* LocalConsumer */
254 extern jclass g_caller_jc;
255 extern jmethodID g_gethandle_jm;
256 extern jmethodID g_sethandle_jm;
257 extern jmethodID g_pdatanext_jm;
258 extern jmethodID g_drop_jm;
259 extern jmethodID g_error_jm;
260 extern jmethodID g_proc_jm;
261 extern jmethodID g_interval_began_jm;
262 extern jmethodID g_interval_ended_jm;
263 extern jfieldID g_consumer_lock_jf;
264 
265 /* DTraceException */
266 extern jclass g_dtx_jc;
267 extern jmethodID g_dtxinit_jm;
268 
269 /* InterfaceAttributes */
270 extern jclass g_attr_jc;
271 extern jmethodID g_attrinit_jm;
272 extern jmethodID g_attrset_name_jm;
273 extern jmethodID g_attrset_data_jm;
274 extern jmethodID g_attrset_class_jm;
275 
276 /* ProbeDescription */
277 extern jclass g_probedesc_jc;
278 extern jmethodID g_probedescinit_jm;
279 extern jfieldID g_probedesc_id_jf;
280 
281 /* ProbeInfo */
282 extern jclass g_probeinfo_jc;
283 extern jmethodID g_probeinfoinit_jm;
284 
285 /* Probe */
286 extern jclass g_probe_jc;
287 extern jmethodID g_probeinit_jm;
288 
289 /* Program */
290 extern jclass g_program_jc;
291 extern jmethodID g_proginit_jm;
292 extern jfieldID g_progid_jf;
293 extern jfieldID g_proginfo_jf;
294 
295 /* Program.File */
296 extern jclass g_programfile_jc;
297 extern jmethodID g_fproginit_jm;
298 
299 /* ProgramInfo */
300 extern jclass g_proginfo_jc;
301 extern jmethodID g_proginfoinit_jm;
302 
303 /* Flow */
304 extern jclass g_flow_jc;
305 extern jmethodID g_flowinit_jm;
306 
307 /* ProbeData */
308 extern jclass g_pdata_jc;
309 extern jmethodID g_pdatainit_jm;
310 extern jmethodID g_pdataadd_jm;
311 extern jmethodID g_pdataadd_rec_jm;
312 extern jmethodID g_pdataadd_trace_jm;
313 extern jmethodID g_pdataadd_stack_jm;
314 extern jmethodID g_pdataadd_symbol_jm;
315 extern jmethodID g_pdataadd_printf_jm;
316 extern jmethodID g_pdataadd_printa_jm;
317 extern jmethodID g_pdatainvalidate_printa_jm;
318 extern jmethodID g_pdataadd_aggrec_jm;
319 extern jmethodID g_pdataadd_printa_str_jm;
320 extern jmethodID g_pdataadd_exit_jm;
321 extern jmethodID g_pdataattach_jm;
322 extern jmethodID g_pdataset_formatted_jm;
323 extern jmethodID g_pdataclear_jm;
324 
325 /* Drop */
326 extern jclass g_drop_jc;
327 extern jmethodID g_dropinit_jm;
328 
329 /* Error */
330 extern jclass g_error_jc;
331 extern jmethodID g_errinit_jm;
332 
333 /* ProcessState */
334 extern jclass g_process_jc;
335 extern jmethodID g_procinit_jm;
336 extern jmethodID g_procexit_jm;
337 
338 /* Aggregate */
339 extern jclass g_agg_jc;
340 extern jmethodID g_agginit_jm;
341 extern jmethodID g_aggaddrec_jm;
342 
343 /* AggregateSpec */
344 extern jclass g_aggspec_jc;
345 extern jmethodID g_aggspec_included_jm;
346 extern jmethodID g_aggspec_cleared_jm;
347 
348 /* Tuple */
349 extern jclass g_tuple_jc;
350 extern jmethodID g_tupleinit_jm;
351 extern jmethodID g_tupleadd_jm;
352 extern jmethodID g_tuplesize_jm;
353 extern jfieldID g_tuple_EMPTY_jsf;
354 
355 /* AggregationRecord */
356 extern jclass g_aggrec_jc;
357 extern jmethodID g_aggrecinit_jm;
358 extern jmethodID g_aggrecget_tuple_jm;
359 
360 /* SumValue */
361 extern jclass g_aggsum_jc;
362 extern jmethodID g_aggsuminit_jm;
363 
364 /* CountValue */
365 extern jclass g_aggcount_jc;
366 extern jmethodID g_aggcountinit_jm;
367 
368 /* AvgValue */
369 extern jclass g_aggavg_jc;
370 extern jmethodID g_aggavginit_jm;
371 
372 /* MinValue */
373 extern jclass g_aggmin_jc;
374 extern jmethodID g_aggmininit_jm;
375 
376 /* MaxValue */
377 extern jclass g_aggmax_jc;
378 extern jmethodID g_aggmaxinit_jm;
379 
380 /* StddevValue */
381 extern jclass g_aggstddev_jc;
382 extern jmethodID g_aggstddevinit_jm;
383 
384 /* KernelStackRecord */
385 extern jclass g_stack_jc;
386 extern jmethodID g_parsestack_jsm;
387 extern jmethodID g_stackinit_jm;
388 extern jmethodID g_stackset_frames_jm;
389 
390 /* UserStackRecord */
391 extern jclass g_ustack_jc;
392 extern jmethodID g_ustackinit_jm;
393 extern jmethodID g_ustackset_frames_jm;
394 
395 /* Distribution */
396 extern jclass g_adist_jc;
397 extern jmethodID g_dist_normal_jm;
398 
399 /* LogDistribution */
400 extern jclass g_dist_jc;
401 extern jmethodID g_distinit_jm;
402 
403 /* LogLinearDistribution */
404 extern jclass g_lldist_jc;
405 extern jmethodID g_lldistinit_jm;
406 
407 /* LinearDistribution */
408 extern jclass g_ldist_jc;
409 extern jmethodID g_ldistinit_jm;
410 
411 /* KernelSymbolRecord */
412 extern jclass g_symbol_jc;
413 extern jmethodID g_symbolinit_jm;
414 extern jmethodID g_symbolset_name_jm;
415 
416 /* UserSymbolRecord */
417 extern jclass g_usymbol_jc;
418 extern jmethodID g_usymbolinit_jm;
419 extern jmethodID g_usymbolset_name_jm;
420 
421 /* ScalarRecord */
422 extern jclass g_scalar_jc;
423 extern jmethodID g_scalarinit_jm;
424 
425 /*
426  * Populates the java class references and associated method and field IDs
427  * declared in this file (above).
428  *
429  * Throws NoClassDefFoundError, NoSuchMethodError, or NoSuchFieldError if any
430  * dtj_table_entry_t in dtj_jnitab.c is incorrect.
431  */
432 extern dtj_status_t dtj_load(JNIEnv *);
433 
434 /*
435  * Functions that create a structure return NULL if out of memory.  A Java
436  * OutOfMemoryError is pending in that case.
437  */
438 extern dtj_request_t *dtj_request_create(JNIEnv *, dtj_request_type_t, ...);
439 extern dtj_program_t *dtj_program_create(JNIEnv *, dtj_program_type_t,
440     const char *);
441 extern dtj_aggval_t *dtj_aggval_create(JNIEnv *, jobject, const char *,
442     int64_t);
443 
444 /*
445  * uu_list_t element destructors' signatures match uuwrap_value_destroy_f
446  */
447 extern void dtj_request_destroy(void *, void *); /* expects NULL user arg */
448 extern void dtj_program_destroy(void *, void *); /* expects NULL user arg */
449 extern void dtj_aggval_destroy(void *, void *);	/* expects JNIEnv * user arg */
450 
451 /* Allocates and frees per-consumer state kept in the global consumer table */
452 extern dtj_consumer_t *dtj_consumer_create(JNIEnv *);
453 extern void dtj_consumer_destroy(dtj_consumer_t *);
454 
455 /* Sets callback handlers before calling dtrace_go() */
456 extern dtj_status_t dtj_set_callback_handlers(dtj_java_consumer_t *);
457 
458 /*
459  * Initializes Java Object references cached across multiple functions called
460  * within the consumer loop.  Deletes the references after exiting the consumer
461  * loop.  It is only necessary to initialize and finalize a dtj_java_consumer_t
462  * if the native method call will enter the consumer loop.
463  */
464 extern dtj_status_t dtj_java_consumer_init(JNIEnv *, dtj_java_consumer_t *);
465 extern void dtj_java_consumer_fini(JNIEnv *, dtj_java_consumer_t *);
466 
467 /*
468  * Throws a DTraceException with a message constructed from the given format
469  * string and variable arg list.
470  */
471 extern void dtj_throw_dtrace_exception(dtj_java_consumer_t *,
472     const char *, ...);
473 
474 /* Returns NULL if pending Java Exception or OutOfMemoryError */
475 extern jobject dtj_new_probedesc(dtj_java_consumer_t *,
476     const dtrace_probedesc_t *);
477 extern jobject dtj_new_probeinfo(dtj_java_consumer_t *,
478     const dtrace_probeinfo_t *);
479 extern jobject dtj_new_attribute(dtj_java_consumer_t *,
480     const dtrace_attribute_t *);
481 
482 /*
483  * Returns NULL if the given fault is unrecognized, otherwise returns the name
484  * of the fault, guaranteed not to change across multiple versions of this API
485  * even if the integer value changes in libdtrace.
486  */
487 extern const char *dtj_get_fault_name(int);
488 
489 /* Gets the libdtrace error number and message */
490 extern dtj_status_t dtj_get_dtrace_error(dtj_java_consumer_t *, dtj_error_t *);
491 
492 /* Stops the DTrace consumer */
493 extern void dtj_stop(dtj_java_consumer_t *);
494 
495 /*
496  * The Consumer getAggregate() method runs in the caller's current thread
497  * separate from the consumer loop.
498  */
499 extern jobject dtj_get_aggregate(dtj_java_consumer_t *);
500 
501 /*
502  * A blocking call that runs the consumer loop.  If this function returns an
503  * error status, it is necessary to call stop() in order to dtrace_stop() the
504  * consumer in libdtrace (it is safe to call stop() in either case).
505  */
506 extern dtj_status_t dtj_consume(dtj_java_consumer_t *);
507 
508 #ifdef	__cplusplus
509 }
510 #endif
511 
512 #endif	/* _DTRACE_JNI_H */
513